Sort main members

This commit is contained in:
Gary Gregory 2023-11-03 11:25:53 -04:00
parent 972b3f218e
commit dc83c2820b
96 changed files with 20282 additions and 20282 deletions

View File

@ -64,6 +64,17 @@ public class AnnotationUtils {
setArrayEnd("]");
}
/**
* {@inheritDoc}
*/
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) {
if (value instanceof Annotation) {
value = AnnotationUtils.toString((Annotation) value);
}
super.appendDetail(buffer, fieldName, value);
}
/**
* {@inheritDoc}
*/
@ -76,27 +87,99 @@ public class AnnotationUtils {
// formatter:on
}
/**
* {@inheritDoc}
*/
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) {
if (value instanceof Annotation) {
value = AnnotationUtils.toString((Annotation) value);
}
super.appendDetail(buffer, fieldName, value);
}
};
/**
* {@link AnnotationUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used statically.
* Helper method for comparing two arrays of annotations.
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
* @param a1 the first array
* @param a2 the second array
* @return a flag whether these arrays are equal
*/
public AnnotationUtils() {
private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) {
if (a1.length != a2.length) {
return false;
}
for (int i = 0; i < a1.length; i++) {
if (!equals(a1[i], a2[i])) {
return false;
}
}
return true;
}
/**
* Helper method for comparing two objects of an array type.
*
* @param componentType the component type of the array
* @param o1 the first object
* @param o2 the second object
* @return a flag whether these objects are equal
*/
private static boolean arrayMemberEquals(final Class<?> componentType, final Object o1, final Object o2) {
if (componentType.isAnnotation()) {
return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
}
if (componentType.equals(Byte.TYPE)) {
return Arrays.equals((byte[]) o1, (byte[]) o2);
}
if (componentType.equals(Short.TYPE)) {
return Arrays.equals((short[]) o1, (short[]) o2);
}
if (componentType.equals(Integer.TYPE)) {
return Arrays.equals((int[]) o1, (int[]) o2);
}
if (componentType.equals(Character.TYPE)) {
return Arrays.equals((char[]) o1, (char[]) o2);
}
if (componentType.equals(Long.TYPE)) {
return Arrays.equals((long[]) o1, (long[]) o2);
}
if (componentType.equals(Float.TYPE)) {
return Arrays.equals((float[]) o1, (float[]) o2);
}
if (componentType.equals(Double.TYPE)) {
return Arrays.equals((double[]) o1, (double[]) o2);
}
if (componentType.equals(Boolean.TYPE)) {
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
}
return Arrays.equals((Object[]) o1, (Object[]) o2);
}
/**
* Helper method for generating a hash code for an array.
*
* @param componentType the component type of the array
* @param o the array
* @return a hash code for the specified array
*/
private static int arrayMemberHash(final Class<?> componentType, final Object o) {
if (componentType.equals(Byte.TYPE)) {
return Arrays.hashCode((byte[]) o);
}
if (componentType.equals(Short.TYPE)) {
return Arrays.hashCode((short[]) o);
}
if (componentType.equals(Integer.TYPE)) {
return Arrays.hashCode((int[]) o);
}
if (componentType.equals(Character.TYPE)) {
return Arrays.hashCode((char[]) o);
}
if (componentType.equals(Long.TYPE)) {
return Arrays.hashCode((long[]) o);
}
if (componentType.equals(Float.TYPE)) {
return Arrays.hashCode((float[]) o);
}
if (componentType.equals(Double.TYPE)) {
return Arrays.hashCode((double[]) o);
}
if (componentType.equals(Boolean.TYPE)) {
return Arrays.hashCode((boolean[]) o);
}
return Arrays.hashCode((Object[]) o);
}
/**
@ -170,27 +253,23 @@ public class AnnotationUtils {
return result;
}
//besides modularity, this has the advantage of autoboxing primitives:
/**
* Generate a string representation of an Annotation, as suggested by
* {@link Annotation#toString()}.
* Helper method for generating a hash code for a member of an annotation.
*
* @param a the annotation of which a string representation is desired
* @return the standard string representation of an annotation, not
* {@code null}
* @param name the name of the member
* @param value the value of the member
* @return a hash code for this member
*/
public static String toString(final Annotation a) {
final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE);
for (final Method m : a.annotationType().getDeclaredMethods()) {
if (m.getParameterTypes().length > 0) {
continue; // wtf?
}
try {
builder.append(m.getName(), m.invoke(a));
} catch (final ReflectiveOperationException ex) {
throw new UncheckedException(ex);
}
private static int hashMember(final String name, final Object value) {
final int part1 = name.hashCode() * 127;
if (ObjectUtils.isArray(value)) {
return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value);
}
return builder.build();
if (value instanceof Annotation) {
return part1 ^ hashCode((Annotation) value);
}
return part1 ^ value.hashCode();
}
/**
@ -215,25 +294,6 @@ public class AnnotationUtils {
|| String.class.equals(type) || Class.class.equals(type);
}
//besides modularity, this has the advantage of autoboxing primitives:
/**
* Helper method for generating a hash code for a member of an annotation.
*
* @param name the name of the member
* @param value the value of the member
* @return a hash code for this member
*/
private static int hashMember(final String name, final Object value) {
final int part1 = name.hashCode() * 127;
if (ObjectUtils.isArray(value)) {
return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value);
}
if (value instanceof Annotation) {
return part1 ^ hashCode((Annotation) value);
}
return part1 ^ value.hashCode();
}
/**
* Helper method for checking whether two objects of the given type are
* equal. This method is used to compare the parameters of two annotation
@ -261,95 +321,35 @@ public class AnnotationUtils {
}
/**
* Helper method for comparing two objects of an array type.
* Generate a string representation of an Annotation, as suggested by
* {@link Annotation#toString()}.
*
* @param componentType the component type of the array
* @param o1 the first object
* @param o2 the second object
* @return a flag whether these objects are equal
* @param a the annotation of which a string representation is desired
* @return the standard string representation of an annotation, not
* {@code null}
*/
private static boolean arrayMemberEquals(final Class<?> componentType, final Object o1, final Object o2) {
if (componentType.isAnnotation()) {
return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
}
if (componentType.equals(Byte.TYPE)) {
return Arrays.equals((byte[]) o1, (byte[]) o2);
}
if (componentType.equals(Short.TYPE)) {
return Arrays.equals((short[]) o1, (short[]) o2);
}
if (componentType.equals(Integer.TYPE)) {
return Arrays.equals((int[]) o1, (int[]) o2);
}
if (componentType.equals(Character.TYPE)) {
return Arrays.equals((char[]) o1, (char[]) o2);
}
if (componentType.equals(Long.TYPE)) {
return Arrays.equals((long[]) o1, (long[]) o2);
}
if (componentType.equals(Float.TYPE)) {
return Arrays.equals((float[]) o1, (float[]) o2);
}
if (componentType.equals(Double.TYPE)) {
return Arrays.equals((double[]) o1, (double[]) o2);
}
if (componentType.equals(Boolean.TYPE)) {
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
}
return Arrays.equals((Object[]) o1, (Object[]) o2);
}
/**
* Helper method for comparing two arrays of annotations.
*
* @param a1 the first array
* @param a2 the second array
* @return a flag whether these arrays are equal
*/
private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) {
if (a1.length != a2.length) {
return false;
}
for (int i = 0; i < a1.length; i++) {
if (!equals(a1[i], a2[i])) {
return false;
public static String toString(final Annotation a) {
final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE);
for (final Method m : a.annotationType().getDeclaredMethods()) {
if (m.getParameterTypes().length > 0) {
continue; // wtf?
}
try {
builder.append(m.getName(), m.invoke(a));
} catch (final ReflectiveOperationException ex) {
throw new UncheckedException(ex);
}
}
return true;
return builder.build();
}
/**
* Helper method for generating a hash code for an array.
* {@link AnnotationUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used statically.
*
* @param componentType the component type of the array
* @param o the array
* @return a hash code for the specified array
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
private static int arrayMemberHash(final Class<?> componentType, final Object o) {
if (componentType.equals(Byte.TYPE)) {
return Arrays.hashCode((byte[]) o);
}
if (componentType.equals(Short.TYPE)) {
return Arrays.hashCode((short[]) o);
}
if (componentType.equals(Integer.TYPE)) {
return Arrays.hashCode((int[]) o);
}
if (componentType.equals(Character.TYPE)) {
return Arrays.hashCode((char[]) o);
}
if (componentType.equals(Long.TYPE)) {
return Arrays.hashCode((long[]) o);
}
if (componentType.equals(Float.TYPE)) {
return Arrays.hashCode((float[]) o);
}
if (componentType.equals(Double.TYPE)) {
return Arrays.hashCode((double[]) o);
}
if (componentType.equals(Boolean.TYPE)) {
return Arrays.hashCode((boolean[]) o);
}
return Arrays.hashCode((Object[]) o);
public AnnotationUtils() {
}
}

View File

@ -39,44 +39,6 @@ public class ArchUtils {
init();
}
private static void init() {
init_X86_32Bit();
init_X86_64Bit();
init_IA64_32Bit();
init_IA64_64Bit();
init_PPC_32Bit();
init_PPC_64Bit();
init_Aarch_64Bit();
}
private static void init_Aarch_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.AARCH_64), "aarch64");
}
private static void init_X86_32Bit() {
addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.X86), "x86", "i386", "i486", "i586", "i686", "pentium");
}
private static void init_X86_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.X86), "x86_64", "amd64", "em64t", "universal");
}
private static void init_IA64_32Bit() {
addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.IA_64), "ia64_32", "ia64n");
}
private static void init_IA64_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.IA_64), "ia64", "ia64w");
}
private static void init_PPC_32Bit() {
addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.PPC), "ppc", "power", "powerpc", "power_pc", "power_rs");
}
private static void init_PPC_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.PPC), "ppc64", "power64", "powerpc64", "power_pc64", "power_rs64");
}
/**
* Adds the given {@link Processor} with the given key {@link String} to the map.
*
@ -126,4 +88,42 @@ public class ArchUtils {
return ARCH_TO_PROCESSOR.get(value);
}
private static void init() {
init_X86_32Bit();
init_X86_64Bit();
init_IA64_32Bit();
init_IA64_64Bit();
init_PPC_32Bit();
init_PPC_64Bit();
init_Aarch_64Bit();
}
private static void init_Aarch_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.AARCH_64), "aarch64");
}
private static void init_IA64_32Bit() {
addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.IA_64), "ia64_32", "ia64n");
}
private static void init_IA64_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.IA_64), "ia64", "ia64w");
}
private static void init_PPC_32Bit() {
addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.PPC), "ppc", "power", "powerpc", "power_pc", "power_rs");
}
private static void init_PPC_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.PPC), "ppc64", "power64", "powerpc64", "power_pc64", "power_rs64");
}
private static void init_X86_32Bit() {
addProcessors(new Processor(Processor.Arch.BIT_32, Processor.Type.X86), "x86", "i386", "i486", "i586", "i686", "pentium");
}
private static void init_X86_64Bit() {
addProcessors(new Processor(Processor.Arch.BIT_64, Processor.Type.X86), "x86_64", "amd64", "em64t", "universal");
}
}

View File

@ -88,39 +88,40 @@ public class BitField {
}
/**
* Obtains the value for the specified BitField, appropriately
* shifted right.
* Clears the bits.
*
* <p>Many users of a BitField will want to treat the specified
* bits as an int value, and will not want to be aware that the
* value is stored as a BitField (and so shifted left so many
* bits).</p>
*
* @see #setValue(int,int)
* @param holder the int data containing the bits we're interested
* in
* @return the selected bits, shifted right appropriately
* @param holder the int data containing the bits we're
* interested in
* @return the value of holder with the specified bits cleared
* (set to {@code 0})
*/
public int getValue(final int holder) {
return getRawValue(holder) >> shiftCount;
public int clear(final int holder) {
return holder & ~mask;
}
/**
* Obtains the value for the specified BitField, appropriately
* shifted right, as a short.
* Clears the bits.
*
* <p>Many users of a BitField will want to treat the specified
* bits as an int value, and will not want to be aware that the
* value is stored as a BitField (and so shifted left so many
* bits).</p>
* @param holder the byte data containing the bits we're
* interested in
*
* @return the value of holder with the specified bits cleared
* (set to {@code 0})
*/
public byte clearByte(final byte holder) {
return (byte) clear(holder);
}
/**
* Clears the bits.
*
* @see #setShortValue(short,short)
* @param holder the short data containing the bits we're
* interested in
* @return the selected bits, shifted right appropriately
* @return the value of holder with the specified bits cleared
* (set to {@code 0})
*/
public short getShortValue(final short holder) {
return (short) getValue(holder);
public short clearShort(final short holder) {
return (short) clear(holder);
}
/**
@ -146,20 +147,39 @@ public class BitField {
}
/**
* Returns whether the field is set or not.
* Obtains the value for the specified BitField, appropriately
* shifted right, as a short.
*
* <p>This is most commonly used for a single-bit field, which is
* often used to represent a boolean value; the results of using
* it for a multi-bit field is to determine whether *any* of its
* bits are set.</p>
* <p>Many users of a BitField will want to treat the specified
* bits as an int value, and will not want to be aware that the
* value is stored as a BitField (and so shifted left so many
* bits).</p>
*
* @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) {
return (short) getValue(holder);
}
/**
* Obtains the value for the specified BitField, appropriately
* shifted right.
*
* <p>Many users of a BitField will want to treat the specified
* bits as an int value, and will not want to be aware that the
* value is stored as a BitField (and so shifted left so many
* bits).</p>
*
* @see #setValue(int,int)
* @param holder the int data containing the bits we're interested
* in
* @return {@code true} if any of the bits are set,
* else {@code false}
* @return the selected bits, shifted right appropriately
*/
public boolean isSet(final int holder) {
return (holder & mask) != 0;
public int getValue(final int holder) {
return getRawValue(holder) >> shiftCount;
}
/**
@ -179,68 +199,20 @@ public class BitField {
}
/**
* Replaces the bits with new values.
* Returns whether the field is set or not.
*
* @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
* <p>This is most commonly used for a single-bit field, which is
* often used to represent a boolean value; the results of using
* it for a multi-bit field is to determine whether *any* of its
* bits are set.</p>
*
* @param holder the int data containing the bits we're interested
* in
* @return {@code true} if any of the bits are set,
* else {@code false}
*/
public int setValue(final int holder, final int value) {
return (holder & ~mask) | ((value << shiftCount) & mask);
}
/**
* Replaces the bits with new values.
*
* @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) {
return (short) setValue(holder, value);
}
/**
* Clears the bits.
*
* @param holder the int data containing the bits we're
* interested in
* @return the value of holder with the specified bits cleared
* (set to {@code 0})
*/
public int clear(final int holder) {
return holder & ~mask;
}
/**
* Clears the bits.
*
* @param holder the short data containing the bits we're
* interested in
* @return the value of holder with the specified bits cleared
* (set to {@code 0})
*/
public short clearShort(final short holder) {
return (short) clear(holder);
}
/**
* Clears the bits.
*
* @param holder the byte data containing the bits we're
* interested in
*
* @return the value of holder with the specified bits cleared
* (set to {@code 0})
*/
public byte clearByte(final byte holder) {
return (byte) clear(holder);
public boolean isSet(final int holder) {
return (holder & mask) != 0;
}
/**
@ -256,15 +228,16 @@ public class BitField {
}
/**
* Sets the bits.
* Sets a boolean BitField.
*
* @param holder the short data containing the bits we're
* @param holder the int data containing the bits we're
* interested in
* @return the value of holder with the specified bits set
* to {@code 1}
* @param flag indicating whether to set or clear the bits
* @return the value of holder with the specified bits set or
* cleared
*/
public short setShort(final short holder) {
return (short) set(holder);
public int setBoolean(final int holder, final boolean flag) {
return flag ? set(holder) : clear(holder);
}
/**
@ -283,14 +256,26 @@ public class BitField {
/**
* Sets a boolean BitField.
*
* @param holder the int data containing the bits we're
* @param holder the byte data containing the bits we're
* interested in
* @param flag indicating whether to set or clear the bits
* @return the value of holder with the specified bits set or
* cleared
* cleared
*/
public int setBoolean(final int holder, final boolean flag) {
return flag ? set(holder) : clear(holder);
public byte setByteBoolean(final byte holder, final boolean flag) {
return flag ? setByte(holder) : clearByte(holder);
}
/**
* Sets the bits.
*
* @param holder the short data containing the bits we're
* interested in
* @return the value of holder with the specified bits set
* to {@code 1}
*/
public short setShort(final short holder) {
return (short) set(holder);
}
/**
@ -307,16 +292,31 @@ public class BitField {
}
/**
* Sets a boolean BitField.
* Replaces the bits with new values.
*
* @param holder the byte data containing the bits we're
* @see #getShortValue(short)
* @param holder the short data containing the bits we're
* interested in
* @param flag indicating whether to set or clear the bits
* @return the value of holder with the specified bits set or
* cleared
* @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 byte setByteBoolean(final byte holder, final boolean flag) {
return flag ? setByte(holder) : clearByte(holder);
public short setShortValue(final short holder, final short value) {
return (short) setValue(holder, value);
}
/**
* Replaces the bits with new values.
*
* @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) {
return (holder & ~mask) | ((value << shiftCount) & mask);
}
}

View File

@ -33,6 +33,102 @@ import java.util.Objects;
// to depend on Range.
final class CharRange implements Iterable<Character>, Serializable {
/**
* Character {@link Iterator}.
* <p>#NotThreadSafe#</p>
*/
private static final class CharacterIterator implements Iterator<Character> {
/** The current character */
private char current;
private final CharRange range;
private boolean hasNext;
/**
* Constructs a new iterator for the character range.
*
* @param r The character range
*/
private CharacterIterator(final 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;
}
}
/**
* Has the iterator not reached the end character yet?
*
* @return {@code true} if the iterator has yet to reach the character date
*/
@Override
public boolean hasNext() {
return hasNext;
}
/**
* Returns the next character in the iteration
*
* @return {@link Character} for the next character
*/
@Override
public Character next() {
if (!hasNext) {
throw new NoSuchElementException();
}
final char cur = current;
prepareNext();
return Character.valueOf(cur);
}
/**
* Prepares 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;
}
}
/**
* Always throws UnsupportedOperationException.
*
* @throws UnsupportedOperationException Always thrown.
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* Required for serialization support. Lang version 2.0.
*
@ -40,6 +136,67 @@ final class CharRange implements Iterable<Character>, Serializable {
*/
private static final long serialVersionUID = 8270183163158333422L;
/** Empty array. */
static final CharRange[] EMPTY_ARRAY = {};
/**
* Constructs a {@link CharRange} over a single character.
*
* @param ch only character in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange is(final char ch) {
return new CharRange(ch, ch, false);
}
/**
* Constructs a {@link CharRange} over a set of characters.
*
* <p>If start and end are in the wrong order, they are reversed.
* Thus {@code a-e} is the same as {@code e-a}.</p>
*
* @param start first character, inclusive, in this range
* @param end last character, inclusive, in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange isIn(final char start, final char end) {
return new CharRange(start, end, false);
}
/**
* Constructs a negated {@link CharRange} over a single character.
*
* <p>A negated range includes everything except that defined by the
* single character.</p>
*
* @param ch only character in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange isNot(final char ch) {
return new CharRange(ch, ch, true);
}
/**
* Constructs a negated {@link CharRange} over a set of characters.
*
* <p>A negated range includes everything except that defined by the
* start and end characters.</p>
*
* <p>If start and end are in the wrong order, they are reversed.
* Thus {@code a-e} is the same as {@code e-a}.</p>
*
* @param start first character, inclusive, in this range
* @param end last character, inclusive, in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange isNotIn(final char start, final char end) {
return new CharRange(start, end, true);
}
/** The first character, inclusive, in the range. */
private final char start;
@ -52,9 +209,6 @@ final class CharRange implements Iterable<Character>, Serializable {
/** Cached toString. */
private transient String iToString;
/** Empty array. */
static final CharRange[] EMPTY_ARRAY = {};
/**
* Constructs a {@link CharRange} over a set of characters,
* optionally negating the range.
@ -81,95 +235,6 @@ final class CharRange implements Iterable<Character>, Serializable {
this.negated = negated;
}
/**
* Constructs a {@link CharRange} over a single character.
*
* @param ch only character in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange is(final char ch) {
return new CharRange(ch, ch, false);
}
/**
* Constructs a negated {@link CharRange} over a single character.
*
* <p>A negated range includes everything except that defined by the
* single character.</p>
*
* @param ch only character in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange isNot(final char ch) {
return new CharRange(ch, ch, true);
}
/**
* Constructs a {@link CharRange} over a set of characters.
*
* <p>If start and end are in the wrong order, they are reversed.
* Thus {@code a-e} is the same as {@code e-a}.</p>
*
* @param start first character, inclusive, in this range
* @param end last character, inclusive, in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange isIn(final char start, final char end) {
return new CharRange(start, end, false);
}
/**
* Constructs a negated {@link CharRange} over a set of characters.
*
* <p>A negated range includes everything except that defined by the
* start and end characters.</p>
*
* <p>If start and end are in the wrong order, they are reversed.
* Thus {@code a-e} is the same as {@code e-a}.</p>
*
* @param start first character, inclusive, in this range
* @param end last character, inclusive, in this range
* @return the new CharRange object
* @since 2.5
*/
public static CharRange isNotIn(final char start, final char end) {
return new CharRange(start, end, true);
}
// Accessors
/**
* Gets the start character for this character range.
*
* @return the start char (inclusive)
*/
public char getStart() {
return this.start;
}
/**
* Gets the end character for this character range.
*
* @return the end char (inclusive)
*/
public char getEnd() {
return this.end;
}
/**
* Is this {@link CharRange} negated.
*
* <p>A negated range includes everything except that defined by the
* start and end characters.</p>
*
* @return {@code true} if negated
*/
public boolean isNegated() {
return negated;
}
// Contains
/**
* Is the character specified contained in this range.
@ -223,6 +288,25 @@ final class CharRange implements Iterable<Character>, Serializable {
return start == other.start && end == other.end && negated == other.negated;
}
/**
* Gets the end character for this character range.
*
* @return the end char (inclusive)
*/
public char getEnd() {
return this.end;
}
// Accessors
/**
* Gets the start character for this character range.
*
* @return the start char (inclusive)
*/
public char getStart() {
return this.start;
}
/**
* Gets a hashCode compatible with the equals method.
*
@ -233,6 +317,30 @@ final class CharRange implements Iterable<Character>, Serializable {
return 83 + start + 7 * end + (negated ? 1 : 0);
}
/**
* Is this {@link CharRange} negated.
*
* <p>A negated range includes everything except that defined by the
* start and end characters.</p>
*
* @return {@code true} if negated
*/
public boolean isNegated() {
return negated;
}
/**
* Returns an iterator which can be used to walk through the characters described by this range.
*
* <p>#NotThreadSafe# the iterator is not thread-safe</p>
* @return an iterator to the chars represented by this range
* @since 2.5
*/
@Override
public Iterator<Character> iterator() {
return new CharacterIterator(this);
}
/**
* Gets a string representation of the character range.
*
@ -254,112 +362,4 @@ final class CharRange implements Iterable<Character>, Serializable {
}
return iToString;
}
/**
* Returns an iterator which can be used to walk through the characters described by this range.
*
* <p>#NotThreadSafe# the iterator is not thread-safe</p>
* @return an iterator to the chars represented by this range
* @since 2.5
*/
@Override
public Iterator<Character> iterator() {
return new CharacterIterator(this);
}
/**
* Character {@link Iterator}.
* <p>#NotThreadSafe#</p>
*/
private static final class CharacterIterator implements Iterator<Character> {
/** The current character */
private char current;
private final CharRange range;
private boolean hasNext;
/**
* Constructs a new iterator for the character range.
*
* @param r The character range
*/
private CharacterIterator(final 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;
}
}
/**
* Prepares 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 {@code true} if the iterator has yet to reach the character date
*/
@Override
public boolean hasNext() {
return hasNext;
}
/**
* Returns the next character in the iteration
*
* @return {@link Character} for the next character
*/
@Override
public Character next() {
if (!hasNext) {
throw new NoSuchElementException();
}
final char cur = current;
prepareNext();
return Character.valueOf(cur);
}
/**
* Always throws UnsupportedOperationException.
*
* @throws UnsupportedOperationException Always thrown.
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}

View File

@ -88,9 +88,6 @@ public class CharSet implements Serializable {
COMMON.put("0-9", ASCII_NUMERIC);
}
/** The set of CharRange objects. */
private final Set<CharRange> set = Collections.synchronizedSet(new HashSet<>());
/**
* Factory method to create a new CharSet using a special syntax.
*
@ -165,6 +162,9 @@ public class CharSet implements Serializable {
return new CharSet(setStrs);
}
/** The set of CharRange objects. */
private final Set<CharRange> set = Collections.synchronizedSet(new HashSet<>());
/**
* Constructs a new CharSet using the set syntax.
* Each string is merged in with the set.
@ -210,18 +210,6 @@ public class CharSet implements Serializable {
}
}
/**
* Gets the internal set as an array of CharRange objects.
*
* @return an array of immutable CharRange objects
* @since 2.0
*/
// NOTE: This is no longer public as CharRange is no longer a public class.
// It may be replaced when CharSet moves to Range.
/*public*/ CharRange[] getCharRanges() {
return set.toArray(CharRange.EMPTY_ARRAY);
}
/**
* Does the {@link CharSet} contain the specified
* character {@code ch}.
@ -259,6 +247,18 @@ public class CharSet implements Serializable {
return set.equals(other.set);
}
/**
* Gets the internal set as an array of CharRange objects.
*
* @return an array of immutable CharRange objects
* @since 2.0
*/
// NOTE: This is no longer public as CharRange is no longer a public class.
// It may be replaced when CharSet moves to Range.
/*public*/ CharRange[] getCharRanges() {
return set.toArray(CharRange.EMPTY_ARRAY);
}
/**
* Gets a hash code compatible with the equals method.
*

View File

@ -64,54 +64,169 @@ public class CharUtils {
}
/**
* {@link CharUtils} instances should NOT be constructed in standard programming.
* Instead, the class should be used as {@code CharUtils.toString('c');}.
* Compares two {@code char} values numerically. This is the same functionality as provided in Java 7.
*
* <p>This constructor is public to permit tools that require a JavaBean instance
* to operate.</p>
* @param x the first {@code char} to compare
* @param y the second {@code char} to compare
* @return the value {@code 0} if {@code x == y};
* a value less than {@code 0} if {@code x < y}; and
* a value greater than {@code 0} if {@code x > y}
* @since 3.4
*/
public CharUtils() {
public static int compare(final char x, final char y) {
return x - y;
}
/**
* Converts the character to a Character.
*
* <p>For ASCII 7 bit characters, this uses a cache that will return the
* same Character object each time.</p>
* Checks whether the character is ASCII 7 bit.
*
* <pre>
* CharUtils.toCharacterObject(' ') = ' '
* CharUtils.toCharacterObject('A') = 'A'
* CharUtils.isAscii('a') = true
* CharUtils.isAscii('A') = true
* CharUtils.isAscii('3') = true
* CharUtils.isAscii('-') = true
* CharUtils.isAscii('\n') = true
* CharUtils.isAscii('&copy;') = false
* </pre>
*
* @deprecated Java 5 introduced {@link Character#valueOf(char)} which caches chars 0 through 127.
* @param ch the character to convert
* @return a Character of the specified character
* @param ch the character to check
* @return true if less than 128
*/
@Deprecated
public static Character toCharacterObject(final char ch) {
return Character.valueOf(ch);
public static boolean isAscii(final char ch) {
return ch < 128;
}
/**
* Converts the String to a Character using the first character, returning
* null for empty Strings.
*
* <p>For ASCII 7 bit characters, this uses a cache that will return the
* same Character object each time.</p>
* Checks whether the character is ASCII 7 bit alphabetic.
*
* <pre>
* CharUtils.toCharacterObject(null) = null
* CharUtils.toCharacterObject("") = null
* CharUtils.toCharacterObject("A") = 'A'
* CharUtils.toCharacterObject("BA") = 'B'
* CharUtils.isAsciiAlpha('a') = true
* CharUtils.isAsciiAlpha('A') = true
* CharUtils.isAsciiAlpha('3') = false
* CharUtils.isAsciiAlpha('-') = false
* CharUtils.isAsciiAlpha('\n') = false
* CharUtils.isAsciiAlpha('&copy;') = false
* </pre>
*
* @param str the character to convert
* @return the Character value of the first letter of the String
* @param ch the character to check
* @return true if between 65 and 90 or 97 and 122 inclusive
*/
public static Character toCharacterObject(final String str) {
return StringUtils.isEmpty(str) ? null : Character.valueOf(str.charAt(0));
public static boolean isAsciiAlpha(final char ch) {
return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch);
}
/**
* Checks whether the character is ASCII 7 bit alphabetic lower case.
*
* <pre>
* CharUtils.isAsciiAlphaLower('a') = true
* CharUtils.isAsciiAlphaLower('A') = false
* CharUtils.isAsciiAlphaLower('3') = false
* CharUtils.isAsciiAlphaLower('-') = false
* CharUtils.isAsciiAlphaLower('\n') = false
* CharUtils.isAsciiAlphaLower('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 97 and 122 inclusive
*/
public static boolean isAsciiAlphaLower(final char ch) {
return ch >= 'a' && ch <= 'z';
}
/**
* Checks whether the character is ASCII 7 bit numeric.
*
* <pre>
* CharUtils.isAsciiAlphanumeric('a') = true
* CharUtils.isAsciiAlphanumeric('A') = true
* CharUtils.isAsciiAlphanumeric('3') = true
* CharUtils.isAsciiAlphanumeric('-') = false
* CharUtils.isAsciiAlphanumeric('\n') = false
* CharUtils.isAsciiAlphanumeric('&copy;') = false
* </pre>
*
* @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(final char ch) {
return isAsciiAlpha(ch) || isAsciiNumeric(ch);
}
/**
* Checks whether the character is ASCII 7 bit alphabetic upper case.
*
* <pre>
* CharUtils.isAsciiAlphaUpper('a') = false
* CharUtils.isAsciiAlphaUpper('A') = true
* CharUtils.isAsciiAlphaUpper('3') = false
* CharUtils.isAsciiAlphaUpper('-') = false
* CharUtils.isAsciiAlphaUpper('\n') = false
* CharUtils.isAsciiAlphaUpper('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 65 and 90 inclusive
*/
public static boolean isAsciiAlphaUpper(final char ch) {
return ch >= 'A' && ch <= 'Z';
}
/**
* Checks whether the character is ASCII 7 bit control.
*
* <pre>
* CharUtils.isAsciiControl('a') = false
* CharUtils.isAsciiControl('A') = false
* CharUtils.isAsciiControl('3') = false
* CharUtils.isAsciiControl('-') = false
* CharUtils.isAsciiControl('\n') = true
* CharUtils.isAsciiControl('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if less than 32 or equals 127
*/
public static boolean isAsciiControl(final char ch) {
return ch < 32 || ch == 127;
}
/**
* Checks whether the character is ASCII 7 bit numeric.
*
* <pre>
* CharUtils.isAsciiNumeric('a') = false
* CharUtils.isAsciiNumeric('A') = false
* CharUtils.isAsciiNumeric('3') = true
* CharUtils.isAsciiNumeric('-') = false
* CharUtils.isAsciiNumeric('\n') = false
* CharUtils.isAsciiNumeric('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 48 and 57 inclusive
*/
public static boolean isAsciiNumeric(final char ch) {
return ch >= '0' && ch <= '9';
}
/**
* Checks whether the character is ASCII 7 bit printable.
*
* <pre>
* CharUtils.isAsciiPrintable('a') = true
* CharUtils.isAsciiPrintable('A') = true
* CharUtils.isAsciiPrintable('3') = true
* CharUtils.isAsciiPrintable('-') = true
* CharUtils.isAsciiPrintable('\n') = false
* CharUtils.isAsciiPrintable('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 32 and 126 inclusive
*/
public static boolean isAsciiPrintable(final char ch) {
return ch >= 32 && ch < 127;
}
/**
@ -188,6 +303,47 @@ public class CharUtils {
return StringUtils.isEmpty(str) ? defaultValue : str.charAt(0);
}
/**
* Converts the character to a Character.
*
* <p>For ASCII 7 bit characters, this uses a cache that will return the
* same Character object each time.</p>
*
* <pre>
* CharUtils.toCharacterObject(' ') = ' '
* CharUtils.toCharacterObject('A') = 'A'
* </pre>
*
* @deprecated Java 5 introduced {@link Character#valueOf(char)} which caches chars 0 through 127.
* @param ch the character to convert
* @return a Character of the specified character
*/
@Deprecated
public static Character toCharacterObject(final char ch) {
return Character.valueOf(ch);
}
/**
* Converts the String to a Character using the first character, returning
* null for empty Strings.
*
* <p>For ASCII 7 bit characters, this uses a cache that will return the
* same Character object each time.</p>
*
* <pre>
* CharUtils.toCharacterObject(null) = null
* CharUtils.toCharacterObject("") = null
* CharUtils.toCharacterObject("A") = 'A'
* CharUtils.toCharacterObject("BA") = 'B'
* </pre>
*
* @param str the character to convert
* @return the Character value of the first letter of the String
*/
public static Character toCharacterObject(final String str) {
return StringUtils.isEmpty(str) ? null : Character.valueOf(str.charAt(0));
}
/**
* Converts the character to the Integer it represents, throwing an
* exception if the character is not numeric.
@ -354,168 +510,12 @@ public class CharUtils {
}
/**
* Checks whether the character is ASCII 7 bit.
* {@link CharUtils} instances should NOT be constructed in standard programming.
* Instead, the class should be used as {@code CharUtils.toString('c');}.
*
* <pre>
* CharUtils.isAscii('a') = true
* CharUtils.isAscii('A') = true
* CharUtils.isAscii('3') = true
* CharUtils.isAscii('-') = true
* CharUtils.isAscii('\n') = true
* CharUtils.isAscii('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if less than 128
* <p>This constructor is public to permit tools that require a JavaBean instance
* to operate.</p>
*/
public static boolean isAscii(final char ch) {
return ch < 128;
}
/**
* Checks whether the character is ASCII 7 bit printable.
*
* <pre>
* CharUtils.isAsciiPrintable('a') = true
* CharUtils.isAsciiPrintable('A') = true
* CharUtils.isAsciiPrintable('3') = true
* CharUtils.isAsciiPrintable('-') = true
* CharUtils.isAsciiPrintable('\n') = false
* CharUtils.isAsciiPrintable('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 32 and 126 inclusive
*/
public static boolean isAsciiPrintable(final char ch) {
return ch >= 32 && ch < 127;
}
/**
* Checks whether the character is ASCII 7 bit control.
*
* <pre>
* CharUtils.isAsciiControl('a') = false
* CharUtils.isAsciiControl('A') = false
* CharUtils.isAsciiControl('3') = false
* CharUtils.isAsciiControl('-') = false
* CharUtils.isAsciiControl('\n') = true
* CharUtils.isAsciiControl('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if less than 32 or equals 127
*/
public static boolean isAsciiControl(final char ch) {
return ch < 32 || ch == 127;
}
/**
* Checks whether the character is ASCII 7 bit alphabetic.
*
* <pre>
* CharUtils.isAsciiAlpha('a') = true
* CharUtils.isAsciiAlpha('A') = true
* CharUtils.isAsciiAlpha('3') = false
* CharUtils.isAsciiAlpha('-') = false
* CharUtils.isAsciiAlpha('\n') = false
* CharUtils.isAsciiAlpha('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 65 and 90 or 97 and 122 inclusive
*/
public static boolean isAsciiAlpha(final char ch) {
return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch);
}
/**
* Checks whether the character is ASCII 7 bit alphabetic upper case.
*
* <pre>
* CharUtils.isAsciiAlphaUpper('a') = false
* CharUtils.isAsciiAlphaUpper('A') = true
* CharUtils.isAsciiAlphaUpper('3') = false
* CharUtils.isAsciiAlphaUpper('-') = false
* CharUtils.isAsciiAlphaUpper('\n') = false
* CharUtils.isAsciiAlphaUpper('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 65 and 90 inclusive
*/
public static boolean isAsciiAlphaUpper(final char ch) {
return ch >= 'A' && ch <= 'Z';
}
/**
* Checks whether the character is ASCII 7 bit alphabetic lower case.
*
* <pre>
* CharUtils.isAsciiAlphaLower('a') = true
* CharUtils.isAsciiAlphaLower('A') = false
* CharUtils.isAsciiAlphaLower('3') = false
* CharUtils.isAsciiAlphaLower('-') = false
* CharUtils.isAsciiAlphaLower('\n') = false
* CharUtils.isAsciiAlphaLower('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 97 and 122 inclusive
*/
public static boolean isAsciiAlphaLower(final char ch) {
return ch >= 'a' && ch <= 'z';
}
/**
* Checks whether the character is ASCII 7 bit numeric.
*
* <pre>
* CharUtils.isAsciiNumeric('a') = false
* CharUtils.isAsciiNumeric('A') = false
* CharUtils.isAsciiNumeric('3') = true
* CharUtils.isAsciiNumeric('-') = false
* CharUtils.isAsciiNumeric('\n') = false
* CharUtils.isAsciiNumeric('&copy;') = false
* </pre>
*
* @param ch the character to check
* @return true if between 48 and 57 inclusive
*/
public static boolean isAsciiNumeric(final char ch) {
return ch >= '0' && ch <= '9';
}
/**
* Checks whether the character is ASCII 7 bit numeric.
*
* <pre>
* CharUtils.isAsciiAlphanumeric('a') = true
* CharUtils.isAsciiAlphanumeric('A') = true
* CharUtils.isAsciiAlphanumeric('3') = true
* CharUtils.isAsciiAlphanumeric('-') = false
* CharUtils.isAsciiAlphanumeric('\n') = false
* CharUtils.isAsciiAlphanumeric('&copy;') = false
* </pre>
*
* @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(final char ch) {
return isAsciiAlpha(ch) || isAsciiNumeric(ch);
}
/**
* Compares two {@code char} values numerically. This is the same functionality as provided in Java 7.
*
* @param x the first {@code char} to compare
* @param y the second {@code char} to compare
* @return the value {@code 0} if {@code x == y};
* a value less than {@code 0} if {@code x < y}; and
* a value greater than {@code 0} if {@code x > y}
* @since 3.4
*/
public static int compare(final char x, final char y) {
return x - y;
public CharUtils() {
}
}

View File

@ -1408,15 +1408,6 @@ public class ClassUtils {
return cls != null && cls.getEnclosingClass() != null;
}
/**
* Tests whether a {@link Class} is public.
* @param cls Class to test.
* @return {@code true} if {@code cls} is public.
* @since 3.13.0
*/
public static boolean isPublic(final Class<?> cls) {
return Modifier.isPublic(cls.getModifiers());
}
/**
* Returns whether the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte},
* {@link Character}, {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
@ -1432,7 +1423,6 @@ public class ClassUtils {
}
return type.isPrimitive() || isPrimitiveWrapper(type);
}
/**
* Returns whether the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character},
* {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
@ -1446,6 +1436,16 @@ public class ClassUtils {
return wrapperPrimitiveMap.containsKey(type);
}
/**
* Tests whether a {@link Class} is public.
* @param cls Class to test.
* @return {@code true} if {@code cls} is public.
* @since 3.13.0
*/
public static boolean isPublic(final Class<?> cls) {
return Modifier.isPublic(cls.getModifiers());
}
/**
* Converts the specified array of primitive Class objects to an array of its corresponding wrapper Class objects.
*

File diff suppressed because it is too large Load Diff

View File

@ -176,68 +176,6 @@ public enum JavaVersion {
*/
JAVA_RECENT(maxVersion(), Float.toString(maxVersion()));
/**
* The float value.
*/
private final float value;
/**
* The standard name.
*/
private final String name;
/**
* Constructor.
*
* @param value the float value
* @param name the standard name, not null
*/
JavaVersion(final float value, final String name) {
this.value = value;
this.name = name;
}
/**
* Whether this version of Java is at least the version of Java passed in.
*
* <p>For example:<br>
* {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}</p>
*
* @param requiredVersion the version to check against, not null
* @return true if this version is equal to or greater than the specified version
*/
public boolean atLeast(final JavaVersion requiredVersion) {
return this.value >= requiredVersion.value;
}
/**
* Whether this version of Java is at most the version of Java passed in.
*
* <p>For example:<br>
* {@code myVersion.atMost(JavaVersion.JAVA_1_4)}</p>
*
* @param requiredVersion the version to check against, not null
* @return true if this version is equal to or greater than the specified version
* @since 3.9
*/
public boolean atMost(final JavaVersion requiredVersion) {
return this.value <= requiredVersion.value;
}
/**
* Transforms the given string with a Java version number to the
* corresponding constant of this enumeration class. This method is used
* internally.
*
* @param versionStr the Java version as string
* @return the corresponding enumeration constant or <b>null</b> if the
* version is unknown
*/
// helper for static importing
static JavaVersion getJavaVersion(final String versionStr) {
return get(versionStr);
}
/**
* Transforms the given string with a Java version number to the
* corresponding constant of this enumeration class. This method is used
@ -312,15 +250,17 @@ public enum JavaVersion {
}
/**
* The string value is overridden to return the standard name.
* Transforms the given string with a Java version number to the
* corresponding constant of this enumeration class. This method is used
* internally.
*
* <p>For example, {@code "1.5"}.</p>
*
* @return the name, not null
* @param versionStr the Java version as string
* @return the corresponding enumeration constant or <b>null</b> if the
* version is unknown
*/
@Override
public String toString() {
return name;
// helper for static importing
static JavaVersion getJavaVersion(final String versionStr) {
return get(versionStr);
}
/**
@ -350,4 +290,64 @@ public enum JavaVersion {
}
return defaultReturnValue;
}
/**
* The float value.
*/
private final float value;
/**
* The standard name.
*/
private final String name;
/**
* Constructor.
*
* @param value the float value
* @param name the standard name, not null
*/
JavaVersion(final float value, final String name) {
this.value = value;
this.name = name;
}
/**
* Whether this version of Java is at least the version of Java passed in.
*
* <p>For example:<br>
* {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}</p>
*
* @param requiredVersion the version to check against, not null
* @return true if this version is equal to or greater than the specified version
*/
public boolean atLeast(final JavaVersion requiredVersion) {
return this.value >= requiredVersion.value;
}
/**
* Whether this version of Java is at most the version of Java passed in.
*
* <p>For example:<br>
* {@code myVersion.atMost(JavaVersion.JAVA_1_4)}</p>
*
* @param requiredVersion the version to check against, not null
* @return true if this version is equal to or greater than the specified version
* @since 3.9
*/
public boolean atMost(final JavaVersion requiredVersion) {
return this.value <= requiredVersion.value;
}
/**
* The string value is overridden to return the standard name.
*
* <p>For example, {@code "1.5"}.</p>
*
* @return the name, not null
*/
@Override
public String toString() {
return name;
}
}

View File

@ -39,10 +39,6 @@ import java.util.stream.Collectors;
*/
public class LocaleUtils {
private static final char UNDERSCORE = '_';
private static final String UNDETERMINED = "und";
private static final char DASH = '-';
// class to avoid synchronization (Init on demand)
static class SyncAvoid {
/** Unmodifiable list of available locales. */
@ -56,6 +52,10 @@ public class LocaleUtils {
AVAILABLE_LOCALE_SET = Collections.unmodifiableSet(new HashSet<>(list));
}
}
private static final char UNDERSCORE = '_';
private static final String UNDETERMINED = "und";
private static final char DASH = '-';
/** Concurrent map of language locales by country. */
private static final ConcurrentMap<String, List<Locale>> cLanguagesByCountry =

View File

@ -69,11 +69,13 @@ public class NotImplementedException extends UnsupportedOperationException {
/**
* Constructs a NotImplementedException.
*
* @param cause cause of the exception
* @param message description of the exception
* @param code code indicating a resource for more information regarding the lack of implementation
* @since 3.2
*/
public NotImplementedException(final Throwable cause) {
this(cause, null);
public NotImplementedException(final String message, final String code) {
super(message);
this.code = code;
}
/**
@ -91,14 +93,25 @@ public class NotImplementedException extends UnsupportedOperationException {
* Constructs a NotImplementedException.
*
* @param message description of the exception
* @param cause cause of the exception
* @param code code indicating a resource for more information regarding the lack of implementation
* @since 3.2
*/
public NotImplementedException(final String message, final String code) {
super(message);
public NotImplementedException(final String message, final Throwable cause, final String code) {
super(message, cause);
this.code = code;
}
/**
* Constructs a NotImplementedException.
*
* @param cause cause of the exception
* @since 3.2
*/
public NotImplementedException(final Throwable cause) {
this(cause, null);
}
/**
* Constructs a NotImplementedException.
*
@ -111,19 +124,6 @@ public class NotImplementedException extends UnsupportedOperationException {
this.code = code;
}
/**
* Constructs a NotImplementedException.
*
* @param message description of the exception
* @param cause cause of the exception
* @param code code indicating a resource for more information regarding the lack of implementation
* @since 3.2
*/
public NotImplementedException(final String message, final Throwable cause, final String code) {
super(message, cause);
this.code = code;
}
/**
* Obtain the not implemented code. This is an unformatted piece of text intended to point to
* further information regarding the lack of implementation. It might, for example, be an issue

View File

@ -1333,6 +1333,30 @@ public class ObjectUtils {
return obj == null ? nullStr : obj.toString();
}
/**
* Gets the {@code toString} of an {@link Supplier}'s {@link Supplier#get()} returning
* a specified text if {@code null} input.
*
* <pre>
* ObjectUtils.toString(() -&gt; obj, () -&gt; expensive())
* </pre>
* <pre>
* ObjectUtils.toString(() -&gt; null, () -&gt; expensive()) = result of expensive()
* ObjectUtils.toString(() -&gt; null, () -&gt; expensive()) = result of expensive()
* ObjectUtils.toString(() -&gt; "", () -&gt; expensive()) = ""
* ObjectUtils.toString(() -&gt; "bat", () -&gt; expensive()) = "bat"
* ObjectUtils.toString(() -&gt; Boolean.TRUE, () -&gt; expensive()) = "true"
* </pre>
*
* @param obj the Object to {@code toString}, may be null
* @param supplier the Supplier of String used on {@code null} input, may be null
* @return the passed in Object's toString, or {@code nullStr} if {@code null} input
* @since 3.14.0
*/
public static String toString(final Supplier<Object> obj, final Supplier<String> supplier) {
return obj == null ? Suppliers.get(supplier) : toString(obj.get(), supplier);
}
/**
* Gets the {@code toString} of an {@link Object} returning
* a specified text if {@code null} input.
@ -1358,30 +1382,6 @@ public class ObjectUtils {
return obj == null ? Suppliers.get(supplier) : obj.toString();
}
/**
* Gets the {@code toString} of an {@link Supplier}'s {@link Supplier#get()} returning
* a specified text if {@code null} input.
*
* <pre>
* ObjectUtils.toString(() -&gt; obj, () -&gt; expensive())
* </pre>
* <pre>
* ObjectUtils.toString(() -&gt; null, () -&gt; expensive()) = result of expensive()
* ObjectUtils.toString(() -&gt; null, () -&gt; expensive()) = result of expensive()
* ObjectUtils.toString(() -&gt; "", () -&gt; expensive()) = ""
* ObjectUtils.toString(() -&gt; "bat", () -&gt; expensive()) = "bat"
* ObjectUtils.toString(() -&gt; Boolean.TRUE, () -&gt; expensive()) = "true"
* </pre>
*
* @param obj the Object to {@code toString}, may be null
* @param supplier the Supplier of String used on {@code null} input, may be null
* @return the passed in Object's toString, or {@code nullStr} if {@code null} input
* @since 3.14.0
*/
public static String toString(final Supplier<Object> obj, final Supplier<String> supplier) {
return obj == null ? Suppliers.get(supplier) : toString(obj.get(), supplier);
}
/**
* Calls {@link Object#wait(long, int)} for the given Duration.
*

View File

@ -50,17 +50,6 @@ public class SerializationException extends RuntimeException {
super(msg);
}
/**
* Constructs a new {@link SerializationException} with specified
* nested {@link Throwable}.
*
* @param cause The {@link Exception} or {@link Error}
* that caused this exception to be thrown.
*/
public SerializationException(final Throwable cause) {
super(cause);
}
/**
* Constructs a new {@link SerializationException} with specified
* detail message and nested {@link Throwable}.
@ -73,4 +62,15 @@ public class SerializationException extends RuntimeException {
super(msg, cause);
}
/**
* Constructs a new {@link SerializationException} with specified
* nested {@link Throwable}.
*
* @param cause The {@link Exception} or {@link Error}
* that caused this exception to be thrown.
*/
public SerializationException(final Throwable cause) {
super(cause);
}
}

View File

@ -66,6 +66,55 @@ import org.apache.commons.lang3.Functions.FailablePredicate;
@Deprecated
public class Streams {
/**
* A Collector type for arrays.
*
* @param <O> The array type.
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams.ArrayCollector}.
*/
@Deprecated
public static class ArrayCollector<O> implements Collector<O, List<O>, O[]> {
private static final Set<Characteristics> characteristics = Collections.emptySet();
private final Class<O> elementType;
/**
* Constructs a new instance for the given element type.
*
* @param elementType The element type.
*/
public ArrayCollector(final Class<O> elementType) {
this.elementType = elementType;
}
@Override
public BiConsumer<List<O>, O> accumulator() {
return List::add;
}
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
@Override
public BinaryOperator<List<O>> combiner() {
return (left, right) -> {
left.addAll(right);
return left;
};
}
@Override
public Function<List<O>, O[]> finisher() {
return list -> list.toArray(ArrayUtils.newInstance(elementType, list.size()));
}
@Override
public Supplier<List<O>> supplier() {
return ArrayList::new;
}
}
/**
* A reduced, and simplified version of a {@link Stream} with
* failable method signatures.
@ -86,6 +135,58 @@ public class Streams {
this.stream = stream;
}
/**
* Returns whether all elements of this stream match the provided predicate.
* May not evaluate the predicate on all elements if not necessary for
* determining the result. If the stream is empty then {@code true} is
* returned and the predicate is not evaluated.
*
* <p>
* This is a short-circuiting terminal operation.
* </p>
*
* <p>
* Note
* This method evaluates the <em>universal quantification</em> of the
* predicate over the elements of the stream (for all x P(x)). If the
* stream is empty, the quantification is said to be <em>vacuously
* satisfied</em> and is always {@code true} (regardless of P(x)).
* </p>
*
* @param predicate A non-interfering, stateless predicate to apply to
* elements of this stream
* @return {@code true} If either all elements of the stream match the
* provided predicate or the stream is empty, otherwise {@code false}.
*/
public boolean allMatch(final FailablePredicate<O, ?> predicate) {
assertNotTerminated();
return stream().allMatch(Functions.asPredicate(predicate));
}
/**
* Returns whether any elements of this stream match the provided
* predicate. May not evaluate the predicate on all elements if not
* necessary for determining the result. If the stream is empty then
* {@code false} is returned and the predicate is not evaluated.
*
* <p>
* This is a short-circuiting terminal operation.
* </p>
*
* Note
* This method evaluates the <em>existential quantification</em> of the
* predicate over the elements of the stream (for some x P(x)).
*
* @param predicate A non-interfering, stateless predicate to apply to
* elements of this stream
* @return {@code true} if any elements of the stream match the provided
* predicate, otherwise {@code false}
*/
public boolean anyMatch(final FailablePredicate<O, ?> predicate) {
assertNotTerminated();
return stream().anyMatch(Functions.asPredicate(predicate));
}
/**
* Throws IllegalStateException if this stream is already terminated.
*
@ -97,58 +198,6 @@ public class Streams {
}
}
/**
* Marks this stream as terminated.
*
* @throws IllegalStateException if this stream is already terminated.
*/
protected void makeTerminated() {
assertNotTerminated();
terminated = true;
}
/**
* Returns a FailableStream consisting of the elements of this stream that match
* the given FailablePredicate.
*
* <p>
* This is an intermediate operation.
* </p>
*
* @param predicate a non-interfering, stateless predicate to apply to each
* element to determine if it should be included.
* @return the new stream
*/
public FailableStream<O> filter(final FailablePredicate<O, ?> predicate){
assertNotTerminated();
stream = stream.filter(Functions.asPredicate(predicate));
return this;
}
/**
* Performs an action for each element of this stream.
*
* <p>
* This is an intermediate operation.
* </p>
*
* <p>
* The behavior of this operation is explicitly nondeterministic.
* For parallel stream pipelines, this operation does <em>not</em>
* guarantee to respect the encounter order of the stream, as doing so
* would sacrifice the benefit of parallelism. For any given element, the
* action may be performed at whatever time and in whatever thread the
* library chooses. If the action accesses shared state, it is
* responsible for providing the required synchronization.
* </p>
*
* @param action a non-interfering action to perform on the elements
*/
public void forEach(final FailableConsumer<O, ?> action) {
makeTerminated();
stream().forEach(Functions.asConsumer(action));
}
/**
* Performs a mutable reduction operation on the elements of this stream using a
* {@link Collector}. A {@link Collector}
@ -271,6 +320,75 @@ public class Streams {
return stream().collect(supplier, accumulator, combiner);
}
/**
* Returns a FailableStream consisting of the elements of this stream that match
* the given FailablePredicate.
*
* <p>
* This is an intermediate operation.
* </p>
*
* @param predicate a non-interfering, stateless predicate to apply to each
* element to determine if it should be included.
* @return the new stream
*/
public FailableStream<O> filter(final FailablePredicate<O, ?> predicate){
assertNotTerminated();
stream = stream.filter(Functions.asPredicate(predicate));
return this;
}
/**
* Performs an action for each element of this stream.
*
* <p>
* This is an intermediate operation.
* </p>
*
* <p>
* The behavior of this operation is explicitly nondeterministic.
* For parallel stream pipelines, this operation does <em>not</em>
* guarantee to respect the encounter order of the stream, as doing so
* would sacrifice the benefit of parallelism. For any given element, the
* action may be performed at whatever time and in whatever thread the
* library chooses. If the action accesses shared state, it is
* responsible for providing the required synchronization.
* </p>
*
* @param action a non-interfering action to perform on the elements
*/
public void forEach(final FailableConsumer<O, ?> action) {
makeTerminated();
stream().forEach(Functions.asConsumer(action));
}
/**
* Marks this stream as terminated.
*
* @throws IllegalStateException if this stream is already terminated.
*/
protected void makeTerminated() {
assertNotTerminated();
terminated = true;
}
/**
* Returns a stream consisting of the results of applying the given
* function to the elements of this stream.
*
* <p>
* This is an intermediate operation.
* </p>
*
* @param <R> The element type of the new stream
* @param mapper A non-interfering, stateless function to apply to each element
* @return the new stream
*/
public <R> FailableStream<R> map(final FailableFunction<O, R, ?> mapper) {
assertNotTerminated();
return new FailableStream<>(stream.map(Functions.asFunction(mapper)));
}
/**
* Performs a reduction on the elements of this stream, using the provided
* identity value and an associative accumulation function, and returns
@ -325,23 +443,6 @@ public class Streams {
return stream().reduce(identity, accumulator);
}
/**
* Returns a stream consisting of the results of applying the given
* function to the elements of this stream.
*
* <p>
* This is an intermediate operation.
* </p>
*
* @param <R> The element type of the new stream
* @param mapper A non-interfering, stateless function to apply to each element
* @return the new stream
*/
public <R> FailableStream<R> map(final FailableFunction<O, R, ?> mapper) {
assertNotTerminated();
return new FailableStream<>(stream.map(Functions.asFunction(mapper)));
}
/**
* Converts the FailableStream into an equivalent stream.
* @return A stream, which will return the same elements, which this FailableStream would return.
@ -349,100 +450,6 @@ public class Streams {
public Stream<O> stream() {
return stream;
}
/**
* Returns whether all elements of this stream match the provided predicate.
* May not evaluate the predicate on all elements if not necessary for
* determining the result. If the stream is empty then {@code true} is
* returned and the predicate is not evaluated.
*
* <p>
* This is a short-circuiting terminal operation.
* </p>
*
* <p>
* Note
* This method evaluates the <em>universal quantification</em> of the
* predicate over the elements of the stream (for all x P(x)). If the
* stream is empty, the quantification is said to be <em>vacuously
* satisfied</em> and is always {@code true} (regardless of P(x)).
* </p>
*
* @param predicate A non-interfering, stateless predicate to apply to
* elements of this stream
* @return {@code true} If either all elements of the stream match the
* provided predicate or the stream is empty, otherwise {@code false}.
*/
public boolean allMatch(final FailablePredicate<O, ?> predicate) {
assertNotTerminated();
return stream().allMatch(Functions.asPredicate(predicate));
}
/**
* Returns whether any elements of this stream match the provided
* predicate. May not evaluate the predicate on all elements if not
* necessary for determining the result. If the stream is empty then
* {@code false} is returned and the predicate is not evaluated.
*
* <p>
* This is a short-circuiting terminal operation.
* </p>
*
* Note
* This method evaluates the <em>existential quantification</em> of the
* predicate over the elements of the stream (for some x P(x)).
*
* @param predicate A non-interfering, stateless predicate to apply to
* elements of this stream
* @return {@code true} if any elements of the stream match the provided
* predicate, otherwise {@code false}
*/
public boolean anyMatch(final FailablePredicate<O, ?> predicate) {
assertNotTerminated();
return stream().anyMatch(Functions.asPredicate(predicate));
}
}
/**
* Converts the given {@link Stream stream} into a {@link FailableStream}.
* This is basically a simplified, reduced version of the {@link Stream}
* class, with the same underlying element stream, except that failable
* objects, like {@link FailablePredicate}, {@link FailableFunction}, or
* {@link FailableConsumer} may be applied, instead of
* {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
* to rewrite a code snippet like this:
* <pre>
* final List&lt;O&gt; list;
* final Method m;
* final Function&lt;O,String&gt; mapper = (o) -&gt; {
* try {
* return (String) m.invoke(o);
* } catch (Throwable t) {
* throw Functions.rethrow(t);
* }
* };
* final List&lt;String&gt; strList = list.stream()
* .map(mapper).collect(Collectors.toList());
* </pre>
* as follows:
* <pre>
* final List&lt;O&gt; list;
* final Method m;
* final List&lt;String&gt; strList = Functions.stream(list.stream())
* .map((o) -&gt; (String) m.invoke(o)).collect(Collectors.toList());
* </pre>
* While the second version may not be <em>quite</em> as
* efficient (because it depends on the creation of additional,
* intermediate objects, of type FailableStream), it is much more
* concise, and readable, and meets the spirit of Lambdas better
* than the first version.
* @param <O> The streams element type.
* @param stream The stream, which is being converted.
* @return The {@link FailableStream}, which has been created by
* converting the stream.
*/
public static <O> FailableStream<O> stream(final Stream<O> stream) {
return new FailableStream<>(stream);
}
/**
@ -488,52 +495,45 @@ public class Streams {
}
/**
* A Collector type for arrays.
*
* @param <O> The array type.
* @deprecated Use {@link org.apache.commons.lang3.stream.Streams.ArrayCollector}.
* Converts the given {@link Stream stream} into a {@link FailableStream}.
* This is basically a simplified, reduced version of the {@link Stream}
* class, with the same underlying element stream, except that failable
* objects, like {@link FailablePredicate}, {@link FailableFunction}, or
* {@link FailableConsumer} may be applied, instead of
* {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
* to rewrite a code snippet like this:
* <pre>
* final List&lt;O&gt; list;
* final Method m;
* final Function&lt;O,String&gt; mapper = (o) -&gt; {
* try {
* return (String) m.invoke(o);
* } catch (Throwable t) {
* throw Functions.rethrow(t);
* }
* };
* final List&lt;String&gt; strList = list.stream()
* .map(mapper).collect(Collectors.toList());
* </pre>
* as follows:
* <pre>
* final List&lt;O&gt; list;
* final Method m;
* final List&lt;String&gt; strList = Functions.stream(list.stream())
* .map((o) -&gt; (String) m.invoke(o)).collect(Collectors.toList());
* </pre>
* While the second version may not be <em>quite</em> as
* efficient (because it depends on the creation of additional,
* intermediate objects, of type FailableStream), it is much more
* concise, and readable, and meets the spirit of Lambdas better
* than the first version.
* @param <O> The streams element type.
* @param stream The stream, which is being converted.
* @return The {@link FailableStream}, which has been created by
* converting the stream.
*/
@Deprecated
public static class ArrayCollector<O> implements Collector<O, List<O>, O[]> {
private static final Set<Characteristics> characteristics = Collections.emptySet();
private final Class<O> elementType;
/**
* Constructs a new instance for the given element type.
*
* @param elementType The element type.
*/
public ArrayCollector(final Class<O> elementType) {
this.elementType = elementType;
}
@Override
public Supplier<List<O>> supplier() {
return ArrayList::new;
}
@Override
public BiConsumer<List<O>, O> accumulator() {
return List::add;
}
@Override
public BinaryOperator<List<O>> combiner() {
return (left, right) -> {
left.addAll(right);
return left;
};
}
@Override
public Function<List<O>, O[]> finisher() {
return list -> list.toArray(ArrayUtils.newInstance(elementType, list.size()));
}
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
public static <O> FailableStream<O> stream(final Stream<O> stream) {
return new FailableStream<>(stream);
}
/**

View File

@ -45,6 +45,66 @@ public class StringEscapeUtils {
/* ESCAPE TRANSLATORS */
// TODO: Create a parent class - 'SinglePassTranslator' ?
// It would handle the index checking + length returning,
// and could also have an optimization check method.
static class CsvEscaper extends CharSequenceTranslator {
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 = { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF };
@Override
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
if (index != 0) {
throw new IllegalStateException("CsvEscaper should never reach the [1] index");
}
if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) {
out.write(input.toString());
} else {
out.write(CSV_QUOTE);
out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR));
out.write(CSV_QUOTE);
}
return Character.codePointCount(input, 0, input.length());
}
}
static class CsvUnescaper extends CharSequenceTranslator {
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 = {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF};
@Override
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
if (index != 0) {
throw new IllegalStateException("CsvUnescaper should never reach the [1] index");
}
if ( input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE ) {
out.write(input.toString());
return Character.codePointCount(input, 0, input.length());
}
// strip quotes
final String quoteless = input.subSequence(1, input.length() - 1).toString();
if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) {
// deal with escaped quotes; ie) ""
out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR));
} else {
out.write(input.toString());
}
return Character.codePointCount(input, 0, input.length());
}
}
/**
* Translator object for escaping Java.
*
@ -236,6 +296,8 @@ public class StringEscapeUtils {
new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE())
);
/* UNESCAPE TRANSLATORS */
/**
* Translator object for escaping individual Comma Separated Values.
*
@ -247,36 +309,6 @@ public class StringEscapeUtils {
*/
public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper();
// TODO: Create a parent class - 'SinglePassTranslator' ?
// It would handle the index checking + length returning,
// and could also have an optimization check method.
static class CsvEscaper extends CharSequenceTranslator {
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 = { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF };
@Override
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
if (index != 0) {
throw new IllegalStateException("CsvEscaper should never reach the [1] index");
}
if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) {
out.write(input.toString());
} else {
out.write(CSV_QUOTE);
out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR));
out.write(CSV_QUOTE);
}
return Character.codePointCount(input, 0, input.length());
}
}
/* UNESCAPE TRANSLATORS */
/**
* Translator object for unescaping escaped Java.
*
@ -383,75 +415,30 @@ public class StringEscapeUtils {
*/
public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper();
static class CsvUnescaper extends CharSequenceTranslator {
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 = {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF};
@Override
public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
if (index != 0) {
throw new IllegalStateException("CsvUnescaper should never reach the [1] index");
}
if ( input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE ) {
out.write(input.toString());
return Character.codePointCount(input, 0, input.length());
}
// strip quotes
final String quoteless = input.subSequence(1, input.length() - 1).toString();
if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) {
// deal with escaped quotes; ie) ""
out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR));
} else {
out.write(input.toString());
}
return Character.codePointCount(input, 0, input.length());
}
}
/* Helper functions */
/**
* {@link StringEscapeUtils} instances should NOT be constructed in
* standard programming.
* Returns a {@link String} value for a CSV column enclosed in double quotes,
* if required.
*
* <p>Instead, the class should be used as:</p>
* <pre>StringEscapeUtils.escapeJava("foo");</pre>
* <p>If the value contains a comma, newline or double quote, then the
* String value is returned enclosed in double quotes.</p>
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
* <p>Any double quote characters in the value are escaped with another double quote.</p>
*
* <p>If the value does not contain a comma, newline or double quote, then the
* String value is returned unchanged.</p>
*
* see <a href="https://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
* <a href="https://datatracker.ietf.org/doc/html/rfc4180">RFC 4180</a>.
*
* @param input 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, {@code null} if null string input
* @since 2.4
*/
public StringEscapeUtils() {
}
/**
* Escapes the characters in a {@link String} using Java String rules.
*
* <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
*
* <p>So a tab becomes the characters {@code '\\'} and
* {@code 't'}.</p>
*
* <p>The only difference between Java strings and JavaScript strings
* is that in JavaScript, a single quote and forward-slash (/) are escaped.</p>
*
* <p>Example:</p>
* <pre>
* input string: He didn't say, "Stop!"
* output string: He didn't say, \"Stop!\"
* </pre>
*
* @param input String to escape values in, may be null
* @return String with escaped values, {@code null} if null string input
*/
public static final String escapeJava(final String input) {
return ESCAPE_JAVA.translate(input);
public static final String escapeCsv(final String input) {
return ESCAPE_CSV.translate(input);
}
/**
@ -483,78 +470,16 @@ public class StringEscapeUtils {
}
/**
* Escapes the characters in a {@link String} using Json String rules.
* <p>Escapes any values it finds into their Json String form.
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
* Escapes the characters in a {@link String} using HTML entities.
* <p>Supports only the HTML 3.0 entities.</p>
*
* <p>So a tab becomes the characters {@code '\\'} and
* {@code 't'}.</p>
*
* <p>The only difference between Java strings and Json strings
* is that in Json, forward-slash (/) is escaped.</p>
*
* <p>See https://www.ietf.org/rfc/rfc4627.txt for further details.</p>
*
* <p>Example:</p>
* <pre>
* input string: He didn't say, "Stop!"
* output string: He didn't say, \"Stop!\"
* </pre>
*
* @param input String to escape values in, may be null
* @return String with escaped values, {@code null} if null string input
*
* @since 3.2
*/
public static final String escapeJson(final String input) {
return ESCAPE_JSON.translate(input);
}
/**
* Unescapes any Java literals found in the {@link String}.
* For example, it will turn a sequence of {@code '\'} and
* {@code 'n'} into a newline character, unless the {@code '\'}
* is preceded by another {@code '\'}.
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
*/
public static final String unescapeJava(final String input) {
return UNESCAPE_JAVA.translate(input);
}
/**
* Unescapes any EcmaScript literals found in the {@link String}.
*
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
* into a newline character, unless the {@code '\'} is preceded by another
* {@code '\'}.</p>
*
* @see #unescapeJava(String)
* @param input the {@link String} to unescape, may be null
* @return A new unescaped {@link String}, {@code null} if null string input
* @param input the {@link String} to escape, may be null
* @return a new escaped {@link String}, {@code null} if null string input
*
* @since 3.0
*/
public static final String unescapeEcmaScript(final String input) {
return UNESCAPE_ECMASCRIPT.translate(input);
}
/**
* Unescapes any Json literals found in the {@link String}.
*
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
* into a newline character, unless the {@code '\'} is preceded by another
* {@code '\'}.</p>
*
* @see #unescapeJava(String)
* @param input the {@link String} to unescape, may be null
* @return A new unescaped {@link String}, {@code null} if null string input
*
* @since 3.2
*/
public static final String unescapeJson(final String input) {
return UNESCAPE_JSON.translate(input);
public static final String escapeHtml3(final String input) {
return ESCAPE_HTML3.translate(input);
}
/**
@ -589,51 +514,55 @@ public class StringEscapeUtils {
}
/**
* Escapes the characters in a {@link String} using HTML entities.
* <p>Supports only the HTML 3.0 entities.</p>
* Escapes the characters in a {@link String} using Java String rules.
*
* @param input the {@link String} to escape, may be null
* @return a new escaped {@link String}, {@code null} if null string input
* <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
*
* @since 3.0
* <p>So a tab becomes the characters {@code '\\'} and
* {@code 't'}.</p>
*
* <p>The only difference between Java strings and JavaScript strings
* is that in JavaScript, a single quote and forward-slash (/) are escaped.</p>
*
* <p>Example:</p>
* <pre>
* input string: He didn't say, "Stop!"
* output string: He didn't say, \"Stop!\"
* </pre>
*
* @param input String to escape values in, may be null
* @return String with escaped values, {@code null} if null string input
*/
public static final String escapeHtml3(final String input) {
return ESCAPE_HTML3.translate(input);
public static final String escapeJava(final String input) {
return ESCAPE_JAVA.translate(input);
}
/**
* Unescapes a string containing entity escapes to a string
* containing the actual Unicode characters corresponding to the
* escapes. Supports HTML 4.0 entities.
* Escapes the characters in a {@link String} using Json String rules.
* <p>Escapes any values it finds into their Json String form.
* Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
*
* <p>For example, the string {@code "&lt;Fran&ccedil;ais&gt;"}
* will become {@code "<Français>"}</p>
* <p>So a tab becomes the characters {@code '\\'} and
* {@code 't'}.</p>
*
* <p>If an entity is unrecognized, it is left alone, and inserted
* verbatim into the result string. e.g. {@code "&gt;&zzzz;x"} will
* become {@code ">&zzzz;x"}.</p>
* <p>The only difference between Java strings and Json strings
* is that in Json, forward-slash (/) is escaped.</p>
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
* <p>See https://www.ietf.org/rfc/rfc4627.txt for further details.</p>
*
* @since 3.0
* <p>Example:</p>
* <pre>
* input string: He didn't say, "Stop!"
* output string: He didn't say, \"Stop!\"
* </pre>
*
* @param input String to escape values in, may be null
* @return String with escaped values, {@code null} if null string input
*
* @since 3.2
*/
public static final String unescapeHtml4(final String input) {
return UNESCAPE_HTML4.translate(input);
}
/**
* Unescapes a string containing entity escapes to a string
* containing the actual Unicode characters corresponding to the
* escapes. Supports only HTML 3.0 entities.
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
*
* @since 3.0
*/
public static final String unescapeHtml3(final String input) {
return UNESCAPE_HTML3.translate(input);
public static final String escapeJson(final String input) {
return ESCAPE_JSON.translate(input);
}
/**
@ -723,52 +652,6 @@ public class StringEscapeUtils {
return ESCAPE_XML11.translate(input);
}
/**
* Unescapes a string containing XML entity escapes to a string
* containing the actual Unicode characters corresponding to the
* escapes.
*
* <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
* Does not support DTDs or external entities.</p>
*
* <p>Note that numerical \\u Unicode codes are unescaped to their respective
* Unicode characters. This may change in future releases.</p>
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
* @see #escapeXml(String)
* @see #escapeXml10(String)
* @see #escapeXml11(String)
*/
public static final String unescapeXml(final String input) {
return UNESCAPE_XML.translate(input);
}
/**
* Returns a {@link String} value for a CSV column enclosed in double quotes,
* if required.
*
* <p>If the value contains a comma, newline or double quote, then the
* String value is returned enclosed in double quotes.</p>
*
* <p>Any double quote characters in the value are escaped with another double quote.</p>
*
* <p>If the value does not contain a comma, newline or double quote, then the
* String value is returned unchanged.</p>
*
* see <a href="https://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
* <a href="https://datatracker.ietf.org/doc/html/rfc4180">RFC 4180</a>.
*
* @param input 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, {@code null} if null string input
* @since 2.4
*/
public static final String escapeCsv(final String input) {
return ESCAPE_CSV.translate(input);
}
/**
* Returns a {@link String} value for an unescaped CSV column.
*
@ -794,4 +677,121 @@ public class StringEscapeUtils {
return UNESCAPE_CSV.translate(input);
}
/**
* Unescapes any EcmaScript literals found in the {@link String}.
*
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
* into a newline character, unless the {@code '\'} is preceded by another
* {@code '\'}.</p>
*
* @see #unescapeJava(String)
* @param input the {@link String} to unescape, may be null
* @return A new unescaped {@link String}, {@code null} if null string input
*
* @since 3.0
*/
public static final String unescapeEcmaScript(final String input) {
return UNESCAPE_ECMASCRIPT.translate(input);
}
/**
* Unescapes a string containing entity escapes to a string
* containing the actual Unicode characters corresponding to the
* escapes. Supports only HTML 3.0 entities.
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
*
* @since 3.0
*/
public static final String unescapeHtml3(final String input) {
return UNESCAPE_HTML3.translate(input);
}
/**
* Unescapes a string containing entity escapes to a string
* containing the actual Unicode characters corresponding to the
* escapes. Supports HTML 4.0 entities.
*
* <p>For example, the string {@code "&lt;Fran&ccedil;ais&gt;"}
* will become {@code "<Français>"}</p>
*
* <p>If an entity is unrecognized, it is left alone, and inserted
* verbatim into the result string. e.g. {@code "&gt;&zzzz;x"} will
* become {@code ">&zzzz;x"}.</p>
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
*
* @since 3.0
*/
public static final String unescapeHtml4(final String input) {
return UNESCAPE_HTML4.translate(input);
}
/**
* Unescapes any Java literals found in the {@link String}.
* For example, it will turn a sequence of {@code '\'} and
* {@code 'n'} into a newline character, unless the {@code '\'}
* is preceded by another {@code '\'}.
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
*/
public static final String unescapeJava(final String input) {
return UNESCAPE_JAVA.translate(input);
}
/**
* Unescapes any Json literals found in the {@link String}.
*
* <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
* into a newline character, unless the {@code '\'} is preceded by another
* {@code '\'}.</p>
*
* @see #unescapeJava(String)
* @param input the {@link String} to unescape, may be null
* @return A new unescaped {@link String}, {@code null} if null string input
*
* @since 3.2
*/
public static final String unescapeJson(final String input) {
return UNESCAPE_JSON.translate(input);
}
/**
* Unescapes a string containing XML entity escapes to a string
* containing the actual Unicode characters corresponding to the
* escapes.
*
* <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
* Does not support DTDs or external entities.</p>
*
* <p>Note that numerical \\u Unicode codes are unescaped to their respective
* Unicode characters. This may change in future releases.</p>
*
* @param input the {@link String} to unescape, may be null
* @return a new unescaped {@link String}, {@code null} if null string input
* @see #escapeXml(String)
* @see #escapeXml10(String)
* @see #escapeXml11(String)
*/
public static final String unescapeXml(final String input) {
return UNESCAPE_XML.translate(input);
}
/**
* {@link StringEscapeUtils} instances should NOT be constructed in
* standard programming.
*
* <p>Instead, the class should be used as:</p>
* <pre>StringEscapeUtils.escapeJava("foo");</pre>
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public StringEscapeUtils() {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -60,6 +60,15 @@ public abstract class Diff<T> extends Pair<T, T> {
this.fieldName = fieldName;
}
/**
* Gets the name of the field.
*
* @return the field name
*/
public final String getFieldName() {
return fieldName;
}
/**
* Gets the type of the field.
*
@ -70,12 +79,15 @@ public abstract class Diff<T> extends Pair<T, T> {
}
/**
* Gets the name of the field.
* Throws {@link UnsupportedOperationException}.
*
* @return the field name
* @param value
* ignored
* @return nothing
*/
public final String getFieldName() {
return fieldName;
@Override
public final T setValue(final T value) {
throw new UnsupportedOperationException("Cannot alter Diff object.");
}
/**
@ -92,16 +104,4 @@ public abstract class Diff<T> extends Pair<T, T> {
public final String toString() {
return String.format("[%s: %s, %s]", fieldName, getLeft(), getRight());
}
/**
* Throws {@link UnsupportedOperationException}.
*
* @param value
* ignored
* @return nothing
*/
@Override
public final T setValue(final T value) {
throw new UnsupportedOperationException("Cannot alter Diff object.");
}
}

View File

@ -76,6 +76,36 @@ public class DiffBuilder<T> implements Builder<DiffResult<T>> {
private final T right;
private final ToStringStyle style;
/**
* Constructs a builder for the specified objects with the specified style.
*
* <p>
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
* not evaluate any calls to {@code append(...)} and will return an empty
* {@link DiffResult} when {@link #build()} is executed.
* </p>
*
* <p>
* This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)}
* with the testTriviallyEqual flag enabled.
* </p>
*
* @param lhs
* {@code this} object
* @param rhs
* the object to diff against
* @param style
* the style will use when outputting the objects, {@code null}
* uses the default
* @throws NullPointerException
* if {@code lhs} or {@code rhs} is {@code null}
*/
public DiffBuilder(final T lhs, final T rhs,
final ToStringStyle style) {
this(lhs, rhs, style, true);
}
/**
* Constructs a builder for the specified objects with the specified style.
*
@ -117,36 +147,6 @@ public class DiffBuilder<T> implements Builder<DiffResult<T>> {
this.objectsTriviallyEqual = testTriviallyEqual && Objects.equals(lhs, rhs);
}
/**
* Constructs a builder for the specified objects with the specified style.
*
* <p>
* If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
* not evaluate any calls to {@code append(...)} and will return an empty
* {@link DiffResult} when {@link #build()} is executed.
* </p>
*
* <p>
* This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)}
* with the testTriviallyEqual flag enabled.
* </p>
*
* @param lhs
* {@code this} object
* @param rhs
* the object to diff against
* @param style
* the style will use when outputting the objects, {@code null}
* uses the default
* @throws NullPointerException
* if {@code lhs} or {@code rhs} is {@code null}
*/
public DiffBuilder(final T lhs, final T rhs,
final ToStringStyle style) {
this(lhs, rhs, style, true);
}
/**
* Test if two {@code boolean}s are equal.
*
@ -373,6 +373,49 @@ public class DiffBuilder<T> implements Builder<DiffResult<T>> {
return this;
}
/**
* Append diffs from another {@link DiffResult}.
*
* <p>
* This method is useful if you want to compare properties which are
* themselves Diffable and would like to know which specific part of
* it is different.
* </p>
*
* <pre>
* public class Person implements Diffable&lt;Person&gt; {
* String name;
* Address address; // implements Diffable&lt;Address&gt;
*
* ...
*
* public DiffResult diff(Person obj) {
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
* .append("name", this.name, obj.name)
* .append("address", this.address.diff(obj.address))
* .build();
* }
* }
* </pre>
*
* @param fieldName
* the field name
* @param diffResult
* the {@link DiffResult} to append
* @return this
* @throws NullPointerException if field name is {@code null} or diffResult is {@code null}
* @since 3.5
*/
public DiffBuilder<T> append(final String fieldName, final DiffResult<T> diffResult) {
validateFieldNameNotNull(fieldName);
Objects.requireNonNull(diffResult, "diffResult");
if (objectsTriviallyEqual) {
return this;
}
diffResult.getDiffs().forEach(diff -> append(fieldName + "." + diff.getFieldName(), diff.getLeft(), diff.getRight()));
return this;
}
/**
* Test if two {@code double}s are equal.
*
@ -677,82 +720,6 @@ public class DiffBuilder<T> implements Builder<DiffResult<T>> {
return this;
}
/**
* Test if two {@code short}s are equal.
*
* @param fieldName
* the field name
* @param lhs
* the left-hand {@code short}
* @param rhs
* the right-hand {@code short}
* @return this
* @throws NullPointerException
* if field name is {@code null}
*/
public DiffBuilder<T> append(final String fieldName, final short lhs,
final short rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (lhs != rhs) {
diffs.add(new Diff<Short>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Short getLeft() {
return Short.valueOf(lhs);
}
@Override
public Short getRight() {
return Short.valueOf(rhs);
}
});
}
return this;
}
/**
* Test if two {@code short[]}s are equal.
*
* @param fieldName
* the field name
* @param lhs
* the left-hand {@code short[]}
* @param rhs
* the right-hand {@code short[]}
* @return this
* @throws NullPointerException
* if field name is {@code null}
*/
public DiffBuilder<T> append(final String fieldName, final short[] lhs,
final short[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Short[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Short[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Short[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}
/**
* Test if two {@link Objects}s are equal.
*
@ -875,45 +842,78 @@ public class DiffBuilder<T> implements Builder<DiffResult<T>> {
}
/**
* Append diffs from another {@link DiffResult}.
*
* <p>
* This method is useful if you want to compare properties which are
* themselves Diffable and would like to know which specific part of
* it is different.
* </p>
*
* <pre>
* public class Person implements Diffable&lt;Person&gt; {
* String name;
* Address address; // implements Diffable&lt;Address&gt;
*
* ...
*
* public DiffResult diff(Person obj) {
* return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
* .append("name", this.name, obj.name)
* .append("address", this.address.diff(obj.address))
* .build();
* }
* }
* </pre>
* Test if two {@code short}s are equal.
*
* @param fieldName
* the field name
* @param diffResult
* the {@link DiffResult} to append
* @param lhs
* the left-hand {@code short}
* @param rhs
* the right-hand {@code short}
* @return this
* @throws NullPointerException if field name is {@code null} or diffResult is {@code null}
* @since 3.5
* @throws NullPointerException
* if field name is {@code null}
*/
public DiffBuilder<T> append(final String fieldName, final DiffResult<T> diffResult) {
public DiffBuilder<T> append(final String fieldName, final short lhs,
final short rhs) {
validateFieldNameNotNull(fieldName);
Objects.requireNonNull(diffResult, "diffResult");
if (objectsTriviallyEqual) {
return this;
}
diffResult.getDiffs().forEach(diff -> append(fieldName + "." + diff.getFieldName(), diff.getLeft(), diff.getRight()));
if (lhs != rhs) {
diffs.add(new Diff<Short>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Short getLeft() {
return Short.valueOf(lhs);
}
@Override
public Short getRight() {
return Short.valueOf(rhs);
}
});
}
return this;
}
/**
* Test if two {@code short[]}s are equal.
*
* @param fieldName
* the field name
* @param lhs
* the left-hand {@code short[]}
* @param rhs
* the right-hand {@code short[]}
* @return this
* @throws NullPointerException
* if field name is {@code null}
*/
public DiffBuilder<T> append(final String fieldName, final short[] lhs,
final short[] rhs) {
validateFieldNameNotNull(fieldName);
if (objectsTriviallyEqual) {
return this;
}
if (!Arrays.equals(lhs, rhs)) {
diffs.add(new Diff<Short[]>(fieldName) {
private static final long serialVersionUID = 1L;
@Override
public Short[] getLeft() {
return ArrayUtils.toObject(lhs);
}
@Override
public Short[] getRight() {
return ArrayUtils.toObject(rhs);
}
});
}
return this;
}

View File

@ -83,6 +83,16 @@ public class DiffResult<T> implements Iterable<Diff<?>> {
}
}
/**
* Returns an unmodifiable list of {@link Diff}s. The list may be empty if
* there were no differences between the objects.
*
* @return an unmodifiable list of {@link Diff}s
*/
public List<Diff<?>> getDiffs() {
return Collections.unmodifiableList(diffList);
}
/**
* Returns the object the right object has been compared to.
*
@ -93,6 +103,15 @@ public class DiffResult<T> implements Iterable<Diff<?>> {
return this.lhs;
}
/**
* Returns the number of differences between the two objects.
*
* @return the number of differences
*/
public int getNumberOfDiffs() {
return diffList.size();
}
/**
* Returns the object the left object has been compared to.
*
@ -103,25 +122,6 @@ public class DiffResult<T> implements Iterable<Diff<?>> {
return this.rhs;
}
/**
* Returns an unmodifiable list of {@link Diff}s. The list may be empty if
* there were no differences between the objects.
*
* @return an unmodifiable list of {@link Diff}s
*/
public List<Diff<?>> getDiffs() {
return Collections.unmodifiableList(diffList);
}
/**
* Returns the number of differences between the two objects.
*
* @return the number of differences
*/
public int getNumberOfDiffs() {
return diffList.size();
}
/**
* Returns the style used by the {@link #toString()} method.
*
@ -131,6 +131,16 @@ public class DiffResult<T> implements Iterable<Diff<?>> {
return style;
}
/**
* Returns an iterator over the {@link Diff} objects contained in this list.
*
* @return the iterator
*/
@Override
public Iterator<Diff<?>> iterator() {
return diffList.iterator();
}
/**
* Builds a {@link String} description of the differences contained within
* this {@link DiffResult}. A {@link ToStringBuilder} is used for each object
@ -189,14 +199,4 @@ public class DiffResult<T> implements Iterable<Diff<?>> {
return String.format("%s %s %s", lhsBuilder.build(), DIFFERS_STRING, rhsBuilder.build());
}
/**
* Returns an iterator over the {@link Diff} objects contained in this list.
*
* @return the iterator
*/
@Override
public Iterator<Diff<?>> iterator() {
return diffList.iterator();
}
}

View File

@ -43,15 +43,6 @@ final class IDKey {
this.value = value;
}
/**
* returns hash code - i.e. the system identity hash code.
* @return the hash code
*/
@Override
public int hashCode() {
return id;
}
/**
* checks if instances are equal
* @param other The other object to compare to
@ -69,4 +60,13 @@ final class IDKey {
// Note that identity equals is used.
return value == idKey.value;
}
/**
* returns hash code - i.e. the system identity hash code.
* @return the hash code
*/
@Override
public int hashCode() {
return id;
}
}

View File

@ -82,86 +82,8 @@ public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
resetIndent();
}
/**
* Resets the fields responsible for the line breaks and indenting.
* Must be invoked after changing the {@link #spaces} value.
*/
private void resetIndent() {
setArrayStart("{" + System.lineSeparator() + spacer(spaces));
setArraySeparator("," + System.lineSeparator() + spacer(spaces));
setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
setContentStart("[" + System.lineSeparator() + spacer(spaces));
setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
}
/**
* Creates a StringBuilder responsible for the indenting.
*
* @param spaces how far to indent
* @return a StringBuilder with {spaces} leading space characters.
*/
private StringBuilder spacer(final int spaces) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < spaces; i++) {
sb.append(" ");
}
return sb;
}
@Override
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
&& accept(value.getClass())) {
spaces += INDENT;
resetIndent();
buffer.append(ReflectionToStringBuilder.toString(value, this));
spaces -= INDENT;
resetIndent();
} else {
super.appendDetail(buffer, fieldName, value);
}
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
@Override
protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
spaces += INDENT;
resetIndent();
super.reflectionAppendArrayDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
@ -206,7 +128,7 @@ public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
@ -214,4 +136,82 @@ public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
@Override
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
&& accept(value.getClass())) {
spaces += INDENT;
resetIndent();
buffer.append(ReflectionToStringBuilder.toString(value, this));
spaces -= INDENT;
resetIndent();
} else {
super.appendDetail(buffer, fieldName, value);
}
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
spaces += INDENT;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
@Override
protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
spaces += INDENT;
resetIndent();
super.reflectionAppendArrayDetail(buffer, fieldName, array);
spaces -= INDENT;
resetIndent();
}
/**
* Resets the fields responsible for the line breaks and indenting.
* Must be invoked after changing the {@link #spaces} value.
*/
private void resetIndent() {
setArrayStart("{" + System.lineSeparator() + spacer(spaces));
setArraySeparator("," + System.lineSeparator() + spacer(spaces));
setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
setContentStart("[" + System.lineSeparator() + spacer(spaces));
setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
}
/**
* Creates a StringBuilder responsible for the indenting.
*
* @param spaces how far to indent
* @return a StringBuilder with {spaces} leading space characters.
*/
private StringBuilder spacer(final int spaces) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < spaces; i++) {
sb.append(" ");
}
return sb;
}
}

View File

@ -65,24 +65,6 @@ public class RecursiveToStringStyle extends ToStringStyle {
public RecursiveToStringStyle() {
}
@Override
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) &&
!String.class.equals(value.getClass()) &&
accept(value.getClass())) {
buffer.append(ReflectionToStringBuilder.toString(value, this));
} else {
super.appendDetail(buffer, fieldName, value);
}
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
appendClassName(buffer, coll);
appendIdentityHashCode(buffer, coll);
appendDetail(buffer, fieldName, coll.toArray());
}
/**
* Returns whether or not to recursively format the given {@link Class}.
* By default, this method always returns {@code true}, but may be overwritten by
@ -95,4 +77,22 @@ public class RecursiveToStringStyle extends ToStringStyle {
protected boolean accept(final Class<?> clazz) {
return true;
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
appendClassName(buffer, coll);
appendIdentityHashCode(buffer, coll);
appendDetail(buffer, fieldName, coll.toArray());
}
@Override
public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) &&
!String.class.equals(value.getClass()) &&
accept(value.getClass())) {
buffer.append(ReflectionToStringBuilder.toString(value, this));
} else {
super.appendDetail(buffer, fieldName, value);
}
}
}

View File

@ -108,6 +108,49 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
this.diffBuilder = new DiffBuilder<>(lhs, rhs, style);
}
private boolean accept(final Field field) {
if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
return false;
}
if (Modifier.isTransient(field.getModifiers())) {
return false;
}
if (Modifier.isStatic(field.getModifiers())) {
return false;
}
if (this.excludeFieldNames != null
&& Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
// Reject fields from the getExcludeFieldNames list.
return false;
}
return !field.isAnnotationPresent(DiffExclude.class);
}
private void appendFields(final Class<?> clazz) {
for (final Field field : FieldUtils.getAllFields(clazz)) {
if (accept(field)) {
try {
diffBuilder.append(field.getName(), FieldUtils.readField(field, left, true), FieldUtils.readField(field, right, true));
} catch (final IllegalAccessException e) {
// this can't happen. Would get a Security exception instead
// throw a runtime exception in case the impossible happens.
throw new IllegalArgumentException("Unexpected IllegalAccessException: " + e.getMessage(), e);
}
}
}
}
@Override
public DiffResult<T> build() {
if (left.equals(right)) {
return diffBuilder.build();
}
appendFields(left.getClass());
return diffBuilder.build();
}
/**
* Gets the field names that should be excluded from the diff.
*
@ -118,7 +161,6 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
return this.excludeFieldNames.clone();
}
/**
* Sets the field names to exclude.
*
@ -137,46 +179,4 @@ public class ReflectionDiffBuilder<T> implements Builder<DiffResult<T>> {
return this;
}
@Override
public DiffResult<T> build() {
if (left.equals(right)) {
return diffBuilder.build();
}
appendFields(left.getClass());
return diffBuilder.build();
}
private void appendFields(final Class<?> clazz) {
for (final Field field : FieldUtils.getAllFields(clazz)) {
if (accept(field)) {
try {
diffBuilder.append(field.getName(), FieldUtils.readField(field, left, true), FieldUtils.readField(field, right, true));
} catch (final IllegalAccessException e) {
// this can't happen. Would get a Security exception instead
// throw a runtime exception in case the impossible happens.
throw new IllegalArgumentException("Unexpected IllegalAccessException: " + e.getMessage(), e);
}
}
}
}
private boolean accept(final Field field) {
if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
return false;
}
if (Modifier.isTransient(field.getModifiers())) {
return false;
}
if (Modifier.isStatic(field.getModifiers())) {
return false;
}
if (this.excludeFieldNames != null
&& Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
// Reject fields from the getExcludeFieldNames list.
return false;
}
return !field.isAnnotationPresent(DiffExclude.class);
}
}

View File

@ -46,152 +46,6 @@ public class StandardToStringStyle extends ToStringStyle {
public StandardToStringStyle() {
}
/**
* Gets whether to use the class name.
*
* @return the current useClassName flag
*/
@Override
public boolean isUseClassName() {
return super.isUseClassName();
}
/**
* Sets whether to use the class name.
*
* @param useClassName the new useClassName flag
*/
@Override
public void setUseClassName(final boolean useClassName) {
super.setUseClassName(useClassName);
}
/**
* Gets whether to output short or long class names.
*
* @return the current useShortClassName flag
* @since 2.0
*/
@Override
public boolean isUseShortClassName() {
return super.isUseShortClassName();
}
/**
* Sets whether to output short or long class names.
*
* @param useShortClassName the new useShortClassName flag
* @since 2.0
*/
@Override
public void setUseShortClassName(final boolean useShortClassName) {
super.setUseShortClassName(useShortClassName);
}
/**
* Gets whether to use the identity hash code.
* @return the current useIdentityHashCode flag
*/
@Override
public boolean isUseIdentityHashCode() {
return super.isUseIdentityHashCode();
}
/**
* Sets whether to use the identity hash code.
*
* @param useIdentityHashCode the new useIdentityHashCode flag
*/
@Override
public void setUseIdentityHashCode(final boolean useIdentityHashCode) {
super.setUseIdentityHashCode(useIdentityHashCode);
}
/**
* Gets whether to use the field names passed in.
*
* @return the current useFieldNames flag
*/
@Override
public boolean isUseFieldNames() {
return super.isUseFieldNames();
}
/**
* Sets whether to use the field names passed in.
*
* @param useFieldNames the new useFieldNames flag
*/
@Override
public void setUseFieldNames(final boolean useFieldNames) {
super.setUseFieldNames(useFieldNames);
}
/**
* Gets whether to use full detail when the caller doesn't
* specify.
*
* @return the current defaultFullDetail flag
*/
@Override
public boolean isDefaultFullDetail() {
return super.isDefaultFullDetail();
}
/**
* Sets whether to use full detail when the caller doesn't
* specify.
*
* @param defaultFullDetail the new defaultFullDetail flag
*/
@Override
public void setDefaultFullDetail(final boolean defaultFullDetail) {
super.setDefaultFullDetail(defaultFullDetail);
}
/**
* Gets whether to output array content detail.
*
* @return the current array content detail setting
*/
@Override
public boolean isArrayContentDetail() {
return super.isArrayContentDetail();
}
/**
* Sets whether to output array content detail.
*
* @param arrayContentDetail the new arrayContentDetail flag
*/
@Override
public void setArrayContentDetail(final boolean arrayContentDetail) {
super.setArrayContentDetail(arrayContentDetail);
}
/**
* Gets the array start text.
*
* @return the current array start text
*/
@Override
public String getArrayStart() {
return super.getArrayStart();
}
/**
* Sets the array start text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param arrayStart the new array start text
*/
@Override
public void setArrayStart(final String arrayStart) {
super.setArrayStart(arrayStart);
}
/**
* Gets the array end text.
*
@ -202,19 +56,6 @@ public class StandardToStringStyle extends ToStringStyle {
return super.getArrayEnd();
}
/**
* Sets the array end text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param arrayEnd the new array end text
*/
@Override
public void setArrayEnd(final String arrayEnd) {
super.setArrayEnd(arrayEnd);
}
/**
* Gets the array separator text.
*
@ -226,39 +67,13 @@ public class StandardToStringStyle extends ToStringStyle {
}
/**
* Sets the array separator text.
* Gets the array start text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param arraySeparator the new array separator text
* @return the current array start text
*/
@Override
public void setArraySeparator(final String arraySeparator) {
super.setArraySeparator(arraySeparator);
}
/**
* Gets the content start text.
*
* @return the current content start text
*/
@Override
public String getContentStart() {
return super.getContentStart();
}
/**
* Sets the content start text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param contentStart the new content start text
*/
@Override
public void setContentStart(final String contentStart) {
super.setContentStart(contentStart);
public String getArrayStart() {
return super.getArrayStart();
}
/**
@ -272,16 +87,13 @@ public class StandardToStringStyle extends ToStringStyle {
}
/**
* Sets the content end text.
* Gets the content start text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param contentEnd the new content end text
* @return the current content start text
*/
@Override
public void setContentEnd(final String contentEnd) {
super.setContentEnd(contentEnd);
public String getContentStart() {
return super.getContentStart();
}
/**
@ -294,19 +106,6 @@ public class StandardToStringStyle extends ToStringStyle {
return super.getFieldNameValueSeparator();
}
/**
* Sets the field name value separator text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param fieldNameValueSeparator the new field name value separator text
*/
@Override
public void setFieldNameValueSeparator(final String fieldNameValueSeparator) {
super.setFieldNameValueSeparator(fieldNameValueSeparator);
}
/**
* Gets the field separator text.
*
@ -317,67 +116,6 @@ public class StandardToStringStyle extends ToStringStyle {
return super.getFieldSeparator();
}
/**
* Sets the field separator text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param fieldSeparator the new field separator text
*/
@Override
public void setFieldSeparator(final String fieldSeparator) {
super.setFieldSeparator(fieldSeparator);
}
/**
* Gets whether the field separator should be added at the start
* of each buffer.
*
* @return the fieldSeparatorAtStart flag
* @since 2.0
*/
@Override
public boolean isFieldSeparatorAtStart() {
return super.isFieldSeparatorAtStart();
}
/**
* Sets whether the field separator should be added at the start
* of each buffer.
*
* @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
* @since 2.0
*/
@Override
public void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
super.setFieldSeparatorAtStart(fieldSeparatorAtStart);
}
/**
* Gets whether the field separator should be added at the end
* of each buffer.
*
* @return fieldSeparatorAtEnd flag
* @since 2.0
*/
@Override
public boolean isFieldSeparatorAtEnd() {
return super.isFieldSeparatorAtEnd();
}
/**
* Sets whether the field separator should be added at the end
* of each buffer.
*
* @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
* @since 2.0
*/
@Override
public void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd);
}
/**
* Gets the text to output when {@code null} found.
*
@ -389,16 +127,16 @@ public class StandardToStringStyle extends ToStringStyle {
}
/**
* Sets the text to output when {@code null} found.
* Gets the end text to output when a {@link Collection},
* {@link Map} or {@link Array} size is output.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
* <p>This is output after the size value.</p>
*
* @param nullText the new text to output when {@code null} found
* @return the current end of size text
*/
@Override
public void setNullText(final String nullText) {
super.setNullText(nullText);
public String getSizeEndText() {
return super.getSizeEndText();
}
/**
@ -415,32 +153,263 @@ public class StandardToStringStyle extends ToStringStyle {
}
/**
* Sets the start text to output when a {@link Collection},
* {@link Map} or {@link Array} size is output.
*
* <p>This is output before the size value.</p>
*
* <p>{@code null} is accepted, but will be converted to
* an empty String.</p>
*
* @param sizeStartText the new start of size text
*/
@Override
public void setSizeStartText(final String sizeStartText) {
super.setSizeStartText(sizeStartText);
}
/**
* Gets the end text to output when a {@link Collection},
* {@link Map} or {@link Array} size is output.
* Gets the end text to output when an {@link Object} is
* output in summary mode.
*
* <p>This is output after the size value.</p>
*
* @return the current end of size text
* @return the current end of summary text
*/
@Override
public String getSizeEndText() {
return super.getSizeEndText();
public String getSummaryObjectEndText() {
return super.getSummaryObjectEndText();
}
/**
* Gets the start text to output when an {@link Object} is
* output in summary mode.
*
* <p>This is output before the size value.</p>
*
* @return the current start of summary text
*/
@Override
public String getSummaryObjectStartText() {
return super.getSummaryObjectStartText();
}
/**
* Gets whether to output array content detail.
*
* @return the current array content detail setting
*/
@Override
public boolean isArrayContentDetail() {
return super.isArrayContentDetail();
}
/**
* Gets whether to use full detail when the caller doesn't
* specify.
*
* @return the current defaultFullDetail flag
*/
@Override
public boolean isDefaultFullDetail() {
return super.isDefaultFullDetail();
}
/**
* Gets whether the field separator should be added at the end
* of each buffer.
*
* @return fieldSeparatorAtEnd flag
* @since 2.0
*/
@Override
public boolean isFieldSeparatorAtEnd() {
return super.isFieldSeparatorAtEnd();
}
/**
* Gets whether the field separator should be added at the start
* of each buffer.
*
* @return the fieldSeparatorAtStart flag
* @since 2.0
*/
@Override
public boolean isFieldSeparatorAtStart() {
return super.isFieldSeparatorAtStart();
}
/**
* Gets whether to use the class name.
*
* @return the current useClassName flag
*/
@Override
public boolean isUseClassName() {
return super.isUseClassName();
}
/**
* Gets whether to use the field names passed in.
*
* @return the current useFieldNames flag
*/
@Override
public boolean isUseFieldNames() {
return super.isUseFieldNames();
}
/**
* Gets whether to use the identity hash code.
* @return the current useIdentityHashCode flag
*/
@Override
public boolean isUseIdentityHashCode() {
return super.isUseIdentityHashCode();
}
/**
* Gets whether to output short or long class names.
*
* @return the current useShortClassName flag
* @since 2.0
*/
@Override
public boolean isUseShortClassName() {
return super.isUseShortClassName();
}
/**
* Sets whether to output array content detail.
*
* @param arrayContentDetail the new arrayContentDetail flag
*/
@Override
public void setArrayContentDetail(final boolean arrayContentDetail) {
super.setArrayContentDetail(arrayContentDetail);
}
/**
* Sets the array end text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param arrayEnd the new array end text
*/
@Override
public void setArrayEnd(final String arrayEnd) {
super.setArrayEnd(arrayEnd);
}
/**
* Sets the array separator text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param arraySeparator the new array separator text
*/
@Override
public void setArraySeparator(final String arraySeparator) {
super.setArraySeparator(arraySeparator);
}
/**
* Sets the array start text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param arrayStart the new array start text
*/
@Override
public void setArrayStart(final String arrayStart) {
super.setArrayStart(arrayStart);
}
/**
* Sets the content end text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param contentEnd the new content end text
*/
@Override
public void setContentEnd(final String contentEnd) {
super.setContentEnd(contentEnd);
}
/**
* Sets the content start text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param contentStart the new content start text
*/
@Override
public void setContentStart(final String contentStart) {
super.setContentStart(contentStart);
}
/**
* Sets whether to use full detail when the caller doesn't
* specify.
*
* @param defaultFullDetail the new defaultFullDetail flag
*/
@Override
public void setDefaultFullDetail(final boolean defaultFullDetail) {
super.setDefaultFullDetail(defaultFullDetail);
}
/**
* Sets the field name value separator text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param fieldNameValueSeparator the new field name value separator text
*/
@Override
public void setFieldNameValueSeparator(final String fieldNameValueSeparator) {
super.setFieldNameValueSeparator(fieldNameValueSeparator);
}
/**
* Sets the field separator text.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param fieldSeparator the new field separator text
*/
@Override
public void setFieldSeparator(final String fieldSeparator) {
super.setFieldSeparator(fieldSeparator);
}
/**
* Sets whether the field separator should be added at the end
* of each buffer.
*
* @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
* @since 2.0
*/
@Override
public void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd);
}
/**
* Sets whether the field separator should be added at the start
* of each buffer.
*
* @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
* @since 2.0
*/
@Override
public void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
super.setFieldSeparatorAtStart(fieldSeparatorAtStart);
}
/**
* Sets the text to output when {@code null} found.
*
* <p>{@code null} is accepted, but will be converted
* to an empty String.</p>
*
* @param nullText the new text to output when {@code null} found
*/
@Override
public void setNullText(final String nullText) {
super.setNullText(nullText);
}
/**
@ -460,16 +429,35 @@ public class StandardToStringStyle extends ToStringStyle {
}
/**
* Gets the start text to output when an {@link Object} is
* output in summary mode.
* Sets the start text to output when a {@link Collection},
* {@link Map} or {@link Array} size is output.
*
* <p>This is output before the size value.</p>
*
* @return the current start of summary text
* <p>{@code null} is accepted, but will be converted to
* an empty String.</p>
*
* @param sizeStartText the new start of size text
*/
@Override
public String getSummaryObjectStartText() {
return super.getSummaryObjectStartText();
public void setSizeStartText(final String sizeStartText) {
super.setSizeStartText(sizeStartText);
}
/**
* Sets the end text to output when an {@link Object} is
* output in summary mode.
*
* <p>This is output after the size value.</p>
*
* <p>{@code null} is accepted, but will be converted to
* an empty String.</p>
*
* @param summaryObjectEndText the new end of summary text
*/
@Override
public void setSummaryObjectEndText(final String summaryObjectEndText) {
super.setSummaryObjectEndText(summaryObjectEndText);
}
/**
@ -489,32 +477,44 @@ public class StandardToStringStyle extends ToStringStyle {
}
/**
* Gets the end text to output when an {@link Object} is
* output in summary mode.
* Sets whether to use the class name.
*
* <p>This is output after the size value.</p>
*
* @return the current end of summary text
* @param useClassName the new useClassName flag
*/
@Override
public String getSummaryObjectEndText() {
return super.getSummaryObjectEndText();
public void setUseClassName(final boolean useClassName) {
super.setUseClassName(useClassName);
}
/**
* Sets the end text to output when an {@link Object} is
* output in summary mode.
* Sets whether to use the field names passed in.
*
* <p>This is output after the size value.</p>
*
* <p>{@code null} is accepted, but will be converted to
* an empty String.</p>
*
* @param summaryObjectEndText the new end of summary text
* @param useFieldNames the new useFieldNames flag
*/
@Override
public void setSummaryObjectEndText(final String summaryObjectEndText) {
super.setSummaryObjectEndText(summaryObjectEndText);
public void setUseFieldNames(final boolean useFieldNames) {
super.setUseFieldNames(useFieldNames);
}
/**
* Sets whether to use the identity hash code.
*
* @param useIdentityHashCode the new useIdentityHashCode flag
*/
@Override
public void setUseIdentityHashCode(final boolean useIdentityHashCode) {
super.setUseIdentityHashCode(useIdentityHashCode);
}
/**
* Sets whether to output short or long class names.
*
* @param useShortClassName the new useShortClassName flag
* @since 2.0
*/
@Override
public void setUseShortClassName(final boolean useShortClassName) {
super.setUseShortClassName(useShortClassName);
}
}

View File

@ -116,25 +116,6 @@ public class ToStringBuilder implements Builder<String> {
return defaultStyle;
}
/**
* Sets the default {@link ToStringStyle} to use.
*
* <p>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 {@link ToStringStyle} to the constructor instead
* of changing this global default.</p>
*
* <p>This method is not intended for use from multiple threads.
* Internally, a {@code volatile} variable is used to provide the guarantee
* that the latest value set is the value returned from {@link #getDefaultStyle}.</p>
*
* @param style the default {@link ToStringStyle}
* @throws NullPointerException if the style is {@code null}
*/
public static void setDefaultStyle(final ToStringStyle style) {
defaultStyle = Objects.requireNonNull(style, "style");
}
/**
* Uses {@link ReflectionToStringBuilder} to generate a
* {@code toString} for the specified object.
@ -195,6 +176,25 @@ public class ToStringBuilder implements Builder<String> {
return ReflectionToStringBuilder.toString(object, style, outputTransients, false, reflectUpToClass);
}
/**
* Sets the default {@link ToStringStyle} to use.
*
* <p>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 {@link ToStringStyle} to the constructor instead
* of changing this global default.</p>
*
* <p>This method is not intended for use from multiple threads.
* Internally, a {@code volatile} variable is used to provide the guarantee
* that the latest value set is the value returned from {@link #getDefaultStyle}.</p>
*
* @param style the default {@link ToStringStyle}
* @throws NullPointerException if the style is {@code null}
*/
public static void setDefaultStyle(final ToStringStyle style) {
defaultStyle = Objects.requireNonNull(style, "style");
}
/**
* Current toString buffer, not null.
*/
@ -968,6 +968,21 @@ public class ToStringBuilder implements Builder<String> {
return this;
}
/**
* Returns the String that was build as an object representation. The
* default implementation utilizes the {@link #toString()} implementation.
*
* @return the String {@code toString}
*
* @see #toString()
*
* @since 3.0
*/
@Override
public String build() {
return toString();
}
/**
* Returns the {@link Object} being output.
*
@ -1016,19 +1031,4 @@ public class ToStringBuilder implements Builder<String> {
}
return this.getStringBuffer().toString();
}
/**
* Returns the String that was build as an object representation. The
* default implementation utilizes the {@link #toString()} implementation.
*
* @return the String {@code toString}
*
* @see #toString()
*
* @since 3.0
*/
@Override
public String build() {
return toString();
}
}

View File

@ -28,111 +28,6 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public abstract class AbstractCircuitBreaker<T> implements CircuitBreaker<T> {
/**
* The name of the <em>open</em> property as it is passed to registered
* change listeners.
*/
public static final String PROPERTY_NAME = "open";
/** The current state of this circuit breaker. */
protected final AtomicReference<State> state = new AtomicReference<>(State.CLOSED);
/** An object for managing change listeners registered at this instance. */
private final PropertyChangeSupport changeSupport;
/**
* Creates an {@link AbstractCircuitBreaker}. It also creates an internal {@link PropertyChangeSupport}.
*/
public AbstractCircuitBreaker() {
changeSupport = new PropertyChangeSupport(this);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isOpen() {
return isOpen(state.get());
}
/**
* {@inheritDoc}
*/
@Override
public boolean isClosed() {
return !isOpen();
}
/**
* {@inheritDoc}
*/
@Override
public abstract boolean checkState();
/**
* {@inheritDoc}
*/
@Override
public abstract boolean incrementAndCheckState(T increment);
/**
* {@inheritDoc}
*/
@Override
public void close() {
changeState(State.CLOSED);
}
/**
* {@inheritDoc}
*/
@Override
public void open() {
changeState(State.OPEN);
}
/**
* Converts the given state value to a boolean <em>open</em> property.
*
* @param state the state to be converted
* @return the boolean open flag
*/
protected static boolean isOpen(final State state) {
return state == State.OPEN;
}
/**
* Changes the internal state of this circuit breaker. If there is actually a change
* of the state value, all registered change listeners are notified.
*
* @param newState the new state to be set
*/
protected void changeState(final State newState) {
if (state.compareAndSet(newState.oppositeState(), newState)) {
changeSupport.firePropertyChange(PROPERTY_NAME, !isOpen(newState), isOpen(newState));
}
}
/**
* Adds a change listener to this circuit breaker. This listener is notified whenever
* the state of this circuit breaker changes. If the listener is
* <strong>null</strong>, it is silently ignored.
*
* @param listener the listener to be added
*/
public void addChangeListener(final PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
/**
* Removes the specified change listener from this circuit breaker.
*
* @param listener the listener to be removed
*/
public void removeChangeListener(final PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
/**
* An internal enumeration representing the different states of a circuit
* breaker. This class also contains some logic for performing state
@ -172,4 +67,109 @@ public abstract class AbstractCircuitBreaker<T> implements CircuitBreaker<T> {
public abstract State oppositeState();
}
/**
* The name of the <em>open</em> property as it is passed to registered
* change listeners.
*/
public static final String PROPERTY_NAME = "open";
/**
* Converts the given state value to a boolean <em>open</em> property.
*
* @param state the state to be converted
* @return the boolean open flag
*/
protected static boolean isOpen(final State state) {
return state == State.OPEN;
}
/** The current state of this circuit breaker. */
protected final AtomicReference<State> state = new AtomicReference<>(State.CLOSED);
/** An object for managing change listeners registered at this instance. */
private final PropertyChangeSupport changeSupport;
/**
* Creates an {@link AbstractCircuitBreaker}. It also creates an internal {@link PropertyChangeSupport}.
*/
public AbstractCircuitBreaker() {
changeSupport = new PropertyChangeSupport(this);
}
/**
* Adds a change listener to this circuit breaker. This listener is notified whenever
* the state of this circuit breaker changes. If the listener is
* <strong>null</strong>, it is silently ignored.
*
* @param listener the listener to be added
*/
public void addChangeListener(final PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
/**
* Changes the internal state of this circuit breaker. If there is actually a change
* of the state value, all registered change listeners are notified.
*
* @param newState the new state to be set
*/
protected void changeState(final State newState) {
if (state.compareAndSet(newState.oppositeState(), newState)) {
changeSupport.firePropertyChange(PROPERTY_NAME, !isOpen(newState), isOpen(newState));
}
}
/**
* {@inheritDoc}
*/
@Override
public abstract boolean checkState();
/**
* {@inheritDoc}
*/
@Override
public void close() {
changeState(State.CLOSED);
}
/**
* {@inheritDoc}
*/
@Override
public abstract boolean incrementAndCheckState(T increment);
/**
* {@inheritDoc}
*/
@Override
public boolean isClosed() {
return !isOpen();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isOpen() {
return isOpen(state.get());
}
/**
* {@inheritDoc}
*/
@Override
public void open() {
changeState(State.OPEN);
}
/**
* Removes the specified change listener from this circuit breaker.
*
* @param listener the listener to be removed
*/
public void removeChangeListener(final PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
}

View File

@ -147,6 +147,14 @@ public abstract class AbstractConcurrentInitializer<T, E extends Exception> impl
}
}
/**
* Gets an Exception with a type of E as defined by a concrete subclass of this class.
*
* @param e The actual exception that was thrown
* @return a new exception with the actual type of E, that wraps e.
*/
protected abstract E getTypedException(Exception e);
/**
* Creates and initializes the object managed by this {@code
* ConcurrentInitializer}. This method is called by {@link #get()} when the object is accessed for the first time. An implementation can focus on the
@ -187,12 +195,4 @@ public abstract class AbstractConcurrentInitializer<T, E extends Exception> impl
*/
protected abstract boolean isInitialized();
/**
* Gets an Exception with a type of E as defined by a concrete subclass of this class.
*
* @param e The actual exception that was thrown
* @return a new exception with the actual type of E, that wraps e.
*/
protected abstract E getTypedException(Exception e);
}

View File

@ -86,9 +86,6 @@ public class AtomicInitializer<T> extends AbstractConcurrentInitializer<T, Concu
private static final Object NO_INIT = new Object();
/** Holds the reference to the managed object. */
private final AtomicReference<T> reference = new AtomicReference<>(getNoInit());
/**
* Creates a new builder.
*
@ -100,6 +97,9 @@ public class AtomicInitializer<T> extends AbstractConcurrentInitializer<T, Concu
return new Builder<>();
}
/** Holds the reference to the managed object. */
private final AtomicReference<T> reference = new AtomicReference<>(getNoInit());
/**
* Constructs a new instance.
*/
@ -147,6 +147,14 @@ public class AtomicInitializer<T> extends AbstractConcurrentInitializer<T, Concu
return (T) NO_INIT;
}
/**
* {@inheritDoc}
*/
@Override
protected ConcurrentException getTypedException(Exception e) {
return new ConcurrentException(e);
}
/**
* Tests whether this instance is initialized. Once initialized, always returns true.
*
@ -157,12 +165,4 @@ public class AtomicInitializer<T> extends AbstractConcurrentInitializer<T, Concu
public boolean isInitialized() {
return reference.get() != NO_INIT;
}
/**
* {@inheritDoc}
*/
@Override
protected ConcurrentException getTypedException(Exception e) {
return new ConcurrentException(e);
}
}

View File

@ -75,12 +75,6 @@ public class AtomicSafeInitializer<T> extends AbstractConcurrentInitializer<T, C
private static final Object NO_INIT = new Object();
/** A guard which ensures that initialize() is called only once. */
private final AtomicReference<AtomicSafeInitializer<T>> factory = new AtomicReference<>();
/** Holds the reference to the managed object. */
private final AtomicReference<T> reference = new AtomicReference<>(getNoInit());
/**
* Creates a new builder.
*
@ -92,6 +86,12 @@ public class AtomicSafeInitializer<T> extends AbstractConcurrentInitializer<T, C
return new Builder<>();
}
/** A guard which ensures that initialize() is called only once. */
private final AtomicReference<AtomicSafeInitializer<T>> factory = new AtomicReference<>();
/** Holds the reference to the managed object. */
private final AtomicReference<T> reference = new AtomicReference<>(getNoInit());
/**
* Constructs a new instance.
*/
@ -135,6 +135,14 @@ public class AtomicSafeInitializer<T> extends AbstractConcurrentInitializer<T, C
return (T) NO_INIT;
}
/**
* {@inheritDoc}
*/
@Override
protected ConcurrentException getTypedException(Exception e) {
return new ConcurrentException(e);
}
/**
* Tests whether this instance is initialized. Once initialized, always returns true.
*
@ -145,12 +153,4 @@ public class AtomicSafeInitializer<T> extends AbstractConcurrentInitializer<T, C
public boolean isInitialized() {
return reference.get() != NO_INIT;
}
/**
* {@inheritDoc}
*/
@Override
protected ConcurrentException getTypedException(Exception e) {
return new ConcurrentException(e);
}
}

View File

@ -101,6 +101,12 @@ public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, E
*/
private ExecutorService externalExecutor;
@SuppressWarnings("unchecked")
@Override
public I get() {
return (I) new BackgroundInitializer(getInitializer(), getCloser(), externalExecutor);
}
/**
* Sets the external executor service for executing tasks. null is an permitted value.
*
@ -114,12 +120,49 @@ public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, E
return asThis();
}
@SuppressWarnings("unchecked")
@Override
public I get() {
return (I) new BackgroundInitializer(getInitializer(), getCloser(), externalExecutor);
}
private class InitializationTask implements Callable<T> {
/** Stores the executor service to be destroyed at the end. */
private final ExecutorService execFinally;
/**
* Creates a new instance of {@link InitializationTask} and initializes
* it with the {@link ExecutorService} to be destroyed at the end.
*
* @param exec the {@link ExecutorService}
*/
InitializationTask(final ExecutorService exec) {
execFinally = exec;
}
/**
* Initiates initialization and returns the result.
*
* @return the result object
* @throws Exception if an error occurs
*/
@Override
public T call() throws Exception {
try {
return initialize();
} finally {
if (execFinally != null) {
execFinally.shutdown();
}
}
}
}
/**
* Creates a new builder.
*
* @param <T> the type of object to build.
* @return a new builder.
* @since 3.14.0
*/
public static <T> Builder<BackgroundInitializer<T>, T> builder() {
return new Builder<>();
}
/** The external executor service for executing tasks. */
@ -153,17 +196,6 @@ public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, E
setExternalExecutor(exec);
}
/**
* Creates a new builder.
*
* @param <T> the type of object to build.
* @return a new builder.
* @since 3.14.0
*/
public static <T> Builder<BackgroundInitializer<T>, T> builder() {
return new Builder<>();
}
/**
* Constructs a new instance.
*
@ -176,6 +208,72 @@ public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, E
setExternalExecutor(exec);
}
/**
* Creates the {@link ExecutorService} to be used. This method is called if
* no {@link ExecutorService} was provided at construction time.
*
* @return the {@link ExecutorService} to be used
*/
private ExecutorService createExecutor() {
return Executors.newFixedThreadPool(getTaskCount());
}
/**
* Creates a task for the background initialization. The {@link Callable}
* object returned by this method is passed to the {@link ExecutorService}.
* This implementation returns a task that invokes the {@link #initialize()}
* method. If a temporary {@link ExecutorService} is used, it is destroyed
* at the end of the task.
*
* @param execDestroy the {@link ExecutorService} to be destroyed by the
* task
* @return a task for the background initialization
*/
private Callable<T> createTask(final ExecutorService execDestroy) {
return new InitializationTask(execDestroy);
}
/**
* Returns the result of the background initialization. This method blocks
* until initialization is complete. If the background processing caused a
* runtime exception, it is directly thrown by this method. Checked
* exceptions, including {@link InterruptedException} are wrapped in a
* {@link ConcurrentException}. Calling this method before {@link #start()}
* was called causes an {@link IllegalStateException} exception to be
* thrown.
*
* @return the object produced by this initializer
* @throws ConcurrentException if a checked exception occurred during
* background processing
* @throws IllegalStateException if {@link #start()} has not been called
*/
@Override
public T get() throws ConcurrentException {
try {
return getFuture().get();
} catch (final ExecutionException execex) {
ConcurrentUtils.handleCause(execex);
return null; // should not be reached
} catch (final InterruptedException iex) {
// reset interrupted state
Thread.currentThread().interrupt();
throw new ConcurrentException(iex);
}
}
/**
* Returns the {@link ExecutorService} that is actually used for executing
* the background task. This method can be called after {@link #start()}
* (before {@code start()} it returns <b>null</b>). If an external executor
* was set, this is also the active executor. Otherwise this method returns
* the temporary executor that was created by this object.
*
* @return the {@link ExecutorService} for executing the background task
*/
protected final synchronized ExecutorService getActiveExecutor() {
return executor;
}
/**
* Returns the external {@link ExecutorService} to be used by this class.
*
@ -185,6 +283,46 @@ public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, E
return externalExecutor;
}
/**
* Returns the {@link Future} object that was created when {@link #start()}
* was called. Therefore this method can only be called after {@code
* start()}.
*
* @return the {@link Future} object wrapped by this initializer
* @throws IllegalStateException if {@link #start()} has not been called
*/
public synchronized Future<T> getFuture() {
if (future == null) {
throw new IllegalStateException("start() must be called first!");
}
return future;
}
/**
* Returns the number of background tasks to be created for this
* initializer. This information is evaluated when a temporary {@code
* ExecutorService} is created. This base implementation returns 1. Derived
* classes that do more complex background processing can override it. This
* method is called from a synchronized block by the {@link #start()}
* method. Therefore overriding methods should be careful with obtaining
* other locks and return as fast as possible.
*
* @return the number of background tasks required by this initializer
*/
protected int getTaskCount() {
return 1;
}
/**
* {@inheritDoc}
*/
@Override
protected Exception getTypedException(Exception e) {
//This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown
return new Exception(e);
}
/**
* Tests whether this instance is initialized. Once initialized, always returns true.
* If initialization failed then the failure will be cached and this will never return
@ -273,142 +411,4 @@ public class BackgroundInitializer<T> extends AbstractConcurrentInitializer<T, E
return false;
}
/**
* Returns the result of the background initialization. This method blocks
* until initialization is complete. If the background processing caused a
* runtime exception, it is directly thrown by this method. Checked
* exceptions, including {@link InterruptedException} are wrapped in a
* {@link ConcurrentException}. Calling this method before {@link #start()}
* was called causes an {@link IllegalStateException} exception to be
* thrown.
*
* @return the object produced by this initializer
* @throws ConcurrentException if a checked exception occurred during
* background processing
* @throws IllegalStateException if {@link #start()} has not been called
*/
@Override
public T get() throws ConcurrentException {
try {
return getFuture().get();
} catch (final ExecutionException execex) {
ConcurrentUtils.handleCause(execex);
return null; // should not be reached
} catch (final InterruptedException iex) {
// reset interrupted state
Thread.currentThread().interrupt();
throw new ConcurrentException(iex);
}
}
/**
* Returns the {@link Future} object that was created when {@link #start()}
* was called. Therefore this method can only be called after {@code
* start()}.
*
* @return the {@link Future} object wrapped by this initializer
* @throws IllegalStateException if {@link #start()} has not been called
*/
public synchronized Future<T> getFuture() {
if (future == null) {
throw new IllegalStateException("start() must be called first!");
}
return future;
}
/**
* Returns the {@link ExecutorService} that is actually used for executing
* the background task. This method can be called after {@link #start()}
* (before {@code start()} it returns <b>null</b>). If an external executor
* was set, this is also the active executor. Otherwise this method returns
* the temporary executor that was created by this object.
*
* @return the {@link ExecutorService} for executing the background task
*/
protected final synchronized ExecutorService getActiveExecutor() {
return executor;
}
/**
* Returns the number of background tasks to be created for this
* initializer. This information is evaluated when a temporary {@code
* ExecutorService} is created. This base implementation returns 1. Derived
* classes that do more complex background processing can override it. This
* method is called from a synchronized block by the {@link #start()}
* method. Therefore overriding methods should be careful with obtaining
* other locks and return as fast as possible.
*
* @return the number of background tasks required by this initializer
*/
protected int getTaskCount() {
return 1;
}
/**
* Creates a task for the background initialization. The {@link Callable}
* object returned by this method is passed to the {@link ExecutorService}.
* This implementation returns a task that invokes the {@link #initialize()}
* method. If a temporary {@link ExecutorService} is used, it is destroyed
* at the end of the task.
*
* @param execDestroy the {@link ExecutorService} to be destroyed by the
* task
* @return a task for the background initialization
*/
private Callable<T> createTask(final ExecutorService execDestroy) {
return new InitializationTask(execDestroy);
}
/**
* Creates the {@link ExecutorService} to be used. This method is called if
* no {@link ExecutorService} was provided at construction time.
*
* @return the {@link ExecutorService} to be used
*/
private ExecutorService createExecutor() {
return Executors.newFixedThreadPool(getTaskCount());
}
private class InitializationTask implements Callable<T> {
/** Stores the executor service to be destroyed at the end. */
private final ExecutorService execFinally;
/**
* Creates a new instance of {@link InitializationTask} and initializes
* it with the {@link ExecutorService} to be destroyed at the end.
*
* @param exec the {@link ExecutorService}
*/
InitializationTask(final ExecutorService exec) {
execFinally = exec;
}
/**
* Initiates initialization and returns the result.
*
* @return the result object
* @throws Exception if an error occurs
*/
@Override
public T call() throws Exception {
try {
return initialize();
} finally {
if (execFinally != null) {
execFinally.shutdown();
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected Exception getTypedException(Exception e) {
//This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown
return new Exception(e);
}
}

View File

@ -89,6 +89,140 @@ import java.util.concurrent.atomic.AtomicLong;
* @since 3.0
*/
public class BasicThreadFactory implements ThreadFactory {
/**
* A <em>builder</em> class for creating instances of {@code
* BasicThreadFactory}.
*
* <p>
* Using this builder class instances of {@link BasicThreadFactory} can be
* created and initialized. The class provides methods that correspond to
* the configuration options supported by {@link BasicThreadFactory}. Method
* chaining is supported. Refer to the documentation of {@code
* BasicThreadFactory} for a usage example.
* </p>
*
*/
public static class Builder
implements org.apache.commons.lang3.builder.Builder<BasicThreadFactory> {
/** The wrapped factory. */
private ThreadFactory wrappedFactory;
/** The uncaught exception handler. */
private Thread.UncaughtExceptionHandler exceptionHandler;
/** The naming pattern. */
private String namingPattern;
/** The priority. */
private Integer priority;
/** The daemon flag. */
private Boolean daemon;
/**
* Creates a new {@link BasicThreadFactory} with all configuration
* options that have been specified by calling methods on this builder.
* After creating the factory {@link #reset()} is called.
*
* @return the new {@link BasicThreadFactory}
*/
@Override
public BasicThreadFactory build() {
final BasicThreadFactory factory = new BasicThreadFactory(this);
reset();
return factory;
}
/**
* Sets the daemon flag for the new {@link BasicThreadFactory}. If this
* flag is set to <b>true</b> the new thread factory will create daemon
* threads.
*
* @param daemon the value of the daemon flag
* @return a reference to this {@link Builder}
*/
public Builder daemon(final boolean daemon) {
this.daemon = Boolean.valueOf(daemon);
return this;
}
/**
* Sets the naming pattern to be used by the new {@code
* BasicThreadFactory}.
*
* @param pattern the naming pattern (must not be <b>null</b>)
* @return a reference to this {@link Builder}
* @throws NullPointerException if the naming pattern is <b>null</b>
*/
public Builder namingPattern(final String pattern) {
Objects.requireNonNull(pattern, "pattern");
namingPattern = pattern;
return this;
}
/**
* Sets the priority for the threads created by the new {@code
* BasicThreadFactory}.
*
* @param priority the priority
* @return a reference to this {@link Builder}
*/
public Builder priority(final int priority) {
this.priority = Integer.valueOf(priority);
return this;
}
/**
* Resets this builder. All configuration options are set to default
* values. Note: If the {@link #build()} method was called, it is not
* necessary to call {@code reset()} explicitly because this is done
* automatically.
*/
public void reset() {
wrappedFactory = null;
exceptionHandler = null;
namingPattern = null;
priority = null;
daemon = null;
}
/**
* Sets the uncaught exception handler for the threads created by the
* new {@link BasicThreadFactory}.
*
* @param handler the {@link UncaughtExceptionHandler} (must not be
* <b>null</b>)
* @return a reference to this {@link Builder}
* @throws NullPointerException if the exception handler is <b>null</b>
*/
public Builder uncaughtExceptionHandler(
final Thread.UncaughtExceptionHandler handler) {
Objects.requireNonNull(handler, "handler");
exceptionHandler = handler;
return this;
}
/**
* Sets the {@link ThreadFactory} to be wrapped by the new {@code
* BasicThreadFactory}.
*
* @param factory the wrapped {@link ThreadFactory} (must not be
* <b>null</b>)
* @return a reference to this {@link Builder}
* @throws NullPointerException if the passed in {@link ThreadFactory}
* is <b>null</b>
*/
public Builder wrappedFactory(final ThreadFactory factory) {
Objects.requireNonNull(factory, "factory");
wrappedFactory = factory;
return this;
}
}
/** A counter for the threads created by this factory. */
private final AtomicLong threadCounter;
@ -129,15 +263,15 @@ public class BasicThreadFactory implements ThreadFactory {
}
/**
* Returns the wrapped {@link ThreadFactory}. This factory is used for
* actually creating threads. This method never returns <b>null</b>. If no
* {@link ThreadFactory} was passed when this object was created, a default
* thread factory is returned.
* Returns the daemon flag. This flag determines whether newly created
* threads should be daemon threads. If <b>true</b>, this factory object
* calls {@code setDaemon(true)} on the newly created threads. Result can be
* <b>null</b> if no daemon flag was provided at creation time.
*
* @return the wrapped {@link ThreadFactory}
* @return the daemon flag
*/
public final ThreadFactory getWrappedFactory() {
return wrappedFactory;
public final Boolean getDaemonFlag() {
return daemon;
}
/**
@ -150,18 +284,6 @@ public class BasicThreadFactory implements ThreadFactory {
return namingPattern;
}
/**
* Returns the daemon flag. This flag determines whether newly created
* threads should be daemon threads. If <b>true</b>, this factory object
* calls {@code setDaemon(true)} on the newly created threads. Result can be
* <b>null</b> if no daemon flag was provided at creation time.
*
* @return the daemon flag
*/
public final Boolean getDaemonFlag() {
return daemon;
}
/**
* Returns the priority of the threads created by this factory. Result can
* be <b>null</b> if no priority was specified.
@ -172,16 +294,6 @@ public class BasicThreadFactory implements ThreadFactory {
return priority;
}
/**
* Returns the {@link UncaughtExceptionHandler} for the threads created by
* this factory. Result can be <b>null</b> if no handler was provided.
*
* @return the {@link UncaughtExceptionHandler}
*/
public final Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler;
}
/**
* Returns the number of threads this factory has already created. This
* class maintains an internal counter that is incremented each time the
@ -194,19 +306,25 @@ public class BasicThreadFactory implements ThreadFactory {
}
/**
* Creates a new thread. This implementation delegates to the wrapped
* factory for creating the thread. Then, on the newly created thread the
* corresponding configuration options are set.
* Returns the {@link UncaughtExceptionHandler} for the threads created by
* this factory. Result can be <b>null</b> if no handler was provided.
*
* @param runnable the {@link Runnable} to be executed by the new thread
* @return the newly created thread
* @return the {@link UncaughtExceptionHandler}
*/
@Override
public Thread newThread(final Runnable runnable) {
final Thread thread = getWrappedFactory().newThread(runnable);
initializeThread(thread);
public final Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler;
}
return thread;
/**
* Returns the wrapped {@link ThreadFactory}. This factory is used for
* actually creating threads. This method never returns <b>null</b>. If no
* {@link ThreadFactory} was passed when this object was created, a default
* thread factory is returned.
*
* @return the wrapped {@link ThreadFactory}
*/
public final ThreadFactory getWrappedFactory() {
return wrappedFactory;
}
/**
@ -238,136 +356,18 @@ public class BasicThreadFactory implements ThreadFactory {
}
/**
* A <em>builder</em> class for creating instances of {@code
* BasicThreadFactory}.
*
* <p>
* Using this builder class instances of {@link BasicThreadFactory} can be
* created and initialized. The class provides methods that correspond to
* the configuration options supported by {@link BasicThreadFactory}. Method
* chaining is supported. Refer to the documentation of {@code
* BasicThreadFactory} for a usage example.
* </p>
* Creates a new thread. This implementation delegates to the wrapped
* factory for creating the thread. Then, on the newly created thread the
* corresponding configuration options are set.
*
* @param runnable the {@link Runnable} to be executed by the new thread
* @return the newly created thread
*/
public static class Builder
implements org.apache.commons.lang3.builder.Builder<BasicThreadFactory> {
@Override
public Thread newThread(final Runnable runnable) {
final Thread thread = getWrappedFactory().newThread(runnable);
initializeThread(thread);
/** The wrapped factory. */
private ThreadFactory wrappedFactory;
/** The uncaught exception handler. */
private Thread.UncaughtExceptionHandler exceptionHandler;
/** The naming pattern. */
private String namingPattern;
/** The priority. */
private Integer priority;
/** The daemon flag. */
private Boolean daemon;
/**
* Sets the {@link ThreadFactory} to be wrapped by the new {@code
* BasicThreadFactory}.
*
* @param factory the wrapped {@link ThreadFactory} (must not be
* <b>null</b>)
* @return a reference to this {@link Builder}
* @throws NullPointerException if the passed in {@link ThreadFactory}
* is <b>null</b>
*/
public Builder wrappedFactory(final ThreadFactory factory) {
Objects.requireNonNull(factory, "factory");
wrappedFactory = factory;
return this;
}
/**
* Sets the naming pattern to be used by the new {@code
* BasicThreadFactory}.
*
* @param pattern the naming pattern (must not be <b>null</b>)
* @return a reference to this {@link Builder}
* @throws NullPointerException if the naming pattern is <b>null</b>
*/
public Builder namingPattern(final String pattern) {
Objects.requireNonNull(pattern, "pattern");
namingPattern = pattern;
return this;
}
/**
* Sets the daemon flag for the new {@link BasicThreadFactory}. If this
* flag is set to <b>true</b> the new thread factory will create daemon
* threads.
*
* @param daemon the value of the daemon flag
* @return a reference to this {@link Builder}
*/
public Builder daemon(final boolean daemon) {
this.daemon = Boolean.valueOf(daemon);
return this;
}
/**
* Sets the priority for the threads created by the new {@code
* BasicThreadFactory}.
*
* @param priority the priority
* @return a reference to this {@link Builder}
*/
public Builder priority(final int priority) {
this.priority = Integer.valueOf(priority);
return this;
}
/**
* Sets the uncaught exception handler for the threads created by the
* new {@link BasicThreadFactory}.
*
* @param handler the {@link UncaughtExceptionHandler} (must not be
* <b>null</b>)
* @return a reference to this {@link Builder}
* @throws NullPointerException if the exception handler is <b>null</b>
*/
public Builder uncaughtExceptionHandler(
final Thread.UncaughtExceptionHandler handler) {
Objects.requireNonNull(handler, "handler");
exceptionHandler = handler;
return this;
}
/**
* Resets this builder. All configuration options are set to default
* values. Note: If the {@link #build()} method was called, it is not
* necessary to call {@code reset()} explicitly because this is done
* automatically.
*/
public void reset() {
wrappedFactory = null;
exceptionHandler = null;
namingPattern = null;
priority = null;
daemon = null;
}
/**
* Creates a new {@link BasicThreadFactory} with all configuration
* options that have been specified by calling methods on this builder.
* After creating the factory {@link #reset()} is called.
*
* @return the new {@link BasicThreadFactory}
*/
@Override
public BasicThreadFactory build() {
final BasicThreadFactory factory = new BasicThreadFactory(this);
reset();
return factory;
}
return thread;
}
}

View File

@ -96,19 +96,6 @@ public class CallableBackgroundInitializer<T> extends BackgroundInitializer<T> {
callable = call;
}
/**
* Performs initialization in a background thread. This implementation
* delegates to the {@link Callable} passed at construction time of this
* object.
*
* @return the result of the initialization
* @throws Exception if an error occurs
*/
@Override
protected T initialize() throws Exception {
return callable.call();
}
/**
* Tests the passed in {@link Callable} and throws an exception if it is
* undefined.
@ -128,4 +115,17 @@ public class CallableBackgroundInitializer<T> extends BackgroundInitializer<T> {
//This Exception object will be used for type comparison in AbstractConcurrentInitializer.initialize but not thrown
return new Exception(e);
}
/**
* Performs initialization in a background thread. This implementation
* delegates to the {@link Callable} passed at construction time of this
* object.
*
* @return the result of the initialization
* @throws Exception if an error occurs
*/
@Override
protected T initialize() throws Exception {
return callable.call();
}
}

View File

@ -40,24 +40,6 @@ package org.apache.commons.lang3.concurrent;
*/
public interface CircuitBreaker<T> {
/**
* Tests the current open state of this circuit breaker. A return value of
* <strong>true</strong> means that the circuit breaker is currently open indicating a
* problem in the monitored subsystem.
*
* @return the current open state of this circuit breaker.
*/
boolean isOpen();
/**
* Tests the current closed state of this circuit breaker. A return value of
* <strong>true</strong> means that the circuit breaker is currently closed. This
* means that everything is okay with the monitored subsystem.
*
* @return the current closed state of this circuit breaker.
*/
boolean isClosed();
/**
* Checks the state of this circuit breaker and changes it if necessary. The return
* value indicates whether the circuit breaker is now in state <em>closed</em>; a value
@ -74,13 +56,6 @@ public interface CircuitBreaker<T> {
*/
void close();
/**
* Opens this circuit breaker. Its state is changed to open. Depending on a concrete
* implementation, it may close itself again if the monitored subsystem becomes
* available. If this circuit breaker is already open, this method has no effect.
*/
void open();
/**
* Increments the monitored value and performs a check of the current state of this
* circuit breaker. This method works like {@link #checkState()}, but the monitored
@ -91,4 +66,29 @@ public interface CircuitBreaker<T> {
* <strong>false</strong> otherwise
*/
boolean incrementAndCheckState(T increment);
/**
* Tests the current closed state of this circuit breaker. A return value of
* <strong>true</strong> means that the circuit breaker is currently closed. This
* means that everything is okay with the monitored subsystem.
*
* @return the current closed state of this circuit breaker.
*/
boolean isClosed();
/**
* Tests the current open state of this circuit breaker. A return value of
* <strong>true</strong> means that the circuit breaker is currently open indicating a
* problem in the monitored subsystem.
*
* @return the current open state of this circuit breaker.
*/
boolean isOpen();
/**
* Opens this circuit breaker. Its state is changed to open. Depending on a concrete
* implementation, it may close itself again if the monitored subsystem becomes
* available. If this circuit breaker is already open, this method has no effect.
*/
void open();
}

View File

@ -34,6 +34,15 @@ public class CircuitBreakingException extends RuntimeException {
public CircuitBreakingException() {
}
/**
* Creates a new instance of {@link CircuitBreakingException} and initializes it with the given message.
*
* @param message the error message
*/
public CircuitBreakingException(final String message) {
super(message);
}
/**
* Creates a new instance of {@link CircuitBreakingException} and initializes it with the given message and cause.
*
@ -44,15 +53,6 @@ public class CircuitBreakingException extends RuntimeException {
super(message, cause);
}
/**
* Creates a new instance of {@link CircuitBreakingException} and initializes it with the given message.
*
* @param message the error message
*/
public CircuitBreakingException(final String message) {
super(message);
}
/**
* Creates a new instance of {@link CircuitBreakingException} and initializes it with the given cause.
*

View File

@ -41,17 +41,6 @@ public class ConcurrentException extends Exception {
protected ConcurrentException() {
}
/**
* Creates a new instance of {@link ConcurrentException} and initializes it
* with the given cause.
*
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentException(final Throwable cause) {
super(ConcurrentUtils.checkedException(cause));
}
/**
* Creates a new instance of {@link ConcurrentException} and initializes it
* with the given message and cause.
@ -63,4 +52,15 @@ public class ConcurrentException extends Exception {
public ConcurrentException(final String msg, final Throwable cause) {
super(msg, ConcurrentUtils.checkedException(cause));
}
/**
* Creates a new instance of {@link ConcurrentException} and initializes it
* with the given cause.
*
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentException(final Throwable cause) {
super(ConcurrentUtils.checkedException(cause));
}
}

View File

@ -44,17 +44,6 @@ public class ConcurrentRuntimeException extends RuntimeException {
protected ConcurrentRuntimeException() {
}
/**
* Creates a new instance of {@link ConcurrentRuntimeException} and
* initializes it with the given cause.
*
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentRuntimeException(final Throwable cause) {
super(ConcurrentUtils.checkedException(cause));
}
/**
* Creates a new instance of {@link ConcurrentRuntimeException} and
* initializes it with the given message and cause.
@ -66,4 +55,15 @@ public class ConcurrentRuntimeException extends RuntimeException {
public ConcurrentRuntimeException(final String msg, final Throwable cause) {
super(msg, ConcurrentUtils.checkedException(cause));
}
/**
* Creates a new instance of {@link ConcurrentRuntimeException} and
* initializes it with the given cause.
*
* @param cause the cause of this exception
* @throws IllegalArgumentException if the cause is not a checked exception
*/
public ConcurrentRuntimeException(final Throwable cause) {
super(ConcurrentUtils.checkedException(cause));
}
}

View File

@ -33,10 +33,156 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
public class ConcurrentUtils {
/**
* Private constructor so that no instances can be created. This class
* contains only static utility methods.
* A specialized {@link Future} implementation which wraps a constant value.
* @param <T> the type of the value wrapped by this class
*/
private ConcurrentUtils() {
static final class ConstantFuture<T> implements Future<T> {
/** The constant value. */
private final T value;
/**
* Creates a new instance of {@link ConstantFuture} and initializes it
* with the constant value.
*
* @param value the value (may be <b>null</b>)
*/
ConstantFuture(final T value) {
this.value = value;
}
/**
* {@inheritDoc} The cancel operation is not supported. This
* implementation always returns <b>false</b>.
*/
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
return false;
}
/**
* {@inheritDoc} This implementation just returns the constant value.
*/
@Override
public T get() {
return value;
}
/**
* {@inheritDoc} This implementation just returns the constant value; it
* does not block, therefore the timeout has no meaning.
*/
@Override
public T get(final long timeout, final TimeUnit unit) {
return value;
}
/**
* {@inheritDoc} This implementation always returns <b>false</b>; there
* is no background process which could be cancelled.
*/
@Override
public boolean isCancelled() {
return false;
}
/**
* {@inheritDoc} This implementation always returns <b>true</b> because
* the constant object managed by this {@link Future} implementation is
* always available.
*/
@Override
public boolean isDone() {
return true;
}
}
/**
* Tests whether the specified {@link Throwable} is a checked exception. If
* not, an exception is thrown.
*
* @param ex the {@link Throwable} to check
* @return a flag whether the passed in exception is a checked exception
* @throws IllegalArgumentException if the {@link Throwable} is not a
* checked exception
*/
static Throwable checkedException(final Throwable ex) {
Validate.isTrue(ExceptionUtils.isChecked(ex), "Not a checked exception: " + ex);
return ex;
}
/**
* Gets an implementation of {@link Future} that is immediately done
* and returns the specified constant value.
*
* <p>
* This can be useful to return a simple constant immediately from the
* concurrent processing, perhaps as part of avoiding nulls.
* A constant future can also be useful in testing.
* </p>
*
* @param <T> the type of the value used by this {@link Future} object
* @param value the constant value to return, may be null
* @return an instance of Future that will return the value, never null
*/
public static <T> Future<T> constantFuture(final T value) {
return new ConstantFuture<>(value);
}
/**
* Checks if a concurrent map contains a key and creates a corresponding
* value if not. This method first checks the presence of the key in the
* given map. If it is already contained, its value is returned. Otherwise
* the {@code get()} method of the passed in {@link ConcurrentInitializer}
* is called. With the resulting object
* {@link #putIfAbsent(ConcurrentMap, Object, Object)} is called. This
* handles the case that in the meantime another thread has added the key to
* the map. Both the map and the initializer can be <b>null</b>; in this
* case this method simply returns <b>null</b>.
*
* @param <K> the type of the keys of the map
* @param <V> the type of the values of the map
* @param map the map to be modified
* @param key the key of the value to be added
* @param init the {@link ConcurrentInitializer} for creating the value
* @return the value stored in the map after this operation; this may or may
* not be the object created by the {@link ConcurrentInitializer}
* @throws ConcurrentException if the initializer throws an exception
*/
public static <K, V> V createIfAbsent(final ConcurrentMap<K, V> map, final K key,
final ConcurrentInitializer<V> init) throws ConcurrentException {
if (map == null || init == null) {
return null;
}
final V value = map.get(key);
if (value == null) {
return putIfAbsent(map, key, init.get());
}
return value;
}
/**
* Checks if a concurrent map contains a key and creates a corresponding
* value if not, suppressing checked exceptions. This method calls
* {@code createIfAbsent()}. If a {@link ConcurrentException} is thrown, it
* is caught and re-thrown as a {@link ConcurrentRuntimeException}.
*
* @param <K> the type of the keys of the map
* @param <V> the type of the values of the map
* @param map the map to be modified
* @param key the key of the value to be added
* @param init the {@link ConcurrentInitializer} for creating the value
* @return the value stored in the map after this operation; this may or may
* not be the object created by the {@link ConcurrentInitializer}
* @throws ConcurrentRuntimeException if the initializer throws an exception
*/
public static <K, V> V createIfAbsentUnchecked(final ConcurrentMap<K, V> map,
final K key, final ConcurrentInitializer<V> init) {
try {
return createIfAbsent(map, key, init);
} catch (final ConcurrentException cex) {
throw new ConcurrentRuntimeException(cex.getCause());
}
}
/**
@ -130,20 +276,6 @@ public class ConcurrentUtils {
}
}
/**
* Tests whether the specified {@link Throwable} is a checked exception. If
* not, an exception is thrown.
*
* @param ex the {@link Throwable} to check
* @return a flag whether the passed in exception is a checked exception
* @throws IllegalArgumentException if the {@link Throwable} is not a
* checked exception
*/
static Throwable checkedException(final Throwable ex) {
Validate.isTrue(ExceptionUtils.isChecked(ex), "Not a checked exception: " + ex);
return ex;
}
/**
* Invokes the specified {@link ConcurrentInitializer} and returns the
* object produced by the initializer. This method just invokes the {@code
@ -225,142 +357,10 @@ public class ConcurrentUtils {
}
/**
* Checks if a concurrent map contains a key and creates a corresponding
* value if not. This method first checks the presence of the key in the
* given map. If it is already contained, its value is returned. Otherwise
* the {@code get()} method of the passed in {@link ConcurrentInitializer}
* is called. With the resulting object
* {@link #putIfAbsent(ConcurrentMap, Object, Object)} is called. This
* handles the case that in the meantime another thread has added the key to
* the map. Both the map and the initializer can be <b>null</b>; in this
* case this method simply returns <b>null</b>.
*
* @param <K> the type of the keys of the map
* @param <V> the type of the values of the map
* @param map the map to be modified
* @param key the key of the value to be added
* @param init the {@link ConcurrentInitializer} for creating the value
* @return the value stored in the map after this operation; this may or may
* not be the object created by the {@link ConcurrentInitializer}
* @throws ConcurrentException if the initializer throws an exception
* Private constructor so that no instances can be created. This class
* contains only static utility methods.
*/
public static <K, V> V createIfAbsent(final ConcurrentMap<K, V> map, final K key,
final ConcurrentInitializer<V> init) throws ConcurrentException {
if (map == null || init == null) {
return null;
}
final V value = map.get(key);
if (value == null) {
return putIfAbsent(map, key, init.get());
}
return value;
}
/**
* Checks if a concurrent map contains a key and creates a corresponding
* value if not, suppressing checked exceptions. This method calls
* {@code createIfAbsent()}. If a {@link ConcurrentException} is thrown, it
* is caught and re-thrown as a {@link ConcurrentRuntimeException}.
*
* @param <K> the type of the keys of the map
* @param <V> the type of the values of the map
* @param map the map to be modified
* @param key the key of the value to be added
* @param init the {@link ConcurrentInitializer} for creating the value
* @return the value stored in the map after this operation; this may or may
* not be the object created by the {@link ConcurrentInitializer}
* @throws ConcurrentRuntimeException if the initializer throws an exception
*/
public static <K, V> V createIfAbsentUnchecked(final ConcurrentMap<K, V> map,
final K key, final ConcurrentInitializer<V> init) {
try {
return createIfAbsent(map, key, init);
} catch (final ConcurrentException cex) {
throw new ConcurrentRuntimeException(cex.getCause());
}
}
/**
* Gets an implementation of {@link Future} that is immediately done
* and returns the specified constant value.
*
* <p>
* This can be useful to return a simple constant immediately from the
* concurrent processing, perhaps as part of avoiding nulls.
* A constant future can also be useful in testing.
* </p>
*
* @param <T> the type of the value used by this {@link Future} object
* @param value the constant value to return, may be null
* @return an instance of Future that will return the value, never null
*/
public static <T> Future<T> constantFuture(final T value) {
return new ConstantFuture<>(value);
}
/**
* A specialized {@link Future} implementation which wraps a constant value.
* @param <T> the type of the value wrapped by this class
*/
static final class ConstantFuture<T> implements Future<T> {
/** The constant value. */
private final T value;
/**
* Creates a new instance of {@link ConstantFuture} and initializes it
* with the constant value.
*
* @param value the value (may be <b>null</b>)
*/
ConstantFuture(final T value) {
this.value = value;
}
/**
* {@inheritDoc} This implementation always returns <b>true</b> because
* the constant object managed by this {@link Future} implementation is
* always available.
*/
@Override
public boolean isDone() {
return true;
}
/**
* {@inheritDoc} This implementation just returns the constant value.
*/
@Override
public T get() {
return value;
}
/**
* {@inheritDoc} This implementation just returns the constant value; it
* does not block, therefore the timeout has no meaning.
*/
@Override
public T get(final long timeout, final TimeUnit unit) {
return value;
}
/**
* {@inheritDoc} This implementation always returns <b>false</b>; there
* is no background process which could be cancelled.
*/
@Override
public boolean isCancelled() {
return false;
}
/**
* {@inheritDoc} The cancel operation is not supported. This
* implementation always returns <b>false</b>.
*/
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
return false;
}
private ConcurrentUtils() {
}
}

View File

@ -57,51 +57,6 @@ public class ConstantInitializer<T> implements ConcurrentInitializer<T> {
object = obj;
}
/**
* Directly returns the object that was passed to the constructor. This is
* the same object as returned by {@code get()}. However, this method does
* not declare that it throws an exception.
*
* @return the object managed by this initializer
*/
public final T getObject() {
return object;
}
/**
* Returns the object managed by this initializer. This implementation just
* returns the object passed to the constructor.
*
* @return the object managed by this initializer
* @throws ConcurrentException if an error occurs
*/
@Override
public T get() throws ConcurrentException {
return getObject();
}
/**
* As a {@link ConstantInitializer} is initialized on construction this will
* always return true.
*
* @return true.
* @since 3.14.0
*/
public boolean isInitialized() {
return true;
}
/**
* Returns a hash code for this object. This implementation returns the hash
* code of the managed object.
*
* @return a hash code for this object
*/
@Override
public int hashCode() {
return Objects.hashCode(object);
}
/**
* Compares this object with another one. This implementation returns
* <b>true</b> if and only if the passed in object is an instance of
@ -124,6 +79,51 @@ public class ConstantInitializer<T> implements ConcurrentInitializer<T> {
return Objects.equals(getObject(), c.getObject());
}
/**
* Returns the object managed by this initializer. This implementation just
* returns the object passed to the constructor.
*
* @return the object managed by this initializer
* @throws ConcurrentException if an error occurs
*/
@Override
public T get() throws ConcurrentException {
return getObject();
}
/**
* Directly returns the object that was passed to the constructor. This is
* the same object as returned by {@code get()}. However, this method does
* not declare that it throws an exception.
*
* @return the object managed by this initializer
*/
public final T getObject() {
return object;
}
/**
* Returns a hash code for this object. This implementation returns the hash
* code of the managed object.
*
* @return a hash code for this object
*/
@Override
public int hashCode() {
return Objects.hashCode(object);
}
/**
* As a {@link ConstantInitializer} is initialized on construction this will
* always return true.
*
* @return true.
* @since 3.14.0
*/
public boolean isInitialized() {
return true;
}
/**
* Returns a string representation for this object. This string also
* contains a string representation of the object managed by this

View File

@ -137,9 +137,176 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
/**
* An internally used data class holding information about the checks performed by
* this class. Basically, the number of received events and the start time of the
* current check interval are stored.
*/
private static final class CheckIntervalData {
/** The counter for events. */
private final int eventCount;
/** The start time of the current check interval. */
private final long checkIntervalStart;
/**
* Creates a new instance of {@link CheckIntervalData}.
*
* @param count the current count value
* @param intervalStart the start time of the check interval
*/
CheckIntervalData(final int count, final long intervalStart) {
eventCount = count;
checkIntervalStart = intervalStart;
}
/**
* Returns the start time of the current check interval.
*
* @return the check interval start time
*/
public long getCheckIntervalStart() {
return checkIntervalStart;
}
/**
* Returns the event counter.
*
* @return the number of received events
*/
public int getEventCount() {
return eventCount;
}
/**
* Returns a new instance of {@link CheckIntervalData} with the event counter
* incremented by the given delta. If the delta is 0, this object is returned.
*
* @param delta the delta
* @return the updated instance
*/
public CheckIntervalData increment(final int delta) {
return delta == 0 ? this : new CheckIntervalData(getEventCount() + delta,
getCheckIntervalStart());
}
}
/**
* Internally used class for executing check logic based on the current state of the
* circuit breaker. Having this logic extracted into special classes avoids complex
* if-then-else cascades.
*/
private abstract static class StateStrategy {
/**
* Obtains the check interval to applied for the represented state from the given
* {@link CircuitBreaker}.
*
* @param breaker the {@link CircuitBreaker}
* @return the check interval to be applied
*/
protected abstract long fetchCheckInterval(EventCountCircuitBreaker breaker);
/**
* Returns a flag whether the end of the current check interval is reached.
*
* @param breaker the {@link CircuitBreaker}
* @param currentData the current state object
* @param now the current time
* @return a flag whether the end of the current check interval is reached
*/
public boolean isCheckIntervalFinished(final EventCountCircuitBreaker breaker,
final CheckIntervalData currentData, final long now) {
return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker);
}
/**
* Checks whether the specified {@link CheckIntervalData} objects indicate that a
* state transition should occur. Here the logic which checks for thresholds
* depending on the current state is implemented.
*
* @param breaker the {@link CircuitBreaker}
* @param currentData the current {@link CheckIntervalData} object
* @param nextData the updated {@link CheckIntervalData} object
* @return a flag whether a state transition should be performed
*/
public abstract boolean isStateTransition(EventCountCircuitBreaker breaker,
CheckIntervalData currentData, CheckIntervalData nextData);
}
/**
* A specialized {@link StateStrategy} implementation for the state closed.
*/
private static final class StateStrategyClosed extends StateStrategy {
/**
* {@inheritDoc}
*/
@Override
protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
return breaker.getOpeningInterval();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isStateTransition(final EventCountCircuitBreaker breaker,
final CheckIntervalData currentData, final CheckIntervalData nextData) {
return nextData.getEventCount() > breaker.getOpeningThreshold();
}
}
/**
* A specialized {@link StateStrategy} implementation for the state open.
*/
private static final class StateStrategyOpen extends StateStrategy {
/**
* {@inheritDoc}
*/
@Override
protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
return breaker.getClosingInterval();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isStateTransition(final EventCountCircuitBreaker breaker,
final CheckIntervalData currentData, final CheckIntervalData nextData) {
return nextData.getCheckIntervalStart() != currentData
.getCheckIntervalStart()
&& currentData.getEventCount() < breaker.getClosingThreshold();
}
}
/** A map for accessing the strategy objects for the different states. */
private static final Map<State, StateStrategy> STRATEGY_MAP = createStrategyMap();
/**
* Creates the map with strategy objects. It allows access for a strategy for a given
* state.
*
* @return the strategy map
*/
private static Map<State, StateStrategy> createStrategyMap() {
final Map<State, StateStrategy> map = new EnumMap<>(State.class);
map.put(State.CLOSED, new StateStrategyClosed());
map.put(State.OPEN, new StateStrategyOpen());
return map;
}
/**
* Returns the {@link StateStrategy} object responsible for the given state.
*
* @param state the state
* @return the corresponding {@link StateStrategy}
* @throws CircuitBreakingException if the strategy cannot be resolved
*/
private static StateStrategy stateStrategy(final State state) {
return STRATEGY_MAP.get(state);
}
/** Stores information about the current check interval. */
private final AtomicReference<CheckIntervalData> checkIntervalData;
@ -155,6 +322,39 @@ public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
/** The time interval for closing the circuit breaker. */
private final long closingInterval;
/**
* Creates a new instance of {@link EventCountCircuitBreaker} which uses the same parameters for
* opening and closing checks.
*
* @param threshold the threshold for changing the status of the circuit breaker; if
* the number of events received in a check interval is greater than this value, the
* circuit breaker is opened; if it is lower than this value, it is closed again
* @param checkInterval the check interval for opening or closing the circuit breaker
* @param checkUnit the {@link TimeUnit} defining the check interval
*/
public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) {
this(threshold, checkInterval, checkUnit, threshold);
}
/**
* Creates a new instance of {@link EventCountCircuitBreaker} with the same interval for opening
* and closing checks.
*
* @param openingThreshold the threshold for opening the circuit breaker; if this
* number of events is received in the time span determined by the check interval, the
* circuit breaker is opened
* @param checkInterval the check interval for opening or closing the circuit breaker
* @param checkUnit the {@link TimeUnit} defining the check interval
* @param closingThreshold the threshold for closing the circuit breaker; if the
* number of events received in the time span determined by the check interval goes
* below this threshold, the circuit breaker is closed again
*/
public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit,
final int closingThreshold) {
this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval,
checkUnit);
}
/**
* Creates a new instance of {@link EventCountCircuitBreaker} and initializes all properties for
* opening and closing it based on threshold values for events occurring in specific
@ -182,76 +382,14 @@ public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
}
/**
* Creates a new instance of {@link EventCountCircuitBreaker} with the same interval for opening
* and closing checks.
* Changes the state of this circuit breaker and also initializes a new
* {@link CheckIntervalData} object.
*
* @param openingThreshold the threshold for opening the circuit breaker; if this
* number of events is received in the time span determined by the check interval, the
* circuit breaker is opened
* @param checkInterval the check interval for opening or closing the circuit breaker
* @param checkUnit the {@link TimeUnit} defining the check interval
* @param closingThreshold the threshold for closing the circuit breaker; if the
* number of events received in the time span determined by the check interval goes
* below this threshold, the circuit breaker is closed again
* @param newState the new state to be set
*/
public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit,
final int closingThreshold) {
this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval,
checkUnit);
}
/**
* Creates a new instance of {@link EventCountCircuitBreaker} which uses the same parameters for
* opening and closing checks.
*
* @param threshold the threshold for changing the status of the circuit breaker; if
* the number of events received in a check interval is greater than this value, the
* circuit breaker is opened; if it is lower than this value, it is closed again
* @param checkInterval the check interval for opening or closing the circuit breaker
* @param checkUnit the {@link TimeUnit} defining the check interval
*/
public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) {
this(threshold, checkInterval, checkUnit, threshold);
}
/**
* Returns the threshold value for opening the circuit breaker. If this number of
* events is received in the time span determined by the opening interval, the circuit
* breaker is opened.
*
* @return the opening threshold
*/
public int getOpeningThreshold() {
return openingThreshold;
}
/**
* Returns the interval (in nanoseconds) for checking for the opening threshold.
*
* @return the opening check interval
*/
public long getOpeningInterval() {
return openingInterval;
}
/**
* Returns the threshold value for closing the circuit breaker. If the number of
* events received in the time span determined by the closing interval goes below this
* threshold, the circuit breaker is closed again.
*
* @return the closing threshold
*/
public int getClosingThreshold() {
return closingThreshold;
}
/**
* Returns the interval (in nanoseconds) for checking for the closing threshold.
*
* @return the opening check interval
*/
public long getClosingInterval() {
return closingInterval;
private void changeStateAndStartNewCheckInterval(final State newState) {
changeState(newState);
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
}
/**
@ -269,10 +407,57 @@ public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
/**
* {@inheritDoc}
* <p>
* A new check interval is started. If too many events are received in
* this interval, the circuit breaker changes again to state open. If this circuit
* breaker is already closed, this method has no effect, except that a new check
* interval is started.
* </p>
*/
@Override
public boolean incrementAndCheckState(final Integer increment) {
return performStateCheck(increment);
public void close() {
super.close();
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
}
/**
* Returns the interval (in nanoseconds) for checking for the closing threshold.
*
* @return the opening check interval
*/
public long getClosingInterval() {
return closingInterval;
}
/**
* Returns the threshold value for closing the circuit breaker. If the number of
* events received in the time span determined by the closing interval goes below this
* threshold, the circuit breaker is closed again.
*
* @return the closing threshold
*/
public int getClosingThreshold() {
return closingThreshold;
}
/**
* Returns the interval (in nanoseconds) for checking for the opening threshold.
*
* @return the opening check interval
*/
public long getOpeningInterval() {
return openingInterval;
}
/**
* Returns the threshold value for opening the circuit breaker. If this number of
* events is received in the time span determined by the opening interval, the circuit
* breaker is opened.
*
* @return the opening threshold
*/
public int getOpeningThreshold() {
return openingThreshold;
}
/**
@ -287,6 +472,46 @@ public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
return incrementAndCheckState(1);
}
/**
* {@inheritDoc}
*/
@Override
public boolean incrementAndCheckState(final Integer increment) {
return performStateCheck(increment);
}
/**
* Returns the current time in nanoseconds. This method is used to obtain the current
* time. This is needed to calculate the check intervals correctly.
*
* @return the current time in nanoseconds
*/
long nanoTime() {
return System.nanoTime();
}
/**
* Calculates the next {@link CheckIntervalData} object based on the current data and
* the current state. The next data object takes the counter increment and the current
* time into account.
*
* @param increment the increment for the internal counter
* @param currentData the current check data object
* @param currentState the current state of the circuit breaker
* @param time the current time
* @return the updated {@link CheckIntervalData} object
*/
private CheckIntervalData nextCheckIntervalData(final int increment,
final CheckIntervalData currentData, final State currentState, final long time) {
final CheckIntervalData nextData;
if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) {
nextData = new CheckIntervalData(increment, time);
} else {
nextData = currentData.increment(increment);
}
return nextData;
}
/**
* {@inheritDoc}
* <p>
@ -302,21 +527,6 @@ public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
}
/**
* {@inheritDoc}
* <p>
* A new check interval is started. If too many events are received in
* this interval, the circuit breaker changes again to state open. If this circuit
* breaker is already closed, this method has no effect, except that a new check
* interval is started.
* </p>
*/
@Override
public void close() {
super.close();
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
}
/**
* Actually checks the state of this circuit breaker and executes a state transition
* if necessary.
@ -361,214 +571,4 @@ public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
|| checkIntervalData.compareAndSet(currentData, nextData);
}
/**
* Changes the state of this circuit breaker and also initializes a new
* {@link CheckIntervalData} object.
*
* @param newState the new state to be set
*/
private void changeStateAndStartNewCheckInterval(final State newState) {
changeState(newState);
checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
}
/**
* Calculates the next {@link CheckIntervalData} object based on the current data and
* the current state. The next data object takes the counter increment and the current
* time into account.
*
* @param increment the increment for the internal counter
* @param currentData the current check data object
* @param currentState the current state of the circuit breaker
* @param time the current time
* @return the updated {@link CheckIntervalData} object
*/
private CheckIntervalData nextCheckIntervalData(final int increment,
final CheckIntervalData currentData, final State currentState, final long time) {
final CheckIntervalData nextData;
if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) {
nextData = new CheckIntervalData(increment, time);
} else {
nextData = currentData.increment(increment);
}
return nextData;
}
/**
* Returns the current time in nanoseconds. This method is used to obtain the current
* time. This is needed to calculate the check intervals correctly.
*
* @return the current time in nanoseconds
*/
long nanoTime() {
return System.nanoTime();
}
/**
* Returns the {@link StateStrategy} object responsible for the given state.
*
* @param state the state
* @return the corresponding {@link StateStrategy}
* @throws CircuitBreakingException if the strategy cannot be resolved
*/
private static StateStrategy stateStrategy(final State state) {
return STRATEGY_MAP.get(state);
}
/**
* Creates the map with strategy objects. It allows access for a strategy for a given
* state.
*
* @return the strategy map
*/
private static Map<State, StateStrategy> createStrategyMap() {
final Map<State, StateStrategy> map = new EnumMap<>(State.class);
map.put(State.CLOSED, new StateStrategyClosed());
map.put(State.OPEN, new StateStrategyOpen());
return map;
}
/**
* An internally used data class holding information about the checks performed by
* this class. Basically, the number of received events and the start time of the
* current check interval are stored.
*/
private static final class CheckIntervalData {
/** The counter for events. */
private final int eventCount;
/** The start time of the current check interval. */
private final long checkIntervalStart;
/**
* Creates a new instance of {@link CheckIntervalData}.
*
* @param count the current count value
* @param intervalStart the start time of the check interval
*/
CheckIntervalData(final int count, final long intervalStart) {
eventCount = count;
checkIntervalStart = intervalStart;
}
/**
* Returns the event counter.
*
* @return the number of received events
*/
public int getEventCount() {
return eventCount;
}
/**
* Returns the start time of the current check interval.
*
* @return the check interval start time
*/
public long getCheckIntervalStart() {
return checkIntervalStart;
}
/**
* Returns a new instance of {@link CheckIntervalData} with the event counter
* incremented by the given delta. If the delta is 0, this object is returned.
*
* @param delta the delta
* @return the updated instance
*/
public CheckIntervalData increment(final int delta) {
return delta == 0 ? this : new CheckIntervalData(getEventCount() + delta,
getCheckIntervalStart());
}
}
/**
* Internally used class for executing check logic based on the current state of the
* circuit breaker. Having this logic extracted into special classes avoids complex
* if-then-else cascades.
*/
private abstract static class StateStrategy {
/**
* Returns a flag whether the end of the current check interval is reached.
*
* @param breaker the {@link CircuitBreaker}
* @param currentData the current state object
* @param now the current time
* @return a flag whether the end of the current check interval is reached
*/
public boolean isCheckIntervalFinished(final EventCountCircuitBreaker breaker,
final CheckIntervalData currentData, final long now) {
return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker);
}
/**
* Checks whether the specified {@link CheckIntervalData} objects indicate that a
* state transition should occur. Here the logic which checks for thresholds
* depending on the current state is implemented.
*
* @param breaker the {@link CircuitBreaker}
* @param currentData the current {@link CheckIntervalData} object
* @param nextData the updated {@link CheckIntervalData} object
* @return a flag whether a state transition should be performed
*/
public abstract boolean isStateTransition(EventCountCircuitBreaker breaker,
CheckIntervalData currentData, CheckIntervalData nextData);
/**
* Obtains the check interval to applied for the represented state from the given
* {@link CircuitBreaker}.
*
* @param breaker the {@link CircuitBreaker}
* @return the check interval to be applied
*/
protected abstract long fetchCheckInterval(EventCountCircuitBreaker breaker);
}
/**
* A specialized {@link StateStrategy} implementation for the state closed.
*/
private static final class StateStrategyClosed extends StateStrategy {
/**
* {@inheritDoc}
*/
@Override
public boolean isStateTransition(final EventCountCircuitBreaker breaker,
final CheckIntervalData currentData, final CheckIntervalData nextData) {
return nextData.getEventCount() > breaker.getOpeningThreshold();
}
/**
* {@inheritDoc}
*/
@Override
protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
return breaker.getOpeningInterval();
}
}
/**
* A specialized {@link StateStrategy} implementation for the state open.
*/
private static final class StateStrategyOpen extends StateStrategy {
/**
* {@inheritDoc}
*/
@Override
public boolean isStateTransition(final EventCountCircuitBreaker breaker,
final CheckIntervalData currentData, final CheckIntervalData nextData) {
return nextData.getCheckIntervalStart() != currentData
.getCheckIntervalStart()
&& currentData.getEventCount() < breaker.getClosingThreshold();
}
/**
* {@inheritDoc}
*/
@Override
protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
return breaker.getClosingInterval();
}
}
}

View File

@ -26,10 +26,6 @@ import java.util.concurrent.FutureTask;
*/
public class FutureTasks {
private FutureTasks() {
// No instances needed.
}
/**
* Creates a {@link FutureTask} and runs the given {@link Callable}.
*
@ -42,4 +38,8 @@ public class FutureTasks {
futureTask.run();
return futureTask;
}
private FutureTasks() {
// No instances needed.
}
}

View File

@ -148,6 +148,14 @@ public class LazyInitializer<T> extends AbstractConcurrentInitializer<T, Concurr
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected ConcurrentException getTypedException(Exception e) {
return new ConcurrentException(e);
}
/**
* Tests whether this instance is initialized. Once initialized, always returns true.
*
@ -159,12 +167,4 @@ public class LazyInitializer<T> extends AbstractConcurrentInitializer<T, Concurr
return object != NO_INIT;
}
/**
* {@inheritDoc}
*/
@Override
protected ConcurrentException getTypedException(Exception e) {
return new ConcurrentException(e);
}
}

View File

@ -97,6 +97,142 @@ public class MultiBackgroundInitializer
extends
BackgroundInitializer<MultiBackgroundInitializer.MultiBackgroundInitializerResults> {
/**
* A data class for storing the results of the background initialization
* performed by {@link MultiBackgroundInitializer}. Objects of this inner
* class are returned by {@link MultiBackgroundInitializer#initialize()}.
* They allow access to all result objects produced by the
* {@link BackgroundInitializer} objects managed by the owning instance. It
* is also possible to retrieve status information about single
* {@link BackgroundInitializer}s, i.e. whether they completed normally or
* caused an exception.
*/
public static class MultiBackgroundInitializerResults {
/** A map with the child initializers. */
private final Map<String, BackgroundInitializer<?>> initializers;
/** A map with the result objects. */
private final Map<String, Object> resultObjects;
/** A map with the exceptions. */
private final Map<String, ConcurrentException> exceptions;
/**
* Creates a new instance of {@link MultiBackgroundInitializerResults}
* and initializes it with maps for the {@link BackgroundInitializer}
* objects, their result objects and the exceptions thrown by them.
*
* @param inits the {@link BackgroundInitializer} objects
* @param results the result objects
* @param excepts the exceptions
*/
private MultiBackgroundInitializerResults(
final Map<String, BackgroundInitializer<?>> inits,
final Map<String, Object> results,
final Map<String, ConcurrentException> excepts) {
initializers = inits;
resultObjects = results;
exceptions = excepts;
}
/**
* Checks whether an initializer with the given name exists. If not,
* throws an exception. If it exists, the associated child initializer
* is returned.
*
* @param name the name to check
* @return the initializer with this name
* @throws NoSuchElementException if the name is unknown
*/
private BackgroundInitializer<?> checkName(final String name) {
final BackgroundInitializer<?> init = initializers.get(name);
if (init == null) {
throw new NoSuchElementException(
"No child initializer with name " + name);
}
return init;
}
/**
* Returns the {@link ConcurrentException} object that was thrown by the
* {@link BackgroundInitializer} with the given name. If this
* initializer did not throw an exception, the return value is
* <b>null</b>. If the name cannot be resolved, an exception is thrown.
*
* @param name the name of the {@link BackgroundInitializer}
* @return the exception thrown by this initializer
* @throws NoSuchElementException if the name cannot be resolved
*/
public ConcurrentException getException(final String name) {
checkName(name);
return exceptions.get(name);
}
/**
* Returns the {@link BackgroundInitializer} with the given name. If the
* name cannot be resolved, an exception is thrown.
*
* @param name the name of the {@link BackgroundInitializer}
* @return the {@link BackgroundInitializer} with this name
* @throws NoSuchElementException if the name cannot be resolved
*/
public BackgroundInitializer<?> getInitializer(final String name) {
return checkName(name);
}
/**
* Returns the result object produced by the {@code
* BackgroundInitializer} with the given name. This is the object
* returned by the initializer's {@code initialize()} method. If this
* {@link BackgroundInitializer} caused an exception, <b>null</b> is
* returned. If the name cannot be resolved, an exception is thrown.
*
* @param name the name of the {@link BackgroundInitializer}
* @return the result object produced by this {@code
* BackgroundInitializer}
* @throws NoSuchElementException if the name cannot be resolved
*/
public Object getResultObject(final String name) {
checkName(name);
return resultObjects.get(name);
}
/**
* Returns a set with the names of all {@link BackgroundInitializer}
* objects managed by the {@link MultiBackgroundInitializer}.
*
* @return an (unmodifiable) set with the names of the managed {@code
* BackgroundInitializer} objects
*/
public Set<String> initializerNames() {
return Collections.unmodifiableSet(initializers.keySet());
}
/**
* Returns a flag whether the {@link BackgroundInitializer} with the
* given name caused an exception.
*
* @param name the name of the {@link BackgroundInitializer}
* @return a flag whether this initializer caused an exception
* @throws NoSuchElementException if the name cannot be resolved
*/
public boolean isException(final String name) {
checkName(name);
return exceptions.containsKey(name);
}
/**
* Returns a flag whether the whole initialization was successful. This
* is the case if no child initializer has thrown an exception.
*
* @return a flag whether the initialization was successful
*/
public boolean isSuccessful() {
return exceptions.isEmpty();
}
}
/** A map with the child initializers. */
private final Map<String, BackgroundInitializer<?>> childInitializers = new HashMap<>();
@ -142,6 +278,39 @@ public class MultiBackgroundInitializer
}
}
/**
* Calls the closer of all child {@code BackgroundInitializer} objects
*
* @throws ConcurrentException throws an ConcurrentException that will have all other exceptions as suppressed exceptions. ConcurrentException thrown by children will be unwrapped.
* @since 3.14.0
*/
@Override
public void close() throws ConcurrentException {
ConcurrentException exception = null;
for (BackgroundInitializer<?> child : childInitializers.values()) {
try {
child.close();
} catch (Exception e) {
if (exception == null) {
exception = new ConcurrentException();
}
if (e instanceof ConcurrentException) {
// Because ConcurrentException is only created by classes in this package
// we can safely unwrap it.
exception.addSuppressed(e.getCause());
} else {
exception.addSuppressed(e);
}
}
}
if (exception != null) {
throw exception;
}
}
/**
* Returns the number of tasks needed for executing all child {@code
* BackgroundInitializer} objects in parallel. This implementation sums up
@ -214,173 +383,4 @@ public class MultiBackgroundInitializer
return childInitializers.values().stream().allMatch(BackgroundInitializer::isInitialized);
}
/**
* Calls the closer of all child {@code BackgroundInitializer} objects
*
* @throws ConcurrentException throws an ConcurrentException that will have all other exceptions as suppressed exceptions. ConcurrentException thrown by children will be unwrapped.
* @since 3.14.0
*/
@Override
public void close() throws ConcurrentException {
ConcurrentException exception = null;
for (BackgroundInitializer<?> child : childInitializers.values()) {
try {
child.close();
} catch (Exception e) {
if (exception == null) {
exception = new ConcurrentException();
}
if (e instanceof ConcurrentException) {
// Because ConcurrentException is only created by classes in this package
// we can safely unwrap it.
exception.addSuppressed(e.getCause());
} else {
exception.addSuppressed(e);
}
}
}
if (exception != null) {
throw exception;
}
}
/**
* A data class for storing the results of the background initialization
* performed by {@link MultiBackgroundInitializer}. Objects of this inner
* class are returned by {@link MultiBackgroundInitializer#initialize()}.
* They allow access to all result objects produced by the
* {@link BackgroundInitializer} objects managed by the owning instance. It
* is also possible to retrieve status information about single
* {@link BackgroundInitializer}s, i.e. whether they completed normally or
* caused an exception.
*/
public static class MultiBackgroundInitializerResults {
/** A map with the child initializers. */
private final Map<String, BackgroundInitializer<?>> initializers;
/** A map with the result objects. */
private final Map<String, Object> resultObjects;
/** A map with the exceptions. */
private final Map<String, ConcurrentException> exceptions;
/**
* Creates a new instance of {@link MultiBackgroundInitializerResults}
* and initializes it with maps for the {@link BackgroundInitializer}
* objects, their result objects and the exceptions thrown by them.
*
* @param inits the {@link BackgroundInitializer} objects
* @param results the result objects
* @param excepts the exceptions
*/
private MultiBackgroundInitializerResults(
final Map<String, BackgroundInitializer<?>> inits,
final Map<String, Object> results,
final Map<String, ConcurrentException> excepts) {
initializers = inits;
resultObjects = results;
exceptions = excepts;
}
/**
* Returns the {@link BackgroundInitializer} with the given name. If the
* name cannot be resolved, an exception is thrown.
*
* @param name the name of the {@link BackgroundInitializer}
* @return the {@link BackgroundInitializer} with this name
* @throws NoSuchElementException if the name cannot be resolved
*/
public BackgroundInitializer<?> getInitializer(final String name) {
return checkName(name);
}
/**
* Returns the result object produced by the {@code
* BackgroundInitializer} with the given name. This is the object
* returned by the initializer's {@code initialize()} method. If this
* {@link BackgroundInitializer} caused an exception, <b>null</b> is
* returned. If the name cannot be resolved, an exception is thrown.
*
* @param name the name of the {@link BackgroundInitializer}
* @return the result object produced by this {@code
* BackgroundInitializer}
* @throws NoSuchElementException if the name cannot be resolved
*/
public Object getResultObject(final String name) {
checkName(name);
return resultObjects.get(name);
}
/**
* Returns a flag whether the {@link BackgroundInitializer} with the
* given name caused an exception.
*
* @param name the name of the {@link BackgroundInitializer}
* @return a flag whether this initializer caused an exception
* @throws NoSuchElementException if the name cannot be resolved
*/
public boolean isException(final String name) {
checkName(name);
return exceptions.containsKey(name);
}
/**
* Returns the {@link ConcurrentException} object that was thrown by the
* {@link BackgroundInitializer} with the given name. If this
* initializer did not throw an exception, the return value is
* <b>null</b>. If the name cannot be resolved, an exception is thrown.
*
* @param name the name of the {@link BackgroundInitializer}
* @return the exception thrown by this initializer
* @throws NoSuchElementException if the name cannot be resolved
*/
public ConcurrentException getException(final String name) {
checkName(name);
return exceptions.get(name);
}
/**
* Returns a set with the names of all {@link BackgroundInitializer}
* objects managed by the {@link MultiBackgroundInitializer}.
*
* @return an (unmodifiable) set with the names of the managed {@code
* BackgroundInitializer} objects
*/
public Set<String> initializerNames() {
return Collections.unmodifiableSet(initializers.keySet());
}
/**
* Returns a flag whether the whole initialization was successful. This
* is the case if no child initializer has thrown an exception.
*
* @return a flag whether the initialization was successful
*/
public boolean isSuccessful() {
return exceptions.isEmpty();
}
/**
* Checks whether an initializer with the given name exists. If not,
* throws an exception. If it exists, the associated child initializer
* is returned.
*
* @param name the name to check
* @return the initializer with this name
* @throws NoSuchElementException if the name is unknown
*/
private BackgroundInitializer<?> checkName(final String name) {
final BackgroundInitializer<?> init = initializers.get(name);
if (init == null) {
throw new NoSuchElementException(
"No child initializer with name " + name);
}
return init;
}
}
}

View File

@ -75,15 +75,6 @@ public class ThresholdCircuitBreaker extends AbstractCircuitBreaker<Long> {
this.threshold = threshold;
}
/**
* Gets the threshold.
*
* @return the threshold
*/
public long getThreshold() {
return threshold;
}
/**
* {@inheritDoc}
*/
@ -103,6 +94,15 @@ public class ThresholdCircuitBreaker extends AbstractCircuitBreaker<Long> {
this.used.set(INITIAL_COUNT);
}
/**
* Gets the threshold.
*
* @return the threshold
*/
public long getThreshold() {
return threshold;
}
/**
* {@inheritDoc}
*

View File

@ -230,63 +230,6 @@ public class TimedSemaphore {
setLimit(limit);
}
/**
* Returns the limit enforced by this semaphore. The limit determines how
* many invocations of {@link #acquire()} are allowed within the monitored
* period.
*
* @return the limit
*/
public final synchronized int getLimit() {
return limit;
}
/**
* Sets the limit. This is the number of times the {@link #acquire()} method
* can be called within the time period specified. If this limit is reached,
* further invocations of {@link #acquire()} will block. Setting the limit
* to a value &lt;= {@link #NO_LIMIT} will cause the limit to be disabled,
* i.e. an arbitrary number of{@link #acquire()} invocations is allowed in
* the time period.
*
* @param limit the limit
*/
public final synchronized void setLimit(final int limit) {
this.limit = limit;
}
/**
* Initializes a shutdown. After that the object cannot be used anymore.
* This method can be invoked an arbitrary number of times. All invocations
* after the first one do not have any effect.
*/
public synchronized void shutdown() {
if (!shutdown) {
if (ownExecutor) {
// if the executor was created by this instance, it has
// to be shutdown
getExecutorService().shutdownNow();
}
if (task != null) {
task.cancel(false);
}
shutdown = true;
}
}
/**
* Tests whether the {@link #shutdown()} method has been called on this
* object. If this method returns <b>true</b>, this instance cannot be used
* any longer.
*
* @return a flag whether a shutdown has been performed
*/
public synchronized boolean isShutdown() {
return shutdown;
}
/**
* Acquires a permit from this semaphore. This method will block if
* the limit for the current period has already been reached. If
@ -311,33 +254,32 @@ public class TimedSemaphore {
}
/**
* Tries to acquire a permit from this semaphore. If the limit of this semaphore has
* not yet been reached, a permit is acquired, and this method returns
* <strong>true</strong>. Otherwise, this method returns immediately with the result
* <strong>false</strong>.
* Internal helper method for acquiring a permit. This method checks whether currently
* a permit can be acquired and - if so - increases the internal counter. The return
* value indicates whether a permit could be acquired. This method must be called with
* the lock of this object held.
*
* @return <strong>true</strong> if a permit could be acquired; <strong>false</strong>
* otherwise
* @throws IllegalStateException if this semaphore is already shut down
* @since 3.5
* @return a flag whether a permit could be acquired
*/
public synchronized boolean tryAcquire() {
prepareAcquire();
return acquirePermit();
private boolean acquirePermit() {
if (getLimit() <= NO_LIMIT || acquireCount < getLimit()) {
acquireCount++;
return true;
}
return false;
}
/**
* Returns the number of (successful) acquire invocations during the last
* period. This is the number of times the {@link #acquire()} method was
* called without blocking. This can be useful for testing or debugging
* purposes or to determine a meaningful threshold value. If a limit is set,
* the value returned by this method won't be greater than this limit.
*
* @return the number of non-blocking invocations of the {@link #acquire()}
* method
* The current time period is finished. This method is called by the timer
* used internally to monitor the time period. It resets the counter and
* releases the threads waiting for this barrier.
*/
public synchronized int getLastAcquiresPerPeriod() {
return lastCallsPerPeriod;
synchronized void endOfPeriod() {
lastCallsPerPeriod = acquireCount;
totalAcquireCount += acquireCount;
periodCount++;
acquireCount = 0;
notifyAll();
}
/**
@ -379,6 +321,40 @@ public class TimedSemaphore {
/ (double) periodCount;
}
/**
* Returns the executor service used by this instance.
*
* @return the executor service
*/
protected ScheduledExecutorService getExecutorService() {
return executorService;
}
/**
* Returns the number of (successful) acquire invocations during the last
* period. This is the number of times the {@link #acquire()} method was
* called without blocking. This can be useful for testing or debugging
* purposes or to determine a meaningful threshold value. If a limit is set,
* the value returned by this method won't be greater than this limit.
*
* @return the number of non-blocking invocations of the {@link #acquire()}
* method
*/
public synchronized int getLastAcquiresPerPeriod() {
return lastCallsPerPeriod;
}
/**
* Returns the limit enforced by this semaphore. The limit determines how
* many invocations of {@link #acquire()} are allowed within the monitored
* period.
*
* @return the limit
*/
public final synchronized int getLimit() {
return limit;
}
/**
* Returns the time period. This is the time monitored by this semaphore.
* Only a given number of invocations of the {@link #acquire()} method is
@ -400,36 +376,14 @@ public class TimedSemaphore {
}
/**
* Returns the executor service used by this instance.
* Tests whether the {@link #shutdown()} method has been called on this
* object. If this method returns <b>true</b>, this instance cannot be used
* any longer.
*
* @return the executor service
* @return a flag whether a shutdown has been performed
*/
protected ScheduledExecutorService getExecutorService() {
return executorService;
}
/**
* Starts the timer. This method is called when {@link #acquire()} is called
* for the first time. It schedules a task to be executed at fixed rate to
* monitor the time period specified.
*
* @return a future object representing the task scheduled
*/
protected ScheduledFuture<?> startTimer() {
return getExecutorService().scheduleAtFixedRate(this::endOfPeriod, getPeriod(), getPeriod(), getUnit());
}
/**
* The current time period is finished. This method is called by the timer
* used internally to monitor the time period. It resets the counter and
* releases the threads waiting for this barrier.
*/
synchronized void endOfPeriod() {
lastCallsPerPeriod = acquireCount;
totalAcquireCount += acquireCount;
periodCount++;
acquireCount = 0;
notifyAll();
public synchronized boolean isShutdown() {
return shutdown;
}
/**
@ -447,18 +401,64 @@ public class TimedSemaphore {
}
/**
* Internal helper method for acquiring a permit. This method checks whether currently
* a permit can be acquired and - if so - increases the internal counter. The return
* value indicates whether a permit could be acquired. This method must be called with
* the lock of this object held.
* Sets the limit. This is the number of times the {@link #acquire()} method
* can be called within the time period specified. If this limit is reached,
* further invocations of {@link #acquire()} will block. Setting the limit
* to a value &lt;= {@link #NO_LIMIT} will cause the limit to be disabled,
* i.e. an arbitrary number of{@link #acquire()} invocations is allowed in
* the time period.
*
* @return a flag whether a permit could be acquired
* @param limit the limit
*/
private boolean acquirePermit() {
if (getLimit() <= NO_LIMIT || acquireCount < getLimit()) {
acquireCount++;
return true;
public final synchronized void setLimit(final int limit) {
this.limit = limit;
}
/**
* Initializes a shutdown. After that the object cannot be used anymore.
* This method can be invoked an arbitrary number of times. All invocations
* after the first one do not have any effect.
*/
public synchronized void shutdown() {
if (!shutdown) {
if (ownExecutor) {
// if the executor was created by this instance, it has
// to be shutdown
getExecutorService().shutdownNow();
}
if (task != null) {
task.cancel(false);
}
shutdown = true;
}
return false;
}
/**
* Starts the timer. This method is called when {@link #acquire()} is called
* for the first time. It schedules a task to be executed at fixed rate to
* monitor the time period specified.
*
* @return a future object representing the task scheduled
*/
protected ScheduledFuture<?> startTimer() {
return getExecutorService().scheduleAtFixedRate(this::endOfPeriod, getPeriod(), getPeriod(), getUnit());
}
/**
* Tries to acquire a permit from this semaphore. If the limit of this semaphore has
* not yet been reached, a permit is acquired, and this method returns
* <strong>true</strong>. Otherwise, this method returns immediately with the result
* <strong>false</strong>.
*
* @return <strong>true</strong> if a permit could be acquired; <strong>false</strong>
* otherwise
* @throws IllegalStateException if this semaphore is already shut down
* @since 3.5
*/
public synchronized boolean tryAcquire() {
prepareAcquire();
return acquirePermit();
}
}

View File

@ -69,9 +69,55 @@ import org.apache.commons.lang3.Validate;
*/
public class EventListenerSupport<L> implements Serializable {
/**
* An invocation handler used to dispatch the event(s) to all the listeners.
*/
protected class ProxyInvocationHandler implements InvocationHandler {
/**
* Propagates the method call to all registered listeners in place of the proxy listener object.
*
* @param unusedProxy the proxy object representing a listener on which the invocation was called; not used
* @param method the listener method that will be called on all of the listeners.
* @param args event arguments to propagate to the listeners.
* @return the result of the method call
* @throws InvocationTargetException if an error occurs
* @throws IllegalArgumentException if an error occurs
* @throws IllegalAccessException if an error occurs
*/
@Override
public Object invoke(final Object unusedProxy, final Method method, final Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (final L listener : listeners) {
method.invoke(listener, args);
}
return null;
}
}
/** Serialization version */
private static final long serialVersionUID = 3593265990380473632L;
/**
* Creates an EventListenerSupport object which supports the specified
* listener type.
*
* @param <T> the type of the listener interface
* @param listenerInterface the type of listener interface that will receive
* events posted using this class.
*
* @return an EventListenerSupport object which supports the specified
* listener type.
*
* @throws NullPointerException if {@code listenerInterface} is
* {@code null}.
* @throws IllegalArgumentException if {@code listenerInterface} is
* not an interface.
*/
public static <T> EventListenerSupport<T> create(final Class<T> listenerInterface) {
return new EventListenerSupport<>(listenerInterface);
}
/**
* The list used to hold the registered listeners. This list is
* intentionally a thread-safe copy-on-write-array so that traversals over
@ -91,23 +137,10 @@ public class EventListenerSupport<L> implements Serializable {
private transient L[] prototypeArray;
/**
* Creates an EventListenerSupport object which supports the specified
* listener type.
*
* @param <T> the type of the listener interface
* @param listenerInterface the type of listener interface that will receive
* events posted using this class.
*
* @return an EventListenerSupport object which supports the specified
* listener type.
*
* @throws NullPointerException if {@code listenerInterface} is
* {@code null}.
* @throws IllegalArgumentException if {@code listenerInterface} is
* not an interface.
* Create a new EventListenerSupport instance.
* Serialization-friendly constructor.
*/
public static <T> EventListenerSupport<T> create(final Class<T> listenerInterface) {
return new EventListenerSupport<>(listenerInterface);
private EventListenerSupport() {
}
/**
@ -148,25 +181,6 @@ public class EventListenerSupport<L> implements Serializable {
initializeTransientFields(listenerInterface, classLoader);
}
/**
* Create a new EventListenerSupport instance.
* Serialization-friendly constructor.
*/
private EventListenerSupport() {
}
/**
* Returns a proxy object which can be used to call listener methods on all
* of the registered event listeners. All calls made to this proxy will be
* forwarded to all registered listeners.
*
* @return a proxy object which can be used to call listener methods on all
* of the registered event listeners
*/
public L fire() {
return proxy;
}
//**********************************************************************************************************************
// Other Methods
//**********************************************************************************************************************
@ -201,6 +215,37 @@ public class EventListenerSupport<L> implements Serializable {
}
}
/**
* Create the {@link InvocationHandler} responsible for broadcasting calls
* to the managed listeners. Subclasses can override to provide custom behavior.
* @return ProxyInvocationHandler
*/
protected InvocationHandler createInvocationHandler() {
return new ProxyInvocationHandler();
}
/**
* Create the proxy object.
* @param listenerInterface the class of the listener interface
* @param classLoader the class loader to be used
*/
private void createProxy(final Class<L> listenerInterface, final ClassLoader classLoader) {
proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader,
new Class[] { listenerInterface }, createInvocationHandler()));
}
/**
* Returns a proxy object which can be used to call listener methods on all
* of the registered event listeners. All calls made to this proxy will be
* forwarded to all registered listeners.
*
* @return a proxy object which can be used to call listener methods on all
* of the registered event listeners
*/
public L fire() {
return proxy;
}
/**
* Returns the number of registered listeners.
*
@ -210,6 +255,44 @@ public class EventListenerSupport<L> implements Serializable {
return listeners.size();
}
/**
* Gets an array containing the currently registered listeners.
* Modification to this array's elements will have no effect on the
* {@link EventListenerSupport} instance.
* @return L[]
*/
public L[] getListeners() {
return listeners.toArray(prototypeArray);
}
/**
* Initialize transient fields.
* @param listenerInterface the class of the listener interface
* @param classLoader the class loader to be used
*/
private void initializeTransientFields(final Class<L> listenerInterface, final ClassLoader classLoader) {
// Will throw CCE here if not correct
this.prototypeArray = ArrayUtils.newInstance(listenerInterface, 0);
createProxy(listenerInterface, classLoader);
}
/**
* Deserialize.
* @param objectInputStream the input stream
* @throws IOException if an IO error occurs
* @throws ClassNotFoundException if the class cannot be resolved
*/
private void readObject(final ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
@SuppressWarnings("unchecked") // Will throw CCE here if not correct
final L[] srcListeners = (L[]) objectInputStream.readObject();
this.listeners = new CopyOnWriteArrayList<>(srcListeners);
final Class<L> listenerInterface = ArrayUtils.getComponentType(srcListeners);
initializeTransientFields(listenerInterface, Thread.currentThread().getContextClassLoader());
}
/**
* Unregisters an event listener.
*
@ -223,16 +306,6 @@ public class EventListenerSupport<L> implements Serializable {
listeners.remove(listener);
}
/**
* Gets an array containing the currently registered listeners.
* Modification to this array's elements will have no effect on the
* {@link EventListenerSupport} instance.
* @return L[]
*/
public L[] getListeners() {
return listeners.toArray(prototypeArray);
}
/**
* Serialize.
* @param objectOutputStream the output stream
@ -258,77 +331,4 @@ public class EventListenerSupport<L> implements Serializable {
*/
objectOutputStream.writeObject(serializableListeners.toArray(prototypeArray));
}
/**
* Deserialize.
* @param objectInputStream the input stream
* @throws IOException if an IO error occurs
* @throws ClassNotFoundException if the class cannot be resolved
*/
private void readObject(final ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
@SuppressWarnings("unchecked") // Will throw CCE here if not correct
final L[] srcListeners = (L[]) objectInputStream.readObject();
this.listeners = new CopyOnWriteArrayList<>(srcListeners);
final Class<L> listenerInterface = ArrayUtils.getComponentType(srcListeners);
initializeTransientFields(listenerInterface, Thread.currentThread().getContextClassLoader());
}
/**
* Initialize transient fields.
* @param listenerInterface the class of the listener interface
* @param classLoader the class loader to be used
*/
private void initializeTransientFields(final Class<L> listenerInterface, final ClassLoader classLoader) {
// Will throw CCE here if not correct
this.prototypeArray = ArrayUtils.newInstance(listenerInterface, 0);
createProxy(listenerInterface, classLoader);
}
/**
* Create the proxy object.
* @param listenerInterface the class of the listener interface
* @param classLoader the class loader to be used
*/
private void createProxy(final Class<L> listenerInterface, final ClassLoader classLoader) {
proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader,
new Class[] { listenerInterface }, createInvocationHandler()));
}
/**
* Create the {@link InvocationHandler} responsible for broadcasting calls
* to the managed listeners. Subclasses can override to provide custom behavior.
* @return ProxyInvocationHandler
*/
protected InvocationHandler createInvocationHandler() {
return new ProxyInvocationHandler();
}
/**
* An invocation handler used to dispatch the event(s) to all the listeners.
*/
protected class ProxyInvocationHandler implements InvocationHandler {
/**
* Propagates the method call to all registered listeners in place of the proxy listener object.
*
* @param unusedProxy the proxy object representing a listener on which the invocation was called; not used
* @param method the listener method that will be called on all of the listeners.
* @param args event arguments to propagate to the listeners.
* @return the result of the method call
* @throws InvocationTargetException if an error occurs
* @throws IllegalArgumentException if an error occurs
* @throws IllegalAccessException if an error occurs
*/
@Override
public Object invoke(final Object unusedProxy, final Method method, final Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (final L listener : listeners) {
method.invoke(listener, args);
}
return null;
}
}
}

View File

@ -34,6 +34,55 @@ import org.apache.commons.lang3.reflect.MethodUtils;
*/
public class EventUtils {
private static final class EventBindingInvocationHandler implements InvocationHandler {
private final Object target;
private final String methodName;
private final Set<String> eventTypes;
/**
* Creates a new instance of {@link EventBindingInvocationHandler}.
*
* @param target the target object for method invocations
* @param methodName the name of the method to be invoked
* @param eventTypes the names of the supported event types
*/
EventBindingInvocationHandler(final Object target, final String methodName, final String[] eventTypes) {
this.target = target;
this.methodName = methodName;
this.eventTypes = new HashSet<>(Arrays.asList(eventTypes));
}
/**
* Checks whether a method for the passed in parameters can be found.
*
* @param method the listener method invoked
* @return a flag whether the parameters could be matched
*/
private boolean hasMatchingParametersMethod(final Method method) {
return MethodUtils.getAccessibleMethod(target.getClass(), methodName, method.getParameterTypes()) != null;
}
/**
* Handles a method invocation on the proxy object.
*
* @param proxy the proxy instance
* @param method the method to be invoked
* @param parameters the parameters for the method invocation
* @return the result of the method call
* @throws Throwable if an error occurs
*/
@Override
public Object invoke(final Object proxy, final Method method, final Object[] parameters) throws Throwable {
if (eventTypes.isEmpty() || eventTypes.contains(method.getName())) {
if (hasMatchingParametersMethod(method)) {
return MethodUtils.invokeMethod(target, methodName, parameters);
}
return MethodUtils.invokeMethod(target, methodName);
}
return null;
}
}
/**
* Adds an event listener to the specified source. This looks for an "add" method corresponding to the event
* type (addActionListener, for example).
@ -77,53 +126,4 @@ public class EventUtils {
new Class[] { listenerType }, new EventBindingInvocationHandler(target, methodName, eventTypes)));
addEventListener(eventSource, listenerType, listener);
}
private static final class EventBindingInvocationHandler implements InvocationHandler {
private final Object target;
private final String methodName;
private final Set<String> eventTypes;
/**
* Creates a new instance of {@link EventBindingInvocationHandler}.
*
* @param target the target object for method invocations
* @param methodName the name of the method to be invoked
* @param eventTypes the names of the supported event types
*/
EventBindingInvocationHandler(final Object target, final String methodName, final String[] eventTypes) {
this.target = target;
this.methodName = methodName;
this.eventTypes = new HashSet<>(Arrays.asList(eventTypes));
}
/**
* Handles a method invocation on the proxy object.
*
* @param proxy the proxy instance
* @param method the method to be invoked
* @param parameters the parameters for the method invocation
* @return the result of the method call
* @throws Throwable if an error occurs
*/
@Override
public Object invoke(final Object proxy, final Method method, final Object[] parameters) throws Throwable {
if (eventTypes.isEmpty() || eventTypes.contains(method.getName())) {
if (hasMatchingParametersMethod(method)) {
return MethodUtils.invokeMethod(target, methodName, parameters);
}
return MethodUtils.invokeMethod(target, methodName);
}
return null;
}
/**
* Checks whether a method for the passed in parameters can be found.
*
* @param method the listener method invoked
* @return a flag whether the parameters could be matched
*/
private boolean hasMatchingParametersMethod(final Method method) {
return MethodUtils.getAccessibleMethod(target.getClass(), methodName, method.getParameterTypes()) != null;
}
}
}

View File

@ -35,15 +35,6 @@ public class CloneFailedException extends RuntimeException {
super(message);
}
/**
* Constructs a CloneFailedException.
*
* @param cause cause of the exception
*/
public CloneFailedException(final Throwable cause) {
super(cause);
}
/**
* Constructs a CloneFailedException.
*
@ -53,4 +44,13 @@ public class CloneFailedException extends RuntimeException {
public CloneFailedException(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Constructs a CloneFailedException.
*
* @param cause cause of the exception
*/
public CloneFailedException(final Throwable cause) {
super(cause);
}
}

View File

@ -111,18 +111,6 @@ public class ContextedException extends Exception implements ExceptionContext {
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause, but without message.
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedException(final Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause and message.
* <p>
@ -151,6 +139,18 @@ public class ContextedException extends Exception implements ExceptionContext {
exceptionContext = context;
}
/**
* Instantiates ContextedException with cause, but without message.
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedException(final Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Adds information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
@ -171,22 +171,19 @@ public class ContextedException extends Exception implements ExceptionContext {
}
/**
* Sets information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Any existing values with the same labels are removed before the new one is added.
* <p>
* Note: This exception is only serializable if the object added as value is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
* {@inheritDoc}
*/
@Override
public ContextedException setContextValue(final String label, final Object value) {
exceptionContext.setContextValue(label, value);
return this;
public List<Pair<String, Object>> getContextEntries() {
return this.exceptionContext.getContextEntries();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getContextLabels() {
return exceptionContext.getContextLabels();
}
/**
@ -209,16 +206,8 @@ public class ContextedException extends Exception implements ExceptionContext {
* {@inheritDoc}
*/
@Override
public List<Pair<String, Object>> getContextEntries() {
return this.exceptionContext.getContextEntries();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getContextLabels() {
return exceptionContext.getContextLabels();
public String getFormattedExceptionMessage(final String baseMessage) {
return exceptionContext.getFormattedExceptionMessage(baseMessage);
}
/**
@ -244,10 +233,21 @@ public class ContextedException extends Exception implements ExceptionContext {
}
/**
* {@inheritDoc}
* Sets information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Any existing values with the same labels are removed before the new one is added.
* <p>
* Note: This exception is only serializable if the object added as value is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
@Override
public String getFormattedExceptionMessage(final String baseMessage) {
return exceptionContext.getFormattedExceptionMessage(baseMessage);
public ContextedException setContextValue(final String label, final Object value) {
exceptionContext.setContextValue(label, value);
return this;
}
}

View File

@ -111,18 +111,6 @@ public class ContextedRuntimeException extends RuntimeException implements Excep
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause, but without message.
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedRuntimeException(final Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause and message.
* <p>
@ -151,6 +139,18 @@ public class ContextedRuntimeException extends RuntimeException implements Excep
exceptionContext = context;
}
/**
* Instantiates ContextedRuntimeException with cause, but without message.
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedRuntimeException(final Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Adds information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
@ -171,22 +171,19 @@ public class ContextedRuntimeException extends RuntimeException implements Excep
}
/**
* Sets information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Any existing values with the same labels are removed before the new one is added.
* <p>
* Note: This exception is only serializable if the object added as value is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
* {@inheritDoc}
*/
@Override
public ContextedRuntimeException setContextValue(final String label, final Object value) {
exceptionContext.setContextValue(label, value);
return this;
public List<Pair<String, Object>> getContextEntries() {
return this.exceptionContext.getContextEntries();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getContextLabels() {
return exceptionContext.getContextLabels();
}
/**
@ -209,16 +206,8 @@ public class ContextedRuntimeException extends RuntimeException implements Excep
* {@inheritDoc}
*/
@Override
public List<Pair<String, Object>> getContextEntries() {
return this.exceptionContext.getContextEntries();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getContextLabels() {
return exceptionContext.getContextLabels();
public String getFormattedExceptionMessage(final String baseMessage) {
return exceptionContext.getFormattedExceptionMessage(baseMessage);
}
/**
@ -244,11 +233,22 @@ public class ContextedRuntimeException extends RuntimeException implements Excep
}
/**
* {@inheritDoc}
* Sets information helpful to a developer in diagnosing and correcting the problem.
* For the information to be meaningful, the value passed should have a reasonable
* toString() implementation.
* Any existing values with the same labels are removed before the new one is added.
* <p>
* Note: This exception is only serializable if the object added as value is serializable.
* </p>
*
* @param label a textual label associated with information, {@code null} not recommended
* @param value information needed to understand exception, may be {@code null}
* @return {@code this}, for method chaining, not {@code null}
*/
@Override
public String getFormattedExceptionMessage(final String baseMessage) {
return exceptionContext.getFormattedExceptionMessage(baseMessage);
public ContextedRuntimeException setContextValue(final String label, final Object value) {
exceptionContext.setContextValue(label, value);
return this;
}
}

View File

@ -31,10 +31,6 @@ public class Consumers {
@SuppressWarnings("rawtypes")
private static final Consumer NOP = Function.identity()::apply;
private Consumers() {
// No instances.
}
/**
* Gets the NOP Consumer singleton.
*
@ -46,4 +42,8 @@ public class Consumers {
return NOP;
}
private Consumers() {
// No instances.
}
}

View File

@ -35,6 +35,17 @@ public class Suppliers {
@SuppressWarnings("rawtypes")
private static Supplier NUL = () -> null;
/**
* Null-safe call to {@link Supplier#get()}.
*
* @param <T> the type of results supplied by this supplier.
* @param supplier the supplier or null.
* @return Result of {@link Supplier#get()} or null.
*/
public static <T> T get(final Supplier<T> supplier) {
return supplier == null ? null : supplier.get();
}
/**
* Returns the singleton supplier that always returns null.
* <p>
@ -50,15 +61,4 @@ public class Suppliers {
return NUL;
}
/**
* Null-safe call to {@link Supplier#get()}.
*
* @param <T> the type of results supplied by this supplier.
* @param supplier the supplier or null.
* @return Result of {@link Supplier#get()} or null.
*/
public static <T> T get(final Supplier<T> supplier) {
return supplier == null ? null : supplier.get();
}
}

View File

@ -39,16 +39,6 @@ import java.util.function.Function;
@FunctionalInterface
public interface TriFunction<T, U, V, R> {
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @param v the third function argument
* @return the function result
*/
R apply(T t, U u, V v);
/**
* Returns a composed function that first applies this function to its input, and then applies the {@code after}
* function to the result. If evaluation of either function throws an exception, it is relayed to the caller of the
@ -63,4 +53,14 @@ public interface TriFunction<T, U, V, R> {
Objects.requireNonNull(after);
return (final T t, final U u, final V v) -> after.apply(apply(t, u, v));
}
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @param v the third function argument
* @return the function result
*/
R apply(T t, U u, V v);
}

View File

@ -92,143 +92,21 @@ public final class Fraction extends Number implements Comparable<Fraction> {
/**
* The numerator number part of the fraction (the three in three sevenths).
*/
private final int numerator;
/**
* The denominator number part of the fraction (the seven in three sevenths).
*/
private final int denominator;
/**
* Cached output hashCode (class is immutable).
*/
private transient int hashCode;
/**
* Cached output toString (class is immutable).
*/
private transient String toString;
/**
* Cached output toProperString (class is immutable).
*/
private transient String toProperString;
/**
* Constructs a {@link Fraction} instance with the 2 parts
* of a fraction Y/Z.
* Add two integers, checking for overflow.
*
* @param numerator the numerator, for example the three in 'three sevenths'
* @param denominator the denominator, for example the seven in 'three sevenths'
* @param x an addend
* @param y an addend
* @return the sum {@code x+y}
* @throws ArithmeticException if the result can not be represented as
* an int
*/
private Fraction(final int numerator, final int denominator) {
this.numerator = numerator;
this.denominator = denominator;
private static int addAndCheck(final int x, final int y) {
final long s = (long) x + (long) y;
if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
throw new ArithmeticException("overflow: add");
}
return (int) s;
}
/**
* Creates a {@link Fraction} instance with the 2 parts
* of a fraction Y/Z.
*
* <p>Any negative signs are resolved to be on the numerator.</p>
*
* @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
* @throws ArithmeticException if the denominator is {@code zero}
* or the denominator is {@code negative} and the numerator is {@code Integer#MIN_VALUE}
*/
public static Fraction getFraction(int numerator, int denominator) {
if (denominator == 0) {
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;
}
return new Fraction(numerator, denominator);
}
/**
* Creates a {@link Fraction} instance with the 3 parts
* of a fraction X Y/Z.
*
* <p>The negative sign must be passed in on the whole number part.</p>
*
* @param whole the whole number, for example the one in 'one and three sevenths'
* @param numerator the numerator, for example the three in 'one and three sevenths'
* @param denominator the denominator, for example the seven in 'one and three sevenths'
* @return a new fraction instance
* @throws ArithmeticException if the denominator is {@code zero}
* @throws ArithmeticException if the denominator is negative
* @throws ArithmeticException if the numerator is negative
* @throws ArithmeticException if the resulting numerator exceeds
* {@code Integer.MAX_VALUE}
*/
public static Fraction getFraction(final int whole, final int numerator, final int denominator) {
if (denominator == 0) {
throw new ArithmeticException("The denominator must not be zero");
}
if (denominator < 0) {
throw new ArithmeticException("The denominator must not be negative");
}
if (numerator < 0) {
throw new ArithmeticException("The numerator must not be negative");
}
final long numeratorValue;
if (whole < 0) {
numeratorValue = whole * (long) denominator - numerator;
} else {
numeratorValue = whole * (long) denominator + numerator;
}
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 reduced {@link Fraction} instance with the 2 parts
* of a fraction Y/Z.
*
* <p>For example, if the input parameters represent 2/4, then the created
* fraction will be 1/2.</p>
*
* <p>Any negative signs are resolved to be on the numerator.</p>
*
* @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 denominator is {@code 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;
}
// simplify fraction.
final int gcd = greatestCommonDivisor(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
return new Fraction(numerator, denominator);
}
/**
* Creates a {@link Fraction} instance from a {@code double} value.
*
@ -291,6 +169,68 @@ public final class Fraction extends Number implements Comparable<Fraction> {
return getReducedFraction((numer0 + wholeNumber * denom0) * sign, denom0);
}
/**
* Creates a {@link Fraction} instance with the 2 parts
* of a fraction Y/Z.
*
* <p>Any negative signs are resolved to be on the numerator.</p>
*
* @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
* @throws ArithmeticException if the denominator is {@code zero}
* or the denominator is {@code negative} and the numerator is {@code Integer#MIN_VALUE}
*/
public static Fraction getFraction(int numerator, int denominator) {
if (denominator == 0) {
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;
}
return new Fraction(numerator, denominator);
}
/**
* Creates a {@link Fraction} instance with the 3 parts
* of a fraction X Y/Z.
*
* <p>The negative sign must be passed in on the whole number part.</p>
*
* @param whole the whole number, for example the one in 'one and three sevenths'
* @param numerator the numerator, for example the three in 'one and three sevenths'
* @param denominator the denominator, for example the seven in 'one and three sevenths'
* @return a new fraction instance
* @throws ArithmeticException if the denominator is {@code zero}
* @throws ArithmeticException if the denominator is negative
* @throws ArithmeticException if the numerator is negative
* @throws ArithmeticException if the resulting numerator exceeds
* {@code Integer.MAX_VALUE}
*/
public static Fraction getFraction(final int whole, final int numerator, final int denominator) {
if (denominator == 0) {
throw new ArithmeticException("The denominator must not be zero");
}
if (denominator < 0) {
throw new ArithmeticException("The denominator must not be negative");
}
if (numerator < 0) {
throw new ArithmeticException("The numerator must not be negative");
}
final long numeratorValue;
if (whole < 0) {
numeratorValue = whole * (long) denominator - numerator;
} else {
numeratorValue = whole * (long) denominator + numerator;
}
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 from a {@link String}.
*
@ -343,203 +283,43 @@ public final class Fraction extends Number implements Comparable<Fraction> {
}
/**
* Gets the numerator part of the fraction.
* Creates a reduced {@link Fraction} instance with the 2 parts
* of a fraction Y/Z.
*
* <p>This method may return a value greater than the denominator, an
* improper fraction, such as the seven in 7/4.</p>
* <p>For example, if the input parameters represent 2/4, then the created
* fraction will be 1/2.</p>
*
* @return the numerator fraction part
* <p>Any negative signs are resolved to be on the numerator.</p>
*
* @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 denominator is {@code zero}
*/
public int getNumerator() {
return numerator;
}
/**
* Gets the denominator part of the fraction.
*
* @return the denominator fraction part
*/
public int getDenominator() {
return denominator;
}
/**
* Gets the proper numerator, always positive.
*
* <p>An improper fraction 7/4 can be resolved into a proper one, 1 3/4.
* This method returns the 3 from the proper fraction.</p>
*
* <p>If the fraction is negative such as -7/4, it can be resolved into
* -1 3/4, so this method returns the positive proper numerator, 3.</p>
*
* @return the numerator fraction part of a proper fraction, always positive
*/
public int getProperNumerator() {
return Math.abs(numerator % denominator);
}
/**
* Gets the proper whole part of the fraction.
*
* <p>An improper fraction 7/4 can be resolved into a proper one, 1 3/4.
* This method returns the 1 from the proper fraction.</p>
*
* <p>If the fraction is negative such as -7/4, it can be resolved into
* -1 3/4, so this method returns the positive whole part -1.</p>
*
* @return the whole fraction part of a proper fraction, that includes the sign
*/
public int getProperWhole() {
return numerator / denominator;
}
/**
* Gets the fraction as an {@code int}. This returns the whole number
* part of the fraction.
*
* @return the whole number fraction part
*/
@Override
public int intValue() {
return numerator / denominator;
}
/**
* Gets the fraction as a {@code long}. This returns the whole number
* part of the fraction.
*
* @return the whole number fraction part
*/
@Override
public long longValue() {
return (long) numerator / denominator;
}
/**
* Gets the fraction as a {@code float}. This calculates the fraction
* as the numerator divided by denominator.
*
* @return the fraction as a {@code float}
*/
@Override
public float floatValue() {
return (float) numerator / (float) denominator;
}
/**
* Gets the fraction as a {@code double}. This calculates the fraction
* as the numerator divided by denominator.
*
* @return the fraction as a {@code double}
*/
@Override
public double doubleValue() {
return (double) numerator / (double) denominator;
}
/**
* Reduce the fraction to the smallest values for the numerator and
* denominator, returning the result.
*
* <p>For example, if this fraction represents 2/4, then the result
* will be 1/2.</p>
*
* @return a new reduced fraction instance, or this if no simplification possible
*/
public Fraction reduce() {
public static Fraction getReducedFraction(int numerator, int denominator) {
if (denominator == 0) {
throw new ArithmeticException("The denominator must not be zero");
}
if (numerator == 0) {
return equals(ZERO) ? this : ZERO;
return ZERO; // normalize zero.
}
final int gcd = greatestCommonDivisor(Math.abs(numerator), denominator);
if (gcd == 1) {
return this;
// allow 2^k/-2^31 as a valid fraction (where k>0)
if (denominator == Integer.MIN_VALUE && (numerator & 1) == 0) {
numerator /= 2;
denominator /= 2;
}
return getFraction(numerator / gcd, denominator / gcd);
}
/**
* Gets a fraction that is the inverse (1/fraction) of this one.
*
* <p>The returned fraction is not reduced.</p>
*
* @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 zero.");
}
if (numerator==Integer.MIN_VALUE) {
throw new ArithmeticException("overflow: can't negate numerator");
}
if (numerator<0) {
return new Fraction(-denominator, -numerator);
}
return new Fraction(denominator, numerator);
}
/**
* Gets a fraction that is the negative (-fraction) of this one.
*
* <p>The returned fraction is not reduced.</p>
*
* @return a new fraction instance with the opposite signed numerator
*/
public Fraction negate() {
// 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.
* <p>More precisely: {@code (fraction &gt;= 0 ? this : -fraction)}</p>
*
* <p>The returned fraction is not reduced.</p>
*
* @return {@code this} if it is positive, or a new positive fraction
* instance with the opposite signed numerator
*/
public Fraction abs() {
if (numerator >= 0) {
return this;
}
return negate();
}
/**
* Gets a fraction that is raised to the passed in power.
*
* <p>The returned fraction is in reduced form.</p>
*
* @param power the power to raise the fraction to
* @return {@code this} if the power is one, {@link #ONE} if the power
* is zero (even if the fraction equals ZERO) or a new fraction instance
* raised to the appropriate power
* @throws ArithmeticException if the resulting numerator or denominator exceeds
* {@code Integer.MAX_VALUE}
*/
public Fraction pow(final int power) {
if (power == 1) {
return this;
}
if (power == 0) {
return ONE;
}
if (power < 0) {
if (power == Integer.MIN_VALUE) { // MIN_VALUE can't be negated.
return this.invert().pow(2).pow(-(power / 2));
if (denominator < 0) {
if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) {
throw new ArithmeticException("overflow: can't negate");
}
return this.invert().pow(-power);
numerator = -numerator;
denominator = -denominator;
}
final Fraction f = this.multiplyBy(this);
if (power % 2 == 0) { // if even...
return f.pow(power / 2);
}
return f.pow(power / 2).multiplyBy(this);
// simplify fraction.
final int gcd = greatestCommonDivisor(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
return new Fraction(numerator, denominator);
}
/**
@ -644,23 +424,6 @@ public final class Fraction extends Number implements Comparable<Fraction> {
return (int) m;
}
/**
* Add two integers, checking for overflow.
*
* @param x an addend
* @param y an addend
* @return the sum {@code x+y}
* @throws ArithmeticException if the result can not be represented as
* an int
*/
private static int addAndCheck(final int x, final int y) {
final 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.
*
@ -678,6 +441,59 @@ public final class Fraction extends Number implements Comparable<Fraction> {
return (int) s;
}
/**
* The numerator number part of the fraction (the three in three sevenths).
*/
private final int numerator;
/**
* The denominator number part of the fraction (the seven in three sevenths).
*/
private final int denominator;
/**
* Cached output hashCode (class is immutable).
*/
private transient int hashCode;
/**
* Cached output toString (class is immutable).
*/
private transient String toString;
/**
* Cached output toProperString (class is immutable).
*/
private transient String toProperString;
/**
* Constructs a {@link Fraction} instance with the 2 parts
* of a fraction Y/Z.
*
* @param numerator the numerator, for example the three in 'three sevenths'
* @param denominator the denominator, for example the seven in 'three sevenths'
*/
private Fraction(final int numerator, final int denominator) {
this.numerator = numerator;
this.denominator = denominator;
}
/**
* Gets a fraction that is the positive equivalent of this one.
* <p>More precisely: {@code (fraction &gt;= 0 ? this : -fraction)}</p>
*
* <p>The returned fraction is not reduced.</p>
*
* @return {@code this} if it is positive, or a new positive fraction
* instance with the opposite signed numerator
*/
public Fraction abs() {
if (numerator >= 0) {
return this;
}
return negate();
}
/**
* Adds the value of this fraction to another, returning the result in reduced form.
* The algorithm follows Knuth, 4.5.1.
@ -692,20 +508,6 @@ public final class Fraction extends Number implements Comparable<Fraction> {
return addSub(fraction, true /* add */);
}
/**
* 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 {@code null}
* @return a {@link Fraction} instance with the resulting values
* @throws NullPointerException if the fraction is {@code null}
* @throws ArithmeticException if the resulting numerator or denominator
* cannot be represented in an {@code int}.
*/
public Fraction subtract(final Fraction fraction) {
return addSub(fraction, false /* subtract */);
}
/**
* Implement add and subtract using algorithm described in Knuth 4.5.1.
*
@ -754,81 +556,6 @@ public final class Fraction extends Number implements Comparable<Fraction> {
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.
*
* @param fraction the fraction to multiply by, must not be {@code null}
* @return a {@link Fraction} instance with the resulting values
* @throws NullPointerException if the fraction is {@code null}
* @throws ArithmeticException if the resulting numerator or denominator exceeds
* {@code Integer.MAX_VALUE}
*/
public Fraction multiplyBy(final Fraction fraction) {
Objects.requireNonNull(fraction, "fraction");
if (numerator == 0 || fraction.numerator == 0) {
return ZERO;
}
// knuth 4.5.1
// make sure we don't overflow unless the result *must* overflow.
final int d1 = greatestCommonDivisor(numerator, fraction.denominator);
final 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.
*
* @param fraction the fraction to divide by, must not be {@code null}
* @return a {@link Fraction} instance with the resulting values
* @throws NullPointerException if the fraction is {@code null}
* @throws ArithmeticException if the fraction to divide by is zero
* @throws ArithmeticException if the resulting numerator or denominator exceeds
* {@code Integer.MAX_VALUE}
*/
public Fraction divideBy(final Fraction fraction) {
Objects.requireNonNull(fraction, "fraction");
if (fraction.numerator == 0) {
throw new ArithmeticException("The fraction to divide by must not be zero");
}
return multiplyBy(fraction.invert());
}
/**
* Compares this fraction to another object to test if they are equal..
*
* <p>To be equal, both values must be equal. Thus 2/4 is not equal to 1/2.</p>
*
* @param obj the reference object with which to compare
* @return {@code true} if this object is equal
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Fraction)) {
return false;
}
final Fraction other = (Fraction) obj;
return getNumerator() == other.getNumerator() && getDenominator() == other.getDenominator();
}
/**
* Gets a hashCode for the fraction.
*
* @return a hash code value for this object
*/
@Override
public int hashCode() {
if (hashCode == 0) {
// hash code update should be atomic.
hashCode = 37 * (37 * 17 + getNumerator()) + getDenominator();
}
return hashCode;
}
/**
* Compares this object to another based on size.
*
@ -857,18 +584,276 @@ public final class Fraction extends Number implements Comparable<Fraction> {
}
/**
* Gets the fraction as a {@link String}.
* Divide the value of this fraction by another.
*
* <p>The format used is '<i>numerator</i>/<i>denominator</i>' always.
* @param fraction the fraction to divide by, must not be {@code null}
* @return a {@link Fraction} instance with the resulting values
* @throws NullPointerException if the fraction is {@code null}
* @throws ArithmeticException if the fraction to divide by is zero
* @throws ArithmeticException if the resulting numerator or denominator exceeds
* {@code Integer.MAX_VALUE}
*/
public Fraction divideBy(final Fraction fraction) {
Objects.requireNonNull(fraction, "fraction");
if (fraction.numerator == 0) {
throw new ArithmeticException("The fraction to divide by must not be zero");
}
return multiplyBy(fraction.invert());
}
/**
* Gets the fraction as a {@code double}. This calculates the fraction
* as the numerator divided by denominator.
*
* @return a {@link String} form of the fraction
* @return the fraction as a {@code double}
*/
@Override
public String toString() {
if (toString == null) {
toString = getNumerator() + "/" + getDenominator();
public double doubleValue() {
return (double) numerator / (double) denominator;
}
/**
* Compares this fraction to another object to test if they are equal..
*
* <p>To be equal, both values must be equal. Thus 2/4 is not equal to 1/2.</p>
*
* @param obj the reference object with which to compare
* @return {@code true} if this object is equal
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
return toString;
if (!(obj instanceof Fraction)) {
return false;
}
final Fraction other = (Fraction) obj;
return getNumerator() == other.getNumerator() && getDenominator() == other.getDenominator();
}
/**
* Gets the fraction as a {@code float}. This calculates the fraction
* as the numerator divided by denominator.
*
* @return the fraction as a {@code float}
*/
@Override
public float floatValue() {
return (float) numerator / (float) denominator;
}
/**
* Gets the denominator part of the fraction.
*
* @return the denominator fraction part
*/
public int getDenominator() {
return denominator;
}
/**
* Gets the numerator part of the fraction.
*
* <p>This method may return a value greater than the denominator, an
* improper fraction, such as the seven in 7/4.</p>
*
* @return the numerator fraction part
*/
public int getNumerator() {
return numerator;
}
/**
* Gets the proper numerator, always positive.
*
* <p>An improper fraction 7/4 can be resolved into a proper one, 1 3/4.
* This method returns the 3 from the proper fraction.</p>
*
* <p>If the fraction is negative such as -7/4, it can be resolved into
* -1 3/4, so this method returns the positive proper numerator, 3.</p>
*
* @return the numerator fraction part of a proper fraction, always positive
*/
public int getProperNumerator() {
return Math.abs(numerator % denominator);
}
/**
* Gets the proper whole part of the fraction.
*
* <p>An improper fraction 7/4 can be resolved into a proper one, 1 3/4.
* This method returns the 1 from the proper fraction.</p>
*
* <p>If the fraction is negative such as -7/4, it can be resolved into
* -1 3/4, so this method returns the positive whole part -1.</p>
*
* @return the whole fraction part of a proper fraction, that includes the sign
*/
public int getProperWhole() {
return numerator / denominator;
}
/**
* Gets a hashCode for the fraction.
*
* @return a hash code value for this object
*/
@Override
public int hashCode() {
if (hashCode == 0) {
// hash code update should be atomic.
hashCode = 37 * (37 * 17 + getNumerator()) + getDenominator();
}
return hashCode;
}
/**
* Gets the fraction as an {@code int}. This returns the whole number
* part of the fraction.
*
* @return the whole number fraction part
*/
@Override
public int intValue() {
return numerator / denominator;
}
/**
* Gets a fraction that is the inverse (1/fraction) of this one.
*
* <p>The returned fraction is not reduced.</p>
*
* @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 zero.");
}
if (numerator==Integer.MIN_VALUE) {
throw new ArithmeticException("overflow: can't negate numerator");
}
if (numerator<0) {
return new Fraction(-denominator, -numerator);
}
return new Fraction(denominator, numerator);
}
/**
* Gets the fraction as a {@code long}. This returns the whole number
* part of the fraction.
*
* @return the whole number fraction part
*/
@Override
public long longValue() {
return (long) numerator / denominator;
}
/**
* Multiplies the value of this fraction by another, returning the
* result in reduced form.
*
* @param fraction the fraction to multiply by, must not be {@code null}
* @return a {@link Fraction} instance with the resulting values
* @throws NullPointerException if the fraction is {@code null}
* @throws ArithmeticException if the resulting numerator or denominator exceeds
* {@code Integer.MAX_VALUE}
*/
public Fraction multiplyBy(final Fraction fraction) {
Objects.requireNonNull(fraction, "fraction");
if (numerator == 0 || fraction.numerator == 0) {
return ZERO;
}
// knuth 4.5.1
// make sure we don't overflow unless the result *must* overflow.
final int d1 = greatestCommonDivisor(numerator, fraction.denominator);
final int d2 = greatestCommonDivisor(fraction.numerator, denominator);
return getReducedFraction(mulAndCheck(numerator / d1, fraction.numerator / d2),
mulPosAndCheck(denominator / d2, fraction.denominator / d1));
}
/**
* Gets a fraction that is the negative (-fraction) of this one.
*
* <p>The returned fraction is not reduced.</p>
*
* @return a new fraction instance with the opposite signed numerator
*/
public Fraction negate() {
// 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 raised to the passed in power.
*
* <p>The returned fraction is in reduced form.</p>
*
* @param power the power to raise the fraction to
* @return {@code this} if the power is one, {@link #ONE} if the power
* is zero (even if the fraction equals ZERO) or a new fraction instance
* raised to the appropriate power
* @throws ArithmeticException if the resulting numerator or denominator exceeds
* {@code Integer.MAX_VALUE}
*/
public Fraction pow(final int power) {
if (power == 1) {
return this;
}
if (power == 0) {
return ONE;
}
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);
}
final Fraction f = this.multiplyBy(this);
if (power % 2 == 0) { // if even...
return f.pow(power / 2);
}
return f.pow(power / 2).multiplyBy(this);
}
/**
* Reduce the fraction to the smallest values for the numerator and
* denominator, returning the result.
*
* <p>For example, if this fraction represents 2/4, then the result
* will be 1/2.</p>
*
* @return a new reduced fraction instance, or this if no simplification possible
*/
public Fraction reduce() {
if (numerator == 0) {
return equals(ZERO) ? this : ZERO;
}
final int gcd = greatestCommonDivisor(Math.abs(numerator), denominator);
if (gcd == 1) {
return this;
}
return getFraction(numerator / gcd, denominator / gcd);
}
/**
* 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 {@code null}
* @return a {@link Fraction} instance with the resulting values
* @throws NullPointerException if the fraction is {@code null}
* @throws ArithmeticException if the resulting numerator or denominator
* cannot be represented in an {@code int}.
*/
public Fraction subtract(final Fraction fraction) {
return addSub(fraction, false /* subtract */);
}
/**
@ -905,4 +890,19 @@ public final class Fraction extends Number implements Comparable<Fraction> {
}
return toProperString;
}
/**
* Gets the fraction as a {@link String}.
*
* <p>The format used is '<i>numerator</i>/<i>denominator</i>' always.
*
* @return a {@link String} form of the fraction
*/
@Override
public String toString() {
if (toString == null) {
toString = getNumerator() + "/" + getDenominator();
}
return toString;
}
}

View File

@ -30,116 +30,6 @@ import org.apache.commons.lang3.Validate;
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 NullPointerException if {@code array} is {@code null}
* @throws IllegalArgumentException if {@code array} is empty
* @since 3.4 Changed signature from min(double[]) to min(double...)
*/
public static double min(final double... array) {
Objects.requireNonNull(array, "array");
Validate.isTrue(array.length != 0, "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 NullPointerException if {@code array} is {@code null}
* @throws IllegalArgumentException if {@code array} is empty
* @since 3.4 Changed signature from min(float[]) to min(float...)
*/
public static float min(final float... array) {
Objects.requireNonNull(array, "array");
Validate.isTrue(array.length != 0, "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 {@code double} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the smallest of the values
*/
public static double min(final double a, final double b, final double c) {
return min(min(a, b), c);
}
/**
* Gets the minimum of two {@code double} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @return the smallest of the values
*/
public static double min(final double a, final double b) {
if (Double.isNaN(a)) {
return b;
}
if (Double.isNaN(b)) {
return a;
}
return Math.min(a, b);
}
/**
* Gets the minimum of three {@code float} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the smallest of the values
*/
public static float min(final float a, final float b, final float c) {
return min(min(a, b), c);
}
/**
* Gets the minimum of two {@code float} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @return the smallest of the values
*/
public static float min(final float a, final float b) {
if (Float.isNaN(a)) {
return b;
}
if (Float.isNaN(b)) {
return a;
}
return Math.min(a, b);
}
/**
* Returns the maximum value in an array.
*
* @param array an array, must not be null or empty
@ -161,6 +51,39 @@ public class IEEE754rUtils {
return max;
}
/**
* Gets the maximum of two {@code double} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @return the largest of the values
*/
public static double max(final double a, final double b) {
if (Double.isNaN(a)) {
return b;
}
if (Double.isNaN(b)) {
return a;
}
return Math.max(a, b);
}
/**
* Gets the maximum of three {@code double} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the largest of the values
*/
public static double max(final double a, final double b, final double c) {
return max(max(a, b), c);
}
/**
* Returns the maximum value in an array.
*
@ -184,21 +107,7 @@ public class IEEE754rUtils {
}
/**
* Gets the maximum of three {@code double} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the largest of the values
*/
public static double max(final double a, final double b, final double c) {
return max(max(a, b), c);
}
/**
* Gets the maximum of two {@code double} values.
* Gets the maximum of two {@code float} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
@ -206,11 +115,11 @@ public class IEEE754rUtils {
* @param b value 2
* @return the largest of the values
*/
public static double max(final double a, final double b) {
if (Double.isNaN(a)) {
public static float max(final float a, final float b) {
if (Float.isNaN(a)) {
return b;
}
if (Double.isNaN(b)) {
if (Float.isNaN(b)) {
return a;
}
return Math.max(a, b);
@ -231,22 +140,113 @@ public class IEEE754rUtils {
}
/**
* Gets the maximum of two {@code float} values.
* 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 NullPointerException if {@code array} is {@code null}
* @throws IllegalArgumentException if {@code array} is empty
* @since 3.4 Changed signature from min(double[]) to min(double...)
*/
public static double min(final double... array) {
Objects.requireNonNull(array, "array");
Validate.isTrue(array.length != 0, "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;
}
/**
* Gets the minimum of two {@code double} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @return the largest of the values
* @return the smallest of the values
*/
public static float max(final float a, final float b) {
public static double min(final double a, final double b) {
if (Double.isNaN(a)) {
return b;
}
if (Double.isNaN(b)) {
return a;
}
return Math.min(a, b);
}
/**
* Gets the minimum of three {@code double} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the smallest of the values
*/
public static double min(final double a, final double b, final double c) {
return min(min(a, b), c);
}
/**
* 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 NullPointerException if {@code array} is {@code null}
* @throws IllegalArgumentException if {@code array} is empty
* @since 3.4 Changed signature from min(float[]) to min(float...)
*/
public static float min(final float... array) {
Objects.requireNonNull(array, "array");
Validate.isTrue(array.length != 0, "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 two {@code float} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @return the smallest of the values
*/
public static float min(final float a, final float b) {
if (Float.isNaN(a)) {
return b;
}
if (Float.isNaN(b)) {
return a;
}
return Math.max(a, b);
return Math.min(a, b);
}
/**
* Gets the minimum of three {@code float} values.
*
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
*
* @param a value 1
* @param b value 2
* @param c value 3
* @return the smallest of the values
*/
public static float min(final float a, final float b, final float c) {
return min(min(a, b), c);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,43 @@ public class MutableBoolean implements Mutable<Boolean>, Serializable, Comparabl
this.value = value.booleanValue();
}
/**
* Returns the value of this MutableBoolean as a boolean.
*
* @return the boolean value represented by this object.
*/
public boolean booleanValue() {
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other 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
*/
@Override
public int compareTo(final MutableBoolean other) {
return BooleanUtils.compare(this.value, other.value);
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument is
* not {@code null} and is an {@link MutableBoolean} object that contains the same
* {@code boolean} value as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof MutableBoolean) {
return value == ((MutableBoolean) obj).booleanValue();
}
return false;
}
/**
* Gets the value as a Boolean instance.
*
@ -78,12 +115,33 @@ public class MutableBoolean implements Mutable<Boolean>, Serializable, Comparabl
}
/**
* Sets the value.
* Returns a suitable hash code for this mutable.
*
* @param value the value to set
* @return the hash code returned by {@code Boolean.TRUE} or {@code Boolean.FALSE}
*/
public void setValue(final boolean value) {
this.value = value;
@Override
public int hashCode() {
return value ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode();
}
/**
* Checks if the current value is {@code false}.
*
* @return {@code true} if the current value is {@code false}
* @since 2.5
*/
public boolean isFalse() {
return !value;
}
/**
* Checks if the current value is {@code true}.
*
* @return {@code true} if the current value is {@code true}
* @since 2.5
*/
public boolean isTrue() {
return value;
}
/**
@ -104,6 +162,15 @@ public class MutableBoolean implements Mutable<Boolean>, Serializable, Comparabl
this.value = true;
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final boolean value) {
this.value = value;
}
/**
* Sets the value from any Boolean instance.
*
@ -115,35 +182,6 @@ public class MutableBoolean implements Mutable<Boolean>, Serializable, Comparabl
this.value = value.booleanValue();
}
/**
* Checks if the current value is {@code true}.
*
* @return {@code true} if the current value is {@code true}
* @since 2.5
*/
public boolean isTrue() {
return value;
}
/**
* Checks if the current value is {@code false}.
*
* @return {@code true} if the current value is {@code false}
* @since 2.5
*/
public boolean isFalse() {
return !value;
}
/**
* 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.
*
@ -154,44 +192,6 @@ public class MutableBoolean implements Mutable<Boolean>, Serializable, Comparabl
return Boolean.valueOf(booleanValue());
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument is
* not {@code null} and is an {@link MutableBoolean} object that contains the same
* {@code boolean} value as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final 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 {@code Boolean.TRUE} or {@code Boolean.FALSE}
*/
@Override
public int hashCode() {
return value ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode();
}
/**
* Compares this mutable to another in ascending order.
*
* @param other 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
*/
@Override
public int compareTo(final MutableBoolean other) {
return BooleanUtils.compare(this.value, other.value);
}
/**
* Returns the String value of this mutable.
*

View File

@ -75,104 +75,6 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
this.value = Byte.parseByte(value);
}
/**
* Gets the value as a Byte instance.
*
* @return the value as a Byte, never null
*/
@Override
public Byte getValue() {
return Byte.valueOf(this.value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = value.byteValue();
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public byte getAndIncrement() {
final byte last = value;
value++;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public byte incrementAndGet() {
value++;
return value;
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public byte getAndDecrement() {
final byte last = value;
value--;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public byte decrementAndGet() {
value--;
return value;
}
/**
* Adds a value to the value of this instance.
*
@ -194,27 +96,6 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
this.value += operand.byteValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.byteValue();
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
@ -242,6 +123,85 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
return value;
}
// 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.
*/
@Override
public byte byteValue() {
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableByte other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public byte decrementAndGet() {
value--;
return value;
}
/**
* Returns the value of this MutableByte as a double.
*
* @return the numeric value represented by this object after conversion to type double.
*/
@Override
public double doubleValue() {
return value;
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument is
* not {@code null} and is a {@link MutableByte} object that contains the same {@code byte} value
* as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof MutableByte) {
return value == ((MutableByte) obj).byteValue();
}
return false;
}
/**
* Returns the value of this MutableByte as a float.
*
* @return the numeric value represented by this object after conversion to type float.
*/
@Override
public float floatValue() {
return value;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
@ -271,14 +231,70 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
return last;
}
// shortValue relies on Number implementation
/**
* Returns the value of this MutableByte as a byte.
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the numeric value represented by this object after conversion to type byte.
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public byte getAndDecrement() {
final byte last = value;
value--;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public byte getAndIncrement() {
final byte last = value;
value++;
return last;
}
/**
* Gets the value as a Byte instance.
*
* @return the value as a Byte, never null
*/
@Override
public byte byteValue() {
public Byte getValue() {
return Byte.valueOf(this.value);
}
/**
* Returns a suitable hash code for this mutable.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return value;
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public byte incrementAndGet() {
value++;
return value;
}
@ -303,23 +319,44 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
}
/**
* Returns the value of this MutableByte as a float.
* Sets the value.
*
* @return the numeric value represented by this object after conversion to type float.
* @param value the value to set
*/
@Override
public float floatValue() {
return value;
public void setValue(final byte value) {
this.value = value;
}
/**
* Returns the value of this MutableByte as a double.
* Sets the value from any Number instance.
*
* @return the numeric value represented by this object after conversion to type double.
* @param value the value to set, not null
* @throws NullPointerException if the object is null
*/
@Override
public double doubleValue() {
return value;
public void setValue(final Number value) {
this.value = value.byteValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.byteValue();
}
/**
@ -331,43 +368,6 @@ public class MutableByte extends Number implements Comparable<MutableByte>, Muta
return Byte.valueOf(byteValue());
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument is
* not {@code null} and is a {@link MutableByte} object that contains the same {@code byte} value
* as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final 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
*/
@Override
public int hashCode() {
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableByte other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Returns the String value of this mutable.
*

View File

@ -73,122 +73,6 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
this.value = Double.parseDouble(value);
}
/**
* Gets the value as a Double instance.
*
* @return the value as a Double, never null
*/
@Override
public Double getValue() {
return Double.valueOf(this.value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = 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 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public double getAndIncrement() {
final double last = value;
value++;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public double incrementAndGet() {
value++;
return value;
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public double getAndDecrement() {
final double last = value;
value--;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public double decrementAndGet() {
value--;
return value;
}
/**
* Adds a value to the value of this instance.
*
@ -210,27 +94,6 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
this.value += operand.doubleValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.doubleValue();
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
@ -259,63 +122,35 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
* Compares this mutable to another in ascending order.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance immediately before the operand was added
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableDouble other) {
return Double.compare(this.value, other.value);
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public double getAndAdd(final double operand) {
final double last = value;
this.value += operand;
return last;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @throws NullPointerException if {@code operand} is null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public double getAndAdd(final Number operand) {
final double last = value;
this.value += operand.doubleValue();
return last;
}
// shortValue and byteValue 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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
public float floatValue() {
return (float) value;
public double decrementAndGet() {
value--;
return value;
}
/**
@ -328,15 +163,6 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
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 Double.valueOf(doubleValue());
}
/**
* Compares this object against the specified object. The result is {@code true} if and only if the argument
* is not {@code null} and is a {@link Double} object that represents a double that has the identical
@ -372,6 +198,81 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
&& Double.doubleToLongBits(((MutableDouble) obj).value) == Double.doubleToLongBits(value);
}
/**
* Returns the value of this MutableDouble as a float.
*
* @return the numeric value represented by this object after conversion to type float.
*/
@Override
public float floatValue() {
return (float) value;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public double getAndAdd(final double operand) {
final double last = value;
this.value += operand;
return last;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @throws NullPointerException if {@code operand} is null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public double getAndAdd(final Number operand) {
final double last = value;
this.value += operand.doubleValue();
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public double getAndDecrement() {
final double last = value;
value--;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public double getAndIncrement() {
final double last = value;
value++;
return last;
}
/**
* Gets the value as a Double instance.
*
* @return the value as a Double, never null
*/
@Override
public Double getValue() {
return Double.valueOf(this.value);
}
/**
* Returns a suitable hash code for this mutable.
*
@ -384,14 +285,113 @@ public class MutableDouble extends Number implements Comparable<MutableDouble>,
}
/**
* Compares this mutable to another in ascending order.
* Increments the value.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public double incrementAndGet() {
value++;
return value;
}
// shortValue and byteValue 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.
*/
@Override
public int compareTo(final MutableDouble other) {
return Double.compare(this.value, other.value);
public int intValue() {
return (int) value;
}
/**
* Checks whether the double value is infinite.
*
* @return true if infinite
*/
public boolean isInfinite() {
return Double.isInfinite(value);
}
/**
* Checks whether the double value is the special NaN value.
*
* @return true if NaN
*/
public boolean isNaN() {
return Double.isNaN(value);
}
/**
* Returns the value of this MutableDouble as a long.
*
* @return the numeric value represented by this object after conversion to type long.
*/
@Override
public long longValue() {
return (long) value;
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = value.doubleValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.doubleValue();
}
/**
* Gets this mutable as an instance of Double.
*
* @return a Double instance containing the value from this mutable, never null
*/
public Double toDouble() {
return Double.valueOf(doubleValue());
}
/**

View File

@ -73,122 +73,6 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
this.value = Float.parseFloat(value);
}
/**
* Gets the value as a Float instance.
*
* @return the value as a Float, never null
*/
@Override
public Float getValue() {
return Float.valueOf(this.value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = 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 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public float getAndIncrement() {
final float last = value;
value++;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public float incrementAndGet() {
value++;
return value;
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public float getAndDecrement() {
final float last = value;
value--;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public float decrementAndGet() {
value--;
return value;
}
/**
* Adds a value to the value of this instance.
*
@ -210,27 +94,6 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
this.value += operand.floatValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.floatValue();
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
@ -259,62 +122,34 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
* Compares this mutable to another in ascending order.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance immediately before the operand was added
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableFloat other) {
return Float.compare(this.value, other.value);
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public float getAndAdd(final float operand) {
final float last = value;
this.value += operand;
return last;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @throws NullPointerException if {@code operand} is null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public float getAndAdd(final Number operand) {
final float last = value;
this.value += operand.floatValue();
return last;
}
// shortValue and byteValue 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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
public float floatValue() {
public float decrementAndGet() {
value--;
return value;
}
@ -328,15 +163,6 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
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 Float.valueOf(floatValue());
}
/**
* Compares this object against some other object. The result is {@code true} if and only if the argument is
* not {@code null} and is a {@link Float} object that represents a {@code float} that has the
@ -374,6 +200,81 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
&& Float.floatToIntBits(((MutableFloat) obj).value) == Float.floatToIntBits(value);
}
/**
* Returns the value of this MutableFloat as a float.
*
* @return the numeric value represented by this object after conversion to type float.
*/
@Override
public float floatValue() {
return value;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public float getAndAdd(final float operand) {
final float last = value;
this.value += operand;
return last;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @throws NullPointerException if {@code operand} is null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public float getAndAdd(final Number operand) {
final float last = value;
this.value += operand.floatValue();
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public float getAndDecrement() {
final float last = value;
value--;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public float getAndIncrement() {
final float last = value;
value++;
return last;
}
/**
* Gets the value as a Float instance.
*
* @return the value as a Float, never null
*/
@Override
public Float getValue() {
return Float.valueOf(this.value);
}
/**
* Returns a suitable hash code for this mutable.
*
@ -385,14 +286,113 @@ public class MutableFloat extends Number implements Comparable<MutableFloat>, Mu
}
/**
* Compares this mutable to another in ascending order.
* Increments the value.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public float incrementAndGet() {
value++;
return value;
}
// shortValue and byteValue 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.
*/
@Override
public int compareTo(final MutableFloat other) {
return Float.compare(this.value, other.value);
public int intValue() {
return (int) value;
}
/**
* Checks whether the float value is infinite.
*
* @return true if infinite
*/
public boolean isInfinite() {
return Float.isInfinite(value);
}
/**
* Checks whether the float value is the special NaN value.
*
* @return true if NaN
*/
public boolean isNaN() {
return Float.isNaN(value);
}
/**
* Returns the value of this MutableFloat as a long.
*
* @return the numeric value represented by this object after conversion to type long.
*/
@Override
public long longValue() {
return (long) value;
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = value.floatValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.floatValue();
}
/**
* Gets this mutable as an instance of Float.
*
* @return a Float instance containing the value from this mutable, never null
*/
public Float toFloat() {
return Float.valueOf(floatValue());
}
/**

View File

@ -75,104 +75,6 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
this.value = Integer.parseInt(value);
}
/**
* Gets the value as a Integer instance.
*
* @return the value as a Integer, never null
*/
@Override
public Integer getValue() {
return Integer.valueOf(this.value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = value.intValue();
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public int getAndIncrement() {
final int last = value;
value++;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public int incrementAndGet() {
value++;
return value;
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public int getAndDecrement() {
final int last = value;
value--;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public int decrementAndGet() {
value--;
return value;
}
/**
* Adds a value to the value of this instance.
*
@ -194,27 +96,6 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
this.value += operand.intValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.intValue();
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
@ -242,6 +123,74 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableInt other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public int decrementAndGet() {
value--;
return value;
}
/**
* Returns the value of this MutableInt as a double.
*
* @return the numeric value represented by this object after conversion to type double.
*/
@Override
public double doubleValue() {
return value;
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument is
* not {@code null} and is a {@link MutableInt} object that contains the same {@code int} value
* as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof MutableInt) {
return value == ((MutableInt) obj).intValue();
}
return false;
}
/**
* Returns the value of this MutableInt as a float.
*
* @return the numeric value represented by this object after conversion to type float.
*/
@Override
public float floatValue() {
return value;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
@ -271,6 +220,73 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public int getAndDecrement() {
final int last = value;
value--;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public int getAndIncrement() {
final int last = value;
value++;
return last;
}
/**
* Gets the value as a Integer instance.
*
* @return the value as a Integer, never null
*/
@Override
public Integer getValue() {
return Integer.valueOf(this.value);
}
/**
* Returns a suitable hash code for this mutable.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return value;
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public int incrementAndGet() {
value++;
return value;
}
// shortValue and byteValue rely on Number implementation
/**
* Returns the value of this MutableInt as an int.
@ -293,23 +309,44 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
}
/**
* Returns the value of this MutableInt as a float.
* Sets the value.
*
* @return the numeric value represented by this object after conversion to type float.
* @param value the value to set
*/
@Override
public float floatValue() {
return value;
public void setValue(final int value) {
this.value = value;
}
/**
* Returns the value of this MutableInt as a double.
* Sets the value from any Number instance.
*
* @return the numeric value represented by this object after conversion to type double.
* @param value the value to set, not null
* @throws NullPointerException if the object is null
*/
@Override
public double doubleValue() {
return value;
public void setValue(final Number value) {
this.value = value.intValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.intValue();
}
/**
@ -321,43 +358,6 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
return Integer.valueOf(intValue());
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument is
* not {@code null} and is a {@link MutableInt} object that contains the same {@code int} value
* as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final 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
*/
@Override
public int hashCode() {
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableInt other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Returns the String value of this mutable.
*

View File

@ -75,104 +75,6 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
this.value = Long.parseLong(value);
}
/**
* Gets the value as a Long instance.
*
* @return the value as a Long, never null
*/
@Override
public Long getValue() {
return Long.valueOf(this.value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = value.longValue();
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public long getAndIncrement() {
final long last = value;
value++;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public long incrementAndGet() {
value++;
return value;
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public long getAndDecrement() {
final long last = value;
value--;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public long decrementAndGet() {
value--;
return value;
}
/**
* Adds a value to the value of this instance.
*
@ -194,27 +96,6 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
this.value += operand.longValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.longValue();
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
@ -242,6 +123,74 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableLong other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public long decrementAndGet() {
value--;
return value;
}
/**
* Returns the value of this MutableLong as a double.
*
* @return the numeric value represented by this object after conversion to type double.
*/
@Override
public double doubleValue() {
return value;
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument
* is not {@code null} and is a {@link MutableLong} object that contains the same {@code long}
* value as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof MutableLong) {
return value == ((MutableLong) obj).longValue();
}
return false;
}
/**
* Returns the value of this MutableLong as a float.
*
* @return the numeric value represented by this object after conversion to type float.
*/
@Override
public float floatValue() {
return value;
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
@ -271,6 +220,73 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public long getAndDecrement() {
final long last = value;
value--;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public long getAndIncrement() {
final long last = value;
value++;
return last;
}
/**
* Gets the value as a Long instance.
*
* @return the value as a Long, never null
*/
@Override
public Long getValue() {
return Long.valueOf(this.value);
}
/**
* Returns a suitable hash code for this mutable.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return (int) (value ^ (value >>> 32));
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public long incrementAndGet() {
value++;
return value;
}
// shortValue and byteValue rely on Number implementation
/**
* Returns the value of this MutableLong as an int.
@ -293,23 +309,44 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
}
/**
* Returns the value of this MutableLong as a float.
* Sets the value.
*
* @return the numeric value represented by this object after conversion to type float.
* @param value the value to set
*/
@Override
public float floatValue() {
return value;
public void setValue(final long value) {
this.value = value;
}
/**
* Returns the value of this MutableLong as a double.
* Sets the value from any Number instance.
*
* @return the numeric value represented by this object after conversion to type double.
* @param value the value to set, not null
* @throws NullPointerException if the object is null
*/
@Override
public double doubleValue() {
return value;
public void setValue(final Number value) {
this.value = value.longValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.longValue();
}
/**
@ -321,43 +358,6 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
return Long.valueOf(longValue());
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument
* is not {@code null} and is a {@link MutableLong} object that contains the same {@code long}
* value as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final 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
*/
@Override
public int hashCode() {
return (int) (value ^ (value >>> 32));
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableLong other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Returns the String value of this mutable.
*

View File

@ -53,26 +53,6 @@ public class MutableObject<T> implements Mutable<T>, Serializable {
this.value = value;
}
/**
* Gets the value.
*
* @return the value, may be null
*/
@Override
public T getValue() {
return this.value;
}
/**
* Sets the value.
*
* @param value the value to set
*/
@Override
public void setValue(final T value) {
this.value = value;
}
/**
* Compares this object against the specified object. The result is {@code true} if and only if the argument
* is not {@code null} and is a {@link MutableObject} object that contains the same {@link T}
@ -98,6 +78,16 @@ public class MutableObject<T> implements Mutable<T>, Serializable {
return false;
}
/**
* Gets the value.
*
* @return the value, may be null
*/
@Override
public T getValue() {
return this.value;
}
/**
* Returns the value's hash code or {@code 0} if the value is {@code null}.
*
@ -108,6 +98,16 @@ public class MutableObject<T> implements Mutable<T>, Serializable {
return Objects.hashCode(value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
@Override
public void setValue(final T value) {
this.value = value;
}
/**
* Returns the String value of this mutable.
*

View File

@ -45,15 +45,6 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
public MutableShort() {
}
/**
* Constructs a new MutableShort with the specified value.
*
* @param value the initial value to store
*/
public MutableShort(final short value) {
this.value = value;
}
/**
* Constructs a new MutableShort with the specified value.
*
@ -64,6 +55,15 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
this.value = value.shortValue();
}
/**
* Constructs a new MutableShort with the specified value.
*
* @param value the initial value to store
*/
public MutableShort(final short value) {
this.value = value;
}
/**
* Constructs a new MutableShort parsing the given string.
*
@ -75,114 +75,6 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
this.value = Short.parseShort(value);
}
/**
* Gets the value as a Short instance.
*
* @return the value as a Short, never null
*/
@Override
public Short getValue() {
return Short.valueOf(this.value);
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final 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
*/
@Override
public void setValue(final Number value) {
this.value = value.shortValue();
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public short getAndIncrement() {
final short last = value;
value++;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public short incrementAndGet() {
value++;
return value;
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public short getAndDecrement() {
final short last = value;
value--;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public short decrementAndGet() {
value--;
return value;
}
/**
* Adds a value to the value of this instance.
*
* @param operand the value to add, not null
* @since 2.2
*/
public void add(final short operand) {
this.value += operand;
}
/**
* Adds a value to the value of this instance.
*
@ -195,37 +87,13 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
}
/**
* Subtracts a value from the value of this instance.
* Adds a value to the value of this instance.
*
* @param operand the value to subtract, not null
* @param operand the value to add, not null
* @since 2.2
*/
public void subtract(final 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 2.2
*/
public void subtract(final Number operand) {
this.value -= operand.shortValue();
}
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately after the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance after adding the operand
* @since 3.5
*/
public short addAndGet(final short operand) {
public void add(final short operand) {
this.value += operand;
return value;
}
/**
@ -244,16 +112,83 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
/**
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
* immediately after the addition operation. This method is not thread safe.
*
* @param operand the quantity to add, not null
* @return the value associated with this instance immediately before the operand was added
* @return the value associated with this instance after adding the operand
* @since 3.5
*/
public short getAndAdd(final short operand) {
final short last = value;
public short addAndGet(final short operand) {
this.value += operand;
return last;
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableShort other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Decrements the value.
*
* @since 2.2
*/
public void decrement() {
value--;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately after the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance after it is decremented
* @since 3.5
*/
public short decrementAndGet() {
value--;
return value;
}
/**
* Returns the value of this MutableShort as a double.
*
* @return the numeric value represented by this object after conversion to type double.
*/
@Override
public double doubleValue() {
return value;
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument
* is not {@code null} and is a {@link MutableShort} object that contains the same {@code short}
* value as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof MutableShort) {
return value == ((MutableShort) obj).shortValue();
}
return false;
}
/**
* Returns the value of this MutableShort as a float.
*
* @return the numeric value represented by this object after conversion to type float.
*/
@Override
public float floatValue() {
return value;
}
/**
@ -271,14 +206,84 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
return last;
}
// byteValue relies on Number implementation
/**
* Returns the value of this MutableShort as a short.
* Increments this instance's value by {@code operand}; this method returns the value associated with the instance
* immediately prior to the addition operation. This method is not thread safe.
*
* @return the numeric value represented by this object after conversion to type short.
* @param operand the quantity to add, not null
* @return the value associated with this instance immediately before the operand was added
* @since 3.5
*/
public short getAndAdd(final short operand) {
final short last = value;
this.value += operand;
return last;
}
/**
* Decrements this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the decrement operation. This method is not thread safe.
*
* @return the value associated with the instance before it was decremented
* @since 3.5
*/
public short getAndDecrement() {
final short last = value;
value--;
return last;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately prior to the increment operation. This method is not thread safe.
*
* @return the value associated with the instance before it was incremented
* @since 3.5
*/
public short getAndIncrement() {
final short last = value;
value++;
return last;
}
/**
* Gets the value as a Short instance.
*
* @return the value as a Short, never null
*/
@Override
public short shortValue() {
public Short getValue() {
return Short.valueOf(this.value);
}
/**
* Returns a suitable hash code for this mutable.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return value;
}
/**
* Increments the value.
*
* @since 2.2
*/
public void increment() {
value++;
}
/**
* Increments this instance's value by 1; this method returns the value associated with the instance
* immediately after the increment operation. This method is not thread safe.
*
* @return the value associated with the instance after it is incremented
* @since 3.5
*/
public short incrementAndGet() {
value++;
return value;
}
@ -303,23 +308,55 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
}
/**
* Returns the value of this MutableShort as a float.
* Sets the value from any Number instance.
*
* @return the numeric value represented by this object after conversion to type float.
* @param value the value to set, not null
* @throws NullPointerException if the object is null
*/
@Override
public float floatValue() {
public void setValue(final Number value) {
this.value = value.shortValue();
}
/**
* Sets the value.
*
* @param value the value to set
*/
public void setValue(final short value) {
this.value = value;
}
// byteValue 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.
*/
@Override
public short shortValue() {
return value;
}
/**
* Returns the value of this MutableShort as a double.
* Subtracts a value from the value of this instance.
*
* @return the numeric value represented by this object after conversion to type double.
* @param operand the value to subtract, not null
* @throws NullPointerException if the object is null
* @since 2.2
*/
@Override
public double doubleValue() {
return value;
public void subtract(final Number operand) {
this.value -= operand.shortValue();
}
/**
* Subtracts a value from the value of this instance.
*
* @param operand the value to subtract, not null
* @since 2.2
*/
public void subtract(final short operand) {
this.value -= operand;
}
/**
@ -331,43 +368,6 @@ public class MutableShort extends Number implements Comparable<MutableShort>, Mu
return Short.valueOf(shortValue());
}
/**
* Compares this object to the specified object. The result is {@code true} if and only if the argument
* is not {@code null} and is a {@link MutableShort} object that contains the same {@code short}
* value as this object.
*
* @param obj the object to compare with, null returns false
* @return {@code true} if the objects are the same; {@code false} otherwise.
*/
@Override
public boolean equals(final 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
*/
@Override
public int hashCode() {
return value;
}
/**
* Compares this mutable to another in ascending order.
*
* @param other the other mutable to compare to, not null
* @return negative if this is less, zero if equal, positive if greater
*/
@Override
public int compareTo(final MutableShort other) {
return NumberUtils.compare(this.value, other.value);
}
/**
* Returns the String value of this mutable.
*

View File

@ -46,14 +46,99 @@ import org.apache.commons.lang3.ClassUtils;
public class ConstructorUtils {
/**
* ConstructorUtils instances should NOT be constructed in standard
* programming. Instead, the class should be used as
* {@code ConstructorUtils.invokeConstructor(cls, args)}.
* Finds a constructor given a class and signature, checking accessibility.
*
* <p>This constructor is {@code public} to permit tools that require a JavaBean
* instance to operate.</p>
* <p>This finds the constructor and ensures that it is accessible.
* The constructor signature must match the parameter types exactly.</p>
*
* @param <T> the constructor type
* @param cls the class to find a constructor for, not {@code null}
* @param parameterTypes the array of parameter types, {@code null} treated as empty
* @return the constructor, {@code null} if no matching accessible constructor found
* @see Class#getConstructor
* @see #getAccessibleConstructor(java.lang.reflect.Constructor)
* @throws NullPointerException if {@code cls} is {@code null}
*/
public ConstructorUtils() {
public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls,
final Class<?>... parameterTypes) {
Objects.requireNonNull(cls, "cls");
try {
return getAccessibleConstructor(cls.getConstructor(parameterTypes));
} catch (final NoSuchMethodException e) {
return null;
}
}
/**
* Checks if the specified constructor is accessible.
*
* <p>This simply ensures that the constructor is accessible.</p>
*
* @param <T> the constructor type
* @param ctor the prototype constructor object, not {@code null}
* @return the constructor, {@code null} if no matching accessible constructor found
* @see SecurityManager
* @throws NullPointerException if {@code ctor} is {@code null}
*/
public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
Objects.requireNonNull(ctor, "ctor");
return MemberUtils.isAccessible(ctor)
&& isAccessible(ctor.getDeclaringClass()) ? ctor : null;
}
/**
* Finds an accessible constructor with compatible parameters.
*
* <p>This checks all the constructor and finds one with compatible parameters
* This requires that every parameter is assignable from the given parameter types.
* This is a more flexible search than the normal exact matching algorithm.</p>
*
* <p>First it checks if there is a constructor matching the exact signature.
* If not then all the constructors of the class are checked to see if their
* signatures are assignment-compatible with the parameter types.
* The first assignment-compatible matching constructor is returned.</p>
*
* @param <T> the constructor type
* @param cls the class to find a constructor for, not {@code null}
* @param parameterTypes find method with compatible parameters
* @return the constructor, null if no matching accessible constructor found
* @throws NullPointerException if {@code cls} is {@code null}
*/
public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls,
final Class<?>... parameterTypes) {
Objects.requireNonNull(cls, "cls");
// see if we can find the constructor directly
// most of the time this works and it's much faster
try {
return MemberUtils.setAccessibleWorkaround(cls.getConstructor(parameterTypes));
} catch (final NoSuchMethodException ignored) {
// ignore
}
Constructor<T> result = null;
/*
* (1) Class.getConstructors() is documented to return Constructor<T> so as
* long as the array is not subsequently modified, everything's fine.
*/
final Constructor<?>[] ctors = cls.getConstructors();
// return best match:
for (Constructor<?> ctor : ctors) {
// compare parameters
if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
// get accessible version of constructor
ctor = getAccessibleConstructor(ctor);
if (ctor != null) {
MemberUtils.setAccessibleWorkaround(ctor);
if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
// temporary variable for annotation, see comment above (1)
@SuppressWarnings("unchecked")
final Constructor<T> constructor = (Constructor<T>) ctor;
result = constructor;
}
}
}
}
return result;
}
/**
@ -178,102 +263,6 @@ public class ConstructorUtils {
return ctor.newInstance(args);
}
/**
* Finds a constructor given a class and signature, checking accessibility.
*
* <p>This finds the constructor and ensures that it is accessible.
* The constructor signature must match the parameter types exactly.</p>
*
* @param <T> the constructor type
* @param cls the class to find a constructor for, not {@code null}
* @param parameterTypes the array of parameter types, {@code null} treated as empty
* @return the constructor, {@code null} if no matching accessible constructor found
* @see Class#getConstructor
* @see #getAccessibleConstructor(java.lang.reflect.Constructor)
* @throws NullPointerException if {@code cls} is {@code null}
*/
public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls,
final Class<?>... parameterTypes) {
Objects.requireNonNull(cls, "cls");
try {
return getAccessibleConstructor(cls.getConstructor(parameterTypes));
} catch (final NoSuchMethodException e) {
return null;
}
}
/**
* Checks if the specified constructor is accessible.
*
* <p>This simply ensures that the constructor is accessible.</p>
*
* @param <T> the constructor type
* @param ctor the prototype constructor object, not {@code null}
* @return the constructor, {@code null} if no matching accessible constructor found
* @see SecurityManager
* @throws NullPointerException if {@code ctor} is {@code null}
*/
public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
Objects.requireNonNull(ctor, "ctor");
return MemberUtils.isAccessible(ctor)
&& isAccessible(ctor.getDeclaringClass()) ? ctor : null;
}
/**
* Finds an accessible constructor with compatible parameters.
*
* <p>This checks all the constructor and finds one with compatible parameters
* This requires that every parameter is assignable from the given parameter types.
* This is a more flexible search than the normal exact matching algorithm.</p>
*
* <p>First it checks if there is a constructor matching the exact signature.
* If not then all the constructors of the class are checked to see if their
* signatures are assignment-compatible with the parameter types.
* The first assignment-compatible matching constructor is returned.</p>
*
* @param <T> the constructor type
* @param cls the class to find a constructor for, not {@code null}
* @param parameterTypes find method with compatible parameters
* @return the constructor, null if no matching accessible constructor found
* @throws NullPointerException if {@code cls} is {@code null}
*/
public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls,
final Class<?>... parameterTypes) {
Objects.requireNonNull(cls, "cls");
// see if we can find the constructor directly
// most of the time this works and it's much faster
try {
return MemberUtils.setAccessibleWorkaround(cls.getConstructor(parameterTypes));
} catch (final NoSuchMethodException ignored) {
// ignore
}
Constructor<T> result = null;
/*
* (1) Class.getConstructors() is documented to return Constructor<T> so as
* long as the array is not subsequently modified, everything's fine.
*/
final Constructor<?>[] ctors = cls.getConstructors();
// return best match:
for (Constructor<?> ctor : ctors) {
// compare parameters
if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
// get accessible version of constructor
ctor = getAccessibleConstructor(ctor);
if (ctor != null) {
MemberUtils.setAccessibleWorkaround(ctor);
if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
// temporary variable for annotation, see comment above (1)
@SuppressWarnings("unchecked")
final Constructor<T> constructor = (Constructor<T>) ctor;
result = constructor;
}
}
}
}
return result;
}
/**
* Tests whether the specified class is generally accessible, i.e. is
* declared in an entirely {@code public} manner.
@ -292,4 +281,15 @@ public class ConstructorUtils {
return true;
}
/**
* ConstructorUtils instances should NOT be constructed in standard
* programming. Instead, the class should be used as
* {@code ConstructorUtils.invokeConstructor(cls, args)}.
*
* <p>This constructor is {@code public} to permit tools that require a JavaBean
* instance to operate.</p>
*/
public ConstructorUtils() {
}
}

View File

@ -44,12 +44,93 @@ import org.apache.commons.lang3.Validate;
public class FieldUtils {
/**
* {@link FieldUtils} instances should NOT be constructed in standard programming.
* <p>
* This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
* </p>
* Gets all fields of the given class and its parents (if any).
*
* @param cls
* the {@link Class} to query
* @return an array of Fields (possibly empty).
* @throws NullPointerException
* if the class is {@code null}
* @since 3.2
*/
public FieldUtils() {
public static Field[] getAllFields(final Class<?> cls) {
return getAllFieldsList(cls).toArray(ArrayUtils.EMPTY_FIELD_ARRAY);
}
/**
* Gets all fields of the given class and its parents (if any).
*
* @param cls
* the {@link Class} to query
* @return a list of Fields (possibly empty).
* @throws NullPointerException
* if the class is {@code null}
* @since 3.2
*/
public static List<Field> getAllFieldsList(final Class<?> cls) {
Objects.requireNonNull(cls, "cls");
final List<Field> allFields = new ArrayList<>();
Class<?> currentClass = cls;
while (currentClass != null) {
final Field[] declaredFields = currentClass.getDeclaredFields();
Collections.addAll(allFields, declaredFields);
currentClass = currentClass.getSuperclass();
}
return allFields;
}
/**
* Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the Field object
* @throws NullPointerException
* if the class is {@code null}
* @throws IllegalArgumentException
* if the field name is {@code null}, blank, or empty
*/
public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
return getDeclaredField(cls, fieldName, false);
}
/**
* Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
* considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @return the Field object
* @throws NullPointerException
* if the class is {@code null}
* @throws IllegalArgumentException
* if the field name is {@code null}, blank, or empty
*/
public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
Objects.requireNonNull(cls, "cls");
Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
try {
// only consider the specified class by using getDeclaredField()
final Field field = cls.getDeclaredField(fieldName);
if (!MemberUtils.isAccessible(field)) {
if (!forceAccess) {
return null;
}
field.setAccessible(true);
}
return field;
} catch (final NoSuchFieldException ignored) {
// ignore
}
return null;
}
/**
@ -138,93 +219,19 @@ public class FieldUtils {
}
/**
* Gets an accessible {@link Field} by name respecting scope. Only the specified class will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the Field object
* @throws NullPointerException
* if the class is {@code null}
* @throws IllegalArgumentException
* if the field name is {@code null}, blank, or empty
*/
public static Field getDeclaredField(final Class<?> cls, final String fieldName) {
return getDeclaredField(cls, fieldName, false);
}
/**
* Gets an accessible {@link Field} by name, breaking scope if requested. Only the specified class will be
* considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @return the Field object
* @throws NullPointerException
* if the class is {@code null}
* @throws IllegalArgumentException
* if the field name is {@code null}, blank, or empty
*/
public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
Objects.requireNonNull(cls, "cls");
Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty");
try {
// only consider the specified class by using getDeclaredField()
final Field field = cls.getDeclaredField(fieldName);
if (!MemberUtils.isAccessible(field)) {
if (!forceAccess) {
return null;
}
field.setAccessible(true);
}
return field;
} catch (final NoSuchFieldException ignored) {
// ignore
}
return null;
}
/**
* Gets all fields of the given class and its parents (if any).
*
* @param cls
* the {@link Class} to query
* @return an array of Fields (possibly empty).
* @throws NullPointerException
* if the class is {@code null}
* @since 3.2
*/
public static Field[] getAllFields(final Class<?> cls) {
return getAllFieldsList(cls).toArray(ArrayUtils.EMPTY_FIELD_ARRAY);
}
/**
* Gets all fields of the given class and its parents (if any).
*
* Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
* @param cls
* the {@link Class} to query
* @param annotationCls
* the {@link Annotation} that must be present on a field to be matched
* @return a list of Fields (possibly empty).
* @throws NullPointerException
* if the class is {@code null}
* @since 3.2
* if the class or annotation are {@code null}
* @since 3.4
*/
public static List<Field> getAllFieldsList(final Class<?> cls) {
Objects.requireNonNull(cls, "cls");
final List<Field> allFields = new ArrayList<>();
Class<?> currentClass = cls;
while (currentClass != null) {
final Field[] declaredFields = currentClass.getDeclaredFields();
Collections.addAll(allFields, declaredFields);
currentClass = currentClass.getSuperclass();
}
return allFields;
public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
Objects.requireNonNull(annotationCls, "annotationCls");
return getAllFieldsList(cls).stream().filter(field -> field.getAnnotation(annotationCls) != null).collect(Collectors.toList());
}
/**
@ -243,103 +250,50 @@ public class FieldUtils {
}
/**
* Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
* @param cls
* the {@link Class} to query
* @param annotationCls
* the {@link Annotation} that must be present on a field to be matched
* @return a list of Fields (possibly empty).
* @throws NullPointerException
* if the class or annotation are {@code null}
* @since 3.4
*/
public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
Objects.requireNonNull(annotationCls, "annotationCls");
return getAllFieldsList(cls).stream().filter(field -> field.getAnnotation(annotationCls) != null).collect(Collectors.toList());
}
/**
* Reads an accessible {@code static} {@link Field}.
* Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
*
* @param field
* to read
* @return the field value
* @throws NullPointerException
* if the field is {@code null}
* @throws IllegalArgumentException
* if the field is not {@code static}
* @throws IllegalAccessException
* if the field is not accessible
*/
public static Object readStaticField(final Field field) throws IllegalAccessException {
return readStaticField(field, false);
}
/**
* Reads a static {@link Field}.
*
* @param field
* to read
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
* @return the field value
* @throws NullPointerException
* if the field is {@code null}
* @throws IllegalArgumentException
* if the field is not {@code static}
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
Objects.requireNonNull(field, "field");
Validate.isTrue(MemberUtils.isStatic(field), "The field '%s' is not static", field.getName());
return readField(field, (Object) null, forceAccess);
}
/**
* Reads the named {@code public static} {@link Field}. Superclasses will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the value of the field
* @throws NullPointerException
* if the class is {@code null}, or the field could not be found
* if {@code target} is @{code null}
* @throws IllegalArgumentException
* if the field name is {@code null}, blank or empty, or is not {@code static}
* if {@code fieldName} is {@code null}, blank or empty, or could not be found
* @throws IllegalAccessException
* if the field is not accessible
* if the named field is not {@code public}
*/
public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
return readStaticField(cls, fieldName, false);
public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
return readDeclaredField(target, fieldName, false);
}
/**
* Reads the named {@code static} {@link Field}. Superclasses will be considered.
* Gets a {@link Field} value by name. Only the class of the specified object will be considered.
*
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* match public fields.
* @return the Field object
* @throws NullPointerException
* if the class is {@code null}, or the field could not be found
* if {@code target} is @{code null}
* @throws IllegalArgumentException
* if the field name is {@code null}, blank or empty, or is not {@code static}
* if {@code fieldName} is {@code null}, blank or empty, or could not be found
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
final Field field = getField(cls, fieldName, forceAccess);
Validate.notNull(field, "Cannot locate field '%s' on %s", fieldName, cls);
public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
Objects.requireNonNull(target, "target");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
// already forced access above, don't repeat it here:
return readStaticField(field, false);
return readField(field, target, false);
}
/**
@ -479,142 +433,197 @@ public class FieldUtils {
}
/**
* Reads the named {@code public} {@link Field}. Only the class of the specified object will be considered.
* Reads the named {@code public static} {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @return the value of the field
* @throws NullPointerException
* if {@code target} is @{code null}
* if the class is {@code null}, or the field could not be found
* @throws IllegalArgumentException
* if {@code fieldName} is {@code null}, blank or empty, or could not be found
* if the field name is {@code null}, blank or empty, or is not {@code static}
* @throws IllegalAccessException
* if the named field is not {@code public}
* if the field is not accessible
*/
public static Object readDeclaredField(final Object target, final String fieldName) throws IllegalAccessException {
return readDeclaredField(target, fieldName, false);
public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
return readStaticField(cls, fieldName, false);
}
/**
* Gets a {@link Field} value by name. Only the class of the specified object will be considered.
* Reads the named {@code static} {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param cls
* the {@link Class} to reflect, must not be {@code null}
* @param fieldName
* the field name to obtain
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match public fields.
* match {@code public} fields.
* @return the Field object
* @throws NullPointerException
* if {@code target} is @{code null}
* if the class is {@code null}, or the field could not be found
* @throws IllegalArgumentException
* if {@code fieldName} is {@code null}, blank or empty, or could not be found
* if the field name is {@code null}, blank or empty, or is not {@code static}
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readDeclaredField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
Objects.requireNonNull(target, "target");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls, fieldName);
public static Object readStaticField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws IllegalAccessException {
final Field field = getField(cls, fieldName, forceAccess);
Validate.notNull(field, "Cannot locate field '%s' on %s", fieldName, cls);
// already forced access above, don't repeat it here:
return readField(field, target, false);
return readStaticField(field, false);
}
/**
* Writes a {@code public static} {@link Field}.
* Reads an accessible {@code static} {@link Field}.
*
* @param field
* to write
* @param value
* to set
* to read
* @return the field value
* @throws NullPointerException
* if the field is {@code null}
* if the field is {@code null}
* @throws IllegalArgumentException
* if the field is not {@code static}, or {@code value} is not assignable
* if the field is not {@code static}
* @throws IllegalAccessException
* if the field is not {@code public} or is {@code final}
* if the field is not accessible
*/
public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
writeStaticField(field, value, false);
public static Object readStaticField(final Field field) throws IllegalAccessException {
return readStaticField(field, false);
}
/**
* Writes a static {@link Field}.
* Reads a static {@link Field}.
*
* @param field
* to write
* @param value
* to set
* to read
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method.
* @return the field value
* @throws NullPointerException
* if the field is {@code null}
* @throws IllegalArgumentException
* if the field is not {@code static}
* @throws IllegalAccessException
* if the field is not made accessible
*/
public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
Objects.requireNonNull(field, "field");
Validate.isTrue(MemberUtils.isStatic(field), "The field '%s' is not static", field.getName());
return readField(field, (Object) null, forceAccess);
}
/**
* Removes the final modifier from a {@link Field}.
*
* @param field
* to remove the final modifier
* @throws NullPointerException
* if the field is {@code null}
* @since 3.2
*/
public static void removeFinalModifier(final Field field) {
removeFinalModifier(field, true);
}
/**
* Removes the final modifier from a {@link Field}.
*
* @param field
* to remove the final modifier
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws NullPointerException
* if the field is {@code null}
* @throws IllegalArgumentException
* if the field is not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible or is {@code final}
* if the field is {@code null}
* @deprecated As of Java 12, we can no longer drop the {@code final} modifier, thus
* rendering this method obsolete. The JDK discussion about this change can be found
* here: https://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056486.html
* @since 3.3
*/
public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
@Deprecated
public static void removeFinalModifier(final Field field, final boolean forceAccess) {
Objects.requireNonNull(field, "field");
Validate.isTrue(MemberUtils.isStatic(field), "The field %s.%s is not static", field.getDeclaringClass().getName(),
field.getName());
writeField(field, (Object) null, value, forceAccess);
try {
if (Modifier.isFinal(field.getModifiers())) {
// Do all JREs implement Field with a private ivar called "modifiers"?
final Field modifiersField = Field.class.getDeclaredField("modifiers");
final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
if (doForceAccess) {
modifiersField.setAccessible(true);
}
try {
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
} finally {
if (doForceAccess) {
modifiersField.setAccessible(false);
}
}
}
} catch (final NoSuchFieldException | IllegalAccessException e) {
if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_12)) {
throw new UnsupportedOperationException(
"In java 12+ final cannot be removed.",
e
);
}
// else no exception is thrown because we can modify final.
}
}
/**
* Writes a named {@code public static} {@link Field}. Superclasses will be considered.
* Writes a {@code public} {@link Field}. Only the specified class will be considered.
*
* @param cls
* {@link Class} on which the field is to be found
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* to write
* the field name to obtain
* @param value
* to set
* @throws NullPointerException
* if {@code target} is @{code null}
* @throws IllegalArgumentException
* if {@code fieldName} is {@code null}, blank or empty, the field cannot be located or is
* not {@code static}, or {@code value} is not assignable
* if {@code fieldName} is {@code null}, blank or empty, or could not be found,
* or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not {@code public} or is {@code final}
* if the field is not made accessible
*/
public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
writeStaticField(cls, fieldName, value, false);
public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
writeDeclaredField(target, fieldName, value, false);
}
/**
* Writes a named {@code static} {@link Field}. Superclasses will be considered.
* Writes a {@code public} {@link Field}. Only the specified class will be considered.
*
* @param cls
* {@link Class} on which the field is to be found
* @param target
* the object to reflect, must not be {@code null}
* @param fieldName
* to write
* the field name to obtain
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws NullPointerException
* if {@code cls} is {@code null} or the field cannot be located
* @throws IllegalArgumentException
* if {@code fieldName} is {@code null}, blank or empty, the field not {@code static}, or {@code value} is not assignable
* if {@code fieldName} is {@code null}, blank or empty, or could not be found,
* or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible or is {@code final}
* if the field is not made accessible
*/
public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
throws IllegalAccessException {
final Field field = getField(cls, fieldName, forceAccess);
Validate.notNull(field, "Cannot locate field %s on %s", fieldName, cls);
Objects.requireNonNull(target, "target");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
// already forced access above, don't repeat it here:
writeStaticField(field, value, false);
writeField(field, target, value, false);
}
/**
@ -715,66 +724,6 @@ public class FieldUtils {
field.set(target, value);
}
/**
* Removes the final modifier from a {@link Field}.
*
* @param field
* to remove the final modifier
* @throws NullPointerException
* if the field is {@code null}
* @since 3.2
*/
public static void removeFinalModifier(final Field field) {
removeFinalModifier(field, true);
}
/**
* Removes the final modifier from a {@link Field}.
*
* @param field
* to remove the final modifier
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws NullPointerException
* if the field is {@code null}
* @deprecated As of Java 12, we can no longer drop the {@code final} modifier, thus
* rendering this method obsolete. The JDK discussion about this change can be found
* here: https://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056486.html
* @since 3.3
*/
@Deprecated
public static void removeFinalModifier(final Field field, final boolean forceAccess) {
Objects.requireNonNull(field, "field");
try {
if (Modifier.isFinal(field.getModifiers())) {
// Do all JREs implement Field with a private ivar called "modifiers"?
final Field modifiersField = Field.class.getDeclaredField("modifiers");
final boolean doForceAccess = forceAccess && !modifiersField.isAccessible();
if (doForceAccess) {
modifiersField.setAccessible(true);
}
try {
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
} finally {
if (doForceAccess) {
modifiersField.setAccessible(false);
}
}
}
} catch (final NoSuchFieldException | IllegalAccessException e) {
if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_12)) {
throw new UnsupportedOperationException(
"In java 12+ final cannot be removed.",
e
);
}
// else no exception is thrown because we can modify final.
}
}
/**
* Writes a {@code public} {@link Field}. Superclasses will be considered.
*
@ -828,52 +777,103 @@ public class FieldUtils {
}
/**
* Writes a {@code public} {@link Field}. Only the specified class will be considered.
* Writes a named {@code public static} {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param cls
* {@link Class} on which the field is to be found
* @param fieldName
* the field name to obtain
* to write
* @param value
* to set
* @throws NullPointerException
* if {@code target} is @{code null}
* @throws IllegalArgumentException
* if {@code fieldName} is {@code null}, blank or empty, or could not be found,
* or {@code value} is not assignable
* if {@code fieldName} is {@code null}, blank or empty, the field cannot be located or is
* not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible
* if the field is not {@code public} or is {@code final}
*/
public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
writeDeclaredField(target, fieldName, value, false);
public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException {
writeStaticField(cls, fieldName, value, false);
}
/**
* Writes a {@code public} {@link Field}. Only the specified class will be considered.
* Writes a named {@code static} {@link Field}. Superclasses will be considered.
*
* @param target
* the object to reflect, must not be {@code null}
* @param cls
* {@link Class} on which the field is to be found
* @param fieldName
* the field name to obtain
* to write
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws NullPointerException
* if {@code cls} is {@code null} or the field cannot be located
* @throws IllegalArgumentException
* if {@code fieldName} is {@code null}, blank or empty, or could not be found,
* or {@code value} is not assignable
* if {@code fieldName} is {@code null}, blank or empty, the field not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible
* if the field is not made accessible or is {@code final}
*/
public static void writeDeclaredField(final Object target, final String fieldName, final Object value, final boolean forceAccess)
public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value, final boolean forceAccess)
throws IllegalAccessException {
Objects.requireNonNull(target, "target");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, forceAccess);
Validate.isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
final Field field = getField(cls, fieldName, forceAccess);
Validate.notNull(field, "Cannot locate field %s on %s", fieldName, cls);
// already forced access above, don't repeat it here:
writeField(field, target, value, false);
writeStaticField(field, value, false);
}
/**
* Writes a {@code public static} {@link Field}.
*
* @param field
* to write
* @param value
* to set
* @throws NullPointerException
* if the field is {@code null}
* @throws IllegalArgumentException
* if the field is not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not {@code public} or is {@code final}
*/
public static void writeStaticField(final Field field, final Object value) throws IllegalAccessException {
writeStaticField(field, value, false);
}
/**
* Writes a static {@link Field}.
*
* @param field
* to write
* @param value
* to set
* @param forceAccess
* whether to break scope restrictions using the
* {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will only
* match {@code public} fields.
* @throws NullPointerException
* if the field is {@code null}
* @throws IllegalArgumentException
* if the field is not {@code static}, or {@code value} is not assignable
* @throws IllegalAccessException
* if the field is not made accessible or is {@code final}
*/
public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
Objects.requireNonNull(field, "field");
Validate.isTrue(MemberUtils.isStatic(field), "The field %s.%s is not static", field.getDeclaringClass().getName(),
field.getName());
writeField(field, (Object) null, value, forceAccess);
}
/**
* {@link FieldUtils} instances should NOT be constructed in standard programming.
* <p>
* This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
* </p>
*/
public FieldUtils() {
}
}

View File

@ -25,17 +25,6 @@ import org.apache.commons.lang3.BooleanUtils;
*/
public class InheritanceUtils {
/**
* {@link InheritanceUtils} instances should NOT be constructed in standard programming.
* Instead, the class should be used as
* {@code MethodUtils.getAccessibleMethod(method)}.
*
* <p>This constructor is {@code public} to permit tools that require a JavaBean
* instance to operate.</p>
*/
public InheritanceUtils() {
}
/**
* Returns the number of inheritance hops between two classes.
*
@ -63,4 +52,15 @@ public class InheritanceUtils {
d += distance(cParent, parent);
return d > 0 ? d + 1 : -1;
}
/**
* {@link InheritanceUtils} instances should NOT be constructed in standard programming.
* Instead, the class should be used as
* {@code MethodUtils.getAccessibleMethod(method)}.
*
* <p>This constructor is {@code public} to permit tools that require a JavaBean
* instance to operate.</p>
*/
public InheritanceUtils() {
}
}

View File

@ -33,77 +33,47 @@ import org.apache.commons.lang3.ClassUtils;
final class MemberUtils {
// TODO extract an interface to implement compareParameterSets(...)?
/**
* A class providing a subset of the API of java.lang.reflect.Executable in Java 1.8,
* providing a common representation for function signatures for Constructors and Methods.
*/
private static final class Executable {
private static Executable of(final Constructor<?> constructor) {
return new Executable(constructor);
}
private static Executable of(final Method method) {
return new Executable(method);
}
private final Class<?>[] parameterTypes;
private final boolean isVarArgs;
private Executable(final Constructor<?> constructor) {
parameterTypes = constructor.getParameterTypes();
isVarArgs = constructor.isVarArgs();
}
private Executable(final Method method) {
parameterTypes = method.getParameterTypes();
isVarArgs = method.isVarArgs();
}
public Class<?>[] getParameterTypes() {
return parameterTypes;
}
public boolean isVarArgs() {
return isVarArgs;
}
}
private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
/** 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 };
/**
* Default access superclass workaround.
*
* When a {@code public} class has a default access superclass with {@code 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) prevent access even when the modifier is {@code public}.
* Calling {@code setAccessible(true)} solves the problem but will only work from
* sufficiently privileged code. Better workarounds would be gratefully
* accepted.
* @param obj the AccessibleObject to set as accessible
* @return a boolean indicating whether the accessibility of the object was set to true.
*/
static <T extends AccessibleObject> T setAccessibleWorkaround(final T obj) {
if (obj == null || obj.isAccessible()) {
return obj;
}
final Member m = (Member) obj;
if (!obj.isAccessible() && isPublic(m) && isPackageAccess(m.getDeclaringClass().getModifiers())) {
try {
obj.setAccessible(true);
return obj;
} catch (final SecurityException ignored) {
// ignore in favor of subsequent IllegalAccessException
}
}
return obj;
}
/**
* Tests whether a given set of modifiers implies package access.
* @param modifiers to test
* @return {@code true} unless {@code package}/{@code protected}/{@code private} modifier detected
*/
static boolean isPackageAccess(final int modifiers) {
return (modifiers & ACCESS_TEST) == 0;
}
/**
* Tests whether a {@link Member} is public.
* @param member Member to test
* @return {@code true} if {@code m} is public
*/
static boolean isPublic(final Member member) {
return member != null && Modifier.isPublic(member.getModifiers());
}
/**
* Tests whether a {@link Member} is static.
* @param member Member to test
* @return {@code true} if {@code m} is static
*/
static boolean isStatic(final Member member) {
return member != null && Modifier.isStatic(member.getModifiers());
}
/**
* Tests whether a {@link Member} is accessible.
* @param member Member to test
* @return {@code true} if {@code m} is accessible
*/
static boolean isAccessible(final Member member) {
return isPublic(member) && !member.isSynthetic();
}
/**
* Compares the relative fitness of two Constructors in terms of how well they
* match a set of runtime parameter types, such that a list ordered
@ -156,52 +126,6 @@ final class MemberUtils {
return Float.compare(leftCost, rightCost);
}
/**
* Returns the sum of the object transformation cost for each class in the
* source argument list.
* @param srcArgs The source arguments
* @param executable The executable to calculate transformation costs for
* @return The total transformation cost
*/
private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Executable executable) {
final Class<?>[] destArgs = executable.getParameterTypes();
final boolean isVarArgs = executable.isVarArgs();
// "source" and "destination" are the actual and declared args respectively.
float totalCost = 0.0f;
final long normalArgsLen = isVarArgs ? destArgs.length - 1 : destArgs.length;
if (srcArgs.length < normalArgsLen) {
return Float.MAX_VALUE;
}
for (int i = 0; i < normalArgsLen; i++) {
totalCost += getObjectTransformationCost(srcArgs[i], destArgs[i]);
}
if (isVarArgs) {
// When isVarArgs is true, srcArgs and dstArgs may differ in length.
// There are two special cases to consider:
final boolean noVarArgsPassed = srcArgs.length < destArgs.length;
final boolean explicitArrayForVarargs = srcArgs.length == destArgs.length && srcArgs[srcArgs.length - 1] != null
&& srcArgs[srcArgs.length - 1].isArray();
final float varArgsCost = 0.001f;
final Class<?> destClass = destArgs[destArgs.length - 1].getComponentType();
if (noVarArgsPassed) {
// When no varargs passed, the best match is the most generic matching type, not the most specific.
totalCost += getObjectTransformationCost(destClass, Object.class) + varArgsCost;
} else if (explicitArrayForVarargs) {
final Class<?> sourceClass = srcArgs[srcArgs.length - 1].getComponentType();
totalCost += getObjectTransformationCost(sourceClass, destClass) + varArgsCost;
} else {
// This is typical varargs case.
for (int i = destArgs.length - 1; i < srcArgs.length; i++) {
final Class<?> srcClass = srcArgs[i];
totalCost += getObjectTransformationCost(srcClass, destClass) + varArgsCost;
}
}
}
return totalCost;
}
/**
* Gets the number of steps needed to turn the source class into
* the destination class. This represents the number of steps in the object
@ -267,8 +191,59 @@ final class MemberUtils {
return cost;
}
static boolean isMatchingMethod(final Method method, final Class<?>[] parameterTypes) {
return isMatchingExecutable(Executable.of(method), parameterTypes);
/**
* Returns the sum of the object transformation cost for each class in the
* source argument list.
* @param srcArgs The source arguments
* @param executable The executable to calculate transformation costs for
* @return The total transformation cost
*/
private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Executable executable) {
final Class<?>[] destArgs = executable.getParameterTypes();
final boolean isVarArgs = executable.isVarArgs();
// "source" and "destination" are the actual and declared args respectively.
float totalCost = 0.0f;
final long normalArgsLen = isVarArgs ? destArgs.length - 1 : destArgs.length;
if (srcArgs.length < normalArgsLen) {
return Float.MAX_VALUE;
}
for (int i = 0; i < normalArgsLen; i++) {
totalCost += getObjectTransformationCost(srcArgs[i], destArgs[i]);
}
if (isVarArgs) {
// When isVarArgs is true, srcArgs and dstArgs may differ in length.
// There are two special cases to consider:
final boolean noVarArgsPassed = srcArgs.length < destArgs.length;
final boolean explicitArrayForVarargs = srcArgs.length == destArgs.length && srcArgs[srcArgs.length - 1] != null
&& srcArgs[srcArgs.length - 1].isArray();
final float varArgsCost = 0.001f;
final Class<?> destClass = destArgs[destArgs.length - 1].getComponentType();
if (noVarArgsPassed) {
// When no varargs passed, the best match is the most generic matching type, not the most specific.
totalCost += getObjectTransformationCost(destClass, Object.class) + varArgsCost;
} else if (explicitArrayForVarargs) {
final Class<?> sourceClass = srcArgs[srcArgs.length - 1].getComponentType();
totalCost += getObjectTransformationCost(sourceClass, destClass) + varArgsCost;
} else {
// This is typical varargs case.
for (int i = destArgs.length - 1; i < srcArgs.length; i++) {
final Class<?> srcClass = srcArgs[i];
totalCost += getObjectTransformationCost(srcClass, destClass) + varArgsCost;
}
}
}
return totalCost;
}
/**
* Tests whether a {@link Member} is accessible.
* @param member Member to test
* @return {@code true} if {@code m} is accessible
*/
static boolean isAccessible(final Member member) {
return isPublic(member) && !member.isSynthetic();
}
static boolean isMatchingConstructor(final Constructor<?> method, final Class<?>[] parameterTypes) {
@ -300,39 +275,64 @@ final class MemberUtils {
return false;
}
static boolean isMatchingMethod(final Method method, final Class<?>[] parameterTypes) {
return isMatchingExecutable(Executable.of(method), parameterTypes);
}
/**
* A class providing a subset of the API of java.lang.reflect.Executable in Java 1.8,
* providing a common representation for function signatures for Constructors and Methods.
* Tests whether a given set of modifiers implies package access.
* @param modifiers to test
* @return {@code true} unless {@code package}/{@code protected}/{@code private} modifier detected
*/
private static final class Executable {
private final Class<?>[] parameterTypes;
private final boolean isVarArgs;
static boolean isPackageAccess(final int modifiers) {
return (modifiers & ACCESS_TEST) == 0;
}
private static Executable of(final Method method) {
return new Executable(method);
}
/**
* Tests whether a {@link Member} is public.
* @param member Member to test
* @return {@code true} if {@code m} is public
*/
static boolean isPublic(final Member member) {
return member != null && Modifier.isPublic(member.getModifiers());
}
private static Executable of(final Constructor<?> constructor) {
return new Executable(constructor);
}
/**
* Tests whether a {@link Member} is static.
* @param member Member to test
* @return {@code true} if {@code m} is static
*/
static boolean isStatic(final Member member) {
return member != null && Modifier.isStatic(member.getModifiers());
}
private Executable(final Method method) {
parameterTypes = method.getParameterTypes();
isVarArgs = method.isVarArgs();
}
private Executable(final Constructor<?> constructor) {
parameterTypes = constructor.getParameterTypes();
isVarArgs = constructor.isVarArgs();
}
public Class<?>[] getParameterTypes() {
return parameterTypes;
}
public boolean isVarArgs() {
return isVarArgs;
}
/**
* Default access superclass workaround.
*
* When a {@code public} class has a default access superclass with {@code 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) prevent access even when the modifier is {@code public}.
* Calling {@code setAccessible(true)} solves the problem but will only work from
* sufficiently privileged code. Better workarounds would be gratefully
* accepted.
* @param obj the AccessibleObject to set as accessible
* @return a boolean indicating whether the accessibility of the object was set to true.
*/
static <T extends AccessibleObject> T setAccessibleWorkaround(final T obj) {
if (obj == null || obj.isAccessible()) {
return obj;
}
final Member m = (Member) obj;
if (!obj.isAccessible() && isPublic(m) && isPackageAccess(m.getDeclaringClass().getModifiers())) {
try {
obj.setAccessible(true);
return obj;
} catch (final SecurityException ignored) {
// ignore in favor of subsequent IllegalAccessException
}
}
return obj;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -107,6 +107,11 @@ public abstract class TypeLiteral<T> implements Typed<T> {
return TypeUtils.equals(value, other.value);
}
@Override
public Type getType() {
return value;
}
@Override
public int hashCode() {
return 37 << 4 | value.hashCode();
@ -116,9 +121,4 @@ public abstract class TypeLiteral<T> implements Typed<T> {
public String toString() {
return toString;
}
@Override
public Type getType() {
return value;
}
}

View File

@ -71,6 +71,24 @@ public class CompositeFormat extends Format {
return formatter.format(obj, toAppendTo, pos);
}
/**
* Provides access to the parser Format implementation.
*
* @return formatter Format implementation
*/
public Format getFormatter() {
return this.formatter;
}
/**
* Provides access to the parser Format implementation.
*
* @return parser Format implementation
*/
public Format getParser() {
return this.parser;
}
/**
* Uses the parser Format instance.
*
@ -86,24 +104,6 @@ public class CompositeFormat extends Format {
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.
*

View File

@ -111,17 +111,6 @@ public class ExtendedMessageFormat extends MessageFormat {
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(final String pattern, final Map<String, ? extends FormatFactory> registry) {
this(pattern, Locale.getDefault(), registry);
}
/**
* Create a new ExtendedMessageFormat.
*
@ -138,11 +127,48 @@ public class ExtendedMessageFormat extends MessageFormat {
}
/**
* {@inheritDoc}
* 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.
*/
@Override
public String toPattern() {
return toPattern;
public ExtendedMessageFormat(final String pattern, final Map<String, ? extends FormatFactory> registry) {
this(pattern, Locale.getDefault(), registry);
}
/**
* Consume a quoted string, adding it to {@code appendTo} if
* specified.
*
* @param pattern pattern to parse
* @param pos current parse position
* @param appendTo optional StringBuilder to append
* @return {@code appendTo}
*/
private StringBuilder appendQuotedString(final String pattern, final ParsePosition pos,
final StringBuilder appendTo) {
assert pattern.toCharArray()[pos.getIndex()] == QUOTE :
"Quoted string must start with quote character";
// handle quote character at the beginning of the string
if (appendTo != null) {
appendTo.append(QUOTE);
}
next(pos);
final int start = pos.getIndex();
final char[] c = pattern.toCharArray();
for (int i = pos.getIndex(); i < pattern.length(); i++) {
if (c[pos.getIndex()] == QUOTE) {
next(pos);
return appendTo == null ? null : appendTo.append(c, start,
pos.getIndex() - start);
}
next(pos);
}
throw new IllegalArgumentException(
"Unterminated quoted string at position " + start);
}
/**
@ -218,49 +244,15 @@ public class ExtendedMessageFormat extends MessageFormat {
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param formatElementIndex format element index
* @param newFormat the new format
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
* Learn whether the specified Collection contains non-null elements.
* @param coll to check
* @return {@code true} if some Object was found, {@code false} otherwise.
*/
@Override
public void setFormat(final int formatElementIndex, final Format newFormat) {
throw new UnsupportedOperationException();
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param argumentIndex argument index
* @param newFormat the new format
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
*/
@Override
public void setFormatByArgumentIndex(final int argumentIndex, final Format newFormat) {
throw new UnsupportedOperationException();
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param newFormats new formats
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
*/
@Override
public void setFormats(final Format[] newFormats) {
throw new UnsupportedOperationException();
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param newFormats new formats
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
*/
@Override
public void setFormatsByArgumentIndex(final Format[] newFormats) {
throw new UnsupportedOperationException();
private boolean containsElements(final Collection<?> coll) {
if (coll == null || coll.isEmpty()) {
return false;
}
return coll.stream().anyMatch(Objects::nonNull);
}
/**
@ -290,17 +282,6 @@ public class ExtendedMessageFormat extends MessageFormat {
return !ObjectUtils.notEqual(registry, rhs.registry);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int result = super.hashCode();
result = HASH_SEED * result + Objects.hashCode(registry);
result = HASH_SEED * result + Objects.hashCode(toPattern);
return result;
}
/**
* Gets a custom format from a format description.
*
@ -325,79 +306,24 @@ public class ExtendedMessageFormat extends MessageFormat {
}
/**
* Read the argument index from the current format element
* Consume quoted string only
*
* @param pattern pattern to parse
* @param pos current parse position
* @return argument index
*/
private int readArgumentIndex(final String pattern, final ParsePosition pos) {
final int start = pos.getIndex();
seekNonWs(pattern, pos);
final StringBuilder result = new StringBuilder();
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 (final NumberFormatException ignored) {
// 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);
private void getQuotedString(final String pattern, final ParsePosition pos) {
appendQuotedString(pattern, pos, null);
}
/**
* Parse the format component of a format element.
*
* @param pattern string to parse
* @param pos current parse position
* @return Format description String
* {@inheritDoc}
*/
private String parseFormatDescription(final String pattern, final ParsePosition pos) {
final int start = pos.getIndex();
seekNonWs(pattern, pos);
final 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);
break;
default:
break;
}
}
throw new IllegalArgumentException(
"Unterminated format element at position " + start);
@Override
public int hashCode() {
int result = super.hashCode();
result = HASH_SEED * result + Objects.hashCode(registry);
result = HASH_SEED * result + Objects.hashCode(toPattern);
return result;
}
/**
@ -444,6 +370,93 @@ public class ExtendedMessageFormat extends MessageFormat {
return sb.toString();
}
/**
* Convenience method to advance parse position by 1
*
* @param pos ParsePosition
* @return {@code pos}
*/
private ParsePosition next(final ParsePosition pos) {
pos.setIndex(pos.getIndex() + 1);
return pos;
}
/**
* 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(final String pattern, final ParsePosition pos) {
final int start = pos.getIndex();
seekNonWs(pattern, pos);
final 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);
break;
default:
break;
}
}
throw new IllegalArgumentException(
"Unterminated format element at position " + start);
}
/**
* 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(final String pattern, final ParsePosition pos) {
final int start = pos.getIndex();
seekNonWs(pattern, pos);
final StringBuilder result = new StringBuilder();
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 (final NumberFormatException ignored) {
// 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);
}
/**
* Consume whitespace from the current parse position.
*
@ -460,69 +473,56 @@ public class ExtendedMessageFormat extends MessageFormat {
}
/**
* Convenience method to advance parse position by 1
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param pos ParsePosition
* @return {@code pos}
* @param formatElementIndex format element index
* @param newFormat the new format
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
*/
private ParsePosition next(final ParsePosition pos) {
pos.setIndex(pos.getIndex() + 1);
return pos;
@Override
public void setFormat(final int formatElementIndex, final Format newFormat) {
throw new UnsupportedOperationException();
}
/**
* Consume a quoted string, adding it to {@code appendTo} if
* specified.
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param pattern pattern to parse
* @param pos current parse position
* @param appendTo optional StringBuilder to append
* @return {@code appendTo}
* @param argumentIndex argument index
* @param newFormat the new format
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
*/
private StringBuilder appendQuotedString(final String pattern, final ParsePosition pos,
final StringBuilder appendTo) {
assert pattern.toCharArray()[pos.getIndex()] == QUOTE :
"Quoted string must start with quote character";
// handle quote character at the beginning of the string
if (appendTo != null) {
appendTo.append(QUOTE);
}
next(pos);
final int start = pos.getIndex();
final char[] c = pattern.toCharArray();
for (int i = pos.getIndex(); i < pattern.length(); i++) {
if (c[pos.getIndex()] == QUOTE) {
next(pos);
return appendTo == null ? null : appendTo.append(c, start,
pos.getIndex() - start);
}
next(pos);
}
throw new IllegalArgumentException(
"Unterminated quoted string at position " + start);
@Override
public void setFormatByArgumentIndex(final int argumentIndex, final Format newFormat) {
throw new UnsupportedOperationException();
}
/**
* Consume quoted string only
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param pattern pattern to parse
* @param pos current parse position
* @param newFormats new formats
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
*/
private void getQuotedString(final String pattern, final ParsePosition pos) {
appendQuotedString(pattern, pos, null);
@Override
public void setFormats(final Format[] newFormats) {
throw new UnsupportedOperationException();
}
/**
* Learn whether the specified Collection contains non-null elements.
* @param coll to check
* @return {@code true} if some Object was found, {@code false} otherwise.
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param newFormats new formats
* @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat
*/
private boolean containsElements(final Collection<?> coll) {
if (coll == null || coll.isEmpty()) {
return false;
}
return coll.stream().anyMatch(Objects::nonNull);
@Override
public void setFormatsByArgumentIndex(final Format[] newFormats) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public String toPattern() {
return toPattern;
}
}

View File

@ -44,28 +44,6 @@ public class FormattableUtils {
*/
private static final String SIMPLEST_FORMAT = "%s";
/**
* {@link FormattableUtils} instances should NOT be constructed in
* standard programming. Instead, the methods of the class should be invoked
* statically.
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public FormattableUtils() {
}
/**
* Gets the default formatted representation of the specified
* {@link Formattable}.
*
* @param formattable the instance to convert to a string, not null
* @return the resulting string, not null
*/
public static String toString(final Formattable formattable) {
return String.format(SIMPLEST_FORMAT, formattable);
}
/**
* Handles the common {@link Formattable} operations of truncate-pad-append,
* with no ellipsis on precision overflow, and padding width underflow with
@ -100,24 +78,6 @@ public class FormattableUtils {
return append(seq, formatter, flags, width, precision, padChar, null);
}
/**
* Handles the common {@link Formattable} operations of truncate-pad-append,
* padding width underflow with spaces.
*
* @param seq the string to handle, not null
* @param formatter the destination formatter, not null
* @param flags the flags for formatting, see {@link Formattable}
* @param width the width of the output, see {@link Formattable}
* @param precision the precision of the output, see {@link Formattable}
* @param ellipsis the ellipsis to use when precision dictates truncation, null or
* empty causes a hard truncation
* @return the {@code formatter} instance, not null
*/
public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
final int precision, final CharSequence ellipsis) {
return append(seq, formatter, flags, width, precision, ' ', ellipsis);
}
/**
* Handles the common {@link Formattable} operations of truncate-pad-append.
*
@ -148,4 +108,44 @@ public class FormattableUtils {
return formatter;
}
/**
* Handles the common {@link Formattable} operations of truncate-pad-append,
* padding width underflow with spaces.
*
* @param seq the string to handle, not null
* @param formatter the destination formatter, not null
* @param flags the flags for formatting, see {@link Formattable}
* @param width the width of the output, see {@link Formattable}
* @param precision the precision of the output, see {@link Formattable}
* @param ellipsis the ellipsis to use when precision dictates truncation, null or
* empty causes a hard truncation
* @return the {@code formatter} instance, not null
*/
public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
final int precision, final CharSequence ellipsis) {
return append(seq, formatter, flags, width, precision, ' ', ellipsis);
}
/**
* Gets the default formatted representation of the specified
* {@link Formattable}.
*
* @param formattable the instance to convert to a string, not null
* @return the resulting string, not null
*/
public static String toString(final Formattable formattable) {
return String.format(SIMPLEST_FORMAT, formattable);
}
/**
* {@link FormattableUtils} instances should NOT be constructed in
* standard programming. Instead, the methods of the class should be invoked
* statically.
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public FormattableUtils() {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -46,91 +46,6 @@ import org.apache.commons.lang3.SystemProperties;
@Deprecated
public abstract class StrLookup<V> {
/**
* Lookup that always returns null.
*/
private static final StrLookup<String> NONE_LOOKUP = new MapStrLookup<>(null);
/**
* Lookup based on system properties.
*/
private static final StrLookup<String> SYSTEM_PROPERTIES_LOOKUP = new SystemPropertiesStrLookup();
/**
* 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 new lookup which uses a copy of the current
* {@link System#getProperties() System properties}.
* <p>
* If a security manager blocked access to system properties, then null will
* be returned from every lookup.
* </p>
* <p>
* If a null key is used, this lookup will throw a NullPointerException.
* </p>
*
* @return a lookup using system properties, not null
*/
public static StrLookup<String> systemPropertiesLookup() {
return SYSTEM_PROPERTIES_LOOKUP;
}
/**
* Returns a lookup which looks up values using a map.
* <p>
* If the map is null, then null will be returned from every lookup.
* The map result object is converted to a string using toString().
* </p>
*
* @param <V> the type of the values supported by the lookup
* @param map the map of keys to values, may be null
* @return a lookup using the map, not null
*/
public static <V> StrLookup<V> mapLookup(final Map<String, V> map) {
return new MapStrLookup<>(map);
}
/**
* Constructor.
*/
protected StrLookup() {
}
/**
* Looks up a String key to a String value.
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <p>
* The {@link #lookup(String)} method always returns a String, regardless of
* the underlying data, by converting it as necessary. For example:
* </p>
* <pre>
* Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();
* map.put("number", Integer.valueOf(2));
* assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
* </pre>
* @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.
*
@ -181,4 +96,89 @@ public abstract class StrLookup<V> {
return SystemProperties.getProperty(key);
}
}
/**
* Lookup that always returns null.
*/
private static final StrLookup<String> NONE_LOOKUP = new MapStrLookup<>(null);
/**
* Lookup based on system properties.
*/
private static final StrLookup<String> SYSTEM_PROPERTIES_LOOKUP = new SystemPropertiesStrLookup();
/**
* Returns a lookup which looks up values using a map.
* <p>
* If the map is null, then null will be returned from every lookup.
* The map result object is converted to a string using toString().
* </p>
*
* @param <V> the type of the values supported by the lookup
* @param map the map of keys to values, may be null
* @return a lookup using the map, not null
*/
public static <V> StrLookup<V> mapLookup(final Map<String, V> map) {
return new MapStrLookup<>(map);
}
/**
* 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 new lookup which uses a copy of the current
* {@link System#getProperties() System properties}.
* <p>
* If a security manager blocked access to system properties, then null will
* be returned from every lookup.
* </p>
* <p>
* If a null key is used, this lookup will throw a NullPointerException.
* </p>
*
* @return a lookup using system properties, not null
*/
public static StrLookup<String> systemPropertiesLookup() {
return SYSTEM_PROPERTIES_LOOKUP;
}
/**
* Constructor.
*/
protected StrLookup() {
}
/**
* Looks up a String key to a String value.
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <p>
* The {@link #lookup(String)} method always returns a String, regardless of
* the underlying data, by converting it as necessary. For example:
* </p>
* <pre>
* Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();
* map.put("number", Integer.valueOf(2));
* assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
* </pre>
* @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);
}

View File

@ -39,244 +39,35 @@ import org.apache.commons.lang3.StringUtils;
public abstract class StrMatcher {
/**
* Matches the comma character.
* Class used to define a character for matching purposes.
*/
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();
static final class CharMatcher extends StrMatcher {
/** The character to match. */
private final char ch;
/**
* 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(final 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(final char... chars) {
if (ArrayUtils.isEmpty(chars)) {
return NONE_MATCHER;
/**
* Constructor that creates a matcher that matches a single character.
*
* @param ch the character to match
*/
CharMatcher(final char ch) {
this.ch = ch;
}
if (chars.length == 1) {
return new CharMatcher(chars[0]);
/**
* 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
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return ch == buffer[pos] ? 1 : 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(final String chars) {
if (StringUtils.isEmpty(chars)) {
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(final String str) {
if (StringUtils.isEmpty(str)) {
return NONE_MATCHER;
}
return new StringMatcher(str);
}
/**
* Constructor.
*/
protected StrMatcher() {
}
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match.
* The parameter {@code pos} represents the current position to be
* checked in the string {@code buffer} (a character array which must
* not be changed).
* The API guarantees that {@code pos} is a valid index for {@code buffer}.
* </p>
* <p>
* The character array may be larger than the active area to be matched.
* Only values in the buffer between the specified indices may be accessed.
* </p>
* <p>
* The matching code may check one character or many.
* It may check characters preceding {@code pos} as well as those
* after, so long as no checks exceed the bounds specified.
* </p>
* <p>
* 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.
* </p>
*
* @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.
* <p>
* This method is called to check for a match.
* The parameter {@code pos} represents the current position to be
* checked in the string {@code buffer} (a character array which must
* not be changed).
* The API guarantees that {@code pos} is a valid index for {@code buffer}.
* </p>
* <p>
* The matching code may check one character or many.
* It may check characters preceding {@code pos} as well as those after.
* </p>
* <p>
* 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.
* </p>
*
* @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(final char[] buffer, final int pos) {
return isMatch(buffer, pos, 0, buffer.length);
}
/**
* Class used to define a set of characters for matching purposes.
*/
@ -307,25 +98,19 @@ public abstract class StrMatcher {
return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0;
}
}
/**
* Class used to define a character for matching purposes.
* Class used to match no characters.
*/
static final class CharMatcher extends StrMatcher {
/** The character to match. */
private final char ch;
static final class NoMatcher extends StrMatcher {
/**
* Constructor that creates a matcher that matches a single character.
*
* @param ch the character to match
* Constructs a new instance of {@link NoMatcher}.
*/
CharMatcher(final char ch) {
this.ch = ch;
NoMatcher() {
}
/**
* Returns whether or not the given character matches.
* Always returns {@code false}.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
@ -335,10 +120,9 @@ public abstract class StrMatcher {
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return ch == buffer[pos] ? 1 : 0;
return 0;
}
}
/**
* Class used to define a set of characters for matching purposes.
*/
@ -384,33 +168,6 @@ public abstract class StrMatcher {
}
}
/**
* Class used to match no characters.
*/
static final class NoMatcher extends StrMatcher {
/**
* Constructs a new instance of {@link NoMatcher}.
*/
NoMatcher() {
}
/**
* Always returns {@code 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
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return 0;
}
}
/**
* Class used to match whitespace as per trim().
*/
@ -436,5 +193,248 @@ public abstract class StrMatcher {
return buffer[pos] <= 32 ? 1 : 0;
}
}
/**
* 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();
/**
* 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(final 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(final char... chars) {
if (ArrayUtils.isEmpty(chars)) {
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(final String chars) {
if (StringUtils.isEmpty(chars)) {
return NONE_MATCHER;
}
if (chars.length() == 1) {
return new CharMatcher(chars.charAt(0));
}
return new CharSetMatcher(chars.toCharArray());
}
/**
* 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 double quote character.
*
* @return a matcher for a double quote
*/
public static StrMatcher doubleQuoteMatcher() {
return DOUBLE_QUOTE_MATCHER;
}
/**
* Matches no characters.
*
* @return a matcher that matches nothing
*/
public static StrMatcher noneMatcher() {
return NONE_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;
}
/**
* 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 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;
}
/**
* 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(final String str) {
if (StringUtils.isEmpty(str)) {
return NONE_MATCHER;
}
return new StringMatcher(str);
}
/**
* Returns a matcher which matches the tab character.
*
* @return a matcher for a tab
*/
public static StrMatcher tabMatcher() {
return TAB_MATCHER;
}
/**
* Matches the String trim() whitespace characters.
*
* @return the trim matcher
*/
public static StrMatcher trimMatcher() {
return TRIM_MATCHER;
}
/**
* Constructor.
*/
protected StrMatcher() {
}
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match.
* The parameter {@code pos} represents the current position to be
* checked in the string {@code buffer} (a character array which must
* not be changed).
* The API guarantees that {@code pos} is a valid index for {@code buffer}.
* </p>
* <p>
* The matching code may check one character or many.
* It may check characters preceding {@code pos} as well as those after.
* </p>
* <p>
* 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.
* </p>
*
* @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(final char[] buffer, final int pos) {
return isMatch(buffer, pos, 0, buffer.length);
}
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match.
* The parameter {@code pos} represents the current position to be
* checked in the string {@code buffer} (a character array which must
* not be changed).
* The API guarantees that {@code pos} is a valid index for {@code buffer}.
* </p>
* <p>
* The character array may be larger than the active area to be matched.
* Only values in the buffer between the specified indices may be accessed.
* </p>
* <p>
* The matching code may check one character or many.
* It may check characters preceding {@code pos} as well as those
* after, so long as no checks exceed the bounds specified.
* </p>
* <p>
* 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.
* </p>
*
* @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);
}

File diff suppressed because it is too large Load Diff

View File

@ -37,15 +37,375 @@ import org.apache.commons.lang3.StringUtils;
@Deprecated
public class WordUtils {
// Capitalizing
/**
* {@link WordUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used as
* {@code WordUtils.wrap("foo bar", 20);}.
* Capitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed. To convert the
* rest of each word to lowercase at the same time,
* use {@link #capitalizeFully(String)}.
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalize(null) = null
* WordUtils.capitalize("") = ""
* WordUtils.capitalize("i am FINE") = "I Am FINE"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, {@code null} if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
*/
public WordUtils() {
public static String capitalize(final String str) {
return capitalize(str, null);
}
/**
* Capitalizes all the delimiter separated words in a String.
* Only the first character of each word is changed. To convert the
* rest of each word to lowercase at the same time,
* use {@link #capitalizeFully(String, char[])}.
*
* <p>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.</p>
*
* <p>A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* 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"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means whitespace
* @return capitalized String, {@code null} if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
* @since 2.1
*/
public static String capitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean capitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
capitalizeNext = true;
} else if (capitalizeNext) {
buffer[i] = Character.toTitleCase(ch);
capitalizeNext = false;
}
}
return new String(buffer);
}
/**
* 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.
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalizeFully(null) = null
* WordUtils.capitalizeFully("") = ""
* WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, {@code null} if null String input
*/
public static String capitalizeFully(final String str) {
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.
*
* <p>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.</p>
*
* <p>A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalizeFully(null, *) = null
* WordUtils.capitalizeFully("", *) = ""
* WordUtils.capitalizeFully(*, null) = *
* WordUtils.capitalizeFully(*, new char[0]) = *
* WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means whitespace
* @return capitalized String, {@code null} if null String input
* @since 2.1
*/
public static String capitalizeFully(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
return capitalize(str.toLowerCase(), delimiters);
}
/**
* Checks if the String contains all words in the given array.
*
* <p>
* A {@code null} String will return {@code false}. A {@code null}, zero
* length search array or if one element of array is null will return {@code false}.
* </p>
*
* <pre>
* WordUtils.containsAllWords(null, *) = false
* WordUtils.containsAllWords("", *) = false
* WordUtils.containsAllWords(*, null) = false
* WordUtils.containsAllWords(*, []) = false
* WordUtils.containsAllWords("abcd", "ab", "cd") = false
* WordUtils.containsAllWords("abc def", "def", "abc") = true
* </pre>
*
* @param word The CharSequence to check, may be null
* @param words The array of String words to search for, may be null
* @return {@code true} if all search words are found, {@code false} otherwise
* @since 3.5
*/
public static boolean containsAllWords(final CharSequence word, final CharSequence... words) {
if (StringUtils.isEmpty(word) || ArrayUtils.isEmpty(words)) {
return false;
}
for (final CharSequence w : words) {
if (StringUtils.isBlank(w)) {
return false;
}
final Pattern p = Pattern.compile(".*\\b" + w + "\\b.*");
if (!p.matcher(word).matches()) {
return false;
}
}
return true;
}
/**
* Extracts the initial characters from each word in the String.
*
* <p>All first characters after whitespace are returned as a new string.
* Their case is not changed.</p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* WordUtils.initials(null) = null
* WordUtils.initials("") = ""
* WordUtils.initials("Ben John Lee") = "BJL"
* WordUtils.initials("Ben J.Lee") = "BJ"
* </pre>
*
* @param str the String to get initials from, may be null
* @return String of initial letters, {@code null} if null String input
* @see #initials(String,char[])
* @since 2.2
*/
public static String initials(final String str) {
return initials(str, null);
}
/**
* Extracts the initial characters from each word in the String.
*
* <p>All first characters after the defined delimiters are returned as a new string.
* Their case is not changed.</p>
*
* <p>If the delimiters array is null, then Whitespace is used.
* Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.
* An empty delimiter array returns an empty String.</p>
*
* <pre>
* 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]) = ""
* </pre>
*
* @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 characters, {@code null} if null String input
* @see #initials(String)
* @since 2.2
*/
public static String initials(final String str, final char... delimiters) {
if (StringUtils.isEmpty(str)) {
return str;
}
if (delimiters != null && delimiters.length == 0) {
return StringUtils.EMPTY;
}
final int strLen = str.length();
final char[] buf = new char[strLen / 2 + 1];
int count = 0;
boolean lastWasGap = true;
for (int i = 0; i < strLen; i++) {
final char ch = str.charAt(i);
if (isDelimiter(ch, delimiters)) {
lastWasGap = true;
} else if (lastWasGap) {
buf[count++] = ch;
lastWasGap = false;
} else {
continue; // ignore ch
}
}
return new String(buf, 0, count);
}
/**
* Tests if the character is a delimiter.
*
* @param ch the character to check
* @param delimiters the delimiters
* @return true if it is a delimiter
*/
private static boolean isDelimiter(final char ch, final char[] delimiters) {
return delimiters == null ? Character.isWhitespace(ch) : ArrayUtils.contains(delimiters, ch);
}
/**
* Swaps the case of a String using a word based algorithm.
*
* <ul>
* <li>Upper case character converts to Lower case</li>
* <li>Title case character converts to Lower case</li>
* <li>Lower case character after Whitespace or at start converts to Title case</li>
* <li>Other Lower case character converts to Upper case</li>
* </ul>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* StringUtils.swapCase(null) = null
* StringUtils.swapCase("") = ""
* StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
* </pre>
*
* @param str the String to swap case, may be null
* @return the changed String, {@code null} if null String input
*/
public static String swapCase(final String str) {
if (StringUtils.isEmpty(str)) {
return str;
}
final char[] buffer = str.toCharArray();
boolean whitespace = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (Character.isUpperCase(ch) || Character.isTitleCase(ch)) {
buffer[i] = Character.toLowerCase(ch);
whitespace = false;
} else if (Character.isLowerCase(ch)) {
if (whitespace) {
buffer[i] = Character.toTitleCase(ch);
whitespace = false;
} else {
buffer[i] = Character.toUpperCase(ch);
}
} else {
whitespace = Character.isWhitespace(ch);
}
}
return new String(buffer);
}
/**
* Uncapitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed.
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* WordUtils.uncapitalize(null) = null
* WordUtils.uncapitalize("") = ""
* WordUtils.uncapitalize("I Am FINE") = "i am fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @return uncapitalized String, {@code null} if null String input
* @see #capitalize(String)
*/
public static String uncapitalize(final String str) {
return uncapitalize(str, null);
}
/**
* Uncapitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed.
*
* <p>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.</p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* WordUtils.uncapitalize(null, *) = null
* WordUtils.uncapitalize("", *) = ""
* WordUtils.uncapitalize(*, null) = *
* WordUtils.uncapitalize(*, new char[0]) = *
* WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @param delimiters set of characters to determine uncapitalization, null means whitespace
* @return uncapitalized String, {@code null} if null String input
* @see #capitalize(String)
* @since 2.1
*/
public static String uncapitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean uncapitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
uncapitalizeNext = true;
} else if (uncapitalizeNext) {
buffer[i] = Character.toLowerCase(ch);
uncapitalizeNext = false;
}
}
return new String(buffer);
}
/**
@ -343,375 +703,15 @@ public class WordUtils {
return wrappedLine.toString();
}
// Capitalizing
/**
* Capitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed. To convert the
* rest of each word to lowercase at the same time,
* use {@link #capitalizeFully(String)}.
* {@link WordUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used as
* {@code WordUtils.wrap("foo bar", 20);}.
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalize(null) = null
* WordUtils.capitalize("") = ""
* WordUtils.capitalize("i am FINE") = "I Am FINE"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, {@code null} if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public static String capitalize(final String str) {
return capitalize(str, null);
}
/**
* Capitalizes all the delimiter separated words in a String.
* Only the first character of each word is changed. To convert the
* rest of each word to lowercase at the same time,
* use {@link #capitalizeFully(String, char[])}.
*
* <p>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.</p>
*
* <p>A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* 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"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means whitespace
* @return capitalized String, {@code null} if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
* @since 2.1
*/
public static String capitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean capitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
capitalizeNext = true;
} else if (capitalizeNext) {
buffer[i] = Character.toTitleCase(ch);
capitalizeNext = false;
}
}
return new String(buffer);
}
/**
* 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.
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalizeFully(null) = null
* WordUtils.capitalizeFully("") = ""
* WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, {@code null} if null String input
*/
public static String capitalizeFully(final String str) {
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.
*
* <p>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.</p>
*
* <p>A {@code null} input String returns {@code null}.
* Capitalization uses the Unicode title case, normally equivalent to
* upper case.</p>
*
* <pre>
* WordUtils.capitalizeFully(null, *) = null
* WordUtils.capitalizeFully("", *) = ""
* WordUtils.capitalizeFully(*, null) = *
* WordUtils.capitalizeFully(*, new char[0]) = *
* WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means whitespace
* @return capitalized String, {@code null} if null String input
* @since 2.1
*/
public static String capitalizeFully(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
return capitalize(str.toLowerCase(), delimiters);
}
/**
* Uncapitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed.
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* WordUtils.uncapitalize(null) = null
* WordUtils.uncapitalize("") = ""
* WordUtils.uncapitalize("I Am FINE") = "i am fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @return uncapitalized String, {@code null} if null String input
* @see #capitalize(String)
*/
public static String uncapitalize(final String str) {
return uncapitalize(str, null);
}
/**
* Uncapitalizes all the whitespace separated words in a String.
* Only the first character of each word is changed.
*
* <p>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.</p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* WordUtils.uncapitalize(null, *) = null
* WordUtils.uncapitalize("", *) = ""
* WordUtils.uncapitalize(*, null) = *
* WordUtils.uncapitalize(*, new char[0]) = *
* WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @param delimiters set of characters to determine uncapitalization, null means whitespace
* @return uncapitalized String, {@code null} if null String input
* @see #capitalize(String)
* @since 2.1
*/
public static String uncapitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean uncapitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
uncapitalizeNext = true;
} else if (uncapitalizeNext) {
buffer[i] = Character.toLowerCase(ch);
uncapitalizeNext = false;
}
}
return new String(buffer);
}
/**
* Swaps the case of a String using a word based algorithm.
*
* <ul>
* <li>Upper case character converts to Lower case</li>
* <li>Title case character converts to Lower case</li>
* <li>Lower case character after Whitespace or at start converts to Title case</li>
* <li>Other Lower case character converts to Upper case</li>
* </ul>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* StringUtils.swapCase(null) = null
* StringUtils.swapCase("") = ""
* StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
* </pre>
*
* @param str the String to swap case, may be null
* @return the changed String, {@code null} if null String input
*/
public static String swapCase(final String str) {
if (StringUtils.isEmpty(str)) {
return str;
}
final char[] buffer = str.toCharArray();
boolean whitespace = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (Character.isUpperCase(ch) || Character.isTitleCase(ch)) {
buffer[i] = Character.toLowerCase(ch);
whitespace = false;
} else if (Character.isLowerCase(ch)) {
if (whitespace) {
buffer[i] = Character.toTitleCase(ch);
whitespace = false;
} else {
buffer[i] = Character.toUpperCase(ch);
}
} else {
whitespace = Character.isWhitespace(ch);
}
}
return new String(buffer);
}
/**
* Extracts the initial characters from each word in the String.
*
* <p>All first characters after whitespace are returned as a new string.
* Their case is not changed.</p>
*
* <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.</p>
*
* <pre>
* WordUtils.initials(null) = null
* WordUtils.initials("") = ""
* WordUtils.initials("Ben John Lee") = "BJL"
* WordUtils.initials("Ben J.Lee") = "BJ"
* </pre>
*
* @param str the String to get initials from, may be null
* @return String of initial letters, {@code null} if null String input
* @see #initials(String,char[])
* @since 2.2
*/
public static String initials(final String str) {
return initials(str, null);
}
/**
* Extracts the initial characters from each word in the String.
*
* <p>All first characters after the defined delimiters are returned as a new string.
* Their case is not changed.</p>
*
* <p>If the delimiters array is null, then Whitespace is used.
* Whitespace is defined by {@link Character#isWhitespace(char)}.
* A {@code null} input String returns {@code null}.
* An empty delimiter array returns an empty String.</p>
*
* <pre>
* 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]) = ""
* </pre>
*
* @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 characters, {@code null} if null String input
* @see #initials(String)
* @since 2.2
*/
public static String initials(final String str, final char... delimiters) {
if (StringUtils.isEmpty(str)) {
return str;
}
if (delimiters != null && delimiters.length == 0) {
return StringUtils.EMPTY;
}
final int strLen = str.length();
final char[] buf = new char[strLen / 2 + 1];
int count = 0;
boolean lastWasGap = true;
for (int i = 0; i < strLen; i++) {
final char ch = str.charAt(i);
if (isDelimiter(ch, delimiters)) {
lastWasGap = true;
} else if (lastWasGap) {
buf[count++] = ch;
lastWasGap = false;
} else {
continue; // ignore ch
}
}
return new String(buf, 0, count);
}
/**
* Checks if the String contains all words in the given array.
*
* <p>
* A {@code null} String will return {@code false}. A {@code null}, zero
* length search array or if one element of array is null will return {@code false}.
* </p>
*
* <pre>
* WordUtils.containsAllWords(null, *) = false
* WordUtils.containsAllWords("", *) = false
* WordUtils.containsAllWords(*, null) = false
* WordUtils.containsAllWords(*, []) = false
* WordUtils.containsAllWords("abcd", "ab", "cd") = false
* WordUtils.containsAllWords("abc def", "def", "abc") = true
* </pre>
*
* @param word The CharSequence to check, may be null
* @param words The array of String words to search for, may be null
* @return {@code true} if all search words are found, {@code false} otherwise
* @since 3.5
*/
public static boolean containsAllWords(final CharSequence word, final CharSequence... words) {
if (StringUtils.isEmpty(word) || ArrayUtils.isEmpty(words)) {
return false;
}
for (final CharSequence w : words) {
if (StringUtils.isBlank(w)) {
return false;
}
final Pattern p = Pattern.compile(".*\\b" + w + "\\b.*");
if (!p.matcher(word).matches()) {
return false;
}
}
return true;
}
/**
* Tests if the character is a delimiter.
*
* @param ch the character to check
* @param delimiters the delimiters
* @return true if it is a delimiter
*/
private static boolean isDelimiter(final char ch, final char[] delimiters) {
return delimiters == null ? Character.isWhitespace(ch) : ArrayUtils.contains(delimiters, ch);
public WordUtils() {
}
}

View File

@ -39,18 +39,15 @@ public abstract class CharSequenceTranslator {
static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* Translate a set of code points, represented by an int index into a CharSequence,
* into another set of code points. The number of code points consumed must be returned,
* and the only IOExceptions thrown must be from interacting with the Writer so that
* the top level API may reliably ignore StringWriter IOExceptions.
* Returns an upper case hexadecimal {@link String} for the given
* character.
*
* @param input CharSequence that is being translated
* @param index int representing the current point of translation
* @param out Writer to translate the text to
* @return int count of code points consumed
* @throws IOException if and only if the Writer produces an IOException
* @param codePoint The code point to convert.
* @return An upper case hexadecimal {@link String}
*/
public abstract int translate(CharSequence input, int index, Writer out) throws IOException;
public static String hex(final int codePoint) {
return Integer.toHexString(codePoint).toUpperCase(Locale.ENGLISH);
}
/**
* Helper for non-Writer usage.
@ -71,6 +68,20 @@ public abstract class CharSequenceTranslator {
}
}
/**
* Translate a set of code points, represented by an int index into a CharSequence,
* into another set of code points. The number of code points consumed must be returned,
* and the only IOExceptions thrown must be from interacting with the Writer so that
* the top level API may reliably ignore StringWriter IOExceptions.
*
* @param input CharSequence that is being translated
* @param index int representing the current point of translation
* @param out Writer to translate the text to
* @return int count of code points consumed
* @throws IOException if and only if the Writer produces an IOException
*/
public abstract int translate(CharSequence input, int index, Writer out) throws IOException;
/**
* Translate an input onto a Writer. This is intentionally final as its algorithm is
* tightly coupled with the abstract method of this class.
@ -125,15 +136,4 @@ public abstract class CharSequenceTranslator {
return new AggregateTranslator(newArray);
}
/**
* Returns an upper case hexadecimal {@link String} for the given
* character.
*
* @param codePoint The code point to convert.
* @return An upper case hexadecimal {@link String}
*/
public static String hex(final int codePoint) {
return Integer.toHexString(codePoint).toUpperCase(Locale.ENGLISH);
}
}

View File

@ -29,15 +29,6 @@ package org.apache.commons.lang3.text.translate;
@Deprecated
public class EntityArrays {
/**
* Mapping to escape <a href="https://secure.wikimedia.org/wikipedia/en/wiki/ISO/IEC_8859-1">ISO-8859-1</a>
* characters to their named HTML 3.x equivalents.
* @return the mapping table
*/
public static String[][] ISO8859_1_ESCAPE() {
return ISO8859_1_ESCAPE.clone();
}
private static final String[][] ISO8859_1_ESCAPE = {
{"\u00A0", "&nbsp;"}, // non-breaking space
{"\u00A1", "&iexcl;"}, // inverted exclamation mark
@ -137,26 +128,8 @@ public class EntityArrays {
{"\u00FF", "&yuml;"}, // ÿ - lowercase y, umlaut
};
/**
* Reverse of {@link #ISO8859_1_ESCAPE()} for unescaping purposes.
* @return the mapping table
*/
public static String[][] ISO8859_1_UNESCAPE() {
return ISO8859_1_UNESCAPE.clone();
}
private static final String[][] ISO8859_1_UNESCAPE = invert(ISO8859_1_ESCAPE);
/**
* Mapping to escape additional <a href="https://www.w3.org/TR/REC-html40/sgml/entities.html">character entity
* references</a>. Note that this must be used with {@link #ISO8859_1_ESCAPE()} to get the full list of
* HTML 4.0 character entities.
* @return the mapping table
*/
public static String[][] HTML40_EXTENDED_ESCAPE() {
return HTML40_EXTENDED_ESCAPE.clone();
}
private static final String[][] HTML40_EXTENDED_ESCAPE = {
// <!-- Latin Extended-B -->
{"\u0192", "&fnof;"}, // latin small f with hook = function= florin, U+0192 ISOtech -->
@ -354,15 +327,48 @@ public class EntityArrays {
{"\u20AC", "&euro;"}, // -- euro sign, U+20AC NEW -->
};
private static final String[][] HTML40_EXTENDED_UNESCAPE = invert(HTML40_EXTENDED_ESCAPE);
private static final String[][] BASIC_ESCAPE = {
{"\"", "&quot;"}, // " - double-quote
{"&", "&amp;"}, // & - ampersand
{"<", "&lt;"}, // < - less-than
{">", "&gt;"}, // > - greater-than
};
private static final String[][] BASIC_UNESCAPE = invert(BASIC_ESCAPE);
private static final String[][] APOS_ESCAPE = {
{"'", "&apos;"}, // XML apostrophe
};
private static final String[][] APOS_UNESCAPE = invert(APOS_ESCAPE);
private static final String[][] JAVA_CTRL_CHARS_ESCAPE = {
{"\b", "\\b"},
{"\n", "\\n"},
{"\t", "\\t"},
{"\f", "\\f"},
{"\r", "\\r"}
};
private static final String[][] JAVA_CTRL_CHARS_UNESCAPE = invert(JAVA_CTRL_CHARS_ESCAPE);
/**
* Reverse of {@link #HTML40_EXTENDED_ESCAPE()} for unescaping purposes.
* Mapping to escape the apostrophe character to its XML character entity.
* @return the mapping table
*/
public static String[][] HTML40_EXTENDED_UNESCAPE() {
return HTML40_EXTENDED_UNESCAPE.clone();
public static String[][] APOS_ESCAPE() {
return APOS_ESCAPE.clone();
}
private static final String[][] HTML40_EXTENDED_UNESCAPE = invert(HTML40_EXTENDED_ESCAPE);
/**
* Reverse of {@link #APOS_ESCAPE()} for unescaping purposes.
* @return the mapping table
*/
public static String[][] APOS_UNESCAPE() {
return APOS_UNESCAPE.clone();
}
/**
* Mapping to escape the basic XML and HTML character entities.
@ -374,13 +380,6 @@ public class EntityArrays {
return BASIC_ESCAPE.clone();
}
private static final String[][] BASIC_ESCAPE = {
{"\"", "&quot;"}, // " - double-quote
{"&", "&amp;"}, // & - ampersand
{"<", "&lt;"}, // < - less-than
{">", "&gt;"}, // > - greater-than
};
/**
* Reverse of {@link #BASIC_ESCAPE()} for unescaping purposes.
* @return the mapping table
@ -389,58 +388,24 @@ public class EntityArrays {
return BASIC_UNESCAPE.clone();
}
private static final String[][] BASIC_UNESCAPE = invert(BASIC_ESCAPE);
/**
* Mapping to escape the apostrophe character to its XML character entity.
* Mapping to escape additional <a href="https://www.w3.org/TR/REC-html40/sgml/entities.html">character entity
* references</a>. Note that this must be used with {@link #ISO8859_1_ESCAPE()} to get the full list of
* HTML 4.0 character entities.
* @return the mapping table
*/
public static String[][] APOS_ESCAPE() {
return APOS_ESCAPE.clone();
public static String[][] HTML40_EXTENDED_ESCAPE() {
return HTML40_EXTENDED_ESCAPE.clone();
}
private static final String[][] APOS_ESCAPE = {
{"'", "&apos;"}, // XML apostrophe
};
/**
* Reverse of {@link #APOS_ESCAPE()} for unescaping purposes.
* Reverse of {@link #HTML40_EXTENDED_ESCAPE()} for unescaping purposes.
* @return the mapping table
*/
public static String[][] APOS_UNESCAPE() {
return APOS_UNESCAPE.clone();
public static String[][] HTML40_EXTENDED_UNESCAPE() {
return HTML40_EXTENDED_UNESCAPE.clone();
}
private static final String[][] APOS_UNESCAPE = invert(APOS_ESCAPE);
/**
* Mapping to escape the Java control characters.
*
* Namely: {@code \b \n \t \f \r}
* @return the mapping table
*/
public static String[][] JAVA_CTRL_CHARS_ESCAPE() {
return JAVA_CTRL_CHARS_ESCAPE.clone();
}
private static final String[][] JAVA_CTRL_CHARS_ESCAPE = {
{"\b", "\\b"},
{"\n", "\\n"},
{"\t", "\\t"},
{"\f", "\\f"},
{"\r", "\\r"}
};
/**
* Reverse of {@link #JAVA_CTRL_CHARS_ESCAPE()} for unescaping purposes.
* @return the mapping table
*/
public static String[][] JAVA_CTRL_CHARS_UNESCAPE() {
return JAVA_CTRL_CHARS_UNESCAPE.clone();
}
private static final String[][] JAVA_CTRL_CHARS_UNESCAPE = invert(JAVA_CTRL_CHARS_ESCAPE);
/**
* Used to invert an escape array into an unescape array
* @param array String[][] to be inverted
@ -455,4 +420,39 @@ public class EntityArrays {
return newarray;
}
/**
* Mapping to escape <a href="https://secure.wikimedia.org/wikipedia/en/wiki/ISO/IEC_8859-1">ISO-8859-1</a>
* characters to their named HTML 3.x equivalents.
* @return the mapping table
*/
public static String[][] ISO8859_1_ESCAPE() {
return ISO8859_1_ESCAPE.clone();
}
/**
* Reverse of {@link #ISO8859_1_ESCAPE()} for unescaping purposes.
* @return the mapping table
*/
public static String[][] ISO8859_1_UNESCAPE() {
return ISO8859_1_UNESCAPE.clone();
}
/**
* Mapping to escape the Java control characters.
*
* Namely: {@code \b \n \t \f \r}
* @return the mapping table
*/
public static String[][] JAVA_CTRL_CHARS_ESCAPE() {
return JAVA_CTRL_CHARS_ESCAPE.clone();
}
/**
* Reverse of {@link #JAVA_CTRL_CHARS_ESCAPE()} for unescaping purposes.
* @return the mapping table
*/
public static String[][] JAVA_CTRL_CHARS_UNESCAPE() {
return JAVA_CTRL_CHARS_UNESCAPE.clone();
}
}

View File

@ -30,43 +30,6 @@ import java.io.Writer;
@Deprecated
public class NumericEntityEscaper extends CodePointTranslator {
private final int below;
private final int above;
private final boolean between;
/**
* Constructs a {@link NumericEntityEscaper} for the specified range. This is
* the underlying method for the other constructors/builders. The {@code below}
* and {@code above} boundaries are inclusive when {@code between} is
* {@code true} and exclusive when it is {@code false}.
*
* @param below int value representing the lowest code point boundary
* @param above int value representing the highest code point boundary
* @param between whether to escape between the boundaries or outside them
*/
private NumericEntityEscaper(final int below, final int above, final boolean between) {
this.below = below;
this.above = above;
this.between = between;
}
/**
* Constructs a {@link NumericEntityEscaper} for all characters.
*/
public NumericEntityEscaper() {
this(0, Integer.MAX_VALUE, true);
}
/**
* Constructs a {@link NumericEntityEscaper} below the specified value (exclusive).
*
* @param codePoint below which to escape
* @return the newly created {@link NumericEntityEscaper} instance
*/
public static NumericEntityEscaper below(final int codePoint) {
return outsideOf(codePoint, Integer.MAX_VALUE);
}
/**
* Constructs a {@link NumericEntityEscaper} above the specified value (exclusive).
*
@ -76,7 +39,15 @@ public class NumericEntityEscaper extends CodePointTranslator {
public static NumericEntityEscaper above(final int codePoint) {
return outsideOf(0, codePoint);
}
/**
* Constructs a {@link NumericEntityEscaper} below the specified value (exclusive).
*
* @param codePoint below which to escape
* @return the newly created {@link NumericEntityEscaper} instance
*/
public static NumericEntityEscaper below(final int codePoint) {
return outsideOf(codePoint, Integer.MAX_VALUE);
}
/**
* Constructs a {@link NumericEntityEscaper} between the specified values (inclusive).
*
@ -99,6 +70,35 @@ public class NumericEntityEscaper extends CodePointTranslator {
return new NumericEntityEscaper(codePointLow, codePointHigh, false);
}
private final int below;
private final int above;
private final boolean between;
/**
* Constructs a {@link NumericEntityEscaper} for all characters.
*/
public NumericEntityEscaper() {
this(0, Integer.MAX_VALUE, true);
}
/**
* Constructs a {@link NumericEntityEscaper} for the specified range. This is
* the underlying method for the other constructors/builders. The {@code below}
* and {@code above} boundaries are inclusive when {@code between} is
* {@code true} and exclusive when it is {@code false}.
*
* @param below int value representing the lowest code point boundary
* @param above int value representing the highest code point boundary
* @param between whether to escape between the boundaries or outside them
*/
private NumericEntityEscaper(final int below, final int above, final boolean between) {
this.below = below;
this.above = above;
this.between = between;
}
/**
* {@inheritDoc}
*/

View File

@ -35,6 +35,24 @@ import java.io.Writer;
@Deprecated
public class OctalUnescaper extends CharSequenceTranslator {
/**
* Checks if the given char is an octal digit. Octal digits are the character representations of the digits 0 to 7.
* @param ch the char to check
* @return true if the given char is the character representation of one of the digits from 0 to 7
*/
private boolean isOctalDigit(final char ch) {
return ch >= '0' && ch <= '7';
}
/**
* Checks if the given char is the character representation of one of the digit from 0 to 3.
* @param ch the char to check
* @return true if the given char is the character representation of one of the digits from 0 to 3
*/
private boolean isZeroToThree(final char ch) {
return ch >= '0' && ch <= '3';
}
/**
* {@inheritDoc}
*/
@ -62,22 +80,4 @@ public class OctalUnescaper extends CharSequenceTranslator {
}
return 0;
}
/**
* Checks if the given char is an octal digit. Octal digits are the character representations of the digits 0 to 7.
* @param ch the char to check
* @return true if the given char is the character representation of one of the digits from 0 to 7
*/
private boolean isOctalDigit(final char ch) {
return ch >= '0' && ch <= '7';
}
/**
* Checks if the given char is the character representation of one of the digit from 0 to 3.
* @param ch the char to check
* @return true if the given char is the character representation of one of the digits from 0 to 3
*/
private boolean isZeroToThree(final char ch) {
return ch >= '0' && ch <= '3';
}
}

View File

@ -30,8 +30,50 @@ import java.io.Writer;
@Deprecated
public class UnicodeEscaper extends CodePointTranslator {
/**
* Constructs a {@link UnicodeEscaper} above the specified value (exclusive).
*
* @param codePoint above which to escape
* @return the newly created {@link UnicodeEscaper} instance
*/
public static UnicodeEscaper above(final int codePoint) {
return outsideOf(0, codePoint);
}
/**
* Constructs a {@link UnicodeEscaper} below the specified value (exclusive).
*
* @param codePoint below which to escape
* @return the newly created {@link UnicodeEscaper} instance
*/
public static UnicodeEscaper below(final int codePoint) {
return outsideOf(codePoint, Integer.MAX_VALUE);
}
/**
* Constructs a {@link UnicodeEscaper} between the specified values (inclusive).
*
* @param codePointLow above which to escape
* @param codePointHigh below which to escape
* @return the newly created {@link UnicodeEscaper} instance
*/
public static UnicodeEscaper between(final int codePointLow, final int codePointHigh) {
return new UnicodeEscaper(codePointLow, codePointHigh, true);
}
/**
* Constructs a {@link UnicodeEscaper} outside of the specified values (exclusive).
*
* @param codePointLow below which to escape
* @param codePointHigh above which to escape
* @return the newly created {@link UnicodeEscaper} instance
*/
public static UnicodeEscaper outsideOf(final int codePointLow, final int codePointHigh) {
return new UnicodeEscaper(codePointLow, codePointHigh, false);
}
private final int below;
private final int above;
private final boolean between;
/**
@ -58,45 +100,16 @@ public class UnicodeEscaper extends CodePointTranslator {
}
/**
* Constructs a {@link UnicodeEscaper} below the specified value (exclusive).
* Converts the given code point to a hexadecimal string of the form {@code "\\uXXXX"}
*
* @param codePoint below which to escape
* @return the newly created {@link UnicodeEscaper} instance
*/
public static UnicodeEscaper below(final int codePoint) {
return outsideOf(codePoint, Integer.MAX_VALUE);
}
/**
* Constructs a {@link UnicodeEscaper} above the specified value (exclusive).
* @param codePoint
* a Unicode code point
* @return the hexadecimal string for the given code point
*
* @param codePoint above which to escape
* @return the newly created {@link UnicodeEscaper} instance
* @since 3.2
*/
public static UnicodeEscaper above(final int codePoint) {
return outsideOf(0, codePoint);
}
/**
* Constructs a {@link UnicodeEscaper} outside of the specified values (exclusive).
*
* @param codePointLow below which to escape
* @param codePointHigh above which to escape
* @return the newly created {@link UnicodeEscaper} instance
*/
public static UnicodeEscaper outsideOf(final int codePointLow, final int codePointHigh) {
return new UnicodeEscaper(codePointLow, codePointHigh, false);
}
/**
* Constructs a {@link UnicodeEscaper} between the specified values (inclusive).
*
* @param codePointLow above which to escape
* @param codePointHigh below which to escape
* @return the newly created {@link UnicodeEscaper} instance
*/
public static UnicodeEscaper between(final int codePointLow, final int codePointHigh) {
return new UnicodeEscaper(codePointLow, codePointHigh, true);
protected String toUtf16Escape(final int codePoint) {
return "\\u" + hex(codePoint);
}
/**
@ -124,17 +137,4 @@ public class UnicodeEscaper extends CodePointTranslator {
}
return true;
}
/**
* Converts the given code point to a hexadecimal string of the form {@code "\\uXXXX"}
*
* @param codePoint
* a Unicode code point
* @return the hexadecimal string for the given code point
*
* @since 3.2
*/
protected String toUtf16Escape(final int codePoint) {
return "\\u" + hex(codePoint);
}
}

View File

@ -38,14 +38,160 @@ import org.apache.commons.lang3.LocaleUtils;
// TODO: Before making public move from getDateTimeInstance(Integer, ...) to int; or some other approach.
abstract class AbstractFormatCache<F extends Format> {
/**
* Helper class to hold multipart Map keys as arrays.
*/
private static final class ArrayKey {
private static int computeHashCode(final Object[] keys) {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(keys);
return result;
}
private final Object[] keys;
private final int hashCode;
/**
* Constructs an instance of {@link MultipartKey} to hold the specified objects.
*
* @param keys the set of objects that make up the key. Each key may be null.
*/
ArrayKey(final Object... keys) {
this.keys = keys;
this.hashCode = computeHashCode(keys);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ArrayKey other = (ArrayKey) obj;
return Arrays.deepEquals(keys, other.keys);
}
@Override
public int hashCode() {
return hashCode;
}
}
/**
* No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
*/
static final int NONE = -1;
private static final ConcurrentMap<ArrayKey, String> cDateTimeInstanceCache = new ConcurrentHashMap<>(7);
/**
* Gets a date/time format for the specified styles and locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param locale The non-null locale of the desired format
* @return a localized standard date/time format
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final Locale safeLocale = LocaleUtils.toLocale(locale);
final ArrayKey key = new ArrayKey(dateStyle, timeStyle, safeLocale);
return cDateTimeInstanceCache.computeIfAbsent(key, k -> {
try {
final DateFormat formatter;
if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), safeLocale);
} else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), safeLocale);
} else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), safeLocale);
}
return ((SimpleDateFormat) formatter).toPattern();
} catch (final ClassCastException ex) {
throw new IllegalArgumentException("No date time pattern for locale: " + safeLocale);
}
});
}
private final ConcurrentMap<ArrayKey, F> cInstanceCache = new ConcurrentHashMap<>(7);
private static final ConcurrentMap<ArrayKey, String> cDateTimeInstanceCache = new ConcurrentHashMap<>(7);
/**
* Create a format instance using the specified pattern, time zone
* and locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern, this will not be null.
* @param timeZone time zone, this will not be null.
* @param locale locale, this will not be null.
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or {@code null}
*/
protected abstract F createInstance(String pattern, TimeZone timeZone, Locale locale);
/**
* Gets a date formatter instance using the specified style,
* time zone and locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @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
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
}
/**
* Gets a date/time formatter instance using the specified style,
* time zone and 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, null means use default Locale
* @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
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* Gets a date/time formatter instance using the specified style,
* time zone and locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @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
*/
// This must remain private, see LANG-884
private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
locale = LocaleUtils.toLocale(locale);
final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
return getInstance(pattern, timeZone, locale);
}
/**
* Gets a formatter instance using the default pattern in the
@ -77,74 +223,6 @@ abstract class AbstractFormatCache<F extends Format> {
return cInstanceCache.computeIfAbsent(key, k -> createInstance(pattern, actualTimeZone, actualLocale));
}
/**
* Create a format instance using the specified pattern, time zone
* and locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern, this will not be null.
* @param timeZone time zone, this will not be null.
* @param locale locale, this will not be null.
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or {@code null}
*/
protected abstract F createInstance(String pattern, TimeZone timeZone, Locale locale);
/**
* Gets a date/time formatter instance using the specified style,
* time zone and locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @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
*/
// This must remain private, see LANG-884
private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
locale = LocaleUtils.toLocale(locale);
final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
return getInstance(pattern, timeZone, locale);
}
/**
* Gets a date/time formatter instance using the specified style,
* time zone and 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, null means use default Locale
* @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
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* Gets a date formatter instance using the specified style,
* time zone and locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @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
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
}
/**
* Gets a time formatter instance using the specified style,
* time zone and locale.
@ -162,82 +240,4 @@ abstract class AbstractFormatCache<F extends Format> {
return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* Gets a date/time format for the specified styles and locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param locale The non-null locale of the desired format
* @return a localized standard date/time format
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final Locale safeLocale = LocaleUtils.toLocale(locale);
final ArrayKey key = new ArrayKey(dateStyle, timeStyle, safeLocale);
return cDateTimeInstanceCache.computeIfAbsent(key, k -> {
try {
final DateFormat formatter;
if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), safeLocale);
} else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), safeLocale);
} else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), safeLocale);
}
return ((SimpleDateFormat) formatter).toPattern();
} catch (final ClassCastException ex) {
throw new IllegalArgumentException("No date time pattern for locale: " + safeLocale);
}
});
}
/**
* Helper class to hold multipart Map keys as arrays.
*/
private static final class ArrayKey {
private static int computeHashCode(final Object[] keys) {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(keys);
return result;
}
private final Object[] keys;
private final int hashCode;
/**
* Constructs an instance of {@link MultipartKey} to hold the specified objects.
*
* @param keys the set of objects that make up the key. Each key may be null.
*/
ArrayKey(final Object... keys) {
this.keys = keys;
this.hashCode = computeHashCode(keys);
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ArrayKey other = (ArrayKey) obj;
return Arrays.deepEquals(keys, other.keys);
}
}
}

View File

@ -197,15 +197,6 @@ public class DateFormatUtils {
public static final FastDateFormat SMTP_DATETIME_FORMAT
= FastDateFormat.getInstance("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
/**
* DateFormatUtils instances should NOT be constructed in standard programming.
*
* <p>This constructor is public to permit tools that require a JavaBean instance
* to operate.</p>
*/
public DateFormatUtils() {
}
/**
* Formats a calendar into a specific pattern. The TimeZone from the calendar
* will be used for formatting.
@ -412,4 +403,13 @@ public class DateFormatUtils {
return calendar == null ? null : calendar.getTimeZone();
}
/**
* DateFormatUtils instances should NOT be constructed in standard programming.
*
* <p>This constructor is public to permit tools that require a JavaBean instance
* to operate.</p>
*/
public DateFormatUtils() {
}
}

View File

@ -35,6 +35,33 @@ import java.util.TimeZone;
*/
public interface DateParser {
/**
* Gets the locale used by this parser.
*
* @return the locale
*/
Locale getLocale();
// Accessors
/**
* Gets the pattern used by this parser.
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* Gets the time zone used by this parser.
*
* <p>
* The default {@link TimeZone} used to create a {@link Date} when the {@link TimeZone} is not specified by
* the format pattern.
* </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* Equivalent to DateFormat.parse(String).
*
@ -75,33 +102,6 @@ public interface DateParser {
*/
boolean parse(String source, ParsePosition pos, Calendar calendar);
// Accessors
/**
* Gets the pattern used by this parser.
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* Gets the time zone used by this parser.
*
* <p>
* The default {@link TimeZone} used to create a {@link Date} when the {@link TimeZone} is not specified by
* the format pattern.
* </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* Gets the locale used by this parser.
*
* @return the locale
*/
Locale getLocale();
/**
* Parses text from a string to produce a Date.
*

View File

@ -35,23 +35,6 @@ import java.util.TimeZone;
*/
public interface DatePrinter {
/**
* Formats a millisecond {@code long} value.
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
String format(long millis);
/**
* Formats a {@link Date} object using a {@link GregorianCalendar}.
*
* @param date the date to format
* @return the formatted string
*/
String format(Date date);
/**
* Formats a {@link Calendar} object.
* The TimeZone set on the Calendar is only used to adjust the time offset.
@ -64,28 +47,18 @@ public interface DatePrinter {
String format(Calendar calendar);
/**
* Formats a millisecond {@code long} value into the
* supplied {@link StringBuffer}.
* Formats a {@link Calendar} object into the supplied {@link Appendable}.
* The TimeZone set on the Calendar is only used to adjust the time offset.
* The TimeZone specified during the construction of the Parser will determine the TimeZone
* used in the formatted string.
*
* @param millis the millisecond value to format
* @param calendar the calendar to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
* @deprecated Use {{@link #format(long, Appendable)}.
* @since 3.5
*/
@Deprecated
StringBuffer format(long millis, StringBuffer buf);
/**
* Formats a {@link Date} object into the
* supplied {@link StringBuffer} using a {@link GregorianCalendar}.
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
StringBuffer format(Date date, StringBuffer buf);
<B extends Appendable> B format(Calendar calendar, B buf);
/**
* Formats a {@link Calendar} object into the supplied {@link StringBuffer}.
@ -102,16 +75,12 @@ public interface DatePrinter {
StringBuffer format(Calendar calendar, StringBuffer buf);
/**
* Formats a millisecond {@code long} value into the
* supplied {@link Appendable}.
* Formats a {@link Date} object using a {@link GregorianCalendar}.
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
* @since 3.5
* @param date the date to format
* @return the formatted string
*/
<B extends Appendable> B format(long millis, B buf);
String format(Date date);
/**
* Formats a {@link Date} object into the
@ -126,19 +95,69 @@ public interface DatePrinter {
<B extends Appendable> B format(Date date, B buf);
/**
* Formats a {@link Calendar} object into the supplied {@link Appendable}.
* The TimeZone set on the Calendar is only used to adjust the time offset.
* The TimeZone specified during the construction of the Parser will determine the TimeZone
* used in the formatted string.
* Formats a {@link Date} object into the
* supplied {@link StringBuffer} using a {@link GregorianCalendar}.
*
* @param calendar the calendar to format
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
StringBuffer format(Date date, StringBuffer buf);
/**
* Formats a millisecond {@code long} value.
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
String format(long millis);
/**
* Formats a millisecond {@code long} value into the
* supplied {@link Appendable}.
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
* @since 3.5
*/
<B extends Appendable> B format(Calendar calendar, B buf);
<B extends Appendable> B format(long millis, B buf);
/**
* Formats a millisecond {@code long} value into the
* supplied {@link StringBuffer}.
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(long, Appendable)}.
*/
@Deprecated
StringBuffer format(long millis, StringBuffer buf);
/**
* Formats a {@link Date}, {@link Calendar} or
* {@link Long} (milliseconds) object.
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
* @see java.text.DateFormat#format(Object, StringBuffer, FieldPosition)
*/
StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
/**
* Gets the locale used by this printer.
*
* @return the locale
*/
Locale getLocale();
// Accessors
/**
@ -156,23 +175,4 @@ public interface DatePrinter {
* @return the time zone
*/
TimeZone getTimeZone();
/**
* Gets the locale used by this printer.
*
* @return the locale
*/
Locale getLocale();
/**
* Formats a {@link Date}, {@link Calendar} or
* {@link Long} (milliseconds) object.
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
* @see java.text.DateFormat#format(Object, StringBuffer, FieldPosition)
*/
StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
}

File diff suppressed because it is too large Load Diff

View File

@ -80,12 +80,116 @@ import org.apache.commons.lang3.Validate;
public class DurationFormatUtils {
/**
* DurationFormatUtils instances should NOT be constructed in standard programming.
*
* <p>This constructor is public to permit tools that require a JavaBean instance
* to operate.</p>
* Element that is parsed from the format pattern.
*/
public DurationFormatUtils() {
static class Token {
/** Empty array. */
private static final Token[] EMPTY_ARRAY = {};
/**
* Helper method to determine if a set of tokens contain a value
*
* @param tokens set to look in
* @param value to look for
* @return boolean {@code true} if contained
*/
static boolean containsTokenWithValue(final Token[] tokens, final Object value) {
return Stream.of(tokens).anyMatch(token -> token.getValue() == value);
}
private final Object value;
private int count;
private int optionalIndex = -1;
/**
* Wraps a token around a value. A value would be something like a 'Y'.
*
* @param value value to wrap, non-null.
* @param optional whether the token is optional
* @param optionalIndex the index of the optional token within the pattern
*/
Token(final Object value, final boolean optional, final int optionalIndex) {
this.value = Objects.requireNonNull(value, "value");
this.count = 1;
if (optional) {
this.optionalIndex = optionalIndex;
}
}
/**
* Supports equality of this Token to another Token.
*
* @param obj2 Object to consider equality of
* @return boolean {@code true} if equal
*/
@Override
public boolean equals(final Object obj2) {
if (obj2 instanceof Token) {
final Token tok2 = (Token) obj2;
if (this.value.getClass() != tok2.value.getClass()) {
return false;
}
if (this.count != tok2.count) {
return false;
}
if (this.value instanceof StringBuilder) {
return this.value.toString().equals(tok2.value.toString());
}
if (this.value instanceof Number) {
return this.value.equals(tok2.value);
}
return this.value == tok2.value;
}
return false;
}
/**
* 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, non-null.
*/
Object getValue() {
return value;
}
/**
* Returns a hash code for the token equal to the
* hash code for the token's value. Thus 'TT' and 'TTTT'
* will have the same hash code.
*
* @return The hash code for the token
*/
@Override
public int hashCode() {
return this.value.hashCode();
}
/**
* Adds another one of the value
*/
void increment() {
count++;
}
/**
* Represents this token as a String.
*
* @return String representation of the token
*/
@Override
public String toString() {
return StringUtils.repeat(this.value.toString(), this.count);
}
}
/**
@ -97,33 +201,123 @@ public class DurationFormatUtils {
*/
public static final String ISO_EXTENDED_FORMAT_PATTERN = "'P'yyyy'Y'M'M'd'DT'H'H'm'M's.SSS'S'";
/**
* Formats the time gap as a string.
*
* <p>The format used is ISO 8601-like: {@code HH:mm:ss.SSS}.</p>
*
* @param durationMillis the duration to format
* @return the formatted duration, not null
* @throws IllegalArgumentException if durationMillis is negative
*/
public static String formatDurationHMS(final long durationMillis) {
return formatDuration(durationMillis, "HH:mm:ss.SSS");
}
static final String y = "y";
static final String M = "M";
static final String d = "d";
static final String H = "H";
static final String m = "m";
static final String s = "s";
static final String S = "S";
/**
* Formats the time gap as a string.
* The internal method to do the formatting.
*
* <p>The format used is the ISO 8601 period format.</p>
*
* <p>This method formats durations using the days and lower fields of the
* ISO format pattern, such as P7D6TH5M4.321S.</p>
*
* @param durationMillis the duration to format
* @return the formatted duration, not null
* @throws IllegalArgumentException if durationMillis is negative
* @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
*/
public static String formatDurationISO(final long durationMillis) {
return formatDuration(durationMillis, ISO_EXTENDED_FORMAT_PATTERN, false);
static String format(final Token[] tokens, final long years, final long months, final long days, final long hours, final long minutes,
final long seconds,
final long milliseconds, final boolean padWithZeros) {
final StringBuilder buffer = new StringBuilder();
boolean lastOutputSeconds = false;
boolean lastOutputZero = false;
int optionalStart = -1;
boolean firstOptionalNonLiteral = false;
int optionalIndex = -1;
boolean inOptional = false;
for (final Token token : tokens) {
final Object value = token.getValue();
final boolean isLiteral = value instanceof StringBuilder;
final int count = token.getCount();
if (optionalIndex != token.optionalIndex) {
optionalIndex = token.optionalIndex;
if (optionalIndex > -1) {
//entering new optional block
optionalStart = buffer.length();
lastOutputZero = false;
inOptional = true;
firstOptionalNonLiteral = false;
} else {
//leaving optional block
inOptional = false;
}
}
if (isLiteral) {
if (!inOptional || !lastOutputZero) {
buffer.append(value.toString());
}
} else if (value.equals(y)) {
lastOutputSeconds = false;
lastOutputZero = years == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(years, padWithZeros, count));
}
} else if (value.equals(M)) {
lastOutputSeconds = false;
lastOutputZero = months == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(months, padWithZeros, count));
}
} else if (value.equals(d)) {
lastOutputSeconds = false;
lastOutputZero = days == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(days, padWithZeros, count));
}
} else if (value.equals(H)) {
lastOutputSeconds = false;
lastOutputZero = hours == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(hours, padWithZeros, count));
}
} else if (value.equals(m)) {
lastOutputSeconds = false;
lastOutputZero = minutes == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(minutes, padWithZeros, count));
}
} else if (value.equals(s)) {
lastOutputSeconds = true;
lastOutputZero = seconds == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(seconds, padWithZeros, count));
}
} else if (value.equals(S)) {
lastOutputZero = milliseconds == 0;
if (!inOptional || !lastOutputZero) {
if (lastOutputSeconds) {
// ensure at least 3 digits are displayed even if padding is not selected
final int width = padWithZeros ? Math.max(3, count) : 3;
buffer.append(paddedValue(milliseconds, true, width));
} else {
buffer.append(paddedValue(milliseconds, padWithZeros, count));
}
}
lastOutputSeconds = false;
}
//as soon as we hit first nonliteral in optional, check for literal prefix
if (inOptional && !isLiteral && !firstOptionalNonLiteral){
firstOptionalNonLiteral = true;
if (lastOutputZero) {
buffer.delete(optionalStart, buffer.length());
}
}
}
return buffer.toString();
}
/**
@ -185,6 +379,33 @@ public class DurationFormatUtils {
return format(tokens, 0, 0, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
/**
* Formats the time gap as a string.
*
* <p>The format used is ISO 8601-like: {@code HH:mm:ss.SSS}.</p>
*
* @param durationMillis the duration to format
* @return the formatted duration, not null
* @throws IllegalArgumentException if durationMillis is negative
*/
public static String formatDurationHMS(final long durationMillis) {
return formatDuration(durationMillis, "HH:mm:ss.SSS");
}
/**
* Formats the time gap as a string.
*
* <p>The format used is the ISO 8601 period format.</p>
*
* <p>This method formats durations using the days and lower fields of the
* ISO format pattern, such as P7D6TH5M4.321S.</p>
*
* @param durationMillis the duration to format
* @return the formatted duration, not null
* @throws IllegalArgumentException if durationMillis is negative
*/
public static String formatDurationISO(final long durationMillis) {
return formatDuration(durationMillis, ISO_EXTENDED_FORMAT_PATTERN, false);
}
/**
* Formats an elapsed time into a pluralization correct string.
*
@ -246,21 +467,6 @@ public class DurationFormatUtils {
duration = StringUtils.replaceOnce(duration, " 1 days", " 1 day");
return duration.trim();
}
/**
* Formats the time gap as a string.
*
* <p>The format used is the ISO 8601 period format.</p>
*
* @param startMillis the start of the duration to format
* @param endMillis the end of the duration to format
* @return the formatted duration, not null
* @throws IllegalArgumentException if startMillis is greater than endMillis
*/
public static String formatPeriodISO(final long startMillis, final 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.
@ -274,7 +480,6 @@ public class DurationFormatUtils {
public static String formatPeriod(final long startMillis, final long endMillis, final String format) {
return formatPeriod(startMillis, endMillis, format, true, TimeZone.getDefault());
}
/**
* <p>Formats the time gap as a string, using the specified format.
* Padding the left-hand side of numbers with zeroes is optional and
@ -430,134 +635,19 @@ public class DurationFormatUtils {
return format(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
/**
* The internal method to do the formatting.
* Formats the time gap as a string.
*
* @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(final Token[] tokens, final long years, final long months, final long days, final long hours, final long minutes,
final long seconds,
final long milliseconds, final boolean padWithZeros) {
final StringBuilder buffer = new StringBuilder();
boolean lastOutputSeconds = false;
boolean lastOutputZero = false;
int optionalStart = -1;
boolean firstOptionalNonLiteral = false;
int optionalIndex = -1;
boolean inOptional = false;
for (final Token token : tokens) {
final Object value = token.getValue();
final boolean isLiteral = value instanceof StringBuilder;
final int count = token.getCount();
if (optionalIndex != token.optionalIndex) {
optionalIndex = token.optionalIndex;
if (optionalIndex > -1) {
//entering new optional block
optionalStart = buffer.length();
lastOutputZero = false;
inOptional = true;
firstOptionalNonLiteral = false;
} else {
//leaving optional block
inOptional = false;
}
}
if (isLiteral) {
if (!inOptional || !lastOutputZero) {
buffer.append(value.toString());
}
} else if (value.equals(y)) {
lastOutputSeconds = false;
lastOutputZero = years == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(years, padWithZeros, count));
}
} else if (value.equals(M)) {
lastOutputSeconds = false;
lastOutputZero = months == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(months, padWithZeros, count));
}
} else if (value.equals(d)) {
lastOutputSeconds = false;
lastOutputZero = days == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(days, padWithZeros, count));
}
} else if (value.equals(H)) {
lastOutputSeconds = false;
lastOutputZero = hours == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(hours, padWithZeros, count));
}
} else if (value.equals(m)) {
lastOutputSeconds = false;
lastOutputZero = minutes == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(minutes, padWithZeros, count));
}
} else if (value.equals(s)) {
lastOutputSeconds = true;
lastOutputZero = seconds == 0;
if (!inOptional || !lastOutputZero) {
buffer.append(paddedValue(seconds, padWithZeros, count));
}
} else if (value.equals(S)) {
lastOutputZero = milliseconds == 0;
if (!inOptional || !lastOutputZero) {
if (lastOutputSeconds) {
// ensure at least 3 digits are displayed even if padding is not selected
final int width = padWithZeros ? Math.max(3, count) : 3;
buffer.append(paddedValue(milliseconds, true, width));
} else {
buffer.append(paddedValue(milliseconds, padWithZeros, count));
}
}
lastOutputSeconds = false;
}
//as soon as we hit first nonliteral in optional, check for literal prefix
if (inOptional && !isLiteral && !firstOptionalNonLiteral){
firstOptionalNonLiteral = true;
if (lastOutputZero) {
buffer.delete(optionalStart, buffer.length());
}
}
}
return buffer.toString();
}
/**
* Converts a {@code long} to a {@link String} with optional
* zero padding.
* <p>The format used is the ISO 8601 period format.</p>
*
* @param value the value to convert
* @param padWithZeros whether to pad with zeroes
* @param count the size to pad to (ignored if {@code padWithZeros} is false)
* @return the string result
* @param startMillis the start of the duration to format
* @param endMillis the end of the duration to format
* @return the formatted duration, not null
* @throws IllegalArgumentException if startMillis is greater than endMillis
*/
private static String paddedValue(final long value, final boolean padWithZeros, final int count) {
final String longString = Long.toString(value);
return padWithZeros ? StringUtils.leftPad(longString, count, '0') : longString;
public static String formatPeriodISO(final long startMillis, final long endMillis) {
return formatPeriod(startMillis, endMillis, ISO_EXTENDED_FORMAT_PATTERN, false, TimeZone.getDefault());
}
static final String y = "y";
static final String M = "M";
static final String d = "d";
static final String H = "H";
static final String m = "m";
static final String s = "s";
static final String S = "S";
/**
* Parses a classic date format string into Tokens
*
@ -656,116 +746,26 @@ public class DurationFormatUtils {
}
/**
* Element that is parsed from the format pattern.
* Converts a {@code long} to a {@link String} with optional
* zero padding.
*
* @param value the value to convert
* @param padWithZeros whether to pad with zeroes
* @param count the size to pad to (ignored if {@code padWithZeros} is false)
* @return the string result
*/
static class Token {
private static String paddedValue(final long value, final boolean padWithZeros, final int count) {
final String longString = Long.toString(value);
return padWithZeros ? StringUtils.leftPad(longString, count, '0') : longString;
}
/** Empty array. */
private static final Token[] EMPTY_ARRAY = {};
/**
* Helper method to determine if a set of tokens contain a value
*
* @param tokens set to look in
* @param value to look for
* @return boolean {@code true} if contained
*/
static boolean containsTokenWithValue(final Token[] tokens, final Object value) {
return Stream.of(tokens).anyMatch(token -> token.getValue() == value);
}
private final Object value;
private int count;
private int optionalIndex = -1;
/**
* Wraps a token around a value. A value would be something like a 'Y'.
*
* @param value value to wrap, non-null.
* @param optional whether the token is optional
* @param optionalIndex the index of the optional token within the pattern
*/
Token(final Object value, final boolean optional, final int optionalIndex) {
this.value = Objects.requireNonNull(value, "value");
this.count = 1;
if (optional) {
this.optionalIndex = optionalIndex;
}
}
/**
* 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, non-null.
*/
Object getValue() {
return value;
}
/**
* Supports equality of this Token to another Token.
*
* @param obj2 Object to consider equality of
* @return boolean {@code true} if equal
*/
@Override
public boolean equals(final Object obj2) {
if (obj2 instanceof Token) {
final Token tok2 = (Token) obj2;
if (this.value.getClass() != tok2.value.getClass()) {
return false;
}
if (this.count != tok2.count) {
return false;
}
if (this.value instanceof StringBuilder) {
return this.value.toString().equals(tok2.value.toString());
}
if (this.value instanceof Number) {
return this.value.equals(tok2.value);
}
return this.value == tok2.value;
}
return false;
}
/**
* Returns a hash code for the token equal to the
* hash code for the token's value. Thus 'TT' and 'TTTT'
* will have the same hash code.
*
* @return The hash code for the token
*/
@Override
public int hashCode() {
return this.value.hashCode();
}
/**
* Represents this token as a String.
*
* @return String representation of the token
*/
@Override
public String toString() {
return StringUtils.repeat(this.value.toString(), this.count);
}
/**
* DurationFormatUtils instances should NOT be constructed in standard programming.
*
* <p>This constructor is public to permit tools that require a JavaBean instance
* to operate.</p>
*/
public DurationFormatUtils() {
}
}

View File

@ -106,6 +106,12 @@ public class DurationUtils {
return !duration.isNegative() && !duration.isZero();
}
private static <E extends Throwable> Instant now(final FailableConsumer<Instant, E> nowConsumer) throws E {
final Instant start = Instant.now();
nowConsumer.accept(start);
return start;
}
/**
* Runs the lambda and returns the duration of its execution.
*
@ -132,12 +138,6 @@ public class DurationUtils {
return of(start -> runnable.run());
}
private static <E extends Throwable> Instant now(final FailableConsumer<Instant, E> nowConsumer) throws E {
final Instant start = Instant.now();
nowConsumer.accept(start);
return start;
}
/**
* Computes the Duration between a start instant and now.
*

View File

@ -108,81 +108,6 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
}
};
/** Our fast printer. */
private final FastDatePrinter printer;
/** Our fast parser. */
private final FastDateParser parser;
/**
* Gets a formatter instance using the default pattern in the
* default locale.
*
* @return a date/time formatter
*/
public static FastDateFormat getInstance() {
return cache.getInstance();
}
/**
* Gets a formatter instance using the specified pattern in the
* default locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern) {
return cache.getInstance(pattern, null, null);
}
/**
* Gets a formatter instance using the specified pattern and
* time zone.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) {
return cache.getInstance(pattern, timeZone, null);
}
/**
* Gets a formatter instance using the specified pattern and
* locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final Locale locale) {
return cache.getInstance(pattern, null, locale);
}
/**
* Gets a formatter instance using the specified pattern, time zone
* and locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or {@code null}
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return cache.getInstance(pattern, timeZone, locale);
}
/**
* Gets a date formatter instance using the specified style in the
* default time zone and locale.
@ -244,6 +169,141 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
return cache.getDateInstance(style, timeZone, locale);
}
/**
* 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(final int dateStyle, final int timeStyle) {
return cache.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(final int dateStyle, final int timeStyle, final Locale locale) {
return cache.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(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
}
/**
* Gets a date/time formatter instance using the specified style,
* time zone and 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
* @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
*/
public static FastDateFormat getDateTimeInstance(
final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
/**
* Gets a formatter instance using the default pattern in the
* default locale.
*
* @return a date/time formatter
*/
public static FastDateFormat getInstance() {
return cache.getInstance();
}
/**
* Gets a formatter instance using the specified pattern in the
* default locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern) {
return cache.getInstance(pattern, null, null);
}
/**
* Gets a formatter instance using the specified pattern and
* locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final Locale locale) {
return cache.getInstance(pattern, null, locale);
}
/**
* Gets a formatter instance using the specified pattern and
* time zone.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) {
return cache.getInstance(pattern, timeZone, null);
}
/**
* Gets a formatter instance using the specified pattern, time zone
* and locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or {@code null}
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return cache.getInstance(pattern, timeZone, locale);
}
/**
* Gets a time formatter instance using the specified style in the
* default time zone and locale.
@ -305,70 +365,10 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
return cache.getTimeInstance(style, timeZone, locale);
}
/**
* 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(final int dateStyle, final int timeStyle) {
return cache.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(final int dateStyle, final int timeStyle, final Locale locale) {
return cache.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(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
}
/**
* Gets a date/time formatter instance using the specified style,
* time zone and 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
* @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
*/
public static FastDateFormat getDateTimeInstance(
final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
/** Our fast printer. */
private final FastDatePrinter printer;
/** Our fast parser. */
private final FastDateParser parser;
// Constructor
/**
@ -398,43 +398,35 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
parser = new FastDateParser(pattern, timeZone, locale, centuryStart);
}
// Format methods
/**
* Formats a {@link Date}, {@link Calendar} or
* {@link Long} (milliseconds) object.
* This method is an implementation of {@link Format#format(Object, StringBuffer, FieldPosition)}
* Performs the formatting by applying the rules to the
* specified calendar.
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {@link #format(Calendar, Appendable)}
*/
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return toAppendTo.append(printer.format(obj));
@Deprecated
protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
return printer.applyRules(calendar, buf);
}
// Basics
/**
* Formats a millisecond {@code long} value.
* Compares two objects for equality.
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
* @param obj the object to compare to
* @return {@code true} if equal
*/
@Override
public String format(final long millis) {
return printer.format(millis);
}
/**
* Formats a {@link Date} object using a {@link GregorianCalendar}.
*
* @param date the date to format
* @return the formatted string
*/
@Override
public String format(final Date date) {
return printer.format(date);
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateFormat)) {
return false;
}
final FastDateFormat other = (FastDateFormat) obj;
// no need to check parser, as it has same invariants as printer
return printer.equals(other.printer);
}
/**
@ -449,34 +441,17 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
}
/**
* Formats a millisecond {@code long} value into the
* Formats a {@link Calendar} object into the
* supplied {@link StringBuffer}.
*
* @param millis the millisecond value to format
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 2.1
* @deprecated Use {{@link #format(long, Appendable)}.
*/
@Deprecated
* @since 3.5
*/
@Override
public StringBuffer format(final long millis, final StringBuffer buf) {
return printer.format(millis, buf);
}
/**
* Formats a {@link Date} object into the
* supplied {@link StringBuffer} using a {@link GregorianCalendar}.
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final Date date, final StringBuffer buf) {
return printer.format(date, buf);
public <B extends Appendable> B format(final Calendar calendar, final B buf) {
return printer.format(calendar, buf);
}
/**
@ -495,17 +470,14 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
}
/**
* Formats a millisecond {@code long} value into the
* supplied {@link StringBuffer}.
* Formats a {@link Date} object using a {@link GregorianCalendar}.
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
* @param date the date to format
* @return the formatted string
*/
@Override
public <B extends Appendable> B format(final long millis, final B buf) {
return printer.format(millis, buf);
public String format(final Date date) {
return printer.format(date);
}
/**
@ -523,22 +495,137 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
}
/**
* Formats a {@link Calendar} object into the
* Formats a {@link Date} object into the
* supplied {@link StringBuffer} using a {@link GregorianCalendar}.
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final Date date, final StringBuffer buf) {
return printer.format(date, buf);
}
/**
* Formats a millisecond {@code long} value.
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
@Override
public String format(final long millis) {
return printer.format(millis);
}
/**
* Formats a millisecond {@code long} value into the
* supplied {@link StringBuffer}.
*
* @param calendar the calendar to format
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
*/
*/
@Override
public <B extends Appendable> B format(final Calendar calendar, final B buf) {
return printer.format(calendar, buf);
public <B extends Appendable> B format(final long millis, final B buf) {
return printer.format(millis, buf);
}
// Parsing
/**
* Formats a millisecond {@code long} value into the
* supplied {@link StringBuffer}.
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 2.1
* @deprecated Use {{@link #format(long, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final long millis, final StringBuffer buf) {
return printer.format(millis, buf);
}
// Format methods
/**
* Formats a {@link Date}, {@link Calendar} or
* {@link Long} (milliseconds) object.
* This method is an implementation of {@link Format#format(Object, StringBuffer, FieldPosition)}
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
*/
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return toAppendTo.append(printer.format(obj));
}
/**
* Gets the locale used by this formatter.
*
* @return the locale
*/
@Override
public Locale getLocale() {
return printer.getLocale();
}
/**
* Gets an estimate for the maximum string length that the
* formatter will produce.
*
* <p>The actual formatted length will almost always be less than or
* equal to this amount.</p>
*
* @return the maximum formatted length
*/
public int getMaxLengthEstimate() {
return printer.getMaxLengthEstimate();
}
// Accessors
/**
* Gets the pattern used by this formatter.
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
@Override
public String getPattern() {
return printer.getPattern();
}
/**
* Gets the time zone used by this formatter.
*
* <p>This zone is always used for {@link Date} formatting.</p>
*
* @return the time zone
*/
@Override
public TimeZone getTimeZone() {
return printer.getTimeZone();
}
/**
* Returns a hash code compatible with equals.
*
* @return a hash code compatible with equals
*/
@Override
public int hashCode() {
return printer.hashCode();
}
/* (non-Javadoc)
* @see DateParser#parse(String)
*/
@ -572,79 +659,6 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
return parser.parseObject(source, pos);
}
// Accessors
/**
* Gets the pattern used by this formatter.
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
@Override
public String getPattern() {
return printer.getPattern();
}
/**
* Gets the time zone used by this formatter.
*
* <p>This zone is always used for {@link Date} formatting.</p>
*
* @return the time zone
*/
@Override
public TimeZone getTimeZone() {
return printer.getTimeZone();
}
/**
* Gets the locale used by this formatter.
*
* @return the locale
*/
@Override
public Locale getLocale() {
return printer.getLocale();
}
/**
* Gets an estimate for the maximum string length that the
* formatter will produce.
*
* <p>The actual formatted length will almost always be less than or
* equal to this amount.</p>
*
* @return the maximum formatted length
*/
public int getMaxLengthEstimate() {
return printer.getMaxLengthEstimate();
}
// Basics
/**
* Compares two objects for equality.
*
* @param obj the object to compare to
* @return {@code true} if equal
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateFormat)) {
return false;
}
final FastDateFormat other = (FastDateFormat) obj;
// no need to check parser, as it has same invariants as printer
return printer.equals(other.printer);
}
/**
* Returns a hash code compatible with equals.
*
* @return a hash code compatible with equals
*/
@Override
public int hashCode() {
return printer.hashCode();
}
/**
* Gets a debugging string version of this formatter.
*
@ -654,18 +668,4 @@ public class FastDateFormat extends Format implements DateParser, DatePrinter {
public String toString() {
return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
}
/**
* Performs the formatting by applying the rules to the
* specified calendar.
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {@link #format(Calendar, Appendable)}
*/
@Deprecated
protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
return printer.applyRules(calendar, buf);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -28,10 +28,6 @@ import org.apache.commons.lang3.ObjectUtils;
*/
public class TimeZones {
/** Do not instantiate. */
private TimeZones() {
}
/**
* A public version of {@link java.util.TimeZone}'s package private {@code GMT_ID} field.
*/
@ -55,4 +51,8 @@ public class TimeZones {
return ObjectUtils.getIfNull(timeZone, TimeZone::getDefault);
}
/** Do not instantiate. */
private TimeZones() {
}
}