org.apache.wicket.util.string.StringValue.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.util.string.StringValue.java

Source

/*
 * 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.
 * <p>
 * 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.
 * <p>
 * Conversions to a wide variety of types can be found in the to*() methods. A generic conversion
 * can be achieved with to(Class).
 * <P>
 * 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(Locale.Category.FORMAT));
    }

    /**
     * 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> T to(final Class<T> 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> T toOptional(final Class<T> 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 extends Enum<T>> T toEnum(Class<T> 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 extends Enum<T>> T toEnum(final T defaultValue) {
        Args.notNull(defaultValue, "defaultValue");
        return toEnum((Class<T>) 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 extends Enum<T>> T toEnum(Class<T> 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 extends Enum<T>> T toOptionalEnum(Class<T> eClass) throws StringValueConversionException {
        return Strings.isEmpty(text) ? null : toEnum(eClass);
    }

    /**
     * Returns whether the text is null.
     * 
     * @return <code>true</code> if the text is <code>null</code>, <code>false</code> otherwise.
     */
    public boolean isNull() {
        return text == null;
    }

    /**
     * Returns whether the text is null or empty
     * 
     * @return <code>true</code> if the text is <code>null</code> or
     *         <code>.trim().length()==0</code>, <code>false</code> 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;
        }
    }
}