Skip to content

Commit

Permalink
Updated documentation.
Browse files Browse the repository at this point in the history
Added support for alternate spellings and underscores in ColorConverter.
Added support for parsing Java Color#toString values as Color.
Corrected incorrect type registered with ColorConverter.
Updated test classes docs to reference the class they're testing.
Replaced previous tests for exceptions with JUnit 4's dedicated syntax.
  • Loading branch information
SethFalco committed Dec 31, 2020
1 parent 0fe399c commit 77162f5
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 185 deletions.
72 changes: 36 additions & 36 deletions src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,6 @@

package org.apache.commons.beanutils2;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.io.File;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Collection;
import java.util.Locale;
import java.util.UUID;
import java.util.regex.Pattern;

import org.apache.commons.beanutils2.converters.ArrayConverter;
import org.apache.commons.beanutils2.converters.BigDecimalConverter;
import org.apache.commons.beanutils2.converters.BigIntegerConverter;
Expand Down Expand Up @@ -95,6 +64,37 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.io.File;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Collection;
import java.util.Locale;
import java.util.UUID;
import java.util.regex.Pattern;

/**
* <p>Utility methods for converting String scalar values to objects of the
* specified Class, String arrays to arrays of the specified Class. The
Expand Down Expand Up @@ -534,7 +534,7 @@ private void registerStandard(final boolean throwException, final boolean defaul
private void registerOther(final boolean throwException) {
// @formatter:off
register(Class.class, throwException ? new ClassConverter() : new ClassConverter(null));
register(ColorConverter.class, throwException ? new ColorConverter() : new ColorConverter(null));
register(Color.class, throwException ? new ColorConverter() : new ColorConverter(null));
register(Enum.class, throwException ? new EnumConverter() : new EnumConverter(null));
register(java.util.Date.class, throwException ? new DateConverter() : new DateConverter(null));
register(Calendar.class, throwException ? new CalendarConverter() : new CalendarConverter(null));
Expand All @@ -544,17 +544,17 @@ private void registerOther(final boolean throwException) {
register(Path.class, throwException ? new PathConverter() : new PathConverter(null));
register(java.sql.Date.class, throwException ? new SqlDateConverter() : new SqlDateConverter(null));
register(java.sql.Time.class, throwException ? new SqlTimeConverter() : new SqlTimeConverter(null));
register(Timestamp.class, throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
register(Timestamp.class, throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
register(URL.class, throwException ? new URLConverter() : new URLConverter(null));
register(URI.class, throwException ? new URIConverter() : new URIConverter(null));
register(UUID.class, throwException ? new UUIDConverter() : new UUIDConverter(null));
register(LocalDate.class, throwException ? new LocalDateConverter() : new LocalDateConverter(null));
register(LocalDateTime.class, throwException ? new LocalDateTimeConverter() : new LocalDateTimeConverter(null));
register(LocalDateTime.class, throwException ? new LocalDateTimeConverter() : new LocalDateTimeConverter(null));
register(LocalTime.class, throwException ? new LocalTimeConverter() : new LocalTimeConverter(null));
register(Locale.class, throwException ? new LocaleConverter() : new LocaleConverter(null));
register(OffsetDateTime.class,throwException ? new OffsetDateTimeConverter():new OffsetDateTimeConverter(null));
register(OffsetDateTime.class, throwException ? new OffsetDateTimeConverter() :new OffsetDateTimeConverter(null));
register(OffsetTime.class, throwException ? new OffsetTimeConverter() : new OffsetTimeConverter(null));
register(ZonedDateTime.class, throwException ? new ZonedDateTimeConverter() : new ZonedDateTimeConverter(null));
register(ZonedDateTime.class, throwException ? new ZonedDateTimeConverter() : new ZonedDateTimeConverter(null));
register(Duration.class, throwException ? new DurationConverter() : new DurationConverter(null));
register(MonthDay.class, throwException ? new MonthDayConverter() : new MonthDayConverter(null));
register(Pattern.class, throwException ? new PatternConverter() : new PatternConverter(null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@

import java.awt.Color;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* <p>Converts a configuration property into a Java {@link Color} object.</p>
* Converts a configuration property into a Java {@link Color} object.
*
* <p>
* This converter aims to be compatible with some some of the web color
Expand Down Expand Up @@ -51,21 +53,23 @@ public class ColorConverter extends AbstractConverter {
/** To be a web based hexadecimal color, it must be prefixed with this. */
private static final String HEX_COLOR_PREFIX = "#";

/** A regular expression matching the output of {@link Color#toString()} and similar outputs. */
private static final Pattern JAVA_COLOR_PATTERN =
Pattern.compile("^(?:[A-Za-z\\d._]+)??\\[?(?:r=)?(\\d{1,3}),(?:g=)?(\\d{1,3}),(?:b=)?(\\d{1,3})\\]?$");

/**
* Construct a <b>{@link Color}</b> <i>Converter</i> that throws
* a {@code ConversionException} if an error occurs.
* Construct a <b>{@link Color}</b> <i>Converter</i> that throws a {@code ConversionException} if an error occurs.
*/
public ColorConverter() {
super();
}

/**
* Constructs a {@link org.apache.commons.beanutils2.Converter} that will return
* the specified default value if a conversion error occurs.
* Constructs a {@link org.apache.commons.beanutils2.Converter} that will return the specified default value if a
* conversion error occurs.
*
* @param defaultValue The default value to be returned
* if the value to be converted is missing or an error
* occurs converting the value.
* @param defaultValue The default value to be returned if the value to be
* converted is missing or an error occurs converting the value.
*/
public ColorConverter(final Object defaultValue) {
super(defaultValue);
Expand All @@ -89,40 +93,33 @@ protected Class<?> getDefaultType() {
* </p>
*
* <p>
* This can also interpret raw color names based on the standard colors
* defined in Java, such as the following:
* This can also interpret raw color names based on the standard colors defined in Java, such as the following:
* </p>
*
* <ul>
* <li>{@link Color#WHITE}</li>
* <li>{@link Color#LIGHT_GRAY}</li>
* <li>{@link Color#GRAY}</li>
* <li>{@link Color#DARK_GRAY}</li>
* <li>{@link Color#BLACK}</li>
* <li>{@link Color#RED}</li>
* <li>{@link Color#PINK}</li>
* <li>{@link Color#ORANGE}</li>
* <li>{@link Color#YELLOW}</li>
* <li>{@link Color#BLUE}</li>
* <li>{@link Color#CYAN}</li>
* <li>{@link Color#DARK_GRAY}</li>
* <li>{@link Color#GRAY}</li>
* <li>{@link Color#GREEN}</li>
* <li>{@link Color#LIGHT_GRAY}</li>
* <li>{@link Color#MAGENTA}</li>
* <li>{@link Color#CYAN}</li>
* <li>{@link Color#BLUE}</li>
* <li>{@link Color#ORANGE}</li>
* <li>{@link Color#PINK}</li>
* <li>{@link Color#RED}</li>
* <li>{@link Color#WHITE}</li>
* <li>{@link Color#YELLOW}</li>
* </ul>
*
* <small>
* Implementation Notes: We specifically avoid the use of {@link Color#decode(String)}
* for hexadecimal {@link String}s starting with {@link #HEX_COLOR_PREFIX}
* as it does not provide the desired result.
* The {@link Color#decode(String)} method uses {@link Integer#decode(String)}
* under the hood to convert the input to a number, which means input like
* <code>#FFF</code> gets interpreted incorrectly as it's literally converted
* to the number <code>0xFFF</code>, rather than the color, <code>#FFFFFF</code> which
* it is short hand for. It also doesn't work for <code>#FFFFFFFF</code> due to it
* being unable to parse as an {@link Integer}.
* If this is desired, then this method falls back to using {@link Color#decode(String)},
* so for literal hexadecimal values you prefix it with <code>0x</code> instead of
* {@link #HEX_COLOR_PREFIX}.
* </small>
* Implementation Notes: We specifically avoid the use of {@link Color#decode(String)} for hexadecimal
* {@link String}s starting with {@link #HEX_COLOR_PREFIX} as it does not provide the desired result. The
* {@link Color#decode(String)} method uses {@link Integer#decode(String)} under the hood to convert the input to a
* number, which means input like <code>#FFF</code> gets interpreted incorrectly as it's literally converted to the
* number <code>0xFFF</code>, rather than the color, <code>#FFFFFF</code> which it is short hand for. It also
* doesn't work for <code>#FFFFFFFF</code> due to it being unable to parse as an {@link Integer}. If this is
* desired, then this method falls back to using {@link Color#decode(String)}, so for literal hexadecimal values
* you prefix it with <code>0x</code> instead of {@link #HEX_COLOR_PREFIX}.
*
* @param value The String property value to convert.
* @return A {@link Color} which represents the compiled configuration property.
Expand All @@ -132,36 +129,42 @@ protected Class<?> getDefaultType() {
@Override
protected <T> T convertToType(Class<T> type, Object value) throws Throwable {
if (Color.class.isAssignableFrom(type)) {
Objects.requireNonNull(value, "Value can't be null.");
final String stringValue = value.toString();
final String stringValue = toString(value);

switch (stringValue.toLowerCase()) {
case "white":
return type.cast(Color.WHITE);
case "lightgray":
return type.cast(Color.LIGHT_GRAY);
case "gray":
return type.cast(Color.GRAY);
case "darkgray":
return type.cast(Color.DARK_GRAY);
switch (toLowerCase(stringValue)) {
case "black":
return type.cast(Color.BLACK);
case "red":
return type.cast(Color.RED);
case "pink":
return type.cast(Color.PINK);
case "orange":
return type.cast(Color.ORANGE);
case "yellow":
return type.cast(Color.YELLOW);
case "blue":
return type.cast(Color.BLUE);
case "cyan":
return type.cast(Color.CYAN);
case "darkgray":
case "darkgrey":
case "dark_gray":
case "dark_grey":
return type.cast(Color.DARK_GRAY);
case "gray":
case "grey":
return type.cast(Color.GRAY);
case "green":
return type.cast(Color.GREEN);
case "lightgray":
case "lightgrey":
case "light_gray":
case "light_grey":
return type.cast(Color.LIGHT_GRAY);
case "magenta":
return type.cast(Color.MAGENTA);
case "cyan":
return type.cast(Color.CYAN);
case "blue":
return type.cast(Color.BLUE);
case "orange":
return type.cast(Color.ORANGE);
case "pink":
return type.cast(Color.PINK);
case "red":
return type.cast(Color.RED);
case "white":
return type.cast(Color.WHITE);
case "yellow":
return type.cast(Color.YELLOW);
default:
// Do nothing.
}
Expand All @@ -170,18 +173,58 @@ protected <T> T convertToType(Class<T> type, Object value) throws Throwable {
return type.cast(parseWebColor(stringValue));
}

if (stringValue.contains(",")) {
return type.cast(parseToStringColor(stringValue));
}

return type.cast(Color.decode(stringValue));
}

throw conversionException(type, value);
}

/**
* <small>
* Implementation Notes: This must return a {@link Color}; not a {@link Number}
* or {@link String} for {@link Color#decode(String)} as it is not capable of supporting
* a color alpha channel due to the limited range of an {@link Integer}.
* </small>
* Parses the Color based on how the result of the {@link Color#toString()} method.
*
* Accepts the following values:
* <ul>
* <li><code>java.awt.Color[r=255,g=255,b=255]</code></li>
* <li><code>[r=255,g=255,b=255]</code></li>
* <li><code>r=255,g=255,b=255</code></li>
* <li><code>255,255,255</code></li>
* </ul>
*
* @param value A color as represented by {@link Color#toString()}.
* @return The Java friendly {@link Color} this color represents.
* @throws IllegalArgumentException If the input can't be matches by the {@link #JAVA_COLOR_PATTERN}
* or a {@link Color} component specified is over 255.
*/
private Color parseToStringColor(final String value) {
Objects.requireNonNull(value);

Matcher matcher = JAVA_COLOR_PATTERN.matcher(value);

if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid Color String provided. Could not parse.");
}

// Does not require validation as the regular expression already verifies
// each group is a 1-3 digit positive numeric string between 0 and 999.
final int red = Integer.parseInt(matcher.group(1));
final int green = Integer.parseInt(matcher.group(2));
final int blue = Integer.parseInt(matcher.group(3));

if (red > 255 || green > 255 || blue > 255) {
throw new IllegalArgumentException("Color component integers must be between 0 and 255.");
}

return new Color(red, green, blue);
}

/**
* Implementation Notes: This must return a {@link Color}; not a {@link Number} or {@link String} for
* {@link Color#decode(String)} as it is not capable of supporting a color alpha channel due to the
* limited range of an {@link Integer}.
*
* @param value The web friendly hexadecimal {@link String}.
* @return The Java friendly {@link Color} this color represents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@
package org.apache.commons.beanutils2.converters;

import java.awt.Dimension;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* <p>
* Allows converting configuration values to a {@link Dimension}.
* Converting configuration values to a {@link Dimension}.
* This can be used to load settings for default dimensions of {@link java.awt.Component}s
* {@link java.awt.Window}s, or {@link java.awt.Shape}s.
* </p>
Expand Down Expand Up @@ -56,9 +55,8 @@ public DimensionConverter() {
* Constructs a {@link org.apache.commons.beanutils2.Converter} that will return
* the specified default value if a conversion error occurs.
*
* @param defaultValue The default value to be returned
* if the value to be converted is missing or an error
* occurs converting the value.
* @param defaultValue The default value to be returned if the value
* to be converted is missing or an error occurs converting the value.
*/
public DimensionConverter(final Object defaultValue) {
super(defaultValue);
Expand All @@ -84,8 +82,7 @@ protected Class<?> getDefaultType() {
@Override
protected <T> T convertToType(Class<T> type, Object value) throws Throwable {
if (Dimension.class.isAssignableFrom(type)) {
Objects.requireNonNull(value, "Dimensions can not be null.");
final String stringValue = value.toString();
final String stringValue = toString(value);

if (stringValue.isEmpty()) {
throw new IllegalArgumentException("Dimensions can not be empty.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;

/**
* For converting configuration property values to an IP address.
* Converts configuration property values to an IP address.
*
* @since 2.0.0
* @see <a href="https://en.wikipedia.org/wiki/Inet_address">IP Address on Wikipedia</a>
Expand Down Expand Up @@ -70,8 +69,7 @@ protected Class<?> getDefaultType() {
@Override
protected <T> T convertToType(Class<T> type, Object value) throws Throwable {
if (InetAddress.class.isAssignableFrom(type)) {
Objects.requireNonNull(value, "Value can't be null.");
final String stringValue = value.toString();
final String stringValue = toString(value);

try {
return type.cast(InetAddress.getByName(stringValue));
Expand Down
Loading

0 comments on commit 77162f5

Please sign in to comment.