001: /*
002: * $Id: Type.java,v 1.31 2007/09/11 13:24:20 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.common.types;
008:
009: import org.xins.common.MandatoryArgumentChecker;
010: import org.xins.common.Utils;
011:
012: /**
013: * Value type. This is an abstract base class for type classes. Each type
014: * defines a name and it defines what values are considered valid and what
015: * values are considered invalid.
016: *
017: * @version $Revision: 1.31 $ $Date: 2007/09/11 13:24:20 $
018: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
019: *
020: * @since XINS 1.0.0
021: */
022: public abstract class Type {
023:
024: /**
025: * The name of this type. Never <code>null</code>.
026: */
027: private final String _name;
028:
029: /**
030: * The class for all values. Never <code>null</code>.
031: */
032: private final Class _valueClass;
033:
034: /**
035: * Creates a new <code>Type</code> instance. Both the name of the type and
036: * the value class must be specified. The value class in the class (or
037: * interface) that values for this type should be instances of. If
038: * <code>null</code> is specified as the value class, then that is the same
039: * as specifying <code>Object.class</code> as the value class.
040: *
041: * @param name
042: * the name of the type, not <code>null</code>.
043: *
044: * @param valueClass
045: * the class or interface that values should be instances of, or
046: * <code>null</code> if any class is valid.
047: *
048: * @throws IllegalArgumentException
049: * if <code>name == null</code>.
050: */
051: protected Type(String name, Class valueClass)
052: throws IllegalArgumentException {
053:
054: // Check preconditions
055: MandatoryArgumentChecker.check("name", name);
056:
057: // Store the arguments
058: _name = name;
059: _valueClass = valueClass == null ? Object.class : valueClass;
060: }
061:
062: /**
063: * Retrieves the name of this type.
064: *
065: * @return
066: * the name of this type, never <code>null</code>.
067: */
068: public final String getName() {
069: return _name;
070: }
071:
072: /**
073: * Retrieves the description of this type.
074: *
075: * @return
076: * the description of this type, never <code>null</code>.
077: *
078: * @since XINS 2.1.
079: */
080: public String getDescription() {
081: return _name + " type.";
082: }
083:
084: /**
085: * Retrieves the value class. All values for this type are instances of
086: * this class.
087: *
088: * @return
089: * the class values should be instances of, never <code>null</code>.
090: */
091: public final Class getValueClass() {
092: return _valueClass;
093: }
094:
095: /**
096: * Checks if the specified value is valid for this type and throws an
097: * exception if not.
098: *
099: * <p />Note that <code>null</code> values are <em>always</em> considered
100: * to be valid.
101: *
102: * @param value
103: * the value that should be checked for validity, can be
104: * <code>null</code>.
105: *
106: * @throws TypeValueException
107: * if the specified value is invalid for this type.
108: */
109: public final void checkValue(String value)
110: throws TypeValueException {
111: if (value == null) {
112: return;
113: } else if (!isValidValueImpl(value)) {
114: throw new TypeValueException(this , value);
115: }
116: }
117:
118: /**
119: * Determines if the specified <code>String</code> value is considered
120: * valid for this type (wrapper method).
121: *
122: * <p />This method first checks if <code>string == null</code> and if it
123: * is not, then it returns the result of a call to
124: * {@link #isValidValueImpl(String)}. Note that <code>null</code> values
125: * are <em>always</em> considered to be valid.
126: *
127: * @param string
128: * the value that should be checked for validity, can be
129: * <code>null</code>.
130: *
131: * @return
132: * <code>true</code> if and only if the specified value is valid,
133: * <code>false</code> otherwise.
134: */
135: public final boolean isValidValue(String string) {
136: if (string == null) {
137: return true;
138: } else {
139: return isValidValueImpl(string);
140: }
141: }
142:
143: /**
144: * Determines if the specified <code>String</code> value is considered
145: * valid for this type (implementation method).
146: *
147: * <p>This method is called from {@link #isValidValue(String)}. When
148: * called from that method, it is guaranteed that the argument is not
149: * <code>null</code>.
150: *
151: * <p />The implementation of this method in class {@link Type} returns
152: * <code>true</code>.
153: *
154: * @param string
155: * the <code>String</code> value that should be checked for validity,
156: * never <code>null</code>.
157: *
158: * @return
159: * <code>true</code> if and only if the specified <code>String</code>
160: * value is valid, <code>false</code> otherwise.
161: */
162: protected boolean isValidValueImpl(String string) {
163: return true;
164: }
165:
166: /**
167: * Converts from a <code>String</code> to an instance of the value class
168: * for this type (wrapper method).
169: *
170: * <p />This method returns <code>null</code> if <code>string ==
171: * null</code>. Otherwise it first calls {@link #isValidValueImpl(String)}
172: * to check if the value is in principle valid. If it is, then
173: * {@link #fromStringImpl(String)} is called. If the result of that call is
174: * <em>not</em> an instance of the value class, then an
175: * {@link Error} is thrown. Notice that this error is also thrown
176: * if {@link #fromStringImpl(String)} returns <code>null</code>.
177: *
178: * @param string
179: * the string to convert to an instance of the value class, can be
180: * <code>null</code>.
181: *
182: * @return
183: * an instance of the value class, will be <code>null</code> if and only
184: * if <code>string == null</code>.
185: *
186: * @throws TypeValueException
187: * if the specified string does not represent a valid value for this
188: * type.
189: */
190: public final Object fromString(String string)
191: throws TypeValueException {
192:
193: if (string == null) {
194: return null;
195: }
196:
197: // TODO: Catch exceptions thrown by isValidValueImpl(String)
198: if (!isValidValueImpl(string)) {
199: throw new TypeValueException(this , string);
200: }
201:
202: Object value = fromStringImpl(string);
203:
204: // TODO: Create a unit test to check that a null returned from
205: // fromStringImpl(String) is actually causing a
206: // ProgrammingException to be thrown
207:
208: if (!_valueClass.isInstance(value)) {
209: String detail = "The value returned is an instance of class "
210: + value.getClass().getName()
211: + " instead of an instance of "
212: + _valueClass.getName() + '.';
213: throw Utils.logProgrammingError(detail);
214: }
215:
216: return value;
217: }
218:
219: /**
220: * Converts from a <code>String</code> to an instance of the value class
221: * for this type (implementation method).
222: *
223: * <p>This method is not required to check the validity of the specified
224: * value (since {@link #isValidValueImpl(String)} should have been called
225: * before) but if it does, then it may throw a {@link TypeValueException}.
226: *
227: * @param string
228: * the string to convert to an instance of the value class, guaranteed
229: * to be not <code>null</code> and guaranteed to have been passed to
230: * {@link #isValidValueImpl(String)} without getting an exception.
231: *
232: * @return
233: * an instance of the value class, cannot be <code>null</code>.
234: *
235: * @throws TypeValueException
236: * if <code>string</code> is considered to be an invalid value for this
237: * type.
238: */
239: protected abstract Object fromStringImpl(String string)
240: throws TypeValueException;
241:
242: /**
243: * Generates a string representation of the specified value for this type.
244: * The specified value must be an instance of the value class for this type
245: * (see {@link #getValueClass()}). Also, it may have to fall within a
246: * certain range of valid values, depending on the type.
247: *
248: * <p>The default implementation of this method in class {@link Type} does
249: * the following:
250: *
251: * <ul>
252: * <li>if <code>value == null</code> then it throws an
253: * {@link IllegalArgumentException};
254: * <li>if <code>getValueClass().isInstance(value) == false</code> then
255: * it throws a {@link ClassCastException};
256: * <li>otherwise it returns
257: * <code>value.</code>{@link Object#toString()}.
258: * </ul>
259: *
260: * @param value
261: * the value, cannot be <code>null</code>.
262: *
263: * @return
264: * the string representation of the specified value for this type,
265: * cannot be <code>null</code>.
266: *
267: * @throws IllegalArgumentException
268: * if <code>value == null</code>.
269: *
270: * @throws ClassCastException
271: * if <code>getValueClass().isInstance(value) == false</code>.
272: *
273: * @throws TypeValueException
274: * if the specified value is not in the allowed range.
275: */
276: public String toString(Object value)
277: throws IllegalArgumentException, ClassCastException,
278: TypeValueException {
279:
280: // Check preconditions
281: MandatoryArgumentChecker.check("value", value);
282:
283: if (!getValueClass().isInstance(value)) {
284: throw new ClassCastException();
285: }
286: return value.toString();
287: }
288:
289: /**
290: * Returns a textual presentation of this object. The implementation of
291: * this method just returns the name of this type.
292: *
293: * @return
294: * the textual presentation, never <code>null</code>.
295: */
296: public final String toString() {
297: return _name;
298: }
299: }
|