| Type.java |
/*
* $Id: Type.java,v 1.35 2010/11/18 20:35:05 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.common.types;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.Utils;
/**
* Value type. This is an abstract base class for type classes. Each type
* defines a name and it defines what values are considered valid and what
* values are considered invalid.
*
* @version $Revision: 1.35 $ $Date: 2010/11/18 20:35:05 $
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*
* @since XINS 1.0.0
*/
public abstract class Type {
/**
* The name of this type. Never <code>null</code>.
*/
private final String _name;
/**
* The class for all values. Never <code>null</code>.
*/
private final Class _valueClass;
/**
* Creates a new <code>Type</code> instance. Both the name of the type and
* the value class must be specified. The value class in the class (or
* interface) that values for this type should be instances of. If
* <code>null</code> is specified as the value class, then that is the same
* as specifying <code>Object.class</code> as the value class.
*
* @param name
* the name of the type, not <code>null</code>.
*
* @param valueClass
* the class or interface that values should be instances of, or
* <code>null</code> if any class is valid.
*
* @throws IllegalArgumentException
* if <code>name == null</code>.
*/
protected Type(String name, Class valueClass)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("name", name);
// Store the arguments
_name = name;
_valueClass = valueClass == null ? Object.class : valueClass;
}
/**
* Retrieves the name of this type.
*
* @return
* the name of this type, never <code>null</code>.
*/
public final String getName() {
return _name;
}
/**
* Retrieves the description of this type.
*
* @return
* the description of this type, never <code>null</code>.
*
* @since XINS 2.1.
*/
public String getDescription() {
return _name + " type.";
}
/**
* Retrieves the value class. All values for this type are instances of
* this class.
*
* @return
* the class values should be instances of, never <code>null</code>.
*/
public final Class getValueClass() {
return _valueClass;
}
/**
* Checks if the specified value is valid for this type and throws an
* exception if not.
*
* <p />Note that <code>null</code> values are <em>always</em> considered
* to be valid.
*
* @param value
* the value that should be checked for validity, can be
* <code>null</code>.
*
* @throws TypeValueException
* if the specified value is invalid for this type.
*/
public final void checkValue(String value)
throws TypeValueException {
if (value == null) {
return;
} else if (! isValidValueImpl(value)) {
throw new TypeValueException(this, value);
}
}
/**
* Determines if the specified <code>String</code> value is considered
* valid for this type (wrapper method).
*
* <p />This method first checks if <code>string == null</code> and if it
* is not, then it returns the result of a call to
* {@link #isValidValueImpl(String)}. Note that <code>null</code> values
* are <em>always</em> considered to be valid.
*
* @param string
* the value that should be checked for validity, can be
* <code>null</code>.
*
* @return
* <code>true</code> if and only if the specified value is valid,
* <code>false</code> otherwise.
*/
public final boolean isValidValue(String string) {
if (string == null || string.length() == 0) {
return true;
} else {
return isValidValueImpl(string);
}
}
/**
* Determines if the specified <code>String</code> value is considered
* valid for this type (implementation method).
*
* <p>This method is called from {@link #isValidValue(String)}. When
* called from that method, it is guaranteed that the argument is not
* <code>null</code>.
*
* <p />The implementation of this method in class {@link Type} returns
* <code>true</code>.
*
* @param string
* the <code>String</code> value that should be checked for validity,
* never <code>null</code>.
*
* @return
* <code>true</code> if and only if the specified <code>String</code>
* value is valid, <code>false</code> otherwise.
*/
protected boolean isValidValueImpl(String string) {
return true;
}
/**
* Converts from a <code>String</code> to an instance of the value class
* for this type (wrapper method).
*
* <p />This method returns <code>null</code> if <code>string ==
* null</code>. Otherwise it first calls {@link #isValidValueImpl(String)}
* to check if the value is in principle valid. If it is, then
* {@link #fromStringImpl(String)} is called. If the result of that call is
* <em>not</em> an instance of the value class, then an
* {@link Error} is thrown. Notice that this error is also thrown
* if {@link #fromStringImpl(String)} returns <code>null</code>.
*
* @param string
* the string to convert to an instance of the value class, can be
* <code>null</code>.
*
* @return
* an instance of the value class, will be <code>null</code> if and only
* if <code>string == null</code>.
*
* @throws TypeValueException
* if the specified string does not represent a valid value for this
* type.
*/
public final Object fromString(String string)
throws TypeValueException {
if (string == null) {
return null;
}
Object value = fromStringImpl(string);
// TODO: Create a unit test to check that a null returned from
// fromStringImpl(String) is actually causing a
// ProgrammingException to be thrown
if (!_valueClass.isInstance(value)) {
String detail = "The value returned is an instance of class " + value.getClass().getName()
+ " instead of an instance of " + _valueClass.getName() + '.';
throw Utils.logProgrammingError(detail);
}
return value;
}
/**
* Converts from a <code>String</code> to an instance of the value class
* for this type (implementation method).
*
* <p>This method is not required to check the validity of the specified
* value (since {@link #isValidValueImpl(String)} should have been called
* before) but if it does, then it may throw a {@link TypeValueException}.
*
* @param string
* the string to convert to an instance of the value class, guaranteed
* to be not <code>null</code> and guaranteed to have been passed to
* {@link #isValidValueImpl(String)} without getting an exception.
*
* @return
* an instance of the value class, cannot be <code>null</code>.
*
* @throws TypeValueException
* if <code>string</code> is considered to be an invalid value for this
* type.
*/
protected abstract Object fromStringImpl(String string)
throws TypeValueException;
/**
* Generates a string representation of the specified value for this type.
* The specified value must be an instance of the value class for this type
* (see {@link #getValueClass()}). Also, it may have to fall within a
* certain range of valid values, depending on the type.
*
* <p>The default implementation of this method in class {@link Type} does
* the following:
*
* <ul>
* <li>if <code>value == null</code> then it throws an
* {@link IllegalArgumentException};
* <li>if <code>getValueClass().isInstance(value) == false</code> then
* it throws a {@link ClassCastException};
* <li>otherwise it returns
* <code>value.</code>{@link Object#toString()}.
* </ul>
*
* @param value
* the value, cannot be <code>null</code>.
*
* @return
* the string representation of the specified value for this type,
* cannot be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>value == null</code>.
*
* @throws ClassCastException
* if <code>getValueClass().isInstance(value) == false</code>.
*
* @throws TypeValueException
* if the specified value is not in the allowed range.
*/
public String toString(Object value)
throws IllegalArgumentException, ClassCastException, TypeValueException {
// Check preconditions
MandatoryArgumentChecker.check("value", value);
if (!getValueClass().isInstance(value)) {
throw new ClassCastException();
}
return value.toString();
}
/**
* Returns a textual presentation of this object. The implementation of
* this method just returns the name of this type.
*
* @return
* the textual presentation, never <code>null</code>.
*/
public final String toString() {
return _name;
}
}