View Javadoc

1   /*
2    * $Source: /usr/cvsroot/melati/poem/src/main/java/org/melati/poem/BasePoemType.java,v $
3    * $Revision: 1.48 $
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.poem;
47  
48  import java.sql.PreparedStatement;
49  import java.sql.ResultSet;
50  import java.sql.SQLException;
51  import java.util.Enumeration;
52  
53  import org.melati.poem.dbms.Dbms;
54  import org.melati.poem.util.ConsEnumeration;
55  
56  /**
57   * Base class of all fundamental types.
58   */
59  public abstract class BasePoemType implements SQLPoemType, Cloneable {
60    private int sqlTypeCode;
61    protected boolean nullable;
62  
63    private Comparable low = null, limit = null;
64  
65    BasePoemType(int sqlTypeCode, boolean nullable) {
66      this.sqlTypeCode = sqlTypeCode;
67      this.nullable = nullable;
68    }
69  
70    /**
71     * Set the limits, if applicable.
72     * @param low included lower limit
73     * @param limit excluded upper limit
74     */
75    public void setRawRange(Comparable low, Comparable limit) {
76      this.low = low;
77      this.limit = limit;
78    }
79  
80    protected Comparable getLowRaw() {
81      return low;
82    }
83  
84    protected Comparable getLimitRaw() {
85      return limit;
86    }
87  
88    protected abstract void _assertValidRaw(Object raw)
89        throws ValidationPoemException;
90  
91    private void assertRawInRange(Object raw) {
92      // Range check.  Since we can't do this with multiple inheritance, we
93      // provide it as a facility even in types for which it is meaningless.
94  
95      Comparable asComparable;
96      try {
97        asComparable = (Comparable)raw;
98      }
99      catch (ClassCastException e) {
100       throw new NotComparablePoemException(raw, this);
101     }
102 
103     if ((low != null && low.compareTo(asComparable) > 0) ||
104         (limit != null && limit.compareTo(asComparable) <= 0))
105       throw new ValidationPoemException(
106           this, raw, new OutsideRangePoemException(low, limit, raw));
107   }
108 
109   /**
110    * {@inheritDoc}
111    * @see org.melati.poem.PoemType#assertValidRaw(java.lang.Object)
112    */
113   public final void assertValidRaw(Object raw)
114       throws ValidationPoemException {
115     if (raw == null) {
116       if (!nullable)
117         throw new NullTypeMismatchPoemException(this);
118     }
119     else {
120       if (low != null || limit != null)
121         assertRawInRange(raw);
122       _assertValidRaw(raw);
123     }
124   }
125 
126   /**
127    * Check if the raw value is valid, as expected.
128    * @param raw an Object which should be of correct type
129    */
130   private void doubleCheckValidRaw(Object raw) {
131     try {
132       assertValidRaw(raw);
133     }
134     catch (ValidationPoemException e) {
135       throw new UnexpectedValidationPoemException(e);
136     }
137   }
138 
139   protected abstract Object _getRaw(ResultSet rs, int col)
140       throws SQLException;
141 
142   /**
143    * {@inheritDoc}
144    * @see org.melati.poem.SQLType#getRaw(java.sql.ResultSet, int)
145    */
146   public final Object getRaw(ResultSet rs, int col)
147       throws ValidationPoemException {
148     Object o;
149     try {
150       o = _getRaw(rs, col);
151     }
152     catch (SQLException e) {
153       throw new SQLSeriousPoemException(e);
154     }
155 
156     assertValidRaw(o);
157     return o;
158   }
159 
160   protected abstract void _setRaw(PreparedStatement ps, int col,
161                                   Object raw)
162       throws SQLException;
163 
164   /**
165    * {@inheritDoc}
166    * @see org.melati.poem.SQLType#setRaw(java.sql.PreparedStatement, int, java.lang.Object)
167    */
168   public final void setRaw(PreparedStatement ps, int col, Object raw) {
169     doubleCheckValidRaw(raw);
170     try {
171       if (raw == null)
172         ps.setNull(col, sqlTypeCode());
173       else
174         _setRaw(ps, col, raw);
175     }
176     catch (SQLException e) {
177       throw new SQLSeriousPoemException(e);
178     }
179   }
180 
181   protected Enumeration _possibleRaws() {
182     return null;
183   }
184   
185   /**
186    * {@inheritDoc}
187    * @see org.melati.poem.PoemType#possibleRaws()
188    */
189   public Enumeration possibleRaws() {
190     Enumeration them = _possibleRaws();
191     return them == null ? null :
192                    getNullable() ? new ConsEnumeration(null, them) :
193                    them;
194   }
195 
196   protected abstract String _stringOfRaw(Object raw);
197 
198   /**
199    * This <B>doesn't</B> do an explicit <TT>assertValidRaw</TT>.
200       * {@inheritDoc}
201    * @see org.melati.poem.PoemType#stringOfRaw(java.lang.Object)
202    */
203   public final String stringOfRaw(Object raw)
204       throws ValidationPoemException {
205     return raw == null ? null : _stringOfRaw(raw);
206   }
207 
208   /**
209    * Converts a non-null string to an appropriate value 
210    * for insertion into the underlying DBMS.
211    */
212   protected abstract Object _rawOfString(String string)
213       throws ParsingPoemException;
214 
215   /**
216    * Converts a possibly null <code>String</code> to a low level
217    * representation of a valid database column value.
218    * <p>
219    * Null values are not changed.
220    * <p>
221    * This result is validated with {@link #assertValidRaw(Object)}
222    * whereas {@link #stringOfRaw(Object)} assumes this is not
223    * required.
224    * {@inheritDoc}
225    * @see org.melati.poem.PoemType#rawOfString(java.lang.String)
226    */
227   public final Object rawOfString(String string)
228       throws ParsingPoemException, ValidationPoemException {
229     Object raw = string == null ? null : _rawOfString(string);
230     assertValidRaw(raw);
231     return raw;
232   }
233 
234   protected abstract void _assertValidCooked(Object cooked)
235       throws ValidationPoemException;
236 
237   /**
238    * {@inheritDoc}
239    * @see org.melati.poem.PoemType#assertValidCooked(java.lang.Object)
240    */
241   public final void assertValidCooked(Object cooked)
242       throws ValidationPoemException {
243     if (cooked == null) {
244       if (!nullable)
245         throw new NullTypeMismatchPoemException(this);
246     }
247     else {
248       _assertValidCooked(cooked);
249       if (low != null || limit != null)
250         assertRawInRange(_rawOfCooked(cooked));
251     }
252   }
253 
254   /**
255    * Check that object is valid, as expected.
256    * NOTE If it isn't valid then it isn't cooked.
257    * @param cooked the cooked object
258    */
259   final void doubleCheckValidCooked(Object cooked) {
260     try {
261       assertValidCooked(cooked);
262     }
263     catch (ValidationPoemException e) {
264       throw new UnexpectedValidationPoemException(e);
265     }
266   }
267 
268   /**
269    * Converts a non-null low-level representation of a database
270    * column value to the appropriate object.
271    * <p>
272    * For the base object types, (String, Integer etc) this involves no change. 
273    * <p>
274    * For types with an integer id, such as Poem internal types and user defined types, 
275    * then the appropriate instantiated type is returned from its Integer id.
276    * @param raw the base object or Integer object id
277    * @return the unchanged base object or an instantiated type
278    */
279   protected abstract Object _cookedOfRaw(Object raw) throws PoemException;
280 
281   /**
282    * Converts a possibly null low-level representation of a database
283    * column value to its canonical form. 
284    * Types represented as integers in the database are converted to 
285    * corresponding objects .
286    * <p>
287    * The raw value is checked to ensure it is valid.
288    * {@inheritDoc}
289    * @see org.melati.poem.PoemType#cookedOfRaw(java.lang.Object)
290    */
291   public final Object cookedOfRaw(Object raw) throws PoemException {
292     doubleCheckValidRaw(raw);
293     return raw == null ? null : _cookedOfRaw(raw);
294   }
295 
296   protected abstract Object _rawOfCooked(Object raw) throws PoemException;
297 
298   /**
299    * {@inheritDoc}
300    * @see org.melati.poem.PoemType#rawOfCooked(java.lang.Object)
301    */
302   public final Object rawOfCooked(Object cooked) {
303     doubleCheckValidCooked(cooked);
304     return cooked == null ? null : _rawOfCooked(cooked);
305   }
306 
307   protected abstract String _stringOfCooked(Object cooked,
308                                            PoemLocale locale, int style)
309       throws PoemException;
310 
311   /**
312    * {@inheritDoc}
313    * @see org.melati.poem.PoemType#stringOfCooked(java.lang.Object, 
314    *          org.melati.poem.PoemLocale, int)
315    */
316   public final String stringOfCooked(Object cooked,
317                                     PoemLocale locale, int style)
318       throws PoemException {
319     doubleCheckValidCooked(cooked);
320     return cooked == null ? "" : _stringOfCooked(cooked, locale, style);
321   }
322 
323   /**
324    * {@inheritDoc}
325    * @see org.melati.poem.PoemType#getNullable()
326    */
327   public final boolean getNullable() {
328     return nullable;
329   }
330 
331   /**
332    * {@inheritDoc}
333    * @see org.melati.poem.SQLType#sqlTypeCode()
334    */
335   public final int sqlTypeCode() {
336     return sqlTypeCode;
337   }
338 
339   protected abstract String _sqlDefinition(Dbms dbms);
340 
341   /**
342    * {@inheritDoc}
343    * @see org.melati.poem.SQLType#sqlDefinition(org.melati.poem.dbms.Dbms)
344    */
345   public String sqlDefinition(Dbms dbms) {
346     return sqlTypeDefinition(dbms) + (nullable ? "" : " NOT NULL");
347   }
348   /**
349    * {@inheritDoc}
350    * @see org.melati.poem.SQLType#sqlTypeDefinition(org.melati.poem.dbms.Dbms)
351    */
352   public String sqlTypeDefinition(Dbms dbms) {
353     return _sqlDefinition(dbms);
354   }
355   protected abstract boolean _canRepresent(SQLPoemType other);
356 
357   /**
358    * {@inheritDoc}
359    * @see org.melati.poem.PoemType#canRepresent(org.melati.poem.PoemType)
360    */
361   public PoemType canRepresent(PoemType other) {
362     // FIXME takes no account of range---need to decide on semantics for this,
363     // is it subset (inclusion) or some other notion of storability?
364     if (!(other instanceof SQLPoemType)) 
365       // NOTE Never happens as currently all PoemTypes are SQLPoemTypes
366       return null;
367     else {
368       SQLPoemType q = (SQLPoemType)other;
369       return
370           !(!nullable && q.getNullable()) && // Nullable may represent not nullable
371           _canRepresent(q) ?
372               q : null;
373     }
374   }
375 
376   /**
377    * {@inheritDoc}
378    * @see org.melati.poem.PoemType#withNullable(boolean)
379    */
380   public final PoemType withNullable(boolean nullableP) {
381     if (this.nullable == nullableP)
382       return this;
383     else {
384       BasePoemType it = (BasePoemType)clone();
385       it.nullable = nullableP;
386       return it;
387     }
388   }
389 
390   protected abstract void _saveColumnInfo(ColumnInfo info)
391       throws AccessPoemException;
392 
393   /**
394    * {@inheritDoc}
395    * @see org.melati.poem.PoemType#saveColumnInfo(org.melati.poem.ColumnInfo)
396    */
397   public void saveColumnInfo(ColumnInfo info) throws AccessPoemException {
398     info.setNullable(nullable);
399     info.setSize(0);
400     info.setRangelow_string(
401         getLowRaw() == null ? null : stringOfRaw(getLowRaw()));
402     // this _won't_ throw an OutsideRangePoemException since it doesn't check
403     info.setRangelimit_string(
404         getLimitRaw() == null ? null : stringOfRaw(getLimitRaw()));
405     _saveColumnInfo(info);
406   }
407 
408   protected abstract String _quotedRaw(Object raw);
409 
410   /**
411    * {@inheritDoc}
412    * @see org.melati.poem.SQLType#quotedRaw(java.lang.Object)
413    */
414   public String quotedRaw(Object raw) throws ValidationPoemException {
415     assertValidRaw(raw);
416     return raw == null ? "NULL" : _quotedRaw(raw);
417   }
418 
419   protected abstract String _toString();
420 
421   // 
422   // --------
423   //  Object
424   // --------
425   // 
426 
427   /**
428    * {@inheritDoc}
429    * @see java.lang.Object#toString()
430    */
431   public String toString() {
432     return (nullable ? "nullable " : "") + _toString() + 
433     " (" + this.getClass().getName() + ")";
434   }
435 
436   /**
437    * {@inheritDoc}
438    * @see java.lang.Object#clone()
439    */
440   protected Object clone() {
441     try {
442       return super.clone();
443     }
444     catch (CloneNotSupportedException e) {
445       throw new PoemBugPoemException();
446     }
447   }
448 }