org.apache.wicket.util.string.StringValue Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.util.string;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
import org.apache.wicket.util.io.IClusterable;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Objects;
import org.apache.wicket.util.time.Duration;
import org.apache.wicket.util.time.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Holds an immutable String value and optionally a Locale, with methods to convert to various
* types. Also provides some handy parsing methods and a variety of static factory methods.
*
* Objects can be constructed directly from Strings or by using the valueOf() static factory
* methods. The repeat() static factory methods provide a way of generating a String value that
* repeats a given char or String a number of times.
*
* Conversions to a wide variety of types can be found in the to*() methods. A generic conversion
* can be achieved with to(Class).
*
* The beforeFirst(), afterFirst(), beforeLast() and afterLast() methods are handy for parsing
* things like paths and filenames.
*
* @author Jonathan Locke
*/
public class StringValue implements IClusterable
{
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(StringValue.class);
/** Locale to be used for formatting and parsing. */
private final Locale locale;
/** The underlying string. */
private final String text;
/**
* @param times
* Number of times to repeat character
* @param c
* Character to repeat
* @return Repeated character string
*/
public static StringValue repeat(final int times, final char c)
{
final AppendingStringBuffer buffer = new AppendingStringBuffer(times);
for (int i = 0; i < times; i++)
{
buffer.append(c);
}
return valueOf(buffer);
}
/**
* @param times
* Number of times to repeat string
* @param s
* String to repeat
* @return Repeated character string
*/
public static StringValue repeat(final int times, final String s)
{
final AppendingStringBuffer buffer = new AppendingStringBuffer(times);
for (int i = 0; i < times; i++)
{
buffer.append(s);
}
return valueOf(buffer);
}
/**
* Converts the given input to an instance of StringValue.
*
* @param value
* Double precision value
* @return String value formatted with one place after decimal
*/
public static StringValue valueOf(final double value)
{
return valueOf(value, Locale.getDefault());
}
/**
* Converts the given input to an instance of StringValue.
*
* @param value
* Double precision value
* @param places
* Number of places after decimal
* @param locale
* Locale to be used for formatting
* @return String value formatted with the given number of places after decimal
*/
public static StringValue valueOf(final double value, final int places, final Locale locale)
{
if (Double.isNaN(value) || Double.isInfinite(value))
{
return valueOf("N/A");
}
else
{
final DecimalFormat format = new DecimalFormat("#." + repeat(places, '#'),
new DecimalFormatSymbols(locale));
return valueOf(format.format(value));
}
}
/**
* Converts the given input to an instance of StringValue.
*
* @param value
* Double precision value
* @param locale
* Locale to be used for formatting
* @return String value formatted with one place after decimal
*/
public static StringValue valueOf(final double value, final Locale locale)
{
return valueOf(value, 1, locale);
}
/**
* Converts the given input to an instance of StringValue.
*
* @param object
* An object
* @return String value for object
*/
public static StringValue valueOf(final Object object)
{
return valueOf(Strings.toString(object));
}
/**
* Converts the given input to an instance of StringValue.
*
* @param object
* An object
* @param locale
* Locale to be used for formatting
* @return String value for object
*/
public static StringValue valueOf(final Object object, final Locale locale)
{
return valueOf(Strings.toString(object), locale);
}
/**
* Converts the given input to an instance of StringValue.
*
* @param string
* A string
* @return String value for string
*/
public static StringValue valueOf(final String string)
{
return new StringValue(string);
}
/**
* Converts the given input to an instance of StringValue.
*
* @param string
* A string
* @param locale
* Locale to be used for formatting
* @return String value for string
*/
public static StringValue valueOf(final String string, final Locale locale)
{
return new StringValue(string, locale);
}
/**
* Converts the given input to an instance of StringValue.
*
* @param buffer
* A string buffer
* @return String value
*/
public static StringValue valueOf(final AppendingStringBuffer buffer)
{
return valueOf(buffer.toString());
}
/**
* Private constructor to force use of static factory methods.
*
* @param text
* The text for this string value
*/
protected StringValue(final String text)
{
this(text , Locale.getDefault());
}
/**
* Private constructor to force use of static factory methods.
*
* @param text
* The text for this string value
* @param locale
* the locale for formatting and parsing
*/
protected StringValue(final String text, final Locale locale)
{
this.text = text;
this.locale = locale;
}
/**
* Gets the substring after the first occurrence given char.
*
* @param c
* char to scan for
* @return the substring
*/
public final String afterFirst(final char c)
{
return Strings.afterFirst(text, c);
}
/**
* Gets the substring after the last occurrence given char.
*
* @param c
* char to scan for
* @return the substring
*/
public final String afterLast(final char c)
{
return Strings.afterLast(text, c);
}
/**
* Gets the substring before the first occurrence given char.
*
* @param c
* char to scan for
* @return the substring
*/
public final String beforeFirst(final char c)
{
return Strings.beforeFirst(text, c);
}
/**
* Gets the substring before the last occurrence given char.
*
* @param c
* char to scan for
* @return the substring
*/
public final String beforeLast(final char c)
{
return Strings.afterLast(text, c);
}
/**
* Replaces on this text.
*
* @param searchFor
* What to search for
* @param replaceWith
* What to replace with
* @return This string value with searchFor replaces with replaceWith
*/
public final CharSequence replaceAll(final CharSequence searchFor,
final CharSequence replaceWith)
{
return Strings.replaceAll(text, searchFor, replaceWith);
}
/**
* Converts this StringValue to a given type.
*
* @param type
* The type to convert to
* @return The converted value
* @throws StringValueConversionException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public final T to(final Class type) throws StringValueConversionException
{
if (type == null)
{
return null;
}
if (type == String.class)
{
return (T)toString();
}
if ((type == Integer.TYPE) || (type == Integer.class))
{
return (T)toInteger();
}
if ((type == Long.TYPE) || (type == Long.class))
{
return (T)toLongObject();
}
if ((type == Boolean.TYPE) || (type == Boolean.class))
{
return (T)toBooleanObject();
}
if ((type == Double.TYPE) || (type == Double.class))
{
return (T)toDoubleObject();
}
if ((type == Character.TYPE) || (type == Character.class))
{
return (T)toCharacter();
}
if (type == Time.class)
{
return (T)toTime();
}
if (type == Duration.class)
{
return (T)toDuration();
}
if (type.isEnum())
{
return (T)toEnum((Class)type);
}
throw new StringValueConversionException("Cannot convert '" + toString() + "'to type "
+ type);
}
/**
* Converts this StringValue to a given type or {@code null} if the value is empty.
*
* @param type
* The type to convert to
* @return The converted value
* @throws StringValueConversionException
*/
public final T toOptional(final Class type) throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : to(type);
}
/**
* Convert this text to a boolean.
*
* @return This string value as a boolean
* @throws StringValueConversionException
*/
public final boolean toBoolean() throws StringValueConversionException
{
return Strings.isTrue(text);
}
/**
* Convert to boolean, returning default value if text is inconvertible.
*
* @param defaultValue
* the default value
* @return the converted text as a boolean or the default value if text is empty or
* inconvertible
* @see Strings#isTrue(String)
*/
public final boolean toBoolean(final boolean defaultValue)
{
if (text != null)
{
try
{
return toBoolean();
}
catch (StringValueConversionException x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format(
"An error occurred while converting '%s' to a boolean: %s", text,
x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert this text to a boolean.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final Boolean toBooleanObject() throws StringValueConversionException
{
return Strings.toBoolean(text);
}
/**
* Convert this text to a char.
*
* @return This string value as a character
* @throws StringValueConversionException
*/
public final char toChar() throws StringValueConversionException
{
return Strings.toChar(text);
}
/**
* Convert to character, returning default value if text is inconvertible.
*
* @param defaultValue
* the default value
* @return the converted text as a primitive char or the default value if text is not a single
* character
*/
public final char toChar(final char defaultValue)
{
if (text != null)
{
try
{
return toChar();
}
catch (StringValueConversionException x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format(
"An error occurred while converting '%s' to a character: %s", text,
x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert this text to a Character.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final Character toCharacter() throws StringValueConversionException
{
return toChar();
}
/**
* Convert this text to a double.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final double toDouble() throws StringValueConversionException
{
try
{
return NumberFormat.getNumberInstance(locale).parse(text).doubleValue();
}
catch (ParseException e)
{
throw new StringValueConversionException("Unable to convert '" + text
+ "' to a double value", e);
}
}
/**
* Convert to double, returning default value if text is inconvertible.
*
* @param defaultValue
* the default value
* @return the converted text as a double or the default value if text is empty or inconvertible
*/
public final double toDouble(final double defaultValue)
{
if (text != null)
{
try
{
return toDouble();
}
catch (Exception x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format(
"An error occurred while converting '%s' to a double: %s", text,
x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert this text to a Double.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final Double toDoubleObject() throws StringValueConversionException
{
return toDouble();
}
/**
* Convert this text to a Duration instance.
*
* @return Converted text
* @throws StringValueConversionException
* @see Duration#valueOf(String, java.util.Locale)
*/
public final Duration toDuration() throws StringValueConversionException
{
return Duration.valueOf(text, locale);
}
/**
* Convert to duration, returning default value if text is inconvertible.
*
* @param defaultValue
* the default value
* @return the converted text as a duration or the default value if text is empty or
* inconvertible
* @see Duration#valueOf(String, java.util.Locale)
*/
public final Duration toDuration(final Duration defaultValue)
{
if (text != null)
{
try
{
return toDuration();
}
catch (Exception x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format(
"An error occurred while converting '%s' to a Duration: %s", text,
x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert this text to an int.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final int toInt() throws StringValueConversionException
{
try
{
return Integer.parseInt(text);
}
catch (NumberFormatException e)
{
throw new StringValueConversionException("Unable to convert '" + text
+ "' to an int value", e);
}
}
/**
* Convert to integer, returning default value if text is inconvertible.
*
* @param defaultValue
* the default value
* @return the converted text as an integer or the default value if text is not an integer
*/
public final int toInt(final int defaultValue)
{
if (text != null)
{
try
{
return toInt();
}
catch (StringValueConversionException x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format(
"An error occurred while converting '%s' to an integer: %s", text,
x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert this text to an Integer.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final Integer toInteger() throws StringValueConversionException
{
try
{
return new Integer(text);
}
catch (NumberFormatException e)
{
throw new StringValueConversionException("Unable to convert '" + text
+ "' to an Integer value", e);
}
}
/**
* Convert this text to a long.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final long toLong() throws StringValueConversionException
{
try
{
return Long.parseLong(text);
}
catch (NumberFormatException e)
{
throw new StringValueConversionException("Unable to convert '" + text
+ "' to a long value", e);
}
}
/**
* Convert to long integer, returning default value if text is inconvertible.
*
* @param defaultValue
* the default value
* @return the converted text as a long integer or the default value if text is empty or
* inconvertible
*/
public final long toLong(final long defaultValue)
{
if (text != null)
{
try
{
return toLong();
}
catch (StringValueConversionException x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format(
"An error occurred while converting '%s' to a long: %s", text,
x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert this text to a Long.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final Long toLongObject() throws StringValueConversionException
{
try
{
return new Long(text);
}
catch (NumberFormatException e)
{
throw new StringValueConversionException("Unable to convert '" + text
+ "' to a Long value", e);
}
}
/**
* Convert to object types, returning null if text is null or empty.
*
* @return converted
* @throws StringValueConversionException
*/
public final Boolean toOptionalBoolean() throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toBooleanObject();
}
/**
* Convert to object types, returning null if text is null or empty.
*
* @return converted
* @throws StringValueConversionException
*/
public final Character toOptionalCharacter() throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toCharacter();
}
/**
* Convert to object types, returning null if text is null or empty.
*
* @return converted
* @throws StringValueConversionException
*/
public final Double toOptionalDouble() throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toDoubleObject();
}
/**
* Convert to object types, returning null if text is null or empty.
*
* @return converted
* @throws StringValueConversionException
*/
public final Duration toOptionalDuration() throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toDuration();
}
/**
* Convert to object types, returning null if text is null or empty.
*
* @return converted
* @throws StringValueConversionException
*/
public final Integer toOptionalInteger() throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toInteger();
}
/**
* Convert to object types, returning null if text is null or empty.
*
* @return converted
* @throws StringValueConversionException
*/
public final Long toOptionalLong() throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toLongObject();
}
/**
* Convert to object types, returning null if text is null.
*
* @return converted
*/
public final String toOptionalString()
{
return text;
}
/**
* Convert to object types, returning null if text is null or empty.
*
* @return converted
* @throws StringValueConversionException
*/
public final Time toOptionalTime() throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toTime();
}
/**
* @return The string value
*/
@Override
public final String toString()
{
return text;
}
/**
* Convert to primitive types, returning default value if text is null.
*
* @param defaultValue
* the default value to return of text is null
* @return the converted text as a primitive or the default if text is null
*/
public final String toString(final String defaultValue)
{
return (text == null) ? defaultValue : text;
}
/**
* Convert this text to a time instance.
*
* @return Converted text
* @throws StringValueConversionException
*/
public final Time toTime() throws StringValueConversionException
{
try
{
return Time.valueOf(text);
}
catch (ParseException e)
{
throw new StringValueConversionException("Unable to convert '" + text
+ "' to a Time value", e);
}
}
/**
* Convert to time, returning default value if text is inconvertible.
*
* @param defaultValue
* the default value
* @return the converted text as a time or the default value if text is inconvertible.
*/
public final Time toTime(final Time defaultValue)
{
if (text != null)
{
try
{
return toTime();
}
catch (StringValueConversionException x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format(
"An error occurred while converting '%s' to a Time: %s", text,
x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert this text to an enum.
*
* @param eClass
* enum type
* @return The value as an enum
* @throws StringValueConversionException
*/
public final > T toEnum(Class eClass)
throws StringValueConversionException
{
return Strings.toEnum(text, eClass);
}
/**
* Convert this text to an enum.
*
* @param defaultValue
* This will be returned if there is an error converting the value
* @return The value as an enum
*/
@SuppressWarnings("unchecked")
public final > T toEnum(final T defaultValue)
{
Args.notNull(defaultValue, "defaultValue");
return toEnum((Class)defaultValue.getClass(), defaultValue);
}
/**
* Convert this text to an enum.
*
* @param eClass
* enum type
* @param defaultValue
* This will be returned if there is an error converting the value
* @return The value as an enum
*/
public final > T toEnum(Class eClass, final T defaultValue)
{
if (text != null)
{
try
{
return toEnum(eClass);
}
catch (StringValueConversionException x)
{
if (LOG.isDebugEnabled())
{
LOG.debug(String.format("An error occurred while converting '%s' to a %s: %s",
text, eClass, x.getMessage()), x);
}
}
}
return defaultValue;
}
/**
* Convert to enum, returning null if text is null or empty.
*
* @param eClass
* enum type
*
* @return converted
* @throws StringValueConversionException
*/
public final > T toOptionalEnum(Class eClass)
throws StringValueConversionException
{
return Strings.isEmpty(text) ? null : toEnum(eClass);
}
/**
* Returns whether the text is null.
*
* @return true
if the text is null
, false
otherwise.
*/
public boolean isNull()
{
return text == null;
}
/**
* Returns whether the text is null or empty
*
* @return true
if the text is null
or
* .trim().length()==0
, false
otherwise.
*/
public boolean isEmpty()
{
return Strings.isEmpty(text);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode()
{
return Objects.hashCode(locale, text);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj)
{
if (obj instanceof StringValue)
{
StringValue stringValue = (StringValue)obj;
return Objects.isEqual(text, stringValue.text) && locale.equals(stringValue.locale);
}
else
{
return false;
}
}
}