Java tutorial
/* * Copyright (c) 2002-2015 JGoodies Software GmbH. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of JGoodies Software GmbH nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jgoodies.binding.value; import static com.jgoodies.common.base.Preconditions.checkArgument; import static com.jgoodies.common.base.Preconditions.checkNotNull; import java.text.Format; import java.text.ParseException; /** * A factory that vends ValueModels that convert types, for example * Dates to Strings. More formally, a converting ValueModel <i>VM1</i> * converts the type <i>T2</i> of an object being held as a value in * one ValueModel <i>VM2</i> into another type <i>T1</i>. * When reading a value from VM1, instances of T2 are read from VM2 * and are converted to T1. When storing a new value to VM1, * the type converter will perform the inverse conversion and * will convert an instance of T1 to T2.<p> * * Type converters should be used judiciously. To bind non-Strings * to a text UI component * you should favor to use a {@link javax.swing.JFormattedTextField}. * They provide a powerful means to convert Strings to objects * and handle many cases that arise around invalid input. * * @author Karsten Lentzsch * @version $Revision: 1.12 $ * * @see ValueModel * @see Format * @see javax.swing.JFormattedTextField */ public final class ConverterFactory { private ConverterFactory() { // Overrides default constructor; prevents instantiation. } // Factory Methods ******************************************************** /** * Creates and returns a ValueModel that negates Booleans and leaves * <code>null</code> unchanged. * * @param booleanSource a Boolean ValueModel * @return a ValueModel that inverts Booleans * * @throws NullPointerException if the {@code booleanSource} is {@code null} */ public static ValueModel createBooleanNegator(ValueModel booleanSource) { return new ConverterValueModel(booleanSource, new BooleanNegator()); } /** * Creates and returns a ValueModel that converts Booleans * to the associated of the two specified strings, and vice versa. * {@code null} is mapped to an empty string. * Ignores cases when setting a text. * * @param booleanSubject a Boolean ValueModel * @param trueText the text associated with {@code Boolean.TRUE} * @param falseText the text associated with {@code Boolean.FALSE} * * @return a ValueModel that converts boolean to the associated text * * @throws NullPointerException if {@code booleanSource}, * {@code trueText}, {@code falseText} or {@code nullText} is {@code null} * @throws IllegalArgumentException if {@code trueText} equals {@code falseText} */ public static ValueModel createBooleanToStringConverter(ValueModel booleanSubject, String trueText, String falseText) { return createBooleanToStringConverter(booleanSubject, trueText, falseText, ""); } /** * Creates and returns a ValueModel that converts Booleans * to the associated of the two specified strings, and vice versa. * {@code null} is mapped to the specified text. * Ignores cases when setting a text. * * @param booleanSource a Boolean ValueModel * @param trueText the text associated with {@code Boolean.TRUE} * @param falseText the text associated with {@code Boolean.FALSE} * @param nullText the text associated with {@code null} * * @return a ValueModel that converts boolean to the associated text * * @throws NullPointerException if {@code booleanSource}, * {@code trueText}, {@code falseText} or {@code nullText} is {@code null} * @throws IllegalArgumentException if {@code trueText} equals {@code falseText} */ public static ValueModel createBooleanToStringConverter(ValueModel booleanSource, String trueText, String falseText, String nullText) { return new ConverterValueModel(booleanSource, new BooleanToStringConverter(trueText, falseText, nullText)); } /** * Creates and returns a ValueModel that converts Doubles using the * specified multiplier.<p> * * <strong>Examples:</strong> * multiplier=100, Double(1.23) -> Double(123), * multiplier=1000, Double(1.23) -> Double(1230) * * @param doubleSource a Double ValueModel * @param multiplier the multiplier used for the conversion * * @return a ValueModel that converts Doubles using the specified multiplier * * @throws NullPointerException if {@code doubleSource} is {@code null} * * @since 1.0.2 */ public static ValueModel createDoubleConverter(ValueModel doubleSource, double multiplier) { return new ConverterValueModel(doubleSource, new DoubleConverter(multiplier)); } /** * Creates and returns a ValueModel that converts Doubles to Integer, * and vice versa. * * @param doubleSource a Double ValueModel * * @return a ValueModel that converts Doubles to Integer * * @throws NullPointerException if {@code doubleSource} is {@code null} */ public static ValueModel createDoubleToIntegerConverter(ValueModel doubleSource) { return createDoubleToIntegerConverter(doubleSource, 1); } /** * Creates and returns a ValueModel that converts Doubles to Integer, * and vice versa. The multiplier can be used to convert Doubles * to percent, permill, etc. For a percentage, set the multiplier to be 100, * for a permill, set the multiplier to be 1000.<p> * * <strong>Examples:</strong> * multiplier=100, Double(1.23) -> Integer(123), * multiplier=1000, Double(1.23) -> Integer(1230) * * @param doubleSource a Double ValueModel * @param multiplier the multiplier used to convert the Double to Integer * * @return a ValueModel that converts Doubles to Integer * * @throws NullPointerException if {@code doubleSource} is {@code null} */ public static ValueModel createDoubleToIntegerConverter(ValueModel doubleSource, int multiplier) { return new ConverterValueModel(doubleSource, new DoubleToIntegerConverter(multiplier)); } /** * Creates and returns a ValueModel that converts Floats using the * specified multiplier.<p> * * <strong>Examples:</strong> * multiplier=100, Float(1.23) -> Float(123), * multiplier=1000, Float(1.23) -> Float(1230) * * @param floatSource a Float ValueModel * @param multiplier the multiplier used for the conversion * * @return a ValueModel that converts Float using the specified multiplier * * @throws NullPointerException if {@code floatSource} is {@code null} * * @since 1.0.2 */ public static ValueModel createFloatConverter(ValueModel floatSource, float multiplier) { return new ConverterValueModel(floatSource, new FloatConverter(multiplier)); } /** * Creates and returns a ValueModel that converts Floats to Integer, * and vice versa. * s * @param floatSource a Float ValueModel * * @return a ValueModel that converts Floats to Integer * * @throws NullPointerException if {@code floatSource} is {@code null} */ public static ValueModel createFloatToIntegerConverter(ValueModel floatSource) { return createFloatToIntegerConverter(floatSource, 1); } /** * Creates and returns a ValueModel that converts Floats to Integer, * and vice versa. The multiplier can be used to convert Floats * to percent, permill, etc. For a percentage, set the multiplier to be 100, * for a permill, set the multiplier to be 1000. * * @param floatSource a Float ValueModel * @param multiplier the multiplier used to convert the Float to Integer * * @return a ValueModel that converts Floats to Integer * * @throws NullPointerException if {@code floatSource} is {@code null} */ public static ValueModel createFloatToIntegerConverter(ValueModel floatSource, int multiplier) { return new ConverterValueModel(floatSource, new FloatToIntegerConverter(multiplier)); } /** * Creates and returns a ValueModel that converts Integers using the * specified multiplier.<p> * * Examples: multiplier=100, Integer(3) -> Integer(300), * multiplier=1000, Integer(3) -> Integer(3000) * * @param integerSource a Integer ValueModel * @param multiplier the multiplier used for the conversion * * @return a ValueModel that converts Integers using the specified multiplier * * @throws NullPointerException if {@code integerSource} is {@code null} * * @since 1.0.2 */ public static ValueModel createIntegerConverter(ValueModel integerSource, double multiplier) { return new ConverterValueModel(integerSource, new IntegerConverter(multiplier)); } /** * Creates and returns a ValueModel that converts Long using the * specified multiplier.<p> * * Examples: multiplier=100, Long(3) -> Long(300), * multiplier=1000, Long(3) -> Long(3000) * * @param longSource a Long ValueModel * @param multiplier the multiplier used for the conversion * * @return a ValueModel that converts Longs using the specified multiplier * * @throws NullPointerException if {@code longSource} is {@code null} * * @since 1.0.2 */ public static ValueModel createLongConverter(ValueModel longSource, double multiplier) { return new ConverterValueModel(longSource, new LongConverter(multiplier)); } /** * Creates and returns a ValueModel that converts Longs to Integer * and vice versa.<p> * * <strong>Constraints:</strong> The subject is of type {@code Long}, * values written to the converter are of type {@code Integer}. * * @param longSubject a Long ValueModel * @return a ValueModel that converts Longs to Integer * * @throws NullPointerException if the subject is {@code null} */ public static ValueModel createLongToIntegerConverter(ValueModel longSubject) { return createLongToIntegerConverter(longSubject, 1); } /** * Creates and returns a ValueModel that converts Longs to Integer * and vice versa.<p> * * <strong>Constraints:</strong> The subject is of type {@code Long}, * values written to the converter are of type {@code Integer}. * * @param longSource a Long ValueModel * @param multiplier used to multiply the Long when converting to Integer * @return a ValueModel that converts Longs to Integer * * @throws NullPointerException if {@code longSource} is {@code null} */ public static ValueModel createLongToIntegerConverter(ValueModel longSource, int multiplier) { return new ConverterValueModel(longSource, new LongToIntegerConverter(multiplier)); } /** * Creates and returns a ValueModel that converts objects to Strings * and vice versa. The conversion is performed by a {@code Format}.<p> * * <strong>Constraints:</strong> The source is of type {@code Object}; * it must be formattable and parsable via the given {@code Format}. * * @param source the underlying ValueModel. * @param format the {@code Format} used to format and parse * * @return a ValueModel that converts objects to Strings and vice versa * * @throws NullPointerException if {@code source} or {@code format} * is {@code null} */ public static ValueModel createStringConverter(ValueModel source, Format format) { return new ConverterValueModel(source, new StringConverter(format)); } // Converter Implementations ********************************************** /** * Negates Booleans leaving null unchanged. Maps Boolean.TRUE * to Boolean.FALSE, Boolean.FALSE to Boolean.TRUE, and null to null. */ public static final class BooleanNegator implements BindingConverter<Boolean, Boolean> { /** * Returns the negated source Boolean leaving null unchanged. */ @Override public Boolean targetValue(Boolean sourceValue) { return negate(sourceValue); } /** * Returns the negated target Boolean leaving null unchanged. */ @Override public Boolean sourceValue(Boolean targetValue) { return negate(targetValue); } /** * Negates Booleans leaving null unchanged. * Maps Boolean.TRUE to Boolean.FALSE , * Boolean.FALSE to Boolean.TRUE, and null to null. * * @param value the value to invert * @return the inverted Boolean value, or null if value is null */ private static Boolean negate(Boolean value) { return value == null ? null : Boolean.valueOf(!value.booleanValue()); } } /** * Converts Booleans to Strings and vice-versa using given texts for * true, false, and null. */ public static final class BooleanToStringConverter implements BindingConverter<Boolean, String> { private final String trueText; private final String falseText; private final String nullText; BooleanToStringConverter(String trueText, String falseText, String nullText) { this.trueText = checkNotNull(trueText, "The trueText must not be null."); this.falseText = checkNotNull(falseText, "The falseText must not be null."); this.nullText = checkNotNull(nullText, "The nullText must not be null."); checkArgument(!trueText.equals(falseText), "The trueText and falseText must be different."); } /** * Returns the source value's associated text representation. */ @Override public String targetValue(Boolean sourceValue) { if (sourceValue == null) { return nullText; } return sourceValue.booleanValue() ? trueText : falseText; } /** * Converts the given String and sets the associated Boolean as * the subject's new value. In case the new value equals neither * this class' trueText, nor the falseText, nor the nullText, * an IllegalArgumentException is thrown. * * @throws IllegalArgumentException if the new value does neither match * the trueText nor the falseText nor the nullText */ @Override public Boolean sourceValue(String targetValue) { if (trueText.equalsIgnoreCase(targetValue)) { return Boolean.TRUE; } else if (falseText.equalsIgnoreCase(targetValue)) { return Boolean.FALSE; } else if (nullText.equalsIgnoreCase(targetValue)) { return null; } else { throw new IllegalArgumentException( "The new value must be one of: " + trueText + '/' + falseText + '/' + nullText); } } } /** * Converts Doubles using a given multiplier. */ public static final class DoubleConverter implements BindingConverter<Double, Double> { private final double multiplier; DoubleConverter(double multiplier) { this.multiplier = multiplier; } /** * Converts the subject's value and returns a * corresponding {@code Double} using the multiplier. * * @param sourceValue the subject's value * @return the converted subjectValue */ @Override public Double targetValue(Double sourceValue) { return Double.valueOf(sourceValue.doubleValue() * multiplier); } /** * Converts a {@code Double} using the multiplier * and sets it as new value. * * @param targetValue the {@code Double} object that shall be converted */ @Override public Double sourceValue(Double targetValue) { return Double.valueOf(targetValue.doubleValue() / multiplier); } } /** * Converts Doubles to Integers and vice-versa. */ public static final class DoubleToIntegerConverter implements BindingConverter<Double, Integer> { private final int multiplier; DoubleToIntegerConverter(int multiplier) { this.multiplier = multiplier; } /** * Converts the source value and returns a * corresponding {@code Integer} value using the multiplier. * * @param sourceValue the subject's value * @return the converted subjectValue */ @Override public Integer targetValue(Double sourceValue) { double doubleValue = sourceValue.doubleValue(); if (multiplier != 1) { doubleValue *= multiplier; } return Integer.valueOf((int) Math.round(doubleValue)); } /** * Converts a {@code Double} using the multiplier * and sets it as new value. * * @param targetValue the {@code Integer} object that shall * be converted */ @Override public Double sourceValue(Integer targetValue) { double doubleValue = targetValue.doubleValue(); if (multiplier != 1) { doubleValue /= multiplier; } return Double.valueOf(doubleValue); } } /** * Converts Floats using a given multiplier. */ public static final class FloatConverter implements BindingConverter<Float, Float> { private final float multiplier; FloatConverter(float multiplier) { this.multiplier = multiplier; } /** * Converts the subject's value and returns a * corresponding {@code Float} using the multiplier. * * @param sourceValue the subject's value * @return the converted subjectValue */ @Override public Float targetValue(Float sourceValue) { float floatValue = sourceValue.floatValue(); return Float.valueOf(floatValue * multiplier); } /** * Converts a {@code Float} using the multiplier * and sets it as new value. * * @param targetValue the {@code Float} object that shall be converted */ @Override public Float sourceValue(Float targetValue) { float floatValue = targetValue.floatValue(); return Float.valueOf(floatValue / multiplier); } } /** * Converts Floats to Integers and vice-versa. */ public static final class FloatToIntegerConverter implements BindingConverter<Float, Integer> { private final int multiplier; FloatToIntegerConverter(int multiplier) { this.multiplier = multiplier; } /** * @return an Integer that is the Float source value * multiplied with this converter's multiplier */ @Override public Integer targetValue(Float sourceValue) { float floatValue = sourceValue.floatValue(); if (multiplier != 1) { floatValue *= multiplier; } return Integer.valueOf(Math.round(floatValue)); } /** * Converts a {@code Float} using the multiplier. */ @Override public Float sourceValue(Integer sourceValue) { float floatValue = sourceValue.floatValue(); if (multiplier != 1) { floatValue /= multiplier; } return Float.valueOf(floatValue); } } /** * Converts Longs using a given multiplier. */ public static final class LongConverter implements BindingConverter<Long, Long> { private final double multiplier; LongConverter(double multiplier) { this.multiplier = multiplier; } /** * Converts the subject's value and returns a * corresponding {@code Long} using the multiplier. */ @Override public Long targetValue(Long sourceValue) { double doubleValue = sourceValue.doubleValue(); return Long.valueOf((long) (doubleValue * multiplier)); } /** * Converts a {@code Long} using the multiplier * and sets it as new value. */ @Override public Long sourceValue(Long targetValue) { double doubleValue = targetValue.doubleValue(); return Long.valueOf((long) (doubleValue / multiplier)); } } /** * Converts Integers using a given multiplier. */ public static final class IntegerConverter implements BindingConverter<Integer, Integer> { private final double multiplier; IntegerConverter(double multiplier) { this.multiplier = multiplier; } /** * Returns an Integer that is the source Integer * multiplied with this converters' multiplier. */ @Override public Integer targetValue(Integer sourceValue) { double doubleValue = sourceValue.doubleValue(); return Integer.valueOf((int) (doubleValue * multiplier)); } /** * Returns an Integer that is the target Integer * divided by this converters' multiplier. */ @Override public Integer sourceValue(Integer targetValue) { double doubleValue = targetValue.doubleValue(); return Integer.valueOf((int) (doubleValue / multiplier)); } } /** * Converts Longs to Integers and vice-versa. */ public static final class LongToIntegerConverter implements BindingConverter<Long, Integer> { private final int multiplier; LongToIntegerConverter(int multiplier) { this.multiplier = multiplier; } /** * Returns an Integer that is the source Long * multiplied with this converter's multiplier. */ @Override public Integer targetValue(Long sourceValue) { int intValue = sourceValue.intValue(); if (multiplier != 1) { intValue *= multiplier; } return Integer.valueOf(intValue); } /** * Returns a Long that is the target Integer * divided by this converter's multiplier. * * @param targetValue the {@code Integer} object that represents * the percent value */ @Override public Long sourceValue(Integer targetValue) { long longValue = targetValue.longValue(); if (multiplier != 1) { longValue /= multiplier; } return Long.valueOf(longValue); } } /** * Converts Values to Strings and vice-versa using a given Format. */ public static final class StringConverter implements BindingConverter<Object, String> { /** * Holds the {@code Format} used to format and parse. */ private final Format format; // Instance Creation ************************************************** /** * Constructs a {@code StringConverter} using the specified {@code Format}. * * @param format the {@code Format} used to format and parse * @throws NullPointerException if the format is {@code null}. */ StringConverter(Format format) { this.format = checkNotNull(format, "The format must not be null."); } // Implementing Abstract Behavior ************************************* /** * Formats the source value and returns a String representation. * * @param sourceValue the source value * @return the formatted sourceValue */ @Override public String targetValue(Object sourceValue) { return format.format(sourceValue); } /** * Parses the given String encoding and returns the parsed object. * {@code ParseException}s are re-thrown as IllegalArgumentException. * * @param targetValue the value to be converted and set as new subject value */ @Override public Object sourceValue(String targetValue) { try { return format.parseObject(targetValue); } catch (ParseException e) { throw new IllegalArgumentException("Cannot parse the target value " + targetValue, e); } } } }