KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > dbobj > DBObject


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 package com.jcorporate.expresso.core.dbobj;
66
67 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
68 import com.jcorporate.expresso.core.cache.CacheException;
69 import com.jcorporate.expresso.core.cache.CacheManager;
70 import com.jcorporate.expresso.core.cache.CacheSystem;
71 import com.jcorporate.expresso.core.cache.Cacheable;
72 import com.jcorporate.expresso.core.controller.Transition;
73 import com.jcorporate.expresso.core.dataobjects.BaseDataObject;
74 import com.jcorporate.expresso.core.dataobjects.DataException;
75 import com.jcorporate.expresso.core.dataobjects.DataField;
76 import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
77 import com.jcorporate.expresso.core.dataobjects.DataObject;
78 import com.jcorporate.expresso.core.dataobjects.DataObjectMetaData;
79 import com.jcorporate.expresso.core.dataobjects.DataTransferObject;
80 import com.jcorporate.expresso.core.dataobjects.DefaultDataField;
81 import com.jcorporate.expresso.core.dataobjects.jdbc.FieldRangeParser;
82 import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCDataObject;
83 import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData;
84 import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCUtil;
85 import com.jcorporate.expresso.core.db.DBConnection;
86 import com.jcorporate.expresso.core.db.DBConnectionPool;
87 import com.jcorporate.expresso.core.db.DBException;
88 import com.jcorporate.expresso.core.db.exception.DBRecordNotFoundException;
89 import com.jcorporate.expresso.core.misc.ConfigJdbc;
90 import com.jcorporate.expresso.core.misc.ConfigManager;
91 import com.jcorporate.expresso.core.misc.ConfigurationException;
92 import com.jcorporate.expresso.core.misc.DateTime;
93 import com.jcorporate.expresso.core.misc.StringUtil;
94 import com.jcorporate.expresso.core.registry.RequestRegistry;
95 import com.jcorporate.expresso.core.security.filters.Filter;
96 import com.jcorporate.expresso.kernel.util.ClassLocator;
97 import com.jcorporate.expresso.kernel.util.FastStringBuffer;
98 import com.jcorporate.expresso.services.dbobj.ChangeLog;
99 import com.jcorporate.expresso.services.dbobj.DBObjLimit;
100 import com.jcorporate.expresso.services.dbobj.Setup;
101 import org.apache.log4j.Logger;
102 import org.apache.oro.text.regex.Pattern;
103 import org.apache.oro.text.regex.PatternMatcher;
104 import org.apache.oro.text.regex.Perl5Matcher;
105
106 import java.io.IOException JavaDoc;
107 import java.io.InputStream JavaDoc;
108 import java.io.ObjectInputStream JavaDoc;
109 import java.io.ObjectOutputStream JavaDoc;
110 import java.lang.reflect.Method JavaDoc;
111 import java.math.BigDecimal JavaDoc;
112 import java.sql.SQLException JavaDoc;
113 import java.text.DecimalFormat JavaDoc;
114 import java.text.NumberFormat JavaDoc;
115 import java.text.ParseException JavaDoc;
116 import java.util.ArrayList JavaDoc;
117 import java.util.Collections JavaDoc;
118 import java.util.Enumeration JavaDoc;
119 import java.util.HashMap JavaDoc;
120 import java.util.Iterator JavaDoc;
121 import java.util.List JavaDoc;
122 import java.util.Locale JavaDoc;
123 import java.util.Map JavaDoc;
124 import java.util.StringTokenizer JavaDoc;
125 import java.util.Vector JavaDoc;
126
127
128 /**
129  * <p>DBObjects are the core of Expresso's object-relational mapping. They are object-oriented
130  * wrappers for some sort of data. They are generally expected to be used in a single thread, like
131  * in processing a web query.</p>
132  * <p>When making your own application, you derive your classes from DBObject or
133  * SecuredDBObject.</p>
134  *
135  * @author Michael Nash
136  * @see com.jcorporate.expresso.core.dbobj.SecuredDBObject
137  * @see com.jcorporate.expresso.core.db.DBException
138  * @see com.jcorporate.expresso.core.db.DBConnection
139  * @since Expresso 1.0
140  */

141 public abstract class DBObject
142         extends JDBCDataObject
143         implements Cacheable, LookupInterface {
144
145
146     /**
147      * setup code for update() policy:
148      * for efficiency, developers who are confident that their code does not have any
149      * 'blind updates' can set the Setup value UPDATE_CHANGED_ONLY to true
150      * (a blind update is where the object is not retieved before values in it are reset)
151      */

152     public static final String JavaDoc UPDATE_CHANGED_ONLY = "UPDATE_CHANGED_ONLY";
153
154     /**
155      * Attribute String for if there's an error with the field.
156      */

157     public static final String JavaDoc ATTRIBUTE_ERROR = "error";
158
159     /**
160      * Attribute String for what message to display if there's an error
161      * with thie field.
162      */

163     public static final String JavaDoc ATTRIBUTE_ERROR_MESSAGE = "error-message";
164
165     /**
166      * Attribute for what is the limit to retrieve from searchAndRetrieve operations
167      */

168     public static final String JavaDoc ATTRIBUTE_PAGE_LIMIT = "pageLimit";
169
170     /**
171      * Event 'Add' code
172      */

173     public static final String JavaDoc EVENT_ADD = "A";
174
175     /**
176      * Event 'Delete' Code
177      */

178     public static final String JavaDoc EVENT_DELETE = "D";
179
180     /**
181      * Event 'Update' Code
182      */

183     public static final String JavaDoc EVENT_UPDATE = "U";
184
185
186     /**
187      * A static zero BIG DECIMAL object
188      * <p/>
189      * author Peter Pilgrim <peterp@xenonsoft.demon.co.uk>
190      *
191      * @see #getFieldBigDecimal
192      */

193     transient protected static final BigDecimal JavaDoc BIG_DECIMAL_ZERO = new BigDecimal JavaDoc(0.0);
194
195
196     /* the logs
197     * @todo move this static variable outside DBObject so we can wrap it for
198     * EJBs
199     */

200     transient private static Logger log = Logger.getLogger(DBObject.class);
201
202
203     /**
204      * 30 minute DBObjLimit cache tty
205      */

206     transient private static final long DBOBJLIMIT_CACHE_TTL = 60 * 1000 * 30;
207
208     /**
209      * Statistics for Cache entries.
210      */

211     transient private static ConcurrentReaderHashMap sCacheStats = new ConcurrentReaderHashMap();
212
213
214     /**
215      * Cache Util object for bridging DBObject &lt;-&gt; CacheManager
216      */

217     transient final private static CacheUtils cacheUtils = new CacheUtils();
218
219     /**
220      * A Pattern Matcher for examining fields vs validation regular expressions
221      * It has been modified for thead local instantiation to reduce synchronization.
222      */

223     transient private static ThreadLocal JavaDoc patternMatcher = new ThreadLocal JavaDoc() {
224         protected synchronized Object JavaDoc initialValue() {
225             return new Perl5Matcher();
226         }
227     };
228     private static Logger slog = null;
229     public static final String JavaDoc WHERE_KEYWORD = " WHERE ";
230
231     /**
232      * Retrieve a thread local instance of the Perl5 pattern matcher. Allows
233      * for optimization of # of instances of pattern matcher vs synchronization.
234      *
235      * @return PatternMatcher
236      */

237     protected PatternMatcher getPatternMatcher() {
238         return (PatternMatcher) patternMatcher.get();
239     }
240
241
242     /**
243      * Field Range Parser verifies range values to make sure there isn't things
244      * like SQL injection getting passed into the range.
245      */

246     transient private static FieldRangeParser rangeVerifier = new FieldRangeParser();
247
248
249 ///////////////////////////////////////////////////////////////////////////////////////
250
// instance vars
251
///////////////////////////////////////////////////////////////////////////////////////
252

253     /**
254      * string filter class; null defaults to mean HtmlFilter
255      * this should be stored here, in the object instance, rather than a setting
256      * in metadata for all objects of this type: consider a use-case where
257      * one object is getting rendered in HTML while another is simultaneously
258      * rendered in XML.
259      */

260     private Class JavaDoc mFilter = null;
261
262     /**
263      * A DBObject instance often refers to a single row, and this hash map contains
264      * the row data for this instance. This Map specifically contains a list of
265      * <code>DataField</code> objects that in turn contain the actual field data.
266      */

267     private Map fieldData = null;
268
269
270     /**
271      * Contains a map of DBObject.FieldError classes describing the errors
272      * set by checkField().
273      */

274     private HashMap fieldErrors = null;
275
276
277     /* This is the locale of the DBObject. Originally this attribute
278      * was part of the subclass <code>SecuredDBObject</code>. However
279      * there is no reason that explains why all DBObjects should not
280      * support internationalisation (i18n).
281      * Modification by Peter Pilgrim Wed Jan 01 18:27:14 GMT 2003
282      *
283      * @see #getLocale
284      * @see #setLocale( Locale )
285      */

286     private Locale JavaDoc myLocale = Locale.getDefault();
287
288
289     /**
290      * Keys that have been found in the last retrieve.
291      */

292     private ArrayList JavaDoc foundKeys = null;
293
294     /**
295      * Attributes of this DB Object
296      */

297     private HashMap attributes = null;
298
299     /* The cache size of this particular DB object */
300     private int myCacheSize = -2;
301
302
303     /**
304      * Very Similar to "anyFieldsToRetrieve" already present on the DBObject.
305      * author ABHI
306      */

307     boolean anyFieldsToRetrieveMulti = false;
308
309
310     /**
311      * Integer Regular Expression for easy reference
312      */

313     public static final String JavaDoc INT_MASK = "^[+-]?[0-9]+";
314
315     /**
316      * Floating point regular expression syntax for easy reference.
317      */

318     public static final String JavaDoc FLOAT_MASK = "^([+-]?)(?=\\d|\\.\\d)\\d*(\\.\\d*)?([Ee]([+-]?\\d+))?$";
319
320     /**
321      * Email Regular Expression Constant.
322      */

323     public static final String JavaDoc EMAIL_MASK =
324             "^[A-Za-z0-9\\-\\.\\_]+"
325             + "@"
326             + "[A-Za-z0-9\\-\\.\\_]+"
327             + "\\."
328             + "[a-zA-Z]{2,6}$"; // 6 chars in "museum" TLD
329
public static final String JavaDoc IS_CHECK_RELATIONAL_INTEGRITY = "isCheckRelationalIntegrity";
330
331     /**
332      * Default Constructor. This allows a DB object to be dynamically
333      * instantiated (e.g. loaded
334      * with Class.forName()) and does all of the required initializations.
335      *
336      * @throws DBException upon error.
337      */

338     public DBObject()
339             throws DBException {
340         initialize();
341     } /* DBObject() */
342
343     /**
344      * Constructor that sets a connection as the object is created - typically
345      * this is used when a particular DBConnection is required for the purposes of
346      * maintaining a database transaction. If a specific connection is not used,
347      * there is no way to use commit() and rollback() in the event of failure, as a
348      * different DBConnection might be used for each phase of the transaction.
349      * Critial sections should therefore explicity request a DBConnection from the
350      * connection pool and pass it to each of the DB objects in that section.
351      *
352      * @param newConnection The DBConnection to utilize
353      * @throws DBException upon error.
354      */

355     public DBObject(DBConnection newConnection)
356             throws DBException {
357         this(newConnection, newConnection.getDataContext());
358     } /* DBObject(DBConnection) */
359
360
361     /**
362      * Constructor that sets a connection as the object is created - typically
363      * this is used when a particular DBConnection is required for the purposes of
364      * maintaining a database transaction. If a specific connection is not used,
365      * there is no way to use commit() and rollback() in the event of failure, as a
366      * different DBConnection might be used for each phase of the transaction.
367      * Critial sections should therefore explicity request a DBConnection from the
368      * connection pool and pass it to each of the DB objects in that section.
369      * <p>This constructor is neceesary to work with otherDBMap and transaction
370      * capabilities</p>
371      *
372      * @param newConnection The DBConnection to utilize
373      * @param setupTablesContext The data context that contains the setup (and
374      * security) tables for this object
375      * @throws DBException upon error.
376      * @since Expresso 5.0.1
377      */

378     public DBObject(DBConnection newConnection, String JavaDoc setupTablesContext)
379             throws DBException {
380         this();
381         setConnection(newConnection, setupTablesContext);
382     } /* DBObject(DBConnection) */
383
384     /**
385      * For using DBObjects within Controllers. Initializes based upon
386      * the current locale and the requested db context. There is no
387      * current user login id set in this method. If you need the user id
388      * then use you should use the subclass <code>SecuredDBObject</code>
389      * type instead.
390      *
391      * @param request - The controller request handed to you by the framework.
392      * @throws DBException if there's an error constructing the SecuredDBObject
393      */

394     public DBObject(RequestContext request)
395             throws DBException {
396         this();
397         setDataContext(request.getDBName());
398         setLocale(request.getLocale());
399     }
400
401     /**
402      * Get the current locale for this dbobject
403      *
404      * @return The currently set locale or null if there is no locale set.
405      * @since Expresso 5.0.1
406      */

407     public Locale JavaDoc getLocale() {
408         return myLocale;
409     }
410
411     /**
412      * Sets the locale to be used with this DBObject
413      *
414      * @param newLocale The new Locale to use with this object
415      * @since Expresso 5.0.1
416      */

417     public void setLocale(Locale JavaDoc newLocale) {
418         myLocale = newLocale;
419     }
420
421     /**
422      * Initialize this DBObject and set the db/context to the specified key
423      *
424      * @param newdbKey The database Context name
425      * @throws DBException upon error.
426      */

427     public DBObject(String JavaDoc newdbKey)
428             throws DBException {
429         this();
430         setDataContext(newdbKey);
431     } /* DBObject(String) */
432
433     /**
434      * Add a new record to the target table.
435      * Assumes that the fields of this object are populated with data for the new
436      * record. All key fields at least must be supplied with values, and all fields
437      * that are specified as "no nulls". This method also validates all referential
438      * integrity constraints specified by the object.
439      *
440      * @throws DBException If the record cannot be added - this includes if the
441      * record has a duplicate key
442      */

443     public void add()
444             throws DBException {
445
446         getExecutor().add(this);
447
448         /* Now log the change if we are logging */
449         if (getDef().isLoggingEnabled()) {
450             ChangeLog myChangeLog = new ChangeLog();
451             myChangeLog.setDataContext(getDataContext());
452             myChangeLog.setField("ObjectChanged", myClassName);
453             myChangeLog.setField("RecordKey", getMyKeys());
454             myChangeLog.setField("Operation", EVENT_ADD);
455             myChangeLog.setField("ChangedField", "ALL");
456             myChangeLog.add();
457
458             /* We're done tracking changes */
459             myUpdates = null;
460         } /* if */
461
462         // after add(), we know that 'current' values are now the baseline for comparison
463
cacheIsChangedComparison();
464         setStatus(BaseDataObject.STATUS_CURRENT);
465         notifyListeners(EVENT_ADD);
466     } /* add() */
467
468     /**
469      * Hand a dbobject a connection that contains fields corresponding to
470      * what the dbobject expects, and it'll set itself.
471      * <p/>
472      * Does not increment the result set in the DBConnection.
473      *
474      * @param connection The connection that currently has a dbobject ready to
475      * be read in it's result set.
476      * @return The number of fields read. Depending on the SQL you sent to the connection
477      * the DBObject might not have all fields in existence.
478      * @throws DBException upon error.
479      */

480     public synchronized int loadFromConnection(DBConnection connection)
481             throws DBException {
482         String JavaDoc oneFieldName = null;
483         Object JavaDoc tmpData = null;
484         int fieldCount = 0;
485         JDBCObjectMetaData metadata = getJDBCMetaData();
486         for (Iterator JavaDoc it = metadata.getFieldListArray().iterator(); it.hasNext();) {
487             oneFieldName = (String JavaDoc) it.next();
488
489             try {
490                 DataFieldMetaData oneField = metadata.getFieldMetadata(oneFieldName);
491                 if (oneField.isDateType()) {
492                     tmpData = getCustomStringFieldValue(connection, oneFieldName);
493                 } else {
494                     if (!oneField.isLongBinaryType() && !oneField.isLongCharacterType()) {
495                         if (connection.isStringNotTrim()) {
496                             tmpData = connection.getStringNoTrim(oneFieldName);
497                         } else {
498                             tmpData = connection.getString(oneFieldName);
499                         }
500                     } else {
501                         if (oneField.isLongBinaryType()) {
502                             tmpData = null;
503                             InputStream JavaDoc is = connection.getBinaryStream(oneFieldName);
504                             if (is != null) {
505                                 byte[] bstr = new byte[LONGBINARY_READ_DEFAULT_SIZE];
506                                 int j = is.read(bstr);
507                                 if (j > 0) {
508                                     byte[] content = new byte[j];
509                                     System.arraycopy(bstr, 0, content, 0, j);
510                                     tmpData = content;
511                                 }
512                             }
513                         } else {
514                             tmpData = connection.getStringNoTrim(oneFieldName);
515                         }
516                     }
517
518 // if (connection.isStringNotTrimmed()) {
519
// oneFieldValue = connection.getStringNoTrim(oneFieldName);
520
// } else {
521
// oneFieldValue = connection.getString(oneFieldName);
522
// }
523
}
524
525 // this.setField(oneFieldName, oneFieldValue);
526
this.set(oneFieldName, tmpData);
527                 fieldCount++;
528             } catch (DBException de) {
529                 if (log.isDebugEnabled()) {
530                     log.debug("Failed to load field.", de);
531                 }
532
533                 //Failed to load this field, that's fine
534
} catch (Exception JavaDoc e) {
535                 if (log.isDebugEnabled()) {
536                     log.debug("Failed to load field.", e);
537                 }
538
539                 //Failed to load this field, that's fine
540
}
541
542         } /* for each retrieved field name */
543
544
545         setDataContext(getDataContext());
546         cacheIsChangedComparison();
547         setStatus(BaseDataObject.STATUS_CURRENT);
548
549         return fieldCount;
550     }
551
552     /**
553      * reset 'original' value and isChanged flags on all fields, establishing a baseline for comparison.
554      * call when add(), retrieve, or update() has occurred, and currentValue of data fields should be
555      * considered 'original value' for purposes of determining 'isChanged'
556      *
557      * @throws DBException upon error.
558      */

559     public void cacheIsChangedComparison() throws DBException {
560         for (Iterator JavaDoc i = getMetaData().getFieldListArray().iterator(); i.hasNext();) {
561             String JavaDoc oneFieldName = (String JavaDoc) i.next();
562             DataField field = getDataField(oneFieldName);
563             field.cacheIsChangedComparison();
564         }
565     }
566
567
568     /**
569      * This is used internally by JDBC Exceutor's and JDBC Query when dealing
570      * with queries to the underlying datasource. Under normal conditions you
571      * would not used this function directly.
572      *
573      * @param fieldName the name of the fieldname found.
574      */

575     public void addFoundKeys(String JavaDoc fieldName) {
576         if (foundKeys == null) {
577             foundKeys = new ArrayList JavaDoc();
578         }
579
580         foundKeys.add(fieldName);
581     }
582
583     /**
584      * Specify a new "detail" db object, and the fields in this object
585      * they specify the fields in the related object
586      *
587      * @param objName The class name of the related object. There is assumed to be
588      * a one to one or one to many relationship from this object to the specified object
589      * @param keyFieldsLocal A pipe-delimited list of field names in this object
590      * @param keyFieldsForeign A pipe-delimieted list of field names in the other object
591      * @throws DBException upon error.
592      */

593     protected synchronized void addDetail(String JavaDoc objName,
594                                           String JavaDoc keyFieldsLocal,
595                                           String JavaDoc keyFieldsForeign)
596             throws DBException {
597         getDef().addDetail(objName, keyFieldsLocal, keyFieldsForeign);
598     }
599
600     /**
601      * Add a field with more details: This version allows the user to specify a
602      * precision, for fields that use both a size and precision.
603      *
604      * @param fieldName Name of the field
605      * @param fieldType Type of the field - this is the internal Expresso type,
606      * mapping in DBField to a specific database data type.
607      * @param fieldSize Size of the field
608      * @param fieldPrecision The precision of the field
609      * @param allowNull Does this field allow nulls?
610      * @param fieldDescription A longer description of this field
611      * (user-understandable hopefully!)
612      * @throws DBException upon error.
613      */

614     protected synchronized void addField(String JavaDoc fieldName, String JavaDoc fieldType,
615                                          int fieldSize, int fieldPrecision,
616                                          boolean allowNull,
617                                          String JavaDoc fieldDescription)
618             throws DBException {
619         getDef().addField(fieldName, fieldType, fieldSize, fieldPrecision,
620                 allowNull, fieldDescription);
621     } /* addField(String, String, int, int, boolean, String) */
622
623
624     /**
625      * Add a field with more details: This version of addfield supplies
626      * the allowNull flags and a description of the field to be used
627      * when reporting errors to the user. This method is only used by the class that
628      * extends DB object, and typically only in the setupFields() method.
629      *
630      * @param fieldName Name of the field
631      * @param fieldType Type of the field - this is the "internal" Expresso type,
632      * and is mapped to a specific type for the database depending on the
633      * mappings in the properties file (if any). The DBField object contains
634      * the default mappings.
635      * @param fieldSize Size of this field, if specified for this type of field. For
636      * fields that do not use a size (such as "date"), specify 0 for the size.
637      * @param allowNull Does this field allow nulls?
638      * @param fieldDescription A longer description of this field
639      * (user-understandable hopefully!)
640      * @throws DBException upon error.
641      */

642     protected synchronized void addField(String JavaDoc fieldName, String JavaDoc fieldType,
643                                          int fieldSize, boolean allowNull,
644                                          String JavaDoc fieldDescription)
645             throws DBException {
646         getDef().addField(fieldName, fieldType, fieldSize, allowNull,
647                 fieldDescription);
648     } /* addField(String, String, int, boolean, String) */
649
650
651     /**
652      * Determine if a record with this key exists already - if
653      * not, add a new record. Note that this method uses just the key
654      * fields to determine if the record already exists.
655      *
656      * @throws DBException upon error.
657      */

658     public synchronized void addIfNeeded()
659             throws DBException {
660
661         DBObject searchObj = newInstance();
662         searchObj.setDataContext(getDataContext());
663
664         String JavaDoc oneFieldName = null;
665
666         for (Iterator JavaDoc i = getKeyFieldListIterator(); i.hasNext();) {
667             oneFieldName = (String JavaDoc) i.next();
668             DataFieldMetaData oneField = getFieldMetaData(oneFieldName);
669             String JavaDoc value = getField(oneFieldName);
670
671             // warn user if they should not be using this method
672
if (value.length() == 0) {
673                 log.warn(
674                         "a key field is empty, and yet DBObject.addIfNeeded() only uses primary-key fields for search; should you be using searchAndRetrieve() method instead? this addIfNeeded() will add ONLY if there is no other object of this type; after one object, it will always fail.");
675             }
676
677             if (!oneField.isVirtual()) {
678                 searchObj.setField(oneFieldName, value);
679             }
680         }
681         if (!searchObj.find()) {
682             add();
683         }
684     } /* addOrUpdate() */
685
686
687     /**
688      * Use this in your derived checkField() class to add error messages to
689      * be associated with various fields.
690      *
691      * @param fieldName The field name to add the error to
692      * @param errorMessage The custom error message to associate when there's
693      * a problem with this field
694      */

695     protected void addFieldError(String JavaDoc fieldName, String JavaDoc errorMessage) {
696         if (fieldErrors == null) {
697             fieldErrors = new HashMap(5);
698         }
699
700         fieldErrors.put(fieldName, new DBObject.FieldError(fieldName, errorMessage));
701     }
702
703     /**
704      * Retrieve the error message associated with this field.
705      *
706      * @param fieldName the fieldName to get the associated error message
707      * @return A string containing the field error message or NULL if there is
708      * no error for this field. or POSSIBLY if no error message has been set
709      * for this field.
710      */

711     public String JavaDoc getFieldErrorMessage(String JavaDoc fieldName) {
712         if (fieldErrors == null) {
713             return null;
714         }
715
716         DBObject.FieldError fe = (DBObject.FieldError) fieldErrors.get(fieldName);
717         if (fe == null) {
718             return null;
719         }
720
721         return fe.getErrorMessage();
722     }
723
724     /**
725      * Use this to check if a field is in error.
726      *
727      * @param fieldName Check if there's an error set for this field.
728      * @return true if an error is set for this field.
729      */

730     public boolean hasError(String JavaDoc fieldName) {
731         try {
732             DataField df = getDataField(fieldName);
733             if (df.getAttribute(ATTRIBUTE_ERROR) != null) {
734                 return true;
735             }
736         } catch (DBException ex) {
737             log.error("Invalid field name: " + fieldName, ex);
738             return false;
739         }
740
741         if (fieldErrors == null) {
742             return false;
743         }
744
745         DBObject.FieldError fe = (DBObject.FieldError) fieldErrors.get(fieldName);
746         if (fe == null) {
747             return false;
748         }
749
750         return true;
751     }
752
753     /**
754      * Use this to check if any fields are in error.
755      *
756      * @return true if an error is set.
757      */

758     public boolean hasErrors() {
759         if (fieldErrors == null || fieldErrors.isEmpty()) {
760             return false;
761         }
762
763         return true;
764     }
765
766     /**
767      * Used to clear field error flags.
768      *
769      * @param fieldName the name of the field to clear the error flag
770      */

771     protected void clearError(String JavaDoc fieldName) {
772         if (fieldErrors == null) {
773             return;
774         }
775
776         fieldErrors.remove(fieldName);
777     }
778
779     /**
780      * Add an index to the table.
781      *
782      * @param indexName the name to give the index in the table; MUST CONTAIN NO SPACES--use underscores instead
783      * @param fieldNames A comma delimited list of all fields in the index.
784      * @param isUnique - True if this field is a unique index.
785      * @throws IllegalArgumentException of fieldName is null or doesn't exist
786      * or if indexName is null
787      * @throws DBException upon error.
788      */

789     protected void addIndex(String JavaDoc indexName, String JavaDoc fieldNames,
790                             boolean isUnique)
791             throws IllegalArgumentException JavaDoc, DBException {
792         getDef().addIndex(indexName, fieldNames, isUnique);
793     }
794
795     /**
796      * Add a new field to the list of fields that are part of this
797      * object's key. Called after all of the "addField" calls in the setupFields()
798      * method to specify which fields make up the primary key of this object.
799      *
800      * @param keyFieldName The name of the field to add as part of the key
801      * @throws DBException if the field name is not valid or the field
802      * allows nulls
803      */

804     protected synchronized void addKey(String JavaDoc keyFieldName)
805             throws DBException {
806         getDef().addKey(keyFieldName);
807     } /* addKey(String) */
808
809
810     /**
811      * Determine if a record with these fields exists already - if so, update. If
812      * not, add a new record.
813      *
814      * @throws DBException upon error.
815      */

816     public synchronized void addOrUpdate()
817             throws DBException {
818         DBObject searchObj = newInstance();
819         // we must have all key fields for retrieve & update to work
820
boolean canUpdate = true;
821         String JavaDoc oneFieldName = null;
822         JDBCObjectMetaData metadata = getJDBCMetaData();
823         for (Iterator JavaDoc i = metadata.getKeyFieldListArray().iterator(); i.hasNext();) {
824             oneFieldName = (String JavaDoc) i.next();
825             DataField df = getDataField(oneFieldName);
826             if (df == null || df.isNull()) {
827                 canUpdate = false; // we do not have all keys--cannot update
828
break;
829             }
830
831             searchObj.setField(oneFieldName, getField(oneFieldName));
832         }
833
834         if (!canUpdate) {
835             add();
836             return;
837         }
838
839
840         try {
841             searchObj.retrieve(); // will throw if not found
842

843
844             // special case: if there are no fields besides key
845
// fields, then do nothing since key fields never update this way
846
if (metadata.getKeyFieldListArray().size() == metadata.getFieldListArray().size()) {
847                 if (log.isDebugEnabled()) {
848                     log.debug("No fields other than keys. Skipping update");
849                 }
850                 // do nothing
851
// @todo add logic for virtual fields
852
} else {
853                 update();
854             }
855         } catch (DBRecordNotFoundException e) {
856             add();
857         }
858     } /* addOrUpdate() */
859
860
861     /**
862      * ?????
863      *
864      * @param t unknown
865      * @throws DBException upon metadata retrieval error
866      */

867     protected void addTransition(Transition t)
868             throws DBException {
869         getDef().addTransition(t);
870     }
871
872     /**
873      * Add a field with more details: This version allows the user to specify a
874      * precision, for fields that use both a size and precision.
875      *
876      * @param fieldName Name of the field
877      * @param fieldType Type of the field - this is the internal Expresso type,
878      * mapping in DBField to a specific database data type.
879      * @param fieldSize Size of the field
880      * @param fieldPrecision The precision of the field
881      * @param allowNull Does this field allow nulls?
882      * @param fieldDescription A longer description of this field
883      * (user-understandable hopefully!)
884      * @throws DBException upon error.
885      */

886     protected synchronized void addVirtualField(String JavaDoc fieldName,
887                                                 String JavaDoc fieldType,
888                                                 int fieldSize,
889                                                 int fieldPrecision,
890                                                 boolean allowNull,
891                                                 String JavaDoc fieldDescription)
892             throws DBException {
893         getDef().addVirtualField(fieldName, fieldType, fieldSize,
894                 fieldPrecision, allowNull, fieldDescription);
895     } /* addField(String, String, int, int, boolean, String) */
896
897
898     /**
899      * Add a field with more details: This version allows the user to specify a
900      * precision, for fields that use both a size and precision.
901      *
902      * @param fieldName Name of the field
903      * @param fieldType Type of the field - this is the internal Expresso type,
904      * mapping in DBField to a specific database data type.
905      * @param fieldSize Size of the field
906      * @param fieldPrecision The precision of the field
907      * @param allowNull Does this field allow nulls?
908      * @param descripKey The key in the local language file for the
909      * description of this field
910      * @param fieldDescription A longer description of this field
911      * (user-understandable hopefully!)
912      * @throws DBException upon error.
913      */

914     protected synchronized void addVirtualField(String JavaDoc fieldName,
915                                                 String JavaDoc fieldType,
916                                                 int fieldSize,
917                                                 int fieldPrecision,
918                                                 boolean allowNull,
919                                                 String JavaDoc descripKey,
920                                                 String JavaDoc fieldDescription)
921             throws DBException {
922         getDef().addVirtualField(fieldName, fieldType, fieldSize,
923                 fieldPrecision, allowNull, descripKey,
924                 fieldDescription);
925     } /* addField(String, String, int, int, boolean, String, String) */
926
927
928     /**
929      * Add a new virtual field to the definition of this object.
930      * A virtual field is just like a regular one, except it's
931      * not stored in the target table
932      * A normal call to getField or setField on a virtual field will throw
933      * an exception - getField and setField should be extended to handle the virtual
934      * fields for a particular object correctly.
935      *
936      * @param fieldName Name of the field
937      * @param fieldType Database type of the field
938      * @param fieldSize Size of the field in characters
939      * @param fieldDescription A longer description of this field
940      * (user-understandable hopefully!)
941      * @throws DBException upon error.
942      */

943     protected synchronized void addVirtualField(String JavaDoc fieldName,
944                                                 String JavaDoc fieldType,
945                                                 int fieldSize,
946                                                 String JavaDoc fieldDescription)
947             throws DBException {
948         getDef().addVirtualField(fieldName, fieldType, fieldSize,
949                 fieldDescription);
950     } /* addVirtualField(String, String, int, String) */
951
952     /**
953      * Parses the sort key string into useful values. This version forces full
954      * parsing of the input values to prevent SQL injection attacks.
955      *
956      * @param sortKeyString the pipe delimited string of field names with the optional 'ASC or DESC' keyword afterwords; null indicates no sorting
957      */

958     protected void setSortKey(String JavaDoc sortKeyString) {
959         if (sortKeyString == null) {
960             if (sortKeys != null) {
961                 sortKeys.clear();
962             }
963             return;
964         }
965
966         if (sortKeys == null) {
967             sortKeys = new ArrayList JavaDoc();
968         } else {
969             sortKeys.clear();
970         }
971
972         StringTokenizer JavaDoc stk = new StringTokenizer JavaDoc(sortKeyString, "|");
973
974         while (stk.hasMoreTokens()) {
975             String JavaDoc key = stk.nextToken().trim();
976             String JavaDoc fieldName;
977             String JavaDoc sortOrder = null;
978             int spaceLoc = key.indexOf(" ");
979             boolean ascending = true;
980
981             if (spaceLoc > 0) {
982                 fieldName = key.substring(0, spaceLoc);
983                 sortOrder = key.substring(spaceLoc + 1).trim();
984
985                 if (!(sortOrder.equalsIgnoreCase("ASC") ||
986                         sortOrder.equalsIgnoreCase("DESC"))) {
987                     log.error("Invalid set sort key: FieldName: " + fieldName +
988                             " sortOrder: " + sortOrder);
989                     throw new IllegalArgumentException JavaDoc("Must specify a proper " +
990                             "field name and either ASC or DESC only as the sort order");
991                 }
992
993                 if (sortOrder.equalsIgnoreCase("DESC")) {
994                     ascending = false;
995                 }
996             } else {
997                 fieldName = key;
998             }
999
1000
1001            addSortKey(fieldName, ascending);
1002        }
1003    }
1004
1005    /**
1006     * Add a field to be sorted in a search
1007     *
1008     * @param fieldString the name of the field to sort on
1009     * @param ascending true if using ascending sort order, false if descending
1010     * sort order.
1011     * @throws IllegalArgumentException if the fieldString does not exist
1012     */

1013    public void addSortKey(String JavaDoc fieldString, boolean ascending) {
1014        if (sortKeys == null) {
1015            sortKeys = new ArrayList JavaDoc();
1016        }
1017
1018        //
1019
//We do this call because getFieldMetadata throws IllegalArgumentException
1020
//if the field name does not exist. Thus guarding against potential
1021
//SQL injection attacks.
1022
//
1023
getFieldMetaData(fieldString);
1024
1025
1026        if (ascending) {
1027            sortKeys.add(fieldString + " ASC");
1028        } else {
1029            sortKeys.add(fieldString + " DESC");
1030        }
1031    }
1032
1033    /**
1034     * Clear the sort keys
1035     *
1036     * @see #addSortKey
1037     */

1038    public void clearSortKeys() {
1039        sortKeys = null;
1040    }
1041
1042    /**
1043     * Add a new virtual field to the definition of this object.
1044     * A virtual field is just like a regular one, except it's
1045     * not stored in the target table
1046     * A normal call to getField or setField on a virtual field will throw
1047     * an exception - getField and setField should be extended to handle the virtual
1048     * fields for a particular object correctly.
1049     *
1050     * @param fieldName Name of the field
1051     * @param fieldType Database type of the field
1052     * @param fieldSize Size of the field in characters
1053     * @param descripKey Key into the local langauge file for the
1054     * description of this field
1055     * @param fieldDescription A longer description of this field
1056     * (user-understandable hopefully!)
1057     * @throws DBException upon error.
1058     */

1059    protected synchronized void addVirtualField(String JavaDoc fieldName,
1060                                                String JavaDoc fieldType,
1061                                                int fieldSize,
1062                                                String JavaDoc descripKey,
1063                                                String JavaDoc fieldDescription)
1064            throws DBException {
1065        getDef().addVirtualField(fieldName, fieldType, fieldSize, descripKey,
1066                fieldDescription);
1067    } /* addVirtualField(String, String, int, String) */
1068
1069    /**
1070     * Find the average of the values in the specified field of records
1071     * selected by the DBObject selection criteria.
1072     *
1073     * @param fieldName String DBObject fieldName to average
1074     * @return double Average of the records matching the criteria
1075     * @throws DBException If the search could not be completed
1076     */

1077    public double average(String JavaDoc fieldName)
1078            throws DBException {
1079        return sqlAggrFunction("AVG", fieldName);
1080    } /* average() */
1081
1082
1083    /**
1084     * <p/>
1085     * A &quot;simplified&quote; version of add that can be used to do a basic
1086     * INSERT statement into this table. Often used with data import
1087     * facilities, when referential integrity and other validations
1088     * must be temporarily bypassed. </p>
1089     * <p><b>NOTE</b> BLOB Objects are not added with this function!
1090     * <p><b>NOTE</b> this function does NOT handle auto-increment fields!
1091     *
1092     * @throws DBException If the record cannot be added - this includes if the
1093     * record has a duplicate key
1094     * <p/>
1095     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1096     */

1097    public synchronized void basicAdd()
1098            throws DBException {
1099        boolean needComma = false;
1100
1101        if (!haveAllKeys()) {
1102            throw new DBException("(" + myClassName +
1103                    ") All key fields not present - cannot add new record");
1104        }
1105
1106        JDBCObjectMetaData metadata = getJDBCMetaData();
1107        DBField oneField = null;
1108        FastStringBuffer sqlCommand = new FastStringBuffer(128);
1109        sqlCommand.append("INSERT INTO ");
1110        sqlCommand.append(metadata.getTargetSQLTable(getDataContext()));
1111        sqlCommand.append(" (");
1112
1113        for (Iterator JavaDoc i = metadata.getAllFieldsMap().values().iterator(); i.hasNext();) {
1114            oneField = (DBField) i.next();
1115
1116            if (!oneField.isVirtual()) {
1117                if (isFieldNull(oneField.getName())) {
1118                    continue;
1119                }
1120                if (oneField.isBinaryObjectType()) {
1121                    continue;
1122                }
1123                if (needComma) {
1124                    sqlCommand.append(", ");
1125                }
1126
1127                sqlCommand.append(oneField.getName());
1128                needComma = true;
1129            }
1130        } /* for each field */
1131
1132
1133        sqlCommand.append(") VALUES (");
1134        needComma = false;
1135
1136        for (Iterator JavaDoc i = metadata.getAllFieldsMap().values().iterator(); i.hasNext();) {
1137            oneField = (DBField) i.next();
1138
1139            if (!oneField.isVirtual()) {
1140                if (isFieldNull(oneField.getName())) {
1141                    continue;
1142                }
1143                if (oneField.isBinaryObjectType()) {
1144                    continue;
1145                }
1146
1147                String JavaDoc stringToAdd = null;
1148                if (oneField.isDateType()) {
1149                    if (getFieldData(oneField.getName()) == null
1150                            || (getFieldData(oneField.getName()).length() == 0)) {
1151                        stringToAdd = "null";
1152                    } else {
1153                        try {
1154                            stringToAdd = JDBCUtil.getInstance()
1155                                    .formatDateTime(this, oneField.getName());
1156// stringToAdd = formatDateTime(oneField.getName());
1157
} catch (DBException e) {
1158                            log.warn("Error in basic add, setting field to null", e);
1159                            stringToAdd = "null";
1160                        }
1161                    }
1162                }
1163
1164                if (needComma) {
1165                    sqlCommand.append(", ");
1166                }
1167
1168                if (stringToAdd == null) {
1169                    sqlCommand.append(quoteIfNeeded(oneField.getName(), null));
1170                } else {
1171                    sqlCommand.append(stringToAdd);
1172                }
1173                needComma = true;
1174            } /* if field is not virtual */
1175
1176        } /* for each field */
1177
1178
1179        sqlCommand.append(")");
1180
1181        DBConnection myConnection = null;
1182
1183        try {
1184            if (localConnection != null) {
1185                myConnection = localConnection;
1186            } else {
1187                myConnection = getConnectionPool().getConnection(myClassName);
1188            }
1189
1190            myConnection.executeUpdate(sqlCommand.toString());
1191        } catch (DBException de) {
1192            throw new DBException("(" + myClassName +
1193                    ") Unable to add record to database" +
1194                    forKey() + ":" + de.getMessage() + " (" +
1195                    sqlCommand + ")", de.getDBMessage());
1196        } finally {
1197            if (localConnection == null) {
1198                myConnection.release();
1199            }
1200        }
1201        /* Now log the change if we are logging */
1202        if (getDef().isLoggingEnabled()) {
1203            ChangeLog myChangeLog = new ChangeLog();
1204            myChangeLog.setField("ObjectChanged", myClassName);
1205            myChangeLog.setField("RecordKey", getMyKeys());
1206            myChangeLog.setField("Operation", EVENT_ADD);
1207            myChangeLog.setField("ChangedField", "ALL");
1208            myChangeLog.add();
1209
1210            /* We're done tracking changes */
1211            myUpdates = null;
1212        } /* if */
1213
1214        cacheIsChangedComparison();
1215        setStatus(BaseDataObject.STATUS_CURRENT);
1216        notifyListeners(EVENT_ADD);
1217        NextNumber.getInstance().reset(getDataContext(), this);
1218    } /* basicAdd() */
1219
1220
1221    /**
1222     * No security is applied at the DBObject level, only at the level
1223     * of SecuredDBObjects. The method is still here, however, so that
1224     * it can be called by the cascading delete and any other methods
1225     * as required
1226     *
1227     * @param function The appropriate function code. This is mainly used
1228     * in objects derived from SecuredDBObject
1229     * @return true if this function is alloed
1230     * @throws DBException upon error.
1231     */

1232    public boolean checkAllowed(String JavaDoc function)
1233            throws DBException {
1234        if (log.isDebugEnabled()) {
1235            log.debug("checkAllowed. Function requested: " + function +
1236                    " default return value is true");
1237        }
1238        return true;
1239    }
1240
1241    /**
1242     * Extended by subclasses to make calls to ReferredToBy as needed. This method
1243     * implements the basic referential integrity of an object by specifying all of
1244     * the object that refer to this object as a foreign key. This method is called
1245     * when a delete is about to occurr to verify that removing the current object
1246     * would not create invalid references in the "referring" objects - if it would,
1247     * an exception is thrown, preventing the delete.
1248     *
1249     * @throws DBException If the referential integrity is not correct
1250     */

1251    protected synchronized void checkAllReferredToBy()
1252            throws DBException {
1253    } /* checkAllReferredToBy() */
1254
1255
1256    /**
1257     * Extended by subclasses to make calls to checkRef as needed. Thie method
1258     * implements (along with checkAllReferredToBy()) basic referential integrity
1259     * for this object. Whenever an update or add is about to be made, this method is
1260     * called to see if the changed record is referring to invalid foreign keys - e.g.
1261     * lookup tables that do not have an appropriate related record. If so, an
1262     * Exception is thrown. See the checkRef method for the calls that should be made
1263     * in this method.
1264     *
1265     * @throws DBException If the update/add operation would result in an invalid
1266     * foreign key reference.
1267     */

1268    protected synchronized void checkAllRefs()
1269            throws DBException {
1270    } /* checkAllRefs() */
1271
1272    /**
1273     * Public interface to checkAllRefs()
1274     *
1275     * @throws DBException upon error.
1276     */

1277    public synchronized void checkAllRefsPublic() throws DBException {
1278        checkAllRefs();
1279    }
1280
1281    /**
1282     * Check that a given value is valid for a given field.
1283     * This method is overriden by specific DBObjects to do their own field-level
1284     * validations - they should also call super in order to do the
1285     * standard stuff. Every field is automatically checked by this method before the
1286     * database is updated.
1287     *
1288     * @param fieldName Name of the field to verify
1289     * @param fieldValue Value of the field to be evaluated
1290     * @throws DBException If the value is not valid
1291     */

1292    public synchronized void checkField(String JavaDoc fieldName, String JavaDoc fieldValue)
1293            throws DBException {
1294
1295        DataObjectMetaData objMetadata = getMetaData();
1296        DataFieldMetaData oneField = getDef().getDBField(fieldName);
1297
1298        if (oneField == null) {
1299            FastStringBuffer fsb = FastStringBuffer.getInstance();
1300            String JavaDoc stringValue = null;
1301            try {
1302                fsb.append("No such field as '");
1303                fsb.append(objMetadata.getDescription(getLocale(), fieldName));
1304                fsb.append("'");
1305                stringValue = fsb.toString();
1306            } finally {
1307                fsb.release();
1308                fsb = null;
1309            }
1310
1311            throw new DBException(stringValue);
1312        }
1313
1314        if (oneField.isAutoIncremented()) {
1315            return;
1316        }
1317
1318        //Allow autoinc fields through since they're added last minute.
1319
if ((!oneField.allowsNull()) && (fieldValue == null)) {
1320            getDataField(fieldName).setAttribute(ATTRIBUTE_ERROR, "Y");
1321
1322            String JavaDoc msg = (String JavaDoc) oneField.getAttribute(DBObject.ATTRIBUTE_ERROR_MESSAGE);
1323            if (msg == null) {
1324                FastStringBuffer fsb = FastStringBuffer.getInstance();
1325                try {
1326                    fsb.append("Field '");
1327                    fsb.append(objMetadata.getDescription(getLocale(), fieldName));
1328                    fsb.append("' cannot accept a null value ");
1329                    fsb.append(forKey());
1330                    msg = fsb.toString();
1331                } finally {
1332                    fsb.release();
1333                    fsb = null;
1334                }
1335            }
1336
1337            addFieldError(fieldName, msg);
1338            throw new DBException(msg);
1339
1340        }
1341
1342        /**
1343         * Modification done for avoid empty content check for read only
1344         * field in Add state when you use DBMaint or sevlet like DBMaint to fill table
1345         * The field is checked before it's filled in add method within it's own DBObject.
1346         * @author Yves Henri AMAIZO
1347         */

1348        /* Check for null and not read only column*/
1349        if ((!oneField.allowsNull() && !oneField.isReadOnly()) && fieldValue.length() == 0) {
1350            getDataField(fieldName).setAttribute(ATTRIBUTE_ERROR, "Y");
1351            String JavaDoc msg = (String JavaDoc) oneField.getAttribute(DBObject.ATTRIBUTE_ERROR_MESSAGE);
1352            if (msg == null) {
1353                FastStringBuffer fsb = FastStringBuffer.getInstance();
1354                try {
1355                    fsb.append("Field '");
1356                    fsb.append(objMetadata.getDescription(getLocale(), fieldName));
1357                    fsb.append("' cannot be empty ");
1358                    fsb.append(forKey());
1359                    msg = fsb.toString();
1360                } finally {
1361                    fsb.release();
1362                    fsb = null;
1363                }
1364            }
1365
1366            addFieldError(fieldName, msg);
1367            throw new DBException(msg);
1368        }
1369
1370        if (fieldValue != null && fieldValue.length() > 0) {
1371            Pattern mask = oneField.getMask();
1372
1373            if (mask != null) {
1374                if (!getPatternMatcher().matches(fieldValue, mask)) {
1375                    getDataField(fieldName).setAttribute(ATTRIBUTE_ERROR, "Y");
1376                    String JavaDoc msg = (String JavaDoc) oneField.getAttribute(DBObject.ATTRIBUTE_ERROR_MESSAGE);
1377                    if (msg == null) {
1378                        FastStringBuffer fsb = FastStringBuffer.getInstance();
1379                        try {
1380                            fsb.append("Value '");
1381                            fsb.append(fieldValue);
1382                            fsb.append("' supplied for field '");
1383                            fsb.append(objMetadata.getDescription(getLocale(), fieldName));
1384                            fsb.append("' does not match the regular expression specified for this field.");
1385                            fsb.append(forKey());
1386                            msg = fsb.toString();
1387                        } finally {
1388                            fsb.release();
1389                            fsb = null;
1390                        }
1391
1392                    }
1393
1394                    addFieldError(fieldName, msg);
1395                    throw new DBException(msg);
1396                }
1397            }
1398        }
1399
1400        /* For multi-valued fields, the value specified must be one of the valid values
1401           Note: I have changed this behavior slightly from previous versions.
1402           If a field has been marked as "allowsNull", it should pass the check below,
1403           even if the field has also been marked as "multiValued". In other words,
1404           a mutlivalued, allowsnull field should pass the checkField method if it has a
1405           null value.
1406            - Adam
1407        */

1408
1409        // check for configuration (Setup) switch which turns off all relational integrity checking
1410
String JavaDoc isCheckRelationalIntegrity = Setup.getValueUnrequired(getDataContext(), IS_CHECK_RELATIONAL_INTEGRITY);
1411        boolean isCheck = true; // default is true for legacy; this is only a switch used by a minority of developers
1412
if (isCheckRelationalIntegrity != null) {
1413            isCheck = StringUtil.toBoolean(isCheckRelationalIntegrity);
1414        }
1415
1416        if (isCheck && oneField.isMultiValued() && (!fieldValue.equals(""))) {
1417            Vector JavaDoc values = getValidValues(oneField.getName());
1418
1419            if (values == null) {
1420                throw new DBException("(" + myClassName + ") '" +
1421                        objMetadata.getDescription(getLocale(), fieldName) +
1422                        "' is set as multi-valued, but there are no defined " +
1423                        "valid values" + forKey());
1424            }
1425
1426            ValidValue oneValue = null;
1427
1428            for (Enumeration JavaDoc e = values.elements(); e.hasMoreElements();) {
1429                oneValue = (ValidValue) e.nextElement();
1430
1431                if (oneValue.getKey().equals(fieldValue)) {
1432                    return;
1433                }
1434            } /* for each valid value */
1435
1436            oneField.setAttribute(ATTRIBUTE_ERROR, "Y");
1437            throw new DBException("(" + myClassName + ") '" +
1438                    fieldValue +
1439                    "' is not a valid value for field '" +
1440                    objMetadata.getDescription(getLocale(), fieldName) + "' " + forKey());
1441        } /* if field is multi-valued */
1442
1443    } /* checkField(String, String) */
1444
1445
1446    /**
1447     * Convenience method for checking a reference where blank is *not* allowed
1448     * in the foreign key;
1449     * This test can be turned off if you add
1450     * an Expresso setup (configuration) entry, 'isCheckRelationalIntegrity' set to false
1451     *
1452     * @param foreignKeyNames names of the foreign key fields
1453     * @param refObject The foreign DBObject to check against
1454     * @param errorMessage The custom error message to display if check fails
1455     * @throws DBException if check fails
1456     */

1457    protected synchronized void checkRef(String JavaDoc foreignKeyNames,
1458                                         DBObject refObject,
1459                                         String JavaDoc errorMessage)
1460            throws DBException {
1461        // respect transaction if any
1462
if (getLocalConnection() != null) {
1463            refObject.setConnection(getLocalConnection());
1464        }
1465        checkRef(foreignKeyNames, refObject, errorMessage, false);
1466    }
1467
1468    /**
1469     * Verify referential integrity from the given list of fields to the
1470     * key fields in the given DB Object. Calls to this method are made in the
1471     * checkAllRefs method only. checkAllRefs is automatically called before any
1472     * database updates are made to validate the referential integrity of the update.
1473     * Use of this method (and the checkAllRefs method) allow the referential
1474     * constraints to be "declared", then automatically maintained for a dbobject.
1475     *
1476     * @param foreignKeyNames A semicolon-sperated list of foreign
1477     * key fields from this object
1478     * @param refObject Another DBObject that we are to refer to
1479     * @param errorMessage Error string to throw if the check fails
1480     * @param allowBlank true if blank fields are allowed
1481     * @throws DBException If the referential integrity is not correct
1482     */

1483    protected synchronized void checkRef(String JavaDoc foreignKeyNames,
1484                                         DBObject refObject,
1485                                         String JavaDoc errorMessage,
1486                                         boolean allowBlank)
1487            throws DBException {
1488
1489        // check for configuration (Setup) switch which turns off all relational integrity checking
1490
String JavaDoc isCheckRelationalIntegrity = Setup.getValueUnrequired(getDataContext(), IS_CHECK_RELATIONAL_INTEGRITY);
1491        boolean isCheck = true; // default is true for legacy; this is only a switch used by a minority of developers
1492
if (isCheckRelationalIntegrity != null) {
1493            isCheck = StringUtil.toBoolean(isCheckRelationalIntegrity);
1494        }
1495
1496        if (!isCheck) {
1497            return;
1498        }
1499
1500        refObject.setDataContext(getDataContext());
1501
1502        ArrayList JavaDoc foreignKeys = new ArrayList JavaDoc();
1503        String JavaDoc fieldValue = null;
1504
1505        /* do ALL of the key fields listed have a blank/empty/null value */
1506        boolean allBlank = true;
1507
1508        /* Make a list from the foreignKeyNames */
1509        StringTokenizer JavaDoc stk = new StringTokenizer JavaDoc(foreignKeyNames, ";");
1510
1511        while (stk.hasMoreTokens()) {
1512            foreignKeys.add(stk.nextToken());
1513        }
1514
1515        /* Get the list of key fields from the refObject */
1516        Iterator JavaDoc myKeys = foreignKeys.iterator();
1517
1518        for (Iterator JavaDoc refKeys = refObject.getJDBCMetaData()
1519                .getKeyFieldListArray().iterator();
1520             refKeys.hasNext();) {
1521            String JavaDoc oneMyKey;
1522
1523            if (myKeys.hasNext()) {
1524                oneMyKey = (String JavaDoc) myKeys.next();
1525            } else {
1526                throw new DBException("(" + myClassName +
1527                        ") Wrong number of key elements - the call to " +
1528                        "checkRef must refer to the same number" +
1529                        " of key elements as the referred-to object has");
1530            }
1531
1532            String JavaDoc oneRefKey = (String JavaDoc) refKeys.next();
1533            fieldValue = getField(oneMyKey);
1534
1535            if (!StringUtil.notNull(fieldValue).equals("")) {
1536                allBlank = false;
1537            }
1538
1539            refObject.setField(oneRefKey, fieldValue);
1540        }
1541        /* if we allow blanks, and all are blank, then return */
1542        if (allowBlank && allBlank) {
1543            return;
1544        }
1545        /* Call retrieve on the ref object & catch any error */
1546        if (!refObject.find()) {
1547            throw new DBException(errorMessage);
1548        }
1549    } /* checkRef(String, DBObject, String) */
1550
1551
1552    /**
1553     * Set all fields to empty value & clear the last result set & clear sort keys and customWhereClause
1554     *
1555     * @throws DBException If the fields cannot be cleared
1556     */

1557    public synchronized void clear()
1558            throws DBException {
1559
1560        if (fieldData != null) {
1561            DBField oneField = null;
1562            for (Iterator JavaDoc i = getMetaData().getAllFieldsMap().values().iterator();
1563                 i.hasNext();) {
1564                oneField = (DBField) i.next();
1565                if (!oneField.isVirtual()) {
1566                    // avoid use of setField() here
1567
//setField(oneField.getName(), "");
1568
if (getDef().isLoggingEnabled()) {
1569                        // make this new method
1570
logChange(oneField, null);
1571                    }
1572
1573                    // reset underlying hashmap
1574
fieldData.remove(oneField.getName());
1575                }
1576            }
1577        }
1578
1579        foundKeys = null;
1580        clearSortKeys();
1581        customWhereClause = null;
1582    } /* clear() */
1583
1584
1585    /**
1586     * This convenience method clears all the distinct flags of
1587     * <code>DBFields</code> belonging to this
1588     * <code>DBObject</code>
1589     * <p/>
1590     * author Peter Pilgrim <peter.pilgrim@db.com>
1591     *
1592     * @throws DBException upon error.
1593     * @see #getDistinctFieldCount()
1594     * @see #isDistinct
1595     */

1596    public synchronized void clearDistinctFields()
1597            throws DBException {
1598        distinctFields = null;
1599        anyFieldsDistinct = false;
1600    } /* clearDistinctFields() */
1601
1602
1603    /**
1604     * This convenience method clears all the fileds to be retrieved
1605     * <code>DBFields</code> belonging to this
1606     * <code>DBObject</code>
1607     * <p/>
1608     * author Yves Henri Amaizo <amy_amaizo@compuserve.com>
1609     *
1610     * @throws DBException upon error.
1611     * @see #isFieldsToRetrieve()
1612     */

1613    public synchronized void clearFieldsToRetrieve()
1614            throws DBException {
1615        if (retrieveFields == null) {
1616            return;
1617        } else {
1618            retrieveFields = null;
1619            anyFieldsToRetrieve = false;
1620        }
1621    } /* clearFieldsToRetrieve() */
1622
1623
1624    /**
1625     * See if this field value contains wild cards (e.g. pattern matching
1626     * criteria for the database). The wild cards can be configured via the
1627     * properties file.
1628     *
1629     * @param fieldValue The field value to check for wild cards
1630     * @return True if the string does contain wild cards, False if it does not
1631     * @throws DBException upon error.
1632     */

1633    protected synchronized boolean containsWildCards(String JavaDoc fieldValue)
1634            throws DBException {
1635
1636        return getJDBCUtil().containsWildCards(this, fieldValue);
1637    } /* containsWildCards(String) */
1638
1639
1640    /**
1641     * Just like find, but only retrieves the count, not the records themselves.
1642     *
1643     * @return integer Count of the records matching the criteria
1644     * @throws DBException If the search could not be completed
1645     */

1646    public synchronized int count()
1647            throws DBException {
1648        foundKeys = null;
1649
1650        String JavaDoc sqlString = null;
1651        ArrayList JavaDoc keyFields = getMetaData().getKeyFieldListArray();
1652        FastStringBuffer myStatement = FastStringBuffer.getInstance();
1653        try {
1654
1655            //
1656
//Performance improvement. If we only have ony key field, then
1657
//we count the field itself instead of (*). On some databases, this
1658
//is significantly faster.
1659
//TODO: What about performance where there is more than one key?
1660
//
1661
if (keyFields == null || keyFields.size() > 1 || keyFields.size() == 0) {
1662                myStatement.append("SELECT");
1663                myStatement.append(" COUNT(*) FROM ");
1664            } else {
1665                DataFieldMetaData keymetadata = this
1666                        .getFieldMetaData((String JavaDoc) keyFields.get(0));
1667                myStatement.append("SELECT ");
1668                myStatement.append("COUNT( " + keymetadata.getName() + ") FROM ");
1669            }
1670
1671            myStatement.append(getJDBCMetaData().getTargetSQLTable(getDataContext()));
1672
1673            if (customWhereClause != null) {
1674                myStatement.append(customWhereClause);
1675            } else {
1676                FastStringBuffer fsb = FastStringBuffer.getInstance();
1677                try {
1678                    myStatement.append(buildWhereClauseBuffer(true, fsb));
1679                } catch (IllegalArgumentException JavaDoc ex) {
1680                    log.error("Illegal Argument Exception", ex);
1681                    throw ex;
1682                } finally {
1683                    fsb.release();
1684                    fsb = null;
1685                }
1686            }
1687
1688            sqlString = myStatement.toString();
1689        } catch (Throwable JavaDoc t) {
1690            log.error("Error constructing count query", t);
1691            throw new DBException(t.getMessage());
1692        } finally {
1693            myStatement.release();
1694            myStatement = null;
1695        }
1696
1697        DBConnection myConnection = null;
1698
1699        try {
1700            if (localConnection != null) {
1701                myConnection = localConnection;
1702            } else {
1703                myConnection = getConnectionPool().getConnection(myClassName);
1704            }
1705
1706            myConnection.execute(sqlString);
1707
1708            if (myConnection.next()) {
1709                return myConnection.getInt(1);
1710            }
1711        } catch (DBException de) {
1712            log.error("DBException Error", de);
1713            throw de;
1714        } catch (Throwable JavaDoc t) {
1715            log.error("Error counting field", t);
1716            throw new DBException("Error counting field", t);
1717        } finally {
1718            if (localConnection == null) {
1719                if (myConnection != null) {
1720                    myConnection.release();
1721                }
1722            }
1723        }
1724
1725        return 0;
1726    } /* count() */
1727
1728
1729    /**
1730     * Delete this record from the target table. The current field values for the key
1731     * of this object tell us which record to delete. This default version
1732     * of delete also deletes associated detail records (e.g. performs a
1733     * "cascading delete" of details.
1734     *
1735     * @throws DBException If the delete fails or if the referential integrity
1736     * would be violated by the delete
1737     * @see DBObject#deleteAll to delete objects identified by non-key fields
1738     */

1739    public void delete()
1740            throws DBException {
1741        delete(true);
1742    }
1743
1744    /**
1745     * Delete this record, optionally deleting any associated detail
1746     * records.
1747     *
1748     * @param deleteDetails Delete associated detail records if true
1749     * @throws DBException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1750     * @see DBObject#deleteAll to delete objects identified by non-key fields
1751     */

1752    public synchronized void delete(boolean deleteDetails)
1753            throws DBException {
1754
1755        if (!haveAllKeys()) {
1756            throw new DBException("(" + myClassName +
1757                    getKey() + ") All key fields not present - cannot delete record");
1758        }
1759
1760        /* check referential integrity */
1761        checkAllReferredToBy();
1762
1763        FastStringBuffer sqlCommand = new FastStringBuffer(128);
1764        sqlCommand.append("DELETE FROM ");
1765        sqlCommand.append(getJDBCMetaData().getTargetTable());
1766
1767        if (customWhereClause != null) {
1768            sqlCommand.append(customWhereClause);
1769        } else {
1770            FastStringBuffer fsb = FastStringBuffer.getInstance();
1771            try {
1772                // does this object have key fields?
1773
if (getKeyFieldListIterator().hasNext()) {
1774                    // just use key fields to identify
1775
sqlCommand.append(buildWhereClauseBuffer(false, fsb));
1776                } else {
1777                    // use all fields to identify
1778
sqlCommand.append(buildWhereClauseBuffer(true, fsb));
1779                }
1780            } finally {
1781                fsb.release();
1782            }
1783        }
1784
1785        String JavaDoc commandToExecute = sqlCommand.toString();
1786        DBConnection myConnection = null;
1787        boolean detailsRequireCommittment = false;
1788
1789        try {
1790            if (localConnection != null) {
1791                myConnection = localConnection;
1792            } else {
1793                myConnection = getConnectionPool().getConnection(myClassName);
1794            }
1795
1796            if (myConnection == null) {
1797                throw new DBException("null DB connection");
1798            }
1799
1800            if (deleteDetails) {
1801                /**
1802                 * if possible, details are deleted as part of a transaction,
1803                 * (if DB supports transactions). if part of transaction,
1804                 * commit will happen either
1805                 * 1) when parent record is committed in this routine (if auto-commit is true) or
1806                 * 2) when calling routine commits
1807                 */

1808                boolean originalCommitState = myConnection.getAutoCommit();
1809                try {
1810                    if (myConnection.supportsTransactions()) {
1811                        if (originalCommitState) {
1812                            myConnection.setAutoCommit(false);
1813                        }
1814                        deleteDetails(myConnection);
1815                        detailsRequireCommittment = true;
1816
1817                    } else {
1818                        deleteDetails(null);
1819                    }
1820                } finally {
1821                    if (originalCommitState) {
1822                        myConnection.setAutoCommit(originalCommitState);
1823                    }
1824                }
1825            } /* if we delete the details */
1826
1827
1828            // this will commit details too if auto-commit is true;
1829
// otherwise, it is up to the calling routine to commit
1830
myConnection.executeUpdate(commandToExecute);
1831
1832        } catch (DBException e) {
1833            /**
1834             * @todo will rollback here prevent rollback by calling routine?
1835             */

1836
1837            // roll back any details deletion if we are in control of
1838
// the db connection, i.e., if there was no localConnection
1839
// provided to us
1840
if (detailsRequireCommittment && localConnection == null && myConnection.getAutoCommit() == false) {
1841                myConnection.rollback();
1842            }
1843
1844            throw ((DBException) e.fillInStackTrace());
1845        } finally {
1846            if (localConnection == null) {
1847                myConnection.release();
1848            }
1849        }
1850
1851        if (myConnection.getUpdateCount() == 0) {
1852            log.error("No record(s) deleted for SQL '" +
1853                    sqlCommand.toString() + "'");
1854            throw new DBException("No record(s) deleted.");
1855        }
1856        if (isCached()) {
1857            removeFromCache(this);
1858        }
1859
1860        /* Now log the change if we are logging */
1861        if (getDef().isLoggingEnabled()) {
1862            ChangeLog myChangeLog = new ChangeLog();
1863            myChangeLog.setDataContext(getDataContext());
1864            myChangeLog.setField("ObjectChanged", myClassName);
1865            myChangeLog.setField("RecordKey", getMyKeys());
1866            myChangeLog.setField("Operation", EVENT_DELETE);
1867            myChangeLog.setField("ChangedField", "ALL");
1868            myChangeLog.add();
1869
1870            /* We're done tracking changes */
1871            myUpdates = null;
1872        } /* if */
1873
1874
1875        setStatus(BaseDataObject.STATUS_DELETED);
1876        notifyListeners(EVENT_DELETE);
1877    } /* delete() */
1878
1879
1880    /**
1881     * Delete all of the records specified by the current search criteria. If
1882     * you use a blank DBObject, then all the records in the table will be
1883     * deleted
1884     *
1885     * @throws DBException upon error
1886     */

1887    public synchronized void deleteAll()
1888            throws DBException {
1889
1890
1891// Old Memory intensive way of doing it :)
1892
// ArrayList list = searchAndRetrieveList();
1893
// for (Iterator iterator = list.iterator(); iterator.hasNext();) {
1894
// DBObject object = (DBObject) iterator.next();
1895
// object.delete();
1896
// }
1897

1898        //Build an object that we're going to query with.
1899
DBObject testObject = newInstance();
1900        DataObjectMetaData metadata = getMetaData();
1901        for (Iterator JavaDoc i = getMetaData().getFieldListArray().iterator();
1902             i.hasNext();) {
1903            String JavaDoc fieldName = (String JavaDoc) i.next();
1904            DataFieldMetaData fieldMetadata = metadata.getFieldMetadata(fieldName);
1905            if (fieldMetadata.isLongObjectType() || fieldMetadata.isVirtual()) {
1906                if (log.isDebugEnabled()) {
1907                    log.debug("Skipping field type" + fieldName);
1908                }
1909            } else {
1910                // does local field have a value set?
1911
DataField datafield = getDataField(fieldName);
1912                // ignore this field if no setting
1913
if (!datafield.isValueSet()) {
1914                    continue;
1915                }
1916
1917                Object JavaDoc valObj = datafield.getValue();
1918                String JavaDoc valStr = null;
1919                if (valObj != null) {
1920                    valStr = valObj.toString();
1921                }
1922                if (valStr != null && valStr.length() > 0) {
1923                    // use 'raw' value in data field
1924
testObject.setField(fieldName, datafield.getValue().toString());
1925                }
1926            }
1927        }
1928
1929        testObject.setMaxRecords(100);
1930
1931        //
1932
//Iterate for each 100 items. So we don't load more records at once
1933
//than memory can handle
1934
//
1935
int num = 0;
1936        ArrayList JavaDoc al = new ArrayList JavaDoc(100);
1937        do {
1938            if (customWhereClause != null) {
1939                testObject.setCustomWhereClause(getCustomWhereClause().substring(WHERE_KEYWORD.length()),
1940                        appendCustomWhere);
1941            }
1942            al = testObject.searchAndRetrieveList(); // will clear custom where clause
1943
num = al.size();
1944            for (Iterator JavaDoc i = al.iterator(); i.hasNext();) {
1945                DBObject oneObject = (DBObject) i.next();
1946                oneObject.delete();
1947            }
1948
1949            al.clear(); // help gc
1950
} while (num >= 100);
1951
1952
1953// ArrayList retrievedFieldList = new ArrayList();
1954
//
1955
// DBConnection listingConnection = null;
1956
// try {
1957
// listingConnection = createAndExecuteSearch(retrievedFieldList);
1958
// //Only allocate if we're gonna load this record
1959
// DBObject myObj = newInstance();
1960
//
1961
//
1962
// while (listingConnection.next()) {
1963
// myObj.clear();
1964
// loadFromConnection(myObj,listingConnection,retrievedFieldList);
1965
// myObj.delete();
1966
// }
1967
// } catch (DBException de) {
1968
// log.error("Error performing deleteAll", de);
1969
// throw new DBException(de);
1970
// } catch (Throwable t) {
1971
// log.error("Error performing deleteAll", t);
1972
// throw new DBException("Error performing deleteAll", t);
1973
// } finally {
1974
// if (localConnection == null) {
1975
// if (listingConnection != null) {
1976
// getConnectionPool().release(listingConnection);
1977
// }
1978
// }
1979
// }
1980
}
1981
1982    /**
1983     * Delete all of the records specified by the current search criteria. If
1984     * you use a blank DBObject, then all the records in the table will be
1985     * deleted
1986     *
1987     * @param oneByOne set 'true' usually, to make sure that all 'detail' records are also deleted. Set to false if you are sure that the table has no detail records (i.e., it supplies no foreign keys--has no dependent records)
1988     * @throws DBException upon error
1989     */

1990    public synchronized void deleteAll(boolean oneByOne)
1991            throws DBException {
1992
1993        if (oneByOne) {
1994            deleteAll();
1995        } else {
1996            doDeleteAll(true);
1997        }
1998    } /* deleteAll(boolean) */
1999
2000    /**
2001     * Delete all records inside the database.
2002     * Important: this method ignores any 'detail' records. Use this only if you are sure that the table has no detail records (i.e., it supplies no foreign keys--has no dependent records)
2003     *
2004     * @param deleteAllRecords Delete associated detail records if true
2005     * @throws DBException Modify
2006     * @todo move code here to impl. of deleteAll(), using logic to figure out whether efficient technique below can be used; i.e., whether dbobject has any dependent details.
2007     * @see DBObject#deleteAll to delete objects identified by non-key fields
2008     */

2009    private synchronized void doDeleteAll(boolean deleteAllRecords)
2010            throws DBException {
2011
2012        FastStringBuffer sqlCommand = new FastStringBuffer(128);
2013        sqlCommand.append("DELETE FROM ");
2014        sqlCommand.append(getJDBCMetaData().getTargetSQLTable(getDataContext()));
2015
2016        String JavaDoc whereClause;
2017
2018        if (customWhereClause != null) {
2019            if (appendCustomWhere) {
2020                whereClause = buildWhereClause(true) + customWhereClause;
2021                appendCustomWhere = false;
2022            } else {
2023                whereClause = customWhereClause;
2024            }
2025            sqlCommand.append(whereClause);
2026            customWhereClause = null;
2027        } else {
2028            FastStringBuffer fsb = FastStringBuffer.getInstance();
2029            try {
2030                // does this object have key fields?
2031
if (this.getKeyFieldListIterator().hasNext()) {
2032                    // just use key fields to identify
2033
sqlCommand.append(buildWhereClauseBuffer(false, fsb));
2034                } else {
2035                    // use all fields to identify
2036
sqlCommand.append(buildWhereClauseBuffer(true, fsb));
2037                }
2038            } finally {
2039                fsb.release();
2040            }
2041        }
2042
2043        String JavaDoc commandToExecute = sqlCommand.toString();
2044        DBConnection myConnection = null;
2045
2046        try {
2047            if (localConnection != null) {
2048                myConnection = localConnection;
2049            } else {
2050                myConnection = this.getConnectionPool().getConnection(this.myClassName);
2051            }
2052
2053            if (myConnection == null) {
2054                throw new DBException("null DB connection");
2055            }
2056
2057            // this will commit details too if auto-commit is true;
2058
// otherwise, it is up to the calling routine to commit
2059
myConnection.executeUpdate(commandToExecute);
2060
2061        } catch (DBException e) {
2062            /**
2063             * @todo will rollback here prevent rollback by calling routine?
2064             */

2065
2066            throw ((DBException) e.fillInStackTrace());
2067        } finally {
2068            if (localConnection == null) {
2069                myConnection.release();
2070            }
2071        }
2072
2073        if (myConnection.getUpdateCount() == 0) {
2074            log.debug("No record(s) deleted for SQL '" +
2075                    sqlCommand.toString() + "'");
2076// throw new DBException("No record(s) deleted.");
2077
}
2078// if (isCached()) {
2079
// removeFromCache(this);
2080
// }
2081

2082        /* Now log the change if we are logging */
2083        if (getDef().isLoggingEnabled()) {
2084            ChangeLog myChangeLog = new ChangeLog();
2085            myChangeLog.setDataContext(getDataContext());
2086            myChangeLog.setField("ObjectChanged", this.myClassName);
2087            myChangeLog.setField("RecordKey", this.getMyKeys());
2088            myChangeLog.setField("Operation", "D");
2089            myChangeLog.setField("ChangedField", "ALL");
2090            myChangeLog.add();
2091
2092            /* We're done tracking changes */
2093            myUpdates = null;
2094        } /* if */
2095
2096
2097// setStatus(BaseDataObject.STATUS_DELETED);
2098
// notifyListeners("D");
2099
} /* doDeleteAll() */
2100
2101
2102    /**
2103     * If this DB object has associated detail objects,
2104     * locate the appropriate related detail records and
2105     * delete them as well. This is a "cascading delete" process.
2106     *
2107     * @param detailConnection the DBConnection to use while retrieving details
2108     * @throws DBException upon error.
2109     */

2110    protected void deleteDetails(DBConnection detailConnection)
2111            throws DBException {
2112        String JavaDoc oneDet = null;
2113
2114        for (Enumeration JavaDoc ee = getDetails(); ee.hasMoreElements();) {
2115            oneDet = (String JavaDoc) ee.nextElement();
2116
2117            DBObject detailObj = null;
2118
2119            try {
2120                detailObj = (DBObject) ClassLocator.loadClass(oneDet).newInstance();
2121            } catch (Exception JavaDoc e) {
2122                throw new DBException("Unable to instantiate " +
2123                        "detail db object '" + oneDet + "'", e);
2124            }
2125            copyAttributes(detailObj); // copy userId, data context
2126
// use given connection
2127
if (detailConnection != null) {
2128                detailObj.setConnection(detailConnection);
2129            } else {
2130                detailObj.setDataContext(getDataContext());
2131            }
2132
2133            checkDeleteDetailPerm(detailObj);
2134
2135            StringTokenizer JavaDoc stkLocal = new StringTokenizer JavaDoc(getJDBCMetaData().getDetailFieldsLocal(oneDet),
2136                    "|");
2137            StringTokenizer JavaDoc stkForeign = new StringTokenizer JavaDoc(getJDBCMetaData().getDetailFieldsForeign(oneDet),
2138                    "|");
2139
2140            boolean foundValues = false;
2141            while (stkLocal.hasMoreTokens()) {
2142                String JavaDoc localField = stkLocal.nextToken();
2143                String JavaDoc foreignField = stkForeign.nextToken();
2144
2145                // does local field have a value set?
2146
DataField datafield = getDataField(localField);
2147                if (datafield == null || !datafield.isValueSet()) {
2148                    continue;
2149                }
2150
2151                // only set field if we have a value
2152
Object JavaDoc valObj = datafield.getValue();
2153                String JavaDoc value = null;
2154                if (valObj != null) {
2155                    value = valObj.toString();
2156                }
2157                if (value != null && value.length() > 0) {
2158                    detailObj.setField(foreignField, value);
2159                    foundValues = true;
2160                }
2161            }
2162
2163            // do not delete if empty, since all the items in the detail table would be deleted
2164
// if we have no criteria for a detail
2165
if (foundValues) {
2166                try {
2167                    detailObj.deleteAll();
2168                } catch (DBException ex) {
2169                    log.error("Error deleting detail object", ex);
2170                }
2171            }
2172        } /* for each detail */
2173
2174    } /* deleteDetails */
2175
2176    /**
2177     * extracted for subclasses checking
2178     *
2179     * @param obj the dbobject to check it's permission
2180     * @throws DBException upon error
2181     */

2182    protected void checkDeleteDetailPerm(DBObject obj) throws DBException {
2183        obj.checkAllowed("D");
2184    }
2185
2186    /**
2187     * Does a given field value denote a range?
2188     *
2189     * @param fieldValue The field value to check against.
2190     * @return The "range" string if the value starts with a range indicator, null if not
2191     */

2192    protected String JavaDoc denotesRange(String JavaDoc fieldValue) {
2193        return rangeVerifier.denotesRange(fieldValue);
2194    } /* denotesRange(String) */
2195
2196
2197    /**
2198     * A lot like retrieve, but works with any fields, not just the key field. Does not
2199     * throw an exception if the record is not found, just returns false.
2200     * Finds only first record matching the criteria. The current fields in this object
2201     * are populated with the data in the record found after the call to find() if the
2202     * find is successful.
2203     *
2204     * @return boolean If the search resulted in a record
2205     * @throws DBException If the search could not be completed
2206     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
2207     */

2208    public boolean find()
2209            throws DBException {
2210
2211
2212        //
2213
//If we have all the keys, then use retrieve which is much faster
2214
//than find.
2215
//
2216
boolean haveAllKeys = false;
2217        com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData metadata = getJDBCMetaData();
2218
2219        ArrayList JavaDoc keyfields = metadata.getKeyFieldListArray();
2220        int keysize = keyfields.size();
2221        for (int i = 0; i < keysize; i++) {
2222            String JavaDoc oneFieldName = (String JavaDoc) keyfields.get(i);
2223            DataField df = getDataField(oneFieldName);
2224            if (df == null || df.isNull()) {
2225                haveAllKeys = false;
2226                break;
2227            }
2228            haveAllKeys = true;
2229        }
2230
2231// for (Iterator i = getAllKeysIterator(); i.hasNext();) {
2232
// DBField oneField = (DBField) i.next();
2233
//
2234
// DataField df = getDataField(oneField.getName());
2235
// if (df == null || df.isNull()) {
2236
// haveAllKeys = false;
2237
// break;
2238
// }
2239
// haveAllKeys = true;
2240
// }
2241

2242        //Release the reference.
2243
keyfields = null;
2244
2245        if (haveAllKeys && (!anyFieldsDistinct)) {
2246            try {
2247                retrieve();
2248                return true;
2249            } catch (DBRecordNotFoundException ex) {
2250                return false;
2251            }
2252        }
2253
2254        boolean needComma = false;
2255        DataFieldMetaData oneField = null;
2256        foundKeys = null;
2257
2258        //Pre-allocate to the size of the field list.
2259
ArrayList JavaDoc retrievedFieldList = new ArrayList JavaDoc(metadata.getFieldListArray().size());
2260        FastStringBuffer myStatement = FastStringBuffer.getInstance();
2261        String JavaDoc sqlStatement = null;
2262        try {
2263            myStatement.append("SELECT ");
2264
2265            if (anyFieldsDistinct) {
2266                String JavaDoc oneFieldName = null;
2267                myStatement.append(" " + getConnectionPool().getDistinctRowsetKeyword() + " ");
2268
2269                for (Iterator JavaDoc i = getDistinctFieldArrayList().iterator();
2270                     i.hasNext();) {
2271                    oneFieldName = (String JavaDoc) i.next();
2272                    retrievedFieldList.add(oneFieldName);
2273
2274                    if (needComma) {
2275                        myStatement.append(", ");
2276                    }
2277
2278                    myStatement.append(selectFieldString(oneFieldName));
2279                    needComma = true;
2280                }
2281            } else if (anyFieldsToRetrieve) { /* for */
2282                String JavaDoc oneFieldName = null;
2283
2284                for (Iterator JavaDoc i = getFieldsToRetrieveIterator(); i.hasNext();) {
2285                    oneFieldName = (String JavaDoc) i.next();
2286
2287                    if (needComma) {
2288                        myStatement.append(", ");
2289                    }
2290
2291                    retrievedFieldList.add(oneFieldName);
2292                    myStatement.append(selectFieldString(oneFieldName));
2293                    needComma = true;
2294                }
2295            } else { /* for each field */
2296                for (Iterator JavaDoc i = metadata.getAllFieldsMap().values().iterator(); i.hasNext();) {
2297                    oneField = (DBField) i.next();
2298
2299                    if (!oneField.isVirtual() && !oneField.isBinaryObjectType()) {
2300                        if (needComma) {
2301                            myStatement.append(", ");
2302                        }
2303
2304                        retrievedFieldList.add(oneField.getName());
2305                        myStatement.append(selectFieldString(oneField.getName()));
2306                        needComma = true;
2307                    } /* if field is not virtual */
2308
2309                } /* for */
2310            }
2311
2312            myStatement.append(" FROM ");
2313            myStatement.append(metadata.getTargetSQLTable(this.getDataContext()));
2314
2315            if (customWhereClause != null) {
2316                myStatement.append(customWhereClause);
2317                customWhereClause = null;
2318            } else {
2319                FastStringBuffer fsb = FastStringBuffer.getInstance();
2320                try {
2321                    myStatement.append(buildWhereClauseBuffer(true, fsb));
2322                } finally {
2323                    fsb.release();
2324                }
2325            }
2326
2327            sqlStatement = myStatement.toString();
2328        } finally {
2329            myStatement.release();
2330            myStatement = null;
2331
2332        }
2333
2334        DBConnection myConnection = null;
2335
2336        try {
2337            if (localConnection != null) {
2338                myConnection = localConnection;
2339            } else {
2340                myConnection = getConnectionPool().getConnection(myClassName);
2341            }
2342
2343            myConnection.execute(sqlStatement);
2344
2345            String JavaDoc oneFieldName = null;
2346            String JavaDoc oneFieldValue = ("");
2347            if (foundKeys == null) {
2348                //Pre-allocate to the size of the key fields for this object.
2349
foundKeys = new ArrayList JavaDoc(metadata.getKeyFieldListArray().size());
2350            }
2351            if (myConnection.next()) {
2352                String JavaDoc theKeyString = null;
2353                FastStringBuffer oneKeyString = FastStringBuffer.getInstance();
2354                try {
2355
2356                    int i = 1;
2357
2358                    for (Iterator JavaDoc it = retrievedFieldList.iterator();
2359                         it.hasNext();) {
2360                        oneFieldName = (String JavaDoc) it.next();
2361                        oneField = getFieldMetaData(oneFieldName);
2362
2363// * @author Yves Henri AMAIZO
2364
// Handle correctly date from resultSet data retrieve from Database
2365
try {
2366                            if (oneField.isDateType()) {
2367                                oneFieldValue = getCustomStringFieldValue(myConnection, oneField.getName());
2368                            } else {
2369// if (!oneField.isLongBinaryType() && !oneField.isLongCharacterType()) {
2370
// if (myConnection.isStringNotTrimmed()) {
2371
// tmpData = myConnection.getStringNoTrim(oneFieldName);
2372
// } else {
2373
// tmpData = myConnection.getString(oneFieldName);
2374
// }
2375
// } else {
2376
// if (oneField.isLongBinaryType()) {
2377
// tmpData = null;
2378
// InputStream is = myConnection.getBinaryStream(oneFieldName);
2379
// if (is != null) {
2380
// byte[] bstr = new byte[LONGBINARY_READ_DEFAULT_SIZE];
2381
// int j = is.read(bstr);
2382
// if (j > 0) {
2383
// byte[] content = new byte[j];
2384
// System.arraycopy(bstr, 0, content, 0, j);
2385
// tmpData = content;
2386
// }
2387
// }
2388
// } else {
2389
// tmpData = myConnection.getStringNoTrim(oneFieldName);
2390
// }
2391
// }
2392

2393
2394                                if (myConnection.isStringNotTrim()) {
2395                                    oneFieldValue = myConnection.getStringNoTrim(i);
2396                                } else {
2397                                    oneFieldValue = myConnection.getString(i);
2398                                }
2399                            }
2400                        } catch (DBException de1) {
2401                            throw new DBException("(" + myClassName +
2402                                    ") Error retrieving field '" +
2403                                    oneFieldName + "' index " + i +
2404                                    ":" + de1.getMessage(), de1.getDBMessage());
2405                        } catch (Exception JavaDoc e1) {
2406                            throw new DBException("(" + this.myClassName +
2407                                    ") Error retrieving field '" +
2408                                    oneFieldName + "' index " + i +
2409                                    ":" + e1.getMessage(), e1.getMessage());
2410                        }
2411
2412                        i++;
2413                        setField(oneFieldName, oneFieldValue);
2414// set(oneFieldName, tmpData);
2415

2416                        if (oneField.isKey()) {
2417                            if (i > 1) {
2418                                oneKeyString.append("/");
2419                            }
2420
2421                            oneKeyString.append(oneFieldValue);
2422                        } /* if field is key */
2423
2424                    }
2425                    theKeyString = oneKeyString.toString();
2426                } finally {
2427                    oneKeyString.release();
2428                    oneKeyString = null;
2429                }
2430
2431                foundKeys.add(theKeyString);
2432            } else { /* for each field */
2433                return false;
2434            }
2435            if (getDef().isLoggingEnabled()) {
2436                myUpdates = null;
2437            }
2438
2439            cacheIsChangedComparison();
2440            setStatus(BaseDataObject.STATUS_CURRENT);
2441
2442            if (isCached() && !anyFieldsToRetrieve) {
2443                //Doesn't stale anything, drop it in the cache as is.
2444
cacheUtils.addUnmodifiedToCache(this);
2445            }
2446
2447            return true;
2448        } catch (NullPointerException JavaDoc npe) {
2449            npe.printStackTrace();
2450            throw npe;
2451        } catch (ClassCastException JavaDoc cce) {
2452            log.error("Fatal Error in find: " + cce.getMessage(), cce);
2453            throw cce;
2454        } catch (DBException de) {
2455            de.printStackTrace();
2456            log.error("Error in find: " + de.getMessage(), de);
2457            throw de;
2458        } finally {
2459            if (localConnection == null) {
2460                if (myConnection != null) {
2461                    myConnection.release();
2462                }
2463            }
2464        }
2465    } /* find() */
2466
2467
2468    /**
2469     * Return a string we can use in error messages to indicate the record
2470     * that had the problem by including the key and the current database/context
2471     *
2472     * @return String: A formatted string showing the key of this record
2473     */

2474    public String JavaDoc forKey() {
2475
2476        FastStringBuffer keyString = FastStringBuffer.getInstance();
2477        String JavaDoc returnValue = null;
2478        try {
2479            keyString.append(" for record with key '");
2480            boolean needSlash = false;
2481            boolean blankKey = true;
2482
2483            try {
2484                for (Iterator JavaDoc i = getJDBCMetaData().getKeyFieldListArray().iterator(); i.hasNext();) {
2485                    String JavaDoc fieldValue = getField((String JavaDoc) i.next());
2486
2487                    if (fieldValue == null || fieldValue.length() == 0) {
2488                        continue;
2489                    }
2490
2491                    blankKey = false;
2492                    if (needSlash) {
2493                        keyString.append("/");
2494                    }
2495                    keyString.append(fieldValue);
2496
2497                    needSlash = true;
2498                }
2499            } catch (DBException de) {
2500                keyString.append(" -- UNABLE TO GET FIELD LIST --");
2501                log.error(de);
2502            }
2503
2504            if (blankKey) {
2505                return "";
2506            }
2507
2508            keyString.append("' in database '" + getDataContext() + "'");
2509            returnValue = keyString.toString();
2510        } finally {
2511            keyString.release();
2512        }
2513
2514        return returnValue;
2515    } /* forKey() */
2516
2517    /**
2518     * Given the value of a date/time or date/time field, return the value formatted
2519     * as appropriate for the current DBMS. Can be configured using property
2520     * file values.
2521     *
2522     * @param fieldName java.lang.String The value for the date/time field.
2523     * @return java.lang.String The formatted date time, ready for use in the DBMS
2524     * @throws DBException upon error.
2525     */

2526    public String JavaDoc formatDateTime(String JavaDoc fieldName)
2527            throws DBException {
2528        return JDBCUtil.getInstance().formatDateTime(this, fieldName);
2529    } /* formatDateTime(String) */
2530
2531
2532    /**
2533     * Return an "attribute". Attributes are temporary (e.g. not stored in the DBMS)
2534     * values associated with this particular DB object instance.
2535     *
2536     * @param attribName The attribute name to check
2537     * @return the object associated with this attribute
2538     */

2539    public Object JavaDoc getAttribute(String JavaDoc attribName) {
2540        if (attributes != null) {
2541            return attributes.get(attribName);
2542        } else {
2543            return null;
2544        }
2545    } /* getAttribute(String) */
2546
2547
2548    /**
2549     * Get an iterator for all of the attributes specified for a field
2550     *
2551     * @param fieldName The field name to get the attirbutes for
2552     * @return the Iterator for all attributes associated with this field
2553     * @throws DBException upon error.
2554     */

2555    public Iterator JavaDoc getAttributesIterator(String JavaDoc fieldName)
2556            throws DBException {
2557        return getDef().getAttributesIterator(fieldName);
2558    }
2559
2560    /**
2561     * Gets the set size of the cache for this DBOBject
2562     *
2563     * @return The number of cache objects available for this object.
2564     */

2565    public int getCacheSize() {
2566
2567        /* Special case for the DBObjLimit object */
2568        if (myClassName.equals(DBObjLimit.class.getName())) {
2569            return 0;
2570        }
2571        if (myCacheSize == -2) {
2572            if (log.isDebugEnabled()) {
2573                log.debug("Cache size not set for " + myClassName +
2574                        " - setting");
2575            }
2576
2577            setCacheSize();
2578        }
2579        if (log.isDebugEnabled()) {
2580            log.debug("Cache size for " + myClassName + " is " +
2581                    myCacheSize);
2582        }
2583
2584        return myCacheSize;
2585    } /* getCacheSize() */
2586
2587
2588    /**
2589     * @return a HashMap of all cache stats
2590     */

2591    public synchronized static HashMap getCacheStatsMap() {
2592        return new HashMap(sCacheStats);
2593    }
2594
2595
2596    /**
2597     * Returns the metadata for the specified field.
2598     *
2599     * @param fieldName The name of the field to get
2600     * @return The DBField for this fieldName
2601     */

2602    public DataFieldMetaData getFieldMetaData(String JavaDoc fieldName) {
2603        StringUtil.assertNotBlank(fieldName, "Field name may not be blank");
2604        DBField oneField = (DBField) getMetaData().getFieldMetadata(fieldName);
2605
2606        if (oneField == null) {
2607            throw new IllegalArgumentException JavaDoc("(" + myClassName +
2608                    ") No such field as '" + fieldName +
2609                    "' in object '" + myClassName +
2610                    "'");
2611        }
2612
2613        return oneField;
2614    } /* getDBField(String) */
2615
2616    /**
2617     * Return the name of the context/database connection that this DB object is using.
2618     * If none is set, then we are using the "default" database/context.
2619     *
2620     * @return a String containing the name of the DBName to use.
2621     * @deprecated since Expresso 5.1
2622     * Use getDataContext() instead
2623     */

2624    public synchronized String JavaDoc getDBName() {
2625        return getDataContext();
2626    } /* getDBName() */
2627
2628    /**
2629     * Get a list of all db objects that are specified as being "details"
2630     * to this db object. Returns an empty enumeration if there are no details
2631     *
2632     * @return java.util.Enumeration containing all detail objects
2633     * @throws DBException upon error.
2634     */

2635    public Enumeration JavaDoc getDetails()
2636            throws DBException {
2637        return getDef().getDetails();
2638    }
2639
2640
2641    /**
2642     * This convenience method counts
2643     * <code>DBFields</code> belonging to this
2644     * <code>DBObject</code> that are set to <b>distinct</b>.
2645     *
2646     * @return int number of distinct fields.
2647     * <p/>
2648     * author Peter Pilgrim <peter.pilgrim@db.com>
2649     * @throws DBException upon error.
2650     */

2651    public synchronized int getDistinctFieldCount()
2652            throws DBException {
2653        if (distinctFields == null) {
2654            return 0;
2655        }
2656
2657        int num = 0;
2658
2659        for (Iterator JavaDoc i = getMetaData().getAllFieldsMap().values().iterator(); i.hasNext();) {
2660            DBField oneField = (DBField) i.next();
2661
2662            if (distinctFields.containsKey(oneField.getName())) {
2663                ++num;
2664            }
2665        }
2666
2667        return (num);
2668    } /* getDistinctFieldCount() */
2669
2670
2671    /**
2672     * <p>This convenience method iterates through all the
2673     * fields belonging to this <code>DBObject</code>
2674     * returns an array of field names ( <code>String</code> )
2675     * that are set to <b>distinct</b>.</p>
2676     * <p/>
2677     * <p>If there are are no distinct fields then the method
2678     * returns a <code>null</code> reference.</p>
2679     *
2680     * @return String array of <b>distinct</b> field names in this object.
2681     * <p/>
2682     * author Peter Pilgrim <peter.pilgrim@db.com>
2683     * @throws DBException upon error.
2684     * @see #getDistinctFieldCount()
2685     * @see #isDistinct()
2686     */

2687    public String JavaDoc[] getDistinctFields()
2688            throws DBException {
2689        ArrayList JavaDoc al = new ArrayList JavaDoc();
2690
2691        if (distinctFields == null) {
2692            return (String JavaDoc[]) al.toArray();
2693        }
2694        for (Iterator JavaDoc it = getMetaData().getFieldListArray().iterator(); it.hasNext();) {
2695            String JavaDoc fieldName = (String JavaDoc) it.next();
2696
2697            if (distinctFields.containsKey(fieldName)) {
2698                al.add(fieldName);
2699            }
2700        }
2701
2702        int N = al.size();
2703
2704        if (N < 1) { // No fields are distinct.
2705
return null;
2706        }
2707
2708        return (String JavaDoc[]) al.toArray();
2709    } /* getDistinctFields() */
2710
2711
2712    /**
2713     * Get the string value of a field in this object as a string
2714     *
2715     * @param fieldName Name of the field to fetch
2716     * @return The value of the given field as a string - if the field is null,
2717     * an empty string is returned.
2718     * @throws DBException If there is no such field or it's value cannot be accessed
2719     */

2720    public synchronized String JavaDoc getField(String JavaDoc fieldName)
2721            throws DBException {
2722        DataFieldMetaData oneField = getFieldMetaData(fieldName);
2723
2724        if (oneField == null) {
2725            throw new DBException("(" + myClassName +
2726                    ") No such field as '" + fieldName + "'");
2727        }
2728        if (oneField.isVirtual()) {
2729            throw new DBException("(" + myClassName + ") Field " +
2730                    fieldName +
2731                    " is a virtual field. Database object should extend " +
2732                    "getField method to handle requests for this field");
2733        }
2734
2735        String JavaDoc returnValue = StringUtil.notNull(getFieldData(oneField.getName()));
2736
2737        if (returnValue.length() == 0) {
2738            return returnValue;
2739        }
2740
2741        if (oneField.isBooleanType()) {
2742
2743            if (returnValue.equals("true") || returnValue.equals("t") ||
2744                    returnValue.equalsIgnoreCase("y") || returnValue.equals("1")) {
2745                return "true";
2746            } else {
2747                return "false";
2748            }
2749        }
2750
2751        /**
2752         * If the record is not yet stored in the database, don't filter the
2753         * value being returned - it might be a search criteria
2754         */

2755        if (getStatus().equals(BaseDataObject.STATUS_NEW)) {
2756            return returnValue;
2757        }
2758
2759
2760        Class JavaDoc filterclass = getFilterClass(); // instance setting
2761
if (filterclass == null) {
2762            // use static field info
2763
filterclass = ((DBField) oneField).getFilterClass();
2764        }
2765
2766        try {
2767            return getDef().getFilterManager().filterString(returnValue,
2768                    filterclass,
2769                    ((DBField) oneField).getFilterMethod());
2770        } catch (Exception JavaDoc ex) {
2771            throw new DBException("(" + myClassName +
2772                    ") Error Filtering Field: " + fieldName, ex);
2773        }
2774    } /* getField(String) */
2775
2776
2777    /**
2778     * Internal mechanism for getting the raw field data
2779     *
2780     * @param fieldName the name of the field to retrieve the data for
2781     * @return java.lang.String or null if the fieldData doesn't exist in the map
2782     */

2783    protected String JavaDoc getFieldData(String JavaDoc fieldName) {
2784        if (fieldData == null) {
2785            return null;
2786        }
2787
2788        DataField df = (DataField) fieldData.get(fieldName);
2789        if (df == null) {
2790            return null;
2791        }
2792
2793        Object JavaDoc o = df.getValue();
2794        if (o == null) {
2795            return null;
2796        }
2797
2798        return o.toString();
2799    }
2800
2801    /**
2802     * Boolean typesafe getField
2803     *
2804     * @param fieldName to retrieve
2805     * @return boolean true false
2806     * @throws DBException upon error.
2807     * @todo - Do databases localize "true and false" values in strings?
2808     */

2809    public boolean getFieldBoolean(String JavaDoc fieldName)
2810            throws DBException {
2811        DataFieldMetaData oneField = getFieldMetaData(fieldName);
2812
2813        if (oneField == null) {
2814            throw new DBException("(" + myClassName +
2815                    ") No such field as " + fieldName);
2816        }
2817
2818        if (oneField.getAttribute("checkbox") == null && !oneField.isBooleanType()) {
2819            throw new DBException("(" + myClassName + ") Field " +
2820                    fieldName + " is not a boolean field");
2821        }
2822
2823
2824        String JavaDoc strVal = getFieldData(oneField.getName());
2825
2826        if (strVal == null) {
2827            return false;
2828        }
2829
2830        if (strVal.equals("true") || strVal.equals("t") ||
2831                strVal.equalsIgnoreCase("y") || strVal.equals("1")) {
2832            return true;
2833        } else {
2834            return false;
2835        }
2836    }
2837
2838    /**
2839     * Return the value of a field as a Date object
2840     *
2841     * @param fieldName The field to be retrieved
2842     * @return The Date object equivilant to this field's value
2843     * @throws DBException If the field does not exist or it's value
2844     * is not a date or cannot be converted to a date
2845     */

2846    public java.util.Date JavaDoc getFieldDate(String JavaDoc fieldName)
2847            throws DBException {
2848        return getJDBCUtil().getDateField(this, fieldName);
2849
2850    } /* getFieldDate(String) */
2851
2852
2853    /**
2854     * Return the value of a field as a Date object
2855     *
2856     * @param fieldName The field to be retrieved
2857     * @param formatPattern A formatting pattern according to java.text.DecimalFormat.
2858     * Leave null for the default format for this locale.
2859     * <pre>
2860     * Symbol Meaning
2861     * 0 a digit
2862     * # a digit, zero shows as absent
2863     * . placeholder for decimal separator
2864     * , placeholder for grouping separator.
2865     * E separates mantissa and exponent for exponential formats.
2866     * ; separates formats.
2867     * - default negative prefix.
2868     * % multiply by 100 and show as percentage
2869     * ? multiply by 1000 and show as per mille
2870     * � currency sign; replaced by currency symbol; if
2871     * doubled, replaced by international currency symbol.
2872     * If present in a pattern, the monetary decimal separator
2873     * is used instead of the decimal separator.
2874     * X any other characters can be used in the prefix or suffix
2875     * ' used to quote special characters in a prefix or suffix.
2876     * </pre>
2877     * @return The Date object equivilant to this field's value
2878     * @throws DBException If the field does not exist or it's value
2879     * is not a date or cannot be converted to a date
2880     * @see java.text.DecimalFormat
2881     */

2882    public String JavaDoc getFieldDecimalFormatted(String JavaDoc fieldName,
2883                                           String JavaDoc formatPattern)
2884            throws DBException {
2885        DataFieldMetaData oneField = getFieldMetaData(fieldName);
2886
2887        if (oneField == null) {
2888            throw new DBException("(" + myClassName +
2889                    ") No such field as " + fieldName);
2890        }
2891
2892        String JavaDoc strVal = getFieldData(oneField.getName());
2893
2894        if (strVal == null) {
2895            return null;
2896        }
2897        if (strVal.equals("")) {
2898            return null;
2899        }
2900
2901        Double JavaDoc myDouble = new Double JavaDoc(strVal);
2902        DecimalFormat JavaDoc df = null;
2903
2904        if (formatPattern == null) {
2905            df = new DecimalFormat JavaDoc();
2906        } else {
2907            df = new DecimalFormat JavaDoc(formatPattern);
2908        }
2909
2910        String JavaDoc returnValue = df.format(myDouble);
2911
2912        return returnValue;
2913    } /* getFieldDecimalFormatted(String, String) */
2914
2915
2916    /**
2917     * Get the primitive <code>float</code> value of a field in this object. we use the locale specified
2918     * in the properties file directly to determine how to parse the field
2919     *
2920     * @param fieldName Name of the field to be retrieved
2921     * @return float The value of the field as a float
2922     * @throws DBException If there is no such field or it's value
2923     * cannot be converted to a float
2924     * @see #setField(String,double)
2925     */

2926    public float getFieldFloat(String JavaDoc fieldName)
2927            throws DBException {
2928        DataFieldMetaData oneField = getFieldMetaData(fieldName);
2929        String JavaDoc strVal = getFieldData(oneField.getName());
2930        if (strVal == null) {
2931            return 0;
2932        }
2933
2934        Locale JavaDoc myLocale = Locale.getDefault();
2935
2936        try {
2937            myLocale = new Locale JavaDoc(ConfigManager.getContext(getDataContext()).getLanguage(),
2938                    ConfigManager.getContext(getDataContext()).getCountry());
2939        } catch (ConfigurationException ce) {
2940            throw new DBException(ce);
2941        }
2942
2943        NumberFormat JavaDoc formatter = NumberFormat.getInstance(myLocale);
2944        Float JavaDoc returnFloat = null;
2945
2946        try {
2947            returnFloat = new Float JavaDoc(formatter.parse(strVal).floatValue());
2948        } catch (IllegalArgumentException JavaDoc ie) {
2949            throw new DBException("(" + myClassName +
2950                    ") Unable to parse a float value from field '" +
2951                    fieldName + "' which contained '" + strVal +
2952                    "'", ie);
2953        } catch (ParseException JavaDoc pe) {
2954            throw new DBException("(" + myClassName +
2955                    ") Unable to parse a float value from field '" +
2956                    fieldName + "' which contained '" + strVal +
2957                    "'", pe);
2958        }
2959        if (returnFloat == null) {
2960            throw new DBException("(" + myClassName +
2961                    ") Unable to get float value from field '" +
2962                    fieldName + "', value was '" + strVal + "'");
2963        }
2964
2965        return returnFloat.floatValue();
2966    } /* getFieldFloat(String) */
2967
2968
2969    /**
2970     * Get the primitive <code>double</code> value of a field in this object. we use the locale specified
2971     * in the properties file directly to determine how to parse the field
2972     *
2973     * @param fieldName Name of the field to be retrieved
2974     * @return float The value of the field as a float
2975     * @throws DBException If there is no such field or it's value
2976     * cannot be converted to a float
2977     * @see #setField(String,double)
2978     */

2979    public double getFieldDouble(String JavaDoc fieldName)
2980            throws DBException {
2981        DataFieldMetaData oneField = getFieldMetaData(fieldName);
2982        String JavaDoc strVal = getFieldData(oneField.getName());
2983        if (strVal == null) {
2984            return 0;
2985        }
2986
2987        Locale JavaDoc myLocale = Locale.getDefault();
2988
2989        try {
2990            myLocale = new Locale JavaDoc(ConfigManager.getContext(getDataContext()).getLanguage(),
2991                    ConfigManager.getContext(getDataContext()).getCountry());
2992        } catch (ConfigurationException ce) {
2993            throw new DBException(ce);
2994        }
2995
2996        NumberFormat JavaDoc formatter = NumberFormat.getInstance(myLocale);
2997        Double JavaDoc returnDouble = null;
2998
2999        try {
3000            returnDouble = new Double JavaDoc(formatter.parse(strVal).doubleValue());
3001        } catch (IllegalArgumentException JavaDoc ie) {
3002            throw new DBException("(" + getClass().getName() +
3003                    ") Unable to parse a double value from field '" +
3004                    fieldName + "' which contained '" + strVal +
3005                    "'", ie);
3006        } catch (ParseException JavaDoc pe) {
3007            throw new DBException("(" + getClass().getName() +
3008                    ") Unable to parse a double value from field '" +
3009                    fieldName + "' which contained '" + strVal +
3010                    "'", pe);
3011        }
3012        if (returnDouble == null) {
3013            throw new DBException("(" + getClass().getName() +
3014                    ") Unable to get double value from field '" +
3015                    fieldName + "', value was '" + strVal + "'");
3016        }
3017
3018        return returnDouble.doubleValue();
3019    } /* getFieldDouble(String) */
3020
3021    /**
3022     * Get the <code>BigDecimal</code> value of a field in this object.
3023     * We use the locale specified
3024     * in the properties file directly to determine how to parse the field
3025     * <p/>
3026     * <p/>
3027     * NB: Corresponds to <code>java.sql.Type.DECIMAL</code> or <code>java.sql.Type.NUMERIC</code>
3028     * </p>
3029     *
3030     * @param fieldName Name of the field to be retrieved
3031     * @return BigDecimal The value of the field as a BigDecimal object
3032     * @throws DBException If there is no such field or it's value
3033     * cannot be converted to a BigDecimal
3034     * <p/>
3035     * author Peter Pilgrim <peterp@xenonsoft.demon.co.uk>
3036     * @see "'JDBC API Tutorial and Reference', Second Edition, pg 944, by Catell, Hamilton et al; published by Addison Wesley"
3037     * @see #setField(String,BigDecimal)
3038     */

3039    public BigDecimal JavaDoc getFieldBigDecimal(String JavaDoc fieldName)
3040            throws DBException {
3041        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3042        String JavaDoc strVal = getFieldData(oneField.getName());
3043        if (strVal == null) {
3044            return BIG_DECIMAL_ZERO;
3045        }
3046
3047        BigDecimal JavaDoc returnDecimal = null;
3048
3049        try {
3050            returnDecimal = new BigDecimal JavaDoc(strVal);
3051        } catch (NumberFormatException JavaDoc nfe) {
3052            throw new DBException("(" + getClass().getName() +
3053                    ") Unable to parse a BigDecimal value from field '" +
3054                    fieldName + "' which contained '" + strVal +
3055                    "'", nfe);
3056        }
3057        if (returnDecimal == null) {
3058            throw new DBException("(" + getClass().getName() +
3059                    ") Unable to get BigDecimal value from field '" +
3060                    fieldName + "', value was '" + strVal + "'");
3061        }
3062
3063        return returnDecimal;
3064    } /* getFieldBigDecimal(String) */
3065
3066    /**
3067     * Get the primitive <code>byte</code> value of a field in this object.
3068     * A convenience method for <code>getField</code>
3069     *
3070     * @param fieldName Name of a field in this object
3071     * @return int The value of the field as a int
3072     * @throws DBException if there is no such field or it's value cannot be
3073     * converted to a byte integer.
3074     * @see #setField(String,byte)
3075     */

3076    public byte getFieldByte(String JavaDoc fieldName)
3077            throws DBException {
3078        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3079        String JavaDoc strVal = "";
3080        try {
3081            strVal = getFieldData(oneField.getName());
3082            if (strVal == null) {
3083                return 0;
3084            }
3085            return Byte.parseByte(strVal);
3086        } catch (NumberFormatException JavaDoc ex) {
3087            throw new DBException("(" + myClassName +
3088                    ") Unable to parse an byte integer value from field " +
3089                    fieldName + " which contained '" + strVal +
3090                    "'", ex);
3091        }
3092    }
3093
3094    /**
3095     * Get the primitive <code>byteArray</code> value of a field in this object.
3096     * A convenience method for <code>getField</code>
3097     *
3098     * @param fieldName Name of a field in this object
3099     * @return int The value of the field as a byte{]
3100     * @throws DBException if there is no such field or it's value cannot be
3101     * get to a byte array.
3102     * @see #setField(String,byte[])
3103     */

3104    public byte[] getFieldByteArray(String JavaDoc fieldName)
3105            throws DBException {
3106        String JavaDoc strVal = null;
3107        try {
3108            if (fieldData == null) {
3109// fieldData = new HashMap();
3110
return null;
3111            }
3112
3113            DataField df = (DataField) fieldData.get(fieldName);
3114            if (df == null) {
3115                return null;
3116            }
3117
3118            Object JavaDoc o = df.getValue();
3119            if (o == null) {
3120                return null;
3121            }
3122            return (byte[]) o;
3123        } catch (Exception JavaDoc ex) {
3124            throw new DBException("(" + this.myClassName +
3125                    ") Unable to get a byte array value from field " +
3126                    fieldName + " which contained '" + strVal +
3127                    "'", ex.getMessage());
3128        }
3129    }
3130
3131    /**
3132     * Get the primitive <code>integer</code> value of a field in this object.
3133     * A convenience method for <code>getField</code>
3134     *
3135     * @param fieldName Name of a field in this object
3136     * @return int The value of the field as a int
3137     * @throws DBException if there is no such field or it's value cannot be
3138     * converted to a short integer.
3139     * @see #setField(String,short)
3140     */

3141    public short getFieldShort(String JavaDoc fieldName)
3142            throws DBException {
3143        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3144        String JavaDoc strVal = "";
3145        try {
3146            strVal = getFieldData(oneField.getName());
3147            if (strVal == null) {
3148                return 0;
3149            }
3150            return Short.parseShort(strVal);
3151        } catch (NumberFormatException JavaDoc ex) {
3152            throw new DBException("(" + myClassName +
3153                    ") Unable to parse an short integer value from field " +
3154                    fieldName + " which contained '" + strVal +
3155                    "'", ex);
3156        }
3157    }
3158
3159
3160    /**
3161     * Get the primitive <code>integer</code> value of a field in this object.
3162     * A convenience method for <code>getField</code>
3163     *
3164     * @param fieldName Name of a field in this object
3165     * @return int The value of the field as a int; will return 0 if field is null; throw if underlying string is otherwise non-integer;
3166     * @throws DBException if there is no such field or it's value cannot be
3167     * converted to an integer.
3168     * @see #setField(String,int)
3169     * @see #isFieldNull(java.lang.String) in order to know if 0 is 'real' or because of an underlying null
3170     */

3171    public int getFieldInt(String JavaDoc fieldName)
3172            throws DBException {
3173        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3174        String JavaDoc strVal = "";
3175
3176        try {
3177            strVal = getFieldData(oneField.getName());
3178
3179            if (strVal == null) {
3180                return 0;
3181            }
3182
3183            return Integer.parseInt(strVal);
3184        } catch (NumberFormatException JavaDoc ex) {
3185            throw new DBException("(" + myClassName +
3186                    ") Unable to parse an integer value from field " +
3187                    fieldName + " which contained '" + strVal +
3188                    "'", ex);
3189        }
3190    } /* getFieldInt(String) */
3191
3192    /**
3193     * Get the prmitive <code>long</code> value of a field in this object.
3194     * A convenience method for <code>getField</code>
3195     *
3196     * @param fieldName Name of a field in this object
3197     * @return long The value of the field as a long
3198     * @throws DBException if there is no such field or it's value cannot be
3199     * converted to a long integer.
3200     * @see #setField(String,long)
3201     */

3202    public long getFieldLong(String JavaDoc fieldName)
3203            throws DBException {
3204        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3205        String JavaDoc strVal = "";
3206
3207        try {
3208            strVal = getFieldData(oneField.getName());
3209            if (strVal == null) {
3210                return 0;
3211            }
3212            return Long.parseLong(strVal);
3213        } catch (NumberFormatException JavaDoc ex) {
3214            throw new DBException("(" + myClassName +
3215                    ") Unable to parse an long integer value from field " +
3216                    fieldName + " which contained '" + strVal +
3217                    "'", ex);
3218        }
3219    } /* getFieldLong(String) */
3220
3221
3222    /**
3223     * Sets a byte array to the underlying data as a blob
3224     *
3225     * @param fieldName The name of the field to save.
3226     * @param incomingData the stream to write to the database table.
3227     * @throws DBException upon error
3228     * @deprecated since Expresso 5.6. Use LobField directly or
3229     * com.jcorporate.expresso.services.dbobj.MediaDBObject for BLOB storage since
3230     * they all require much less memory as well as provides more dynamic database
3231     * support.
3232     */

3233    public void saveBinaryField(String JavaDoc fieldName,
3234                                byte[] incomingData) throws DBException {
3235        DBConnectionPool connectionPool = null;
3236        DBConnection myConnection = localConnection;
3237
3238        if (localConnection == null) {
3239            connectionPool = DBConnectionPool.getInstance(getDataContext());
3240            myConnection = connectionPool.getConnection();
3241        }
3242
3243        try {
3244            LOBSupport.getInstance().setBLOB(this, fieldName,
3245                    incomingData,
3246                    myConnection);
3247        } finally {
3248            if (localConnection == null) {
3249                if (connectionPool != null) {
3250                    connectionPool.release(myConnection);
3251                }
3252            }
3253        }
3254    }
3255
3256    /**
3257     * This convenience method counts
3258     * <code>DBFields</code> belonging to this
3259     * <code>DBObject</code> that are set to <b>retrieve</b>.
3260     *
3261     * @return int number of retrieve fields.
3262     * @throws DBException upon error.
3263     */

3264    public synchronized int getFieldsToRetrieveCount()
3265            throws DBException {
3266        if (retrieveFields == null) {
3267            return 0;
3268        } else {
3269            return retrieveFields.size();
3270        }
3271    } /* getFieldsToRetrieveCount() */
3272
3273
3274    /**
3275     * Return the number of records found in the last search operation. See also
3276     * count() and find().
3277     *
3278     * @return The count of records found
3279     */

3280    public long getFoundCount() {
3281        if (foundKeys == null) {
3282            return 0;
3283        } else {
3284            return foundKeys.size();
3285        }
3286    } /* getFoundCount() */
3287
3288
3289    /**
3290     * Return the Array of keys (in the form field/field/field) that
3291     * was found in the last search() call
3292     *
3293     * @return An array containing keys as strings
3294     */

3295    public Object JavaDoc[] getFoundKeysArray() {
3296        if (foundKeys == null) {
3297            return new ArrayList JavaDoc().toArray();
3298        } else {
3299            return foundKeys.toArray();
3300        }
3301    }
3302
3303    /**
3304     * Get an array of DBIndex objects for purpose of
3305     * creating them through the schema. Return type
3306     * should actually be DBIndex. <i>Please Note</i> This may cause an
3307     * exception to be thrown if indexList is null. Call hasIndex() first
3308     * before calling getIndexArray
3309     * To be soon deprecated
3310     *
3311     * @return an object containing all the DBOBjects
3312     * @throws DBException upon error.
3313     * @deprecated since 5.6, Use ((DBObjectDef)getMetaData()).getIndexArray() instead
3314     */

3315    public synchronized Object JavaDoc[] getIndexArray()
3316            throws DBException {
3317        return ((DBObjectDef) getMetaData()).getIndexArray();
3318    }
3319
3320    /**
3321     * Get a string consisting of the values of each key field for this object
3322     * appended together with a | between them.
3323     *
3324     * @return Value of all keys appended with a | between
3325     */

3326    public String JavaDoc getKey() {
3327        try {
3328            return getMyKeys();
3329        } catch (DBException de) {
3330            log.error(de);
3331
3332            return null;
3333        }
3334    } /* getKey() */
3335
3336
3337    /**
3338     * Get a list of all of the fields in this object This iterator is not thread
3339     * safe.
3340     *
3341     * @return An iterator of the fieldNamesInOrder array list
3342     * @throws DBException If the list cannot be retrieved
3343     */

3344    public Iterator JavaDoc getKeyFieldListIterator()
3345            throws DBException {
3346        return getDef().getKeyFieldListIterator();
3347    }
3348
3349    /**
3350     * Return the length of a field
3351     *
3352     * @param fieldName The name of the field
3353     * @return String: The length of the field
3354     * @throws DBException If there is no such field in this object
3355     */

3356    public String JavaDoc getLength(String JavaDoc fieldName)
3357            throws DBException {
3358        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3359
3360        return Integer.toString(oneField.getLengthInt());
3361    } /* getLength(String) */
3362
3363
3364    /**
3365     * Get the length of this field as an integer
3366     *
3367     * @param fieldName to check
3368     * @return length of field
3369     * @throws DBException upon error.
3370     */

3371    public int getLengthInt(String JavaDoc fieldName)
3372            throws DBException {
3373        return new Integer JavaDoc(getLength(fieldName)).intValue();
3374    }
3375
3376
3377    /**
3378     * Get a field's lookup object - this is the name of another database
3379     * object that can be used to look up valid values for this object. The lookup
3380     * object for a field is set in the db objects setupFields method, and is used
3381     * by the DBMaint servlet to provide automatic lookup links for fields.
3382     *
3383     * @param fieldName The fieldname to look up
3384     * @return A String containing the classname of the lookup dbobject
3385     * @throws DBException If the specified field does not exist.
3386     */

3387    public String JavaDoc getLookupObject(String JavaDoc fieldName)
3388            throws DBException {
3389        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3390
3391        return oneField.getLookupObject();
3392    } /* getLookupObject(String) */
3393
3394
3395    /**
3396     * Returns the local connection currently associated with this DBObject...
3397     * may be null if no local connection has ever been set.
3398     *
3399     * @return com.jcorporate.expresso.core.db.DBConnection or null
3400     */

3401    public DBConnection getLocalConnection() {
3402        return localConnection;
3403    }
3404
3405
3406    /**
3407     * Get the Maximum value in the table of a particular field
3408     * <p/>
3409     * Contributed by Madan Mohanrao Kulkarni [kulsmadya@rediffmail.com]
3410     *
3411     * @param fieldName the Fieldname to get the max value for.
3412     * @param whereClause Use a custom whereclause?
3413     * @return a String containing the maximum value for this field name
3414     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
3415     * @throws DBException upon error.
3416     */

3417    public String JavaDoc getMax(String JavaDoc fieldName, boolean whereClause)
3418            throws DBException {
3419
3420        DBConnection myConnection = null;
3421        String JavaDoc returnValue = null;
3422
3423        try {
3424            if (localConnection != null) {
3425                myConnection = localConnection;
3426            } else {
3427                myConnection = getConnectionPool().getConnection(myClassName);
3428            }
3429            if (!whereClause) {
3430                myConnection.execute("SELECT MAX(" + fieldName + ") FROM " +
3431                        getJDBCMetaData().getTargetSQLTable(this.getDataContext()));
3432            } else {
3433                FastStringBuffer sqlCommand = new FastStringBuffer(60);
3434                sqlCommand.append("SELECT MAX(" + fieldName + ") FROM " +
3435                        getJDBCMetaData().getTargetSQLTable(this.getDataContext()) + " ");
3436
3437                if (customWhereClause != null) {
3438                    sqlCommand.append(customWhereClause);
3439                } else {
3440                    FastStringBuffer fsb = FastStringBuffer.getInstance();
3441                    try {
3442                        sqlCommand.append(buildWhereClauseBuffer(true, fsb));
3443                    } finally {
3444                        fsb.release();
3445                    }
3446                }
3447
3448                myConnection.execute(sqlCommand.toString());
3449            }
3450            if (myConnection.next()) {
3451                returnValue = myConnection.getString(1);
3452            }
3453        } catch (DBException de) {
3454            throw de;
3455        } finally {
3456            if (localConnection == null) {
3457                myConnection.release();
3458            }
3459        }
3460
3461        return returnValue;
3462    } /* getMax(String) */
3463
3464
3465    /**
3466     * Get the Maximum value in the table of a particular field
3467     * <p/>
3468     * Contributed by Madan Mohanrao Kulkarni [kulsmadya@rediffmail.com]
3469     *
3470     * @param fieldName The fieldName to check against
3471     * @return The maximum value for this field in the table.
3472     * @throws DBException upon error.
3473     */

3474    public String JavaDoc getMax(String JavaDoc fieldName)
3475            throws DBException {
3476        return getMax(fieldName, false);
3477    } /* getMax(String) */
3478
3479
3480    /**
3481     * A DB Object can be told to only retrieve a certain number of records. If a
3482     * "max records" value has been specified, this method provides access to it.
3483     *
3484     * @return The maximum number of records that should be retrieved, or zero
3485     * if no max has been set
3486     */

3487    public int getMaxRecords() {
3488        return maxRecords;
3489    } /* getMaxRecords() */
3490
3491
3492    /**
3493     * Get a string consisting of the values of each key field for this object
3494     * appended together with a | between them.
3495     *
3496     * @return Value of all keys appended with a | between
3497     * @throws DBException If the key list cannot be built.
3498     */

3499    public String JavaDoc getMyKeys()
3500            throws DBException {
3501        FastStringBuffer myKeys = FastStringBuffer.getInstance();
3502        String JavaDoc returnValue = null;
3503        try {
3504            boolean needPipe = false;
3505
3506            for (Iterator JavaDoc i = getMetaData().getKeyFieldListArray().iterator(); i.hasNext();) {
3507                if (needPipe) {
3508                    myKeys.append("|");
3509                }
3510
3511                myKeys.append(getField((String JavaDoc) i.next()));
3512                needPipe = true;
3513            }
3514
3515            returnValue = myKeys.toString();
3516        } finally {
3517            myKeys.release();
3518        }
3519
3520        return returnValue;
3521    } /* getMyKeys() */
3522
3523
3524    /**
3525     * Return the array of updates (see the inner class) that specify what changes
3526     * were just made to this object.
3527     *
3528     * @return Logged Updates in an Object Array
3529     */

3530    protected Object JavaDoc[] getMyUpdatesArray() {
3531        if (myUpdates == null) {
3532            myUpdates = new ArrayList JavaDoc();
3533        }
3534
3535        return myUpdates.toArray();
3536    } /* getMyUpdatesArray() */
3537
3538
3539    /**
3540     * Gets the number of records that be skipped. The offset records.
3541     * A DB Object can be told to skip a certain number of
3542     * records, before reading records from the <code>ResultSet</code>.
3543     * <p/>
3544     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
3545     *
3546     * @return The maximum number of records that should be
3547     * skipped over before reading the data records.
3548     * @see #setOffsetRecord(int)
3549     */

3550    public int getOffsetRecord() {
3551        return offsetRecord;
3552    } /* getOffsetRecord() */
3553
3554
3555    /**
3556     * Get the precision of thie field, if one was specified
3557     *
3558     * @param fieldName The fieldname to get the precision for
3559     * @return length of precision as integer
3560     * @throws DBException upon error.
3561     */

3562    public int getPrecision(String JavaDoc fieldName)
3563            throws DBException {
3564        DataFieldMetaData oneField = getFieldMetaData(fieldName);
3565
3566        return oneField.getPrecision();
3567    } /* getPrecision(String) */
3568
3569
3570    /**
3571     * This function is called whenever the DBField is about to be written to
3572     * the database. It may do additional processing such as encryption depending
3573     * on the field attributes.
3574     *
3575     * @param theField A DBField object
3576     * @return the value to write to the data source.
3577     * @throws DBException upon error.
3578     * @todo This is not completely implemented yet, and this responsibility for
3579     * formating a field should probably be
3580     * put into DBField for better object orientation.
3581     */

3582    public String JavaDoc getSerializedForm(DBField theField)
3583            throws DBException {
3584
3585        return super.getSerialForm(theField);
3586    } /* getSerializedForm() */
3587
3588
3589    /**
3590     * This will return a new object of the type of the subclass.
3591     * <p/>
3592     * OVERRIDE this method with implentation like
3593     * <p>return new MyObject()</p>
3594     * if you want greatest efficiency. For example: A DBObject called "Customer"
3595     * should return a new "Customer" object.
3596     * <p/>
3597     * the implementation in DBObject uses getClass().newInstance(), which seems to be about
3598     * 50% slower than "new MyObject()" calls on JDK 1.4
3599     * <p/>
3600     * Note that this method should never be called directly. It should only
3601     * be called by newInstance()
3602     *
3603     * @return DBObject A newly allocated object of the subclass's class
3604     * @throws DBException upon error.
3605     * @see #getThisDBObj
3606     */

3607    protected DBObject getThisDBObj()
3608            throws DBException {
3609        try {
3610
3611            DBObject returnObj = (DBObject) getClass().newInstance();
3612            return returnObj;
3613
3614        } catch (InstantiationException JavaDoc ie) {
3615            throw new DBException("DBObject object '" +
3616                    myClassName +
3617                    "' cannot be instantiated", ie);
3618        } catch (IllegalAccessException JavaDoc iae) {
3619            throw new DBException("llegal access loading " +
3620                    "DBObject object '" +
3621                    myClassName +
3622                    "' You must have a default public constructor for your DBObject"
3623                    , iae);
3624        }
3625    } /* getThisDBObj() */
3626
3627    /**
3628     * get a new instance of this object, with some basic attributes copied,
3629     * like the local connection of the transaction
3630     * <p/>
3631     * Generally speaking, DO NOT OVERRIDE THIS METHOD. Instead, to improve efficiency,
3632     * most subclasses will override the method getThisDBObj()
3633     *
3634     * @return DBObject A newly allocated object of the subclass's class
3635     * @throws DBException upon error.
3636     * @see #getThisDBObj
3637     */

3638    public DBObject newInstance()
3639            throws DBException {
3640        DBObject returnObj = getThisDBObj();
3641        copyAttributes(returnObj);
3642        return returnObj;
3643    } /* newInstance() */
3644
3645    /**
3646     * called by newInstance(), this method should make sure that the newly created
3647     * object is properly initialized
3648     *
3649     * @param returnObj the object to set the attributes to.
3650     * @throws DBException upon error
3651     */

3652    protected void copyAttributes(DBObject returnObj) throws DBException {
3653        if (localConnection != null) {
3654            returnObj.setConnection(localConnection);
3655        } else {
3656            returnObj.setDataContext(getDataContext());
3657        }
3658    }
3659
3660    /**
3661     * Constructor method that takes a DataTransferObject and builds a full fledged
3662     * DBObject out of it.
3663     *
3664     * @param dto The DataTransferObject
3665     * @return a constructed DBObject that still needs DataContext and record
3666     * ownership to be set.
3667     * @throws DBException upon error
3668     */

3669    public static synchronized DBObject getThisDBbj(DataTransferObject dto) throws DBException {
3670        DBObject newObj = null;
3671        try {
3672
3673            Class JavaDoc clazz = ClassLocator.loadClass(dto.getObjectClassName());
3674            newObj = (DBObject) clazz.newInstance();
3675            newObj.setDataTransferObject(dto);
3676        } catch (Throwable JavaDoc e) {
3677            throw new DBException("Error instantiating dbobject: " + dto.getObjectClassName(),
3678                    e);
3679        }
3680
3681        return newObj;
3682
3683    }
3684
3685
3686    /**
3687     * Gets the data transfer object usually for serialization purposes
3688     *
3689     * @return The built DataTransferObject
3690     * @throws DBException upon error.
3691     */

3692    public synchronized DataTransferObject getDataTransferObject() throws DBException {
3693
3694        DataTransferObject dto = new DataTransferObject();
3695        dto.setDataContext(getDataContext());
3696        dto.setObjectClassName(myClassName);
3697        HashMap data = new HashMap(fieldData.size());
3698        for (Iterator JavaDoc i = fieldData.keySet().iterator(); i.hasNext();) {
3699            String JavaDoc key = (String JavaDoc) i.next();
3700            DataField df = (DataField) fieldData.get(key);
3701            data.put(key, df.getValue());
3702        }
3703        dto.setTableFields(data);
3704        return dto;
3705    }
3706
3707    /**
3708     * fill fields with values found in dto; REPLACES any data field already present.
3709     *
3710     * @param dto The filled out data transfer object
3711     * @throws DBException upon error.
3712     */

3713    public synchronized void setDataTransferObject(DataTransferObject dto) throws DBException {
3714        //
3715
//Attempt to set the dbname as before, HOWEVER, if it's impossible [very likely
3716
//across enterprise systems, then we ignore the error and move on.
3717
//
3718
try {
3719            setDBName(dto.getDataContext());
3720        } catch (DBException dbe) {
3721            if (log.isDebugEnabled()) {
3722                log.debug("Error setting data context", dbe);
3723            }
3724        }
3725
3726        //
3727
//Set the field data - we have to go through the long process here
3728
//because we don't want to be serializing all the parents and metadata
3729
//with each serialization of the data transfer object. [Very Messy]
3730
//
3731
Map dataTransferFields = dto.getTableFields();
3732        fieldData = new HashMap(dataTransferFields.size());
3733        for (Iterator JavaDoc i = dataTransferFields.keySet().iterator(); i.hasNext();) {
3734            String JavaDoc keyName = (String JavaDoc) i.next();
3735            Object JavaDoc newValue = dataTransferFields.get(keyName);
3736            DataField df = DefaultDataField.getInstance(getFieldMetaData(keyName), this);
3737            df.setValue(newValue);
3738            fieldData.put(keyName, df);
3739        }
3740//
3741
// fieldData = new HashMap(dto.getTableFields());
3742
cacheIsChangedComparison();
3743        setStatus(BaseDataObject.STATUS_CURRENT);
3744    }
3745
3746
3747    /**
3748     * This is a convenience method which will return a ValidValue description
3749     * for a multi-valued field.
3750     * (Contributed by Adam Rossi)
3751     *
3752     * @param fieldName java.lang.String
3753     * @return java.lang.String The description of this multi-valued field
3754     * @throws DBException If there is no such field, or the values cannot be retrieved.
3755     */

3756    public String JavaDoc getValidValueDescrip(String JavaDoc fieldName)
3757            throws DBException {
3758        if (!getMetaData().getFieldMetadata(fieldName).isMultiValued()) {
3759            throw new DBException("This getValidValueDescrip method " +
3760                    "cannot be invoked on a non-multivalued field");
3761        }
3762
3763        Vector JavaDoc v = getValidValues(fieldName);
3764
3765        if (v == null) {
3766            throw new DBException("No valid values could be " +
3767                    "found for this field.");
3768        }
3769
3770        ValidValue onevv = null;
3771        String JavaDoc fieldValue = getField(fieldName);
3772
3773        for (Enumeration JavaDoc e = v.elements(); e.hasMoreElements();) {
3774            onevv = (ValidValue) e.nextElement();
3775
3776            if (fieldValue.equals(onevv.getValue())) {
3777                return onevv.getDescription();
3778            }
3779        }
3780
3781        return null;
3782    } /* getValidValueDescrip(String) */
3783
3784
3785    /**
3786     * Retrieve a list of valid value object for this particular dbobject
3787     *
3788     * @param fieldName the name of the field to get the valid values list for
3789     * @return java.util.List of ValidValue objects
3790     * @throws DBException upon error
3791     */

3792    public java.util.List JavaDoc getValidValuesList(String JavaDoc fieldName) throws DBException {
3793        return getValidValues(fieldName);
3794    }
3795
3796    /**
3797     * New method to replace getValues with a structure of valid values
3798     * and descriptions. Database objects should extend this method to
3799     * return Vectors of ValidValue objects for multi-valued fields. A
3800     * specific object can return it's own list of ValidValues, or it
3801     * can call this super method to use the lookup object to get the
3802     * list of valid values instead.
3803     *
3804     * @param fieldName The name of the fields for which a value set is requested
3805     * @return A Vector of ValidValue objects
3806     * @throws DBException upon error.
3807     */

3808    public synchronized Vector JavaDoc getValidValues(String JavaDoc fieldName)
3809            throws DBException {
3810        if (getMetaData().getFieldMetadata(fieldName).isMultiValued()) {
3811            String JavaDoc lookupObj = getLookupObject(fieldName);
3812
3813            if (log.isDebugEnabled()) {
3814                log.debug("Lookup is " + lookupObj);
3815            }
3816            if (StringUtil.notNull(lookupObj).equals("")) {
3817                if (log.isDebugEnabled()) {
3818                    log.debug("No lookup");
3819                }
3820
3821                return null;
3822            } else {
3823                try {
3824                    Class JavaDoc c = ClassLocator.loadClass(lookupObj);
3825                    Object JavaDoc o = c.newInstance();
3826
3827                    //CALL setDBName() ONLY if it exists in the class. It
3828
//might not be necessary across contexts.
3829
if (o instanceof DataObject) {
3830                        DataObject dao = (DataObject) o;
3831                        dao.setDataContext(getDataContext());
3832                    }
3833
3834                    if (o instanceof DBObject) {
3835                        DBObject dbo = (DBObject) o;
3836                        if (localConnection != null) {
3837                            dbo.setConnection(localConnection);
3838                        }
3839                    } else if (o instanceof MultiDBObject) {
3840                        MultiDBObject mdo = (MultiDBObject) o;
3841                        mdo.setDBName(getDataContext());
3842                    } else {
3843                        try {
3844                            Class JavaDoc[] params = {java.lang.String JavaDoc.class};
3845                            Method JavaDoc m = c.getMethod("setDBName", params);
3846
3847                            if (m != null) {
3848                                Object JavaDoc[] paramValues = {getDataContext()};
3849                                m.invoke(o, paramValues);
3850                            }
3851                        } catch (NoSuchMethodException JavaDoc nsme) {
3852                            if (log.isDebugEnabled()) {
3853                                log.debug("No setDBName for this object", nsme);
3854                            }
3855
3856                            //Do nothing, the method just doesn't exist.
3857
} catch (SecurityException JavaDoc se) {
3858                            throw new DBException("Security Exception trying to call" +
3859                                    " setDBName in loading lookup object", se);
3860                        } catch (java.lang.reflect.InvocationTargetException JavaDoc ive) {
3861                            ive.printStackTrace();
3862                            log.error("Error Invoking setDBName() dynamically",
3863                                    ive);
3864                            throw new DBException("Error calling setDBName in " +
3865                                    "loading lookup object",
3866                                    ive.getTargetException());
3867                        } catch (IllegalAccessException JavaDoc ilae) {
3868                            if (log.isDebugEnabled()) {
3869                                log.debug("setDBName() exists, but is not a public method", ilae);
3870                            }
3871
3872                            //Do nothing, setDBName is not public
3873
}
3874
3875                    }
3876                    if (log.isDebugEnabled()) {
3877                        log.debug("Returning values from " + lookupObj);
3878                    }
3879
3880                    return ((LookupInterface) o).getValues();
3881                } catch (ClassNotFoundException JavaDoc cn) {
3882                    throw new DBException("Lookup object not found", cn);
3883                } catch (InstantiationException JavaDoc ie) {
3884                    throw new DBException("Lookup object cannot be instantiated",
3885                            ie);
3886                } catch (IllegalAccessException JavaDoc iae) {
3887                    throw new DBException("llegal access loading Lookup object",
3888                            iae);
3889                }
3890            }
3891        } else { /* if */
3892            throw new DBException("Field '" + fieldName + "' in object '" +
3893                    myClassName +
3894                    "' is not specified as multi-valued, so you cannot " +
3895                    "call getValidValues for this field");
3896        }
3897    } /* getValidValues(String) */
3898
3899
3900    /**
3901     * Method to return a Vector of ValidValue
3902     * Template method--not implemented in this superclass.
3903     * This method may be implemented by objects that want to provide a
3904     * list of valid values for other DB objects. It is strongly recommended
3905     * that the valid value list be cached (via the CacheManager) for performance.
3906     * The naming convention used in Expresso is to store the ValidValue list with
3907     * a cache name the same as the db objects class name with ".validValues" appended
3908     * TODO: This should be converted to array List versions
3909     *
3910     * @return java.util.Vector of valid values
3911     * @throws DBException upon error.
3912     * @see #getValidValues
3913     */

3914    public Vector JavaDoc getValues()
3915            throws DBException {
3916        throw new DBException("This object: " + myClassName + " does not have valid values defined.");
3917    } /* getValues() */
3918
3919
3920    /**
3921     * Basic version of getValidValues that stores/retrieves the valid
3922     * values in a Cache. This method does <em>not</em> support
3923     * internationalisation (i18n).
3924     * <p/>
3925     * <p/>
3926     * Valid values are store inside cache with a key name that equals
3927     * <code>myClassName+".validValues"</code>
3928     * The method creates <code>ValidValue</code> object instances and
3929     * stores them in the cache.
3930     * </p>
3931     *
3932     * @param valueField java.lang.String
3933     * @param descripField java.lang.String
3934     * @return java.util.Vector
3935     * @throws DBException if a database error occurs
3936     * <p/>
3937     * #see #getValuesDefault(String valueField, String descripField, String whereClause)
3938     * #see #getISOValuesDefault(String valueField, String descripField)
3939     */

3940    protected Vector JavaDoc getValuesDefault(String JavaDoc valueField, String JavaDoc descripField)
3941            throws DBException {
3942        return getValuesDefault(valueField, descripField, "");
3943    }
3944
3945    /**
3946     * Basic filtered version of getValidValues that stores/retrieves the valid
3947     * values in a Cache. This method does <em>not</em> support
3948     * internationalisation (i18n).
3949     * <p/>
3950     * <p/>
3951     * Valid values are store inside cache with a key name that equals
3952     * <code>myClassName+".validValues"</code>
3953     * The method creates <code>ValidValue</code> object instances and
3954     * stores them in the cache.
3955     * </p>
3956     *
3957     * @param valueField java.lang.String
3958     * @param descripField java.lang.String
3959     * @param whereClause the where clause that will be pass
3960     * to setCustomWhereClause.
3961     * @return java.util.Vector
3962     * @throws DBException if a database error occurs
3963     * <p/>
3964     * #see #setCustomWhereClause(String newCustomWhere)
3965     * #see #getISOValuesDefault(String valueField, String descripField)
3966     */

3967    protected Vector JavaDoc getValuesDefault(String JavaDoc valueField, String JavaDoc descripField,
3968                                      String JavaDoc whereClause)
3969            throws DBException {
3970        return getValuesDefault(valueField, descripField, whereClause, "");
3971    }
3972
3973    /**
3974     * Basic version of getValidValues that stores/retrieves the valid values
3975     * in a Cache. This method retrieves the valid values in the order
3976     * specified by the sortKeyString, and filters them with the given where
3977     * clause. Note, it does <em>not</em> support internationalisation (i18n).
3978     * <p/>
3979     * <p/>
3980     * Valid values are store inside cache with a key name that equals
3981     * <code>myClassName+".validValues"</code>
3982     * The method creates <code>ValidValue</code> object instances and
3983     * stores them in the cache.
3984     * </p>
3985     *
3986     * @param valueField java.lang.String
3987     * @param descripField java.lang.String
3988     * @param whereClause the where clause that will be pass
3989     * to setCustomWhereClause.
3990     * @param sortKeyString the pipe delimited string of field names with the
3991     * optional 'ASC or DESC' keyword afterwords; pass in null to indicate no sorting
3992     * @return java.util.Vector
3993     * @throws DBException if a database error occurs
3994     * <p/>
3995     * #see #setCustomWhereClause(String newCustomWhere)
3996     * #see #setSortKey(String sortKeyString)
3997     * #see #getISOValuesDefault(String valueField, String descripField)
3998     */

3999    protected Vector JavaDoc getValuesDefault(String JavaDoc valueField, String JavaDoc descripField,
4000                                      String JavaDoc whereClause, String JavaDoc sortKeyString)
4001            throws DBException {
4002        try {
4003            DBObject thisObj = newInstance();
4004
4005
4006            if (!thisObj.myClassName.equals(myClassName)) {
4007                throw new DBException("The newInstance() method in " +
4008                        myClassName + " returned " +
4009                        " a " + thisObj.myClassName +
4010                        " object instead of a " +
4011                        myClassName +
4012                        " object. Please correct the method.");
4013            }
4014
4015            String JavaDoc cacheName = myClassName + ".valueField:" + valueField + "|descrip:" + descripField + "|where:" + whereClause + "|sort:" + sortKeyString;
4016            CacheManager.getInstance();
4017            CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
4018            if (cs != null) {
4019                if (!cs.existsCache(cacheName)) {
4020                    cs.createCache(cacheName, true);
4021                    //Clear the valid values when the main object class is
4022
//modified.
4023
cs.addListener(cacheName, myClassName);
4024                }
4025            }
4026
4027            Vector JavaDoc myValues = null;
4028
4029            //
4030
// Do not use cache if in Transaction. and a localconnection has been set
4031
//
4032
if (localConnection == null || localConnection.getAutoCommit() == false) {
4033                java.util.List JavaDoc temp = cs.getItems(cacheName);
4034                if (temp != null) {
4035                    if (temp instanceof Vector JavaDoc) {
4036                        myValues = (Vector JavaDoc) temp;
4037                    } else {
4038                        myValues = new Vector JavaDoc(temp);
4039                    }
4040                }
4041            }
4042
4043            if (myValues == null) {
4044                myValues = new Vector JavaDoc();
4045                thisObj.setDataContext(getDataContext());
4046
4047                if (sortKeyString != null) {
4048                    String JavaDoc sortField = descripField;
4049                    if (thisObj.getMetaData().getFieldMetadata(descripField).isVirtual()) {
4050                        sortField = valueField;
4051                    }
4052
4053                    if (sortKeyString.length() > 1) {
4054                        sortField = sortKeyString;
4055                    }
4056                    thisObj.setSortKey(sortField);
4057                }
4058
4059
4060                // Retrieve valid values from the database, and
4061
// attempt to localise the `description' attribute by
4062
// automatic canonisation. *PP* 27/12/2002
4063
if (whereClause != null && whereClause.length() > 1) {
4064                    thisObj.setCustomWhereClause(whereClause);
4065                }
4066
4067                DBObject oneObj = null;
4068                for (Iterator JavaDoc oset = thisObj.searchAndRetrieveList().iterator();
4069                     oset.hasNext();) {
4070                    oneObj = (DBObject) oset.next();
4071                    myValues.addElement(new ValidValue(oneObj.getField(valueField),
4072                            oneObj.getField(descripField)));
4073                }
4074
4075                //
4076
//Default expiration of valid values = 15 minutes
4077
//
4078
long expiration = 60 * 1000 * 15;
4079                DBObjLimit limit = new DBObjLimit(SecuredDBObject.SYSTEM_ACCOUNT);
4080                limit.setDataContext(getDataContext());
4081                limit.setField(DBObjLimit.DB_OBJECT_NAME, thisObj.myClassName);
4082                if (limit.find()) {
4083                    String JavaDoc ttl = limit.getField(DBObjLimit.TTL);
4084                    if (ttl != null && ttl.length() > 0) {
4085                        try {
4086                            expiration = Long.parseLong(ttl) * 60 * 1000;
4087                        } catch (NumberFormatException JavaDoc ex) {
4088                            log.error("Invalid TTL value: " + ttl);
4089                        }
4090                    }
4091                }
4092                if (cs != null) {
4093                    cs.setItems(cacheName, myValues, expiration);
4094                    //Add a listener to the cache so that when the target class
4095
//disappears, this cache dies too.
4096
cs.addListener(cacheName, thisObj.myClassName);
4097                }
4098            }
4099
4100            return myValues;
4101        } catch (CacheException ce) {
4102            throw new DBException(ce);
4103        }
4104    } /* getValuesDefault(String, String) */
4105
4106
4107    /**
4108     * Basic version of getValidValues with supports
4109     * <em>internationalisation</code> (i18n) that stores/retrieves
4110     * the valid values in a locale dependent Caches
4111     * <p/>
4112     * <p/>
4113     * <p/>
4114     * Valid values are store inside cache with a key name that equals
4115     * <code>myClassName+"."+oneLocale.getString()+".validValues"</code>.
4116     * For example the key cache could be:
4117     * <pre>
4118     * "com.acme.test.Fruits.en_gb.validValues"
4119     * "com.acme.test.Fruits.de_de.validValues"
4120     * "com.acme.test.Fruits.es_es.validValues"
4121     * </pre>
4122     * <p/>
4123     * The method creates <code>ISOValidValue</code> object instances and
4124     * stores them in the cache.
4125     * <p/>
4126     * </p>
4127     *
4128     * @param valueField java.lang.String
4129     * @param descripField java.lang.String
4130     * @return java.util.Vector
4131     * @throws DBException if a database error occurs
4132     * <p/>
4133     * #see #getValuesDefault(String valueField, String descripField)
4134     */

4135    protected Vector JavaDoc getISOValuesDefault(String JavaDoc valueField, String JavaDoc descripField)
4136            throws DBException {
4137        return getISOValuesDefault(valueField, descripField, "");
4138    }
4139
4140    /**
4141     * Basic filtered version of getValidValues with supports
4142     * <em>internationalisation</code> (i18n) that stores/retrieves
4143     * the valid values in a locale dependent Caches
4144     * <p/>
4145     * <p/>
4146     * <p/>
4147     * Valid values are store inside cache with a key name that equals
4148     * <code>myClassName+"."+oneLocale.getString()+".validValues"</code>.
4149     * For example the key cache could be:
4150     * <pre>
4151     * "com.acme.test.Fruits.en_gb.validValues"
4152     * "com.acme.test.Fruits.de_de.validValues"
4153     * "com.acme.test.Fruits.es_es.validValues"
4154     * </pre>
4155     * <p/>
4156     * The method creates <code>ISOValidValue</code> object instances and
4157     * stores them in the cache.
4158     * <p/>
4159     * </p>
4160     *
4161     * @param valueField java.lang.String
4162     * @param descripField java.lang.String
4163     * @param whereClause the where clause that will be pass
4164     * to setCustomWhereClause.
4165     * @return java.util.Vector
4166     * @throws DBException if a database error occurs
4167     * <p/>
4168     * #see #getValuesDefault(String valueField, String descripField, String whereClause)
4169     */

4170    protected Vector JavaDoc getISOValuesDefault(String JavaDoc valueField, String JavaDoc descripField,
4171                                         String JavaDoc whereClause)
4172            throws DBException {
4173        return getISOValuesDefault(valueField, descripField, whereClause, "");
4174    }
4175
4176    /**
4177     * Basic version of getValidValues with supports
4178     * <em>internationalisation</code> (i18n) that stores/retrieves the valid
4179     * values in a locale dependent Caches. This method retrieves the valid
4180     * values in the order specified by the sortKeyString, and filters them
4181     * with the given where clause.
4182     * <p/>
4183     * <p/>
4184     * <p/>
4185     * Valid values are store inside cache with a key name that equals
4186     * <code>myClassName+"."+oneLocale.getString()+".validValues"</code>.
4187     * For example the key cache could be:
4188     * <pre>
4189     * "com.acme.test.Fruits.en_gb.validValues"
4190     * "com.acme.test.Fruits.de_de.validValues"
4191     * "com.acme.test.Fruits.es_es.validValues"
4192     * </pre>
4193     * <p/>
4194     * The method creates <code>ISOValidValue</code> object instances and
4195     * stores them in the cache.
4196     * <p/>
4197     * </p>
4198     *
4199     * @param valueField java.lang.String
4200     * @param descripField java.lang.String
4201     * @param whereClause the where clause that will be pass
4202     * to setCustomWhereClause.
4203     * @param sortKeyString the pipe delimited string of field names with the
4204     * optional 'ASC or DESC' keyword afterwords
4205     * @return java.util.Vector
4206     * @throws DBException if a database error occurs
4207     * <p/>
4208     * #see #getValuesDefault(String valueField, String descripField, String whereClause, String sortKeyString)
4209     */

4210    protected Vector JavaDoc getISOValuesDefault(String JavaDoc valueField, String JavaDoc descripField,
4211                                         String JavaDoc whereClause, String JavaDoc sortKeyString)
4212            throws DBException {
4213        try {
4214            DBObject thisObj = newInstance();
4215
4216            if (!thisObj.myClassName.equals(myClassName)) {
4217                throw new DBException("The newInstance() method in " +
4218                        myClassName + " returned " +
4219                        " a " + thisObj.myClassName +
4220                        " object instead of a " +
4221                        myClassName +
4222                        " object. Please correct the method.");
4223            }
4224
4225            Locale JavaDoc oneLocale = Locale.getDefault();
4226
4227            String JavaDoc cacheName = myClassName + "." + oneLocale.toString() + ".valueField:" + valueField + "|descrip:" + descripField + "|where:" + whereClause + "|sort:" + sortKeyString;
4228            CacheManager.getInstance();
4229            CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
4230
4231            if (cs != null && !cs.existsCache(cacheName)) {
4232                cs.createCache(cacheName, true);
4233            }
4234
4235            Vector JavaDoc myValues = null;
4236
4237            //
4238
// Do not use cache if in Transaction. and a localconnection has been set
4239
//
4240
if (localConnection == null || localConnection.getAutoCommit() == false) {
4241                if (cs != null) {
4242                    java.util.List JavaDoc temp = cs.getItems(cacheName);
4243                    if (!(temp instanceof Vector JavaDoc)) {
4244                        myValues = new Vector JavaDoc(temp);
4245                    } else {
4246                        myValues = (Vector JavaDoc) temp;
4247                    }
4248                }
4249            }
4250
4251            if (myValues == null) {
4252                myValues = new Vector JavaDoc();
4253                thisObj.setDataContext(getDataContext());
4254
4255                DBObject oneObj = null;
4256                String JavaDoc sortField = descripField;
4257
4258                if (thisObj.getMetaData().getFieldMetadata(descripField).isVirtual()) {
4259                    sortField = valueField;
4260                }
4261
4262                if (sortKeyString.length() > 1) {
4263                    sortField = sortKeyString;
4264                }
4265                thisObj.setSortKey(sortField);
4266
4267                // Get schema and the prefix
4268
String JavaDoc oneSchema = thisObj.getJDBCMetaData().getSchema();
4269                String JavaDoc prefix = thisObj.myClassName;
4270
4271                // Retrieve valid values from the database, and
4272
// attempt to localise the `description' attribute by
4273
// automatic canonisation. *PP* 27/12/2002
4274
if (whereClause.length() > 1) {
4275                    thisObj.setCustomWhereClause(whereClause);
4276                }
4277                for (Iterator JavaDoc oset = thisObj.searchAndRetrieveList(sortField).iterator();
4278                     oset.hasNext();) {
4279                    oneObj = (DBObject) oset.next();
4280                    myValues.addElement(new ISOValidValue(oneSchema, oneLocale, prefix,
4281                            oneObj.getField(valueField),
4282                            oneObj.getField(descripField)));
4283                }
4284
4285                //
4286
//Default expiration of valid values = 15 minutes
4287
//
4288
long expiration = 60 * 1000 * 15;
4289                DBObjLimit limit = new DBObjLimit(SecuredDBObject.SYSTEM_ACCOUNT);
4290                limit.setField("DBObjectName", thisObj.myClassName);
4291                if (limit.find()) {
4292                    String JavaDoc ttl = limit.getField("TTL");
4293                    if (ttl != null && ttl.length() > 0) {
4294                        try {
4295                            expiration = Long.parseLong(ttl) * 60 * 1000;
4296                        } catch (NumberFormatException JavaDoc ex) {
4297                            log.error("Invalid TTL value: " + ttl);
4298                        }
4299                    }
4300                }
4301                if (cs != null) {
4302                    cs.setItems(cacheName, myValues, expiration);
4303                    //Add a listener to the cache so that when the target class
4304
//disappears, this cache dies too.
4305
cs.addListener(cacheName, thisObj.myClassName);
4306                }
4307
4308            }
4309
4310            return myValues;
4311        } catch (CacheException ce) {
4312            throw new DBException(ce);
4313        }
4314    } /* getISOValuesDefault(String, String) */
4315
4316
4317    /**
4318     * See if we have a value for each of the key fields
4319     *
4320     * @return True if all key fields have a value, false if not
4321     * @throws DBException upon error.
4322     */

4323    public boolean haveAllKeys()
4324            throws DBException {
4325        DBField oneField = null;
4326
4327        for (Iterator JavaDoc i = getJDBCMetaData().getAllKeysMap().values().iterator(); i.hasNext();) {
4328            oneField = (DBField) i.next();
4329            DataField df = getDataField(oneField.getName());
4330            if (df == null || df.isNull()) {
4331                return false;
4332            }
4333        }
4334
4335        return true;
4336    } /* haveAllKeys() */
4337
4338
4339    /**
4340     * Sets up metadata for the dbobject via call to setupFields().
4341     * it is important within this method to provide STATIC synchronization
4342     * to enable static (meta)data members to be setup by the first
4343     * instantiation of a given class. Two threads
4344     * can simultaneously create the first instance of different classes,
4345     * and so a simple object-level synchronization alone will not suffice here,
4346     * though it is useful to block out calls from the same class.
4347     * <p/>
4348     * author Adam Rossi, PlatinumSolutions
4349     * author Larry Hamel, CodeGuild.com
4350     *
4351     * @throws DBException The exception description.
4352     */

4353    protected synchronized void initialize() throws DBException {
4354        synchronized (sMetadataMap) {
4355            if (getMetaData() == null) {
4356
4357                DBObjectDef myDef = new DBObjectDef();
4358                myDef.setName(myClassName);
4359
4360                // get static synchronization, since 2 threads with 2 different classes
4361
// could be competing, and sMetadata needs sync for put()
4362
sMetadataMap.put(myClassName, myDef);
4363
4364                // note that DBObjectDef is
4365
// NOT threadsafe during its initialization during setupFields,
4366
// so it is imperitive that it be protected here in this static
4367
// context to protect against 2 threads trying to initialize 2
4368
// different instantiations of the same object; however, we can
4369
// release the sync lock to allow other kinds of objects to
4370
// finish
4371
setupFields();
4372
4373                try {
4374                    //We have to force default here because no data context has been
4375
//set. So checkZeroUpdate is determined
4376
//by the default pool only; or false if cannot find that context
4377
DBConnectionPool tempPool = DBConnectionPool.getInstance("default");
4378                    if (tempPool != null) {
4379                        getDef().setCheckZeroUpdate(tempPool.getCheckZeroUpdate());
4380                    }
4381                } catch (DBException e) {
4382                    getDef().setCheckZeroUpdate(false);
4383                }
4384            }
4385        }
4386    } /* initialize() */
4387
4388    /**
4389     * Is this object using internal caching?
4390     * If the cache value is set to other than zero, it is using caching
4391     *
4392     * @return True if internal caching is enabled, else false
4393     */

4394    public boolean isCached() {
4395        if (getCacheSize() > 0) {
4396            return true;
4397        }
4398
4399        return false;
4400    } /* isCached() */
4401
4402    /**
4403     * This method iterates through all the <code>DBFields</code> belonging
4404     * <code>DBObject</code> returns <code>true</code> if any of them are
4405     * set a <b>distinct</b>.
4406     * <p/>
4407     * author Peter Pilgrim <peter.pilgrim@db.com>
4408     *
4409     * @return true if this is a distinct field
4410     * @throws DBException upon error.
4411     * @see #isFieldDistinct(String)
4412     * @see #getDistinctFields()
4413     * @see #getDistinctFieldCount()
4414     */

4415    public synchronized boolean isDistinct()
4416            throws DBException {
4417        if (distinctFields == null) {
4418            return false;
4419        }
4420
4421        for (Iterator JavaDoc i = getMetaData().getAllFieldsMap().values().iterator(); i.hasNext();) {
4422            DBField oneField = (DBField) i.next();
4423
4424            if (distinctFields.containsKey(oneField.getName())) {
4425
4426                // Ok, we found at least one!
4427
return true;
4428            }
4429        }
4430
4431        return false;
4432    } /* isDistinct() */
4433
4434    /**
4435     * Tells whether a particular field is null or not. <b><i>Note:</i></b>
4436     * To maintain backwards compatibility, if you have a virtual field then
4437     * isFieldNull will always return false. You should override this method
4438     * for virtual fields if you want isNull support.
4439     *
4440     * @param fieldName The name of the field to check for isFieldNull()
4441     * @return true if the field is null
4442     * @throws DBException if the field doesn't exist for the particular
4443     * DBObject.
4444     */

4445    public boolean isFieldNull(String JavaDoc fieldName) throws DBException {
4446        DataFieldMetaData oneField = getFieldMetaData(fieldName);
4447
4448        if (oneField == null) {
4449            throw new DBException("(" + myClassName +
4450                    ") No such field as '" + fieldName + "'");
4451        }
4452
4453        if (oneField.isVirtual()) {
4454            log.warn("(" + myClassName + ") Field " +
4455                    fieldName +
4456                    " is a virtual field. Database object should extend " +
4457                    "getField method to handle requests for this field");
4458            return false;
4459        }
4460
4461        String JavaDoc returnValue = getFieldData(oneField.getName());
4462
4463        return (returnValue == null);
4464    }
4465
4466
4467    /**
4468     * Return true if every field in this object is empty or null. Tests only
4469     * "real" fields, not virtual ones
4470     *
4471     * @return boolean: True if the record is "empty" (all fields blank),
4472     * False if not.
4473     * @throws DBException If the list of fields cannot be traversed
4474     */

4475    public synchronized boolean isEmpty()
4476            throws DBException {
4477        String JavaDoc oneName = null;
4478
4479        for (Iterator JavaDoc i = getJDBCMetaData().getFieldListArray().iterator(); i.hasNext();) {
4480            oneName = (String JavaDoc) i.next();
4481
4482            // only concern ourselves with real fields stored in this object,
4483
// not virtual fields
4484
DataFieldMetaData oneField = getFieldMetaData(oneName);
4485            if (oneField != null && !oneField.isVirtual()) {
4486                String JavaDoc value = getFieldData(oneField.getName());
4487                if (value != null && value.length() > 0) {
4488                    return false;
4489                }
4490            }
4491        } /* for each field */
4492
4493
4494        return true;
4495    } /* isEmpty() */
4496
4497
4498    /**
4499     * Convenience method to check if a field is distinct or not
4500     * within this database object.
4501     *
4502     * @param fieldName the name of the field
4503     * @return boolean value true if the field is set distinct.
4504     * @throws DBException If the operation could not be completed
4505     * <p/>
4506     * author Peter Pilgrim <peter.pilgrim@db.com>
4507     * @see #getDistinctFields()
4508     * @see #getDistinctFieldCount()
4509     * @see #setFieldDistinct
4510     */

4511    public boolean isFieldDistinct(String JavaDoc fieldName)
4512            throws DBException {
4513        if (distinctFields == null) {
4514            return false;
4515        }
4516
4517        DataFieldMetaData oneField = getFieldMetaData(fieldName);
4518
4519        if (oneField.isVirtual()) {
4520            throw new DBException("(" + myClassName + ") Field " +
4521                    fieldName +
4522                    " is a virtual field. Database object should extend " +
4523                    "getField method to handle requests for this field");
4524        }
4525
4526        return distinctFields.containsKey(oneField.getName());
4527    } /* isFieldDistinct(String) */
4528
4529
4530    /**
4531     * This method iterates through all the <code>DBFields</code> belonging
4532     * <code>DBObject</code> returns <code>true</code> if any of them are
4533     * between the <b>retrieve</b> fields.
4534     * <p/>
4535     * author Yves Henri Amaizo <amy_amaizo@compuserve.com>
4536     *
4537     * @return true if there are specific fields to retrieve
4538     * @throws DBException upon error.
4539     * @see #isFieldToRetrieve(String)
4540     */

4541    public synchronized boolean isFieldsToRetrieve()
4542            throws DBException {
4543        if (retrieveFields == null) {
4544            return false;
4545        } else if (retrieveFields.size() > 0) {
4546            return true;
4547        } else {
4548            return false;
4549        }
4550    } /* isFieldsToRetrieve() */
4551
4552
4553    /**
4554     * Convenience method to check if a field is field to be retrieve or not
4555     * within this database object.
4556     *
4557     * @param fieldName the name of the field
4558     * @return boolean value true if the field is set distinct.
4559     * @throws DBException If the operation could not be completed
4560     * <p/>
4561     * author Yves Henri Amaizo <amy_amaizo@compuserve.com>
4562     * @see #setFieldsToRetrieve( String )
4563     */

4564    public boolean isFieldToRetrieve(String JavaDoc fieldName)
4565            throws DBException {
4566        if (retrieveFields == null) {
4567            return false;
4568        }
4569
4570        return retrieveFields.containsKey(fieldName);
4571    } /* isFieldToretrieve(String) */
4572
4573
4574    /**
4575     * Method called to determine if a particular field is multi-valued,
4576     * that is does it have a set of specific values and descriptions
4577     *
4578     * @param fieldName Name of the field
4579     * @return boolean True if the field is multi-valued, false if not
4580     * @throws DBException If there is no such field
4581     * @deprecated since 5.6, use getJDBCMetaData().isMultiValued(String)
4582     */

4583    public synchronized boolean isMultiValued(String JavaDoc fieldName)
4584            throws DBException {
4585        DataFieldMetaData oneField = getFieldMetaData(fieldName);
4586
4587        return oneField.isMultiValued();
4588    } /* isMultiValued(String) */
4589
4590
4591    /**
4592     * Is a given field readOnly - these fields are not offered for entry
4593     * when a form is produced by the generic database maintenance servlet
4594     *
4595     * @param fieldName The field name to check
4596     * @return True of the field is "read only", false if it is not
4597     * @throws DBException Ff there is no such field
4598     * @deprecated since 5.6, use getJDBCMetaData().isReadOnly(String)
4599     */

4600    public boolean isReadOnly(String JavaDoc fieldName)
4601            throws DBException {
4602        DataFieldMetaData oneField = getFieldMetaData(fieldName);
4603
4604        if (oneField == null) {
4605            throw new DBException("(" + myClassName +
4606                    ") No such field '" + fieldName + "'");
4607        }
4608
4609        return oneField.isReadOnly();
4610    } /* isReadOnly(String) */
4611
4612
4613    /**
4614     * Is a given field 'secret' - these fields are not shown
4615     * when a list is produced by the generic database maintenance servlet (DBMaint).
4616     * This means that only users with update permission to the record can see the
4617     * value of the specified field.
4618     *
4619     * @param fieldName The name of the field to check
4620     * @return True if the field is 'secret', false if it is not
4621     * @throws DBException If there is no such field.
4622     * @see #setSecret(String)
4623     * @deprecated since 5.6, use getJDBCMetaData().isSecret(String)
4624     */

4625    public boolean isSecret(String JavaDoc fieldName)
4626            throws DBException {
4627        DataFieldMetaData oneField = getFieldMetaData(fieldName);
4628
4629        return oneField.isSecret();
4630    } /* isSecret(String) */
4631
4632
4633    /**
4634     * Is a given field virtual? A virtual field is not stored in the target table
4635     * for this object - it may be computed, or stored in another table.
4636     *
4637     * @param fieldName The name of the field to check
4638     * @return True of the field is virtual, false if it is not
4639     * @throws DBException If there is no such field
4640     * @see #addVirtualField(String, String, int, String)
4641     * @deprecated since 5.6, use getJDBCMetaData().isVirtual(String)
4642     */

4643    public boolean isVirtual(String JavaDoc fieldName)
4644            throws DBException {
4645        DataFieldMetaData oneField = getFieldMetaData(fieldName);
4646
4647        return oneField.isVirtual();
4648    } /* isVirtual(String) */
4649
4650
4651    /**
4652     * Find the maximum of the values in the specified field of records
4653     * se;lected by the DBObject selection criteria.
4654     *
4655     * @param fieldName String DBObject fieldName to average
4656     * @return double Maximum of the records matching the criteria
4657     * @throws DBException If the search could not be completed
4658     */

4659    public double max(String JavaDoc fieldName)
4660            throws DBException {
4661        return sqlAggrFunction("MAX", fieldName);
4662    } /* max() */
4663
4664
4665    /**
4666     * Find the minimum of the values in the specified field of records
4667     * selected by the DBObject selection criteria.
4668     *
4669     * @param fieldName String DBObject fieldName to average
4670     * @return double Minimum of the records matching the criteria
4671     * @throws DBException If the search could not be completed
4672     */

4673    public double min(String JavaDoc fieldName)
4674            throws DBException {
4675        return sqlAggrFunction("MIN", fieldName);
4676    } /* min() */
4677
4678
4679    /**
4680     * Strip out the newlines out of a string
4681     *
4682     * @param fieldValue the Value to strip of newlines
4683     * @return the data with newlines stripped
4684     */

4685    protected String JavaDoc noNewLine(String JavaDoc fieldValue) {
4686        String JavaDoc returnValue = fieldValue;
4687
4688        if (returnValue.indexOf("\n") != 0) {
4689            returnValue = StringUtil.replace(returnValue, "\n", "");
4690        }
4691        if (returnValue.indexOf("\r") != 0) {
4692            returnValue = StringUtil.replace(returnValue, "\r", "");
4693        }
4694
4695        return returnValue;
4696    } /* noNewLine(String) */
4697
4698    /**
4699     * Utility method to return a string with all single quotes replaced with a pair of
4700     * single quotes, and all double quotes also replaced with a pair of single quotes
4701     *
4702     * @param oldString The original string
4703     * @return The string modified as above
4704     */

4705    protected String JavaDoc noQuotes(String JavaDoc oldString) {
4706        String JavaDoc newString = StringUtil.replace(oldString, "'", "''");
4707        String JavaDoc newString2 = StringUtil.replace(newString, "\"", "''");
4708
4709        return newString2;
4710    } /* noQuotes(String) */
4711
4712    /**
4713     * 'Pseudo' factory method to retrieve the caching utility class instance.
4714     *
4715     * @return CacheUtils instance
4716     */

4717    protected CacheUtils getCacheUtil() {
4718        return cacheUtils;
4719    }
4720
4721    /**
4722     * Used by internal caching to ensure that caches are cleared when an update is
4723     * made. Notifies all of the objects registered to receive "update events" on
4724     * this object of the fact that an update has occurred. The "listener" then
4725     * clears or updates it's cache as appropriate.
4726     *
4727     * @param eventCode The "code" for the event that has just occurred
4728     * @throws DBException If an error occurrs trying to send all of the event notices
4729     */

4730    protected synchronized void notifyListeners(String JavaDoc eventCode)
4731            throws DBException {
4732
4733        /* Tell the cache manager to clear our db object cache and */
4734        /* valid value cache */
4735        try {
4736            CacheManager.getInstance();
4737            CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
4738            //Caching is not enabled if cs == null
4739
if (cs == null) {
4740                return;
4741            }
4742
4743            if (EVENT_ADD.equals(eventCode)) {
4744                //Add will automatically put it in the cache, which, in turn
4745
//will clear out related values
4746
//While it may seem odd that we're removing it when an item
4747
//is added, right now, auto-inc fields won't necessarily
4748
//get transferred properly into the cache.
4749
cs.removeItem(myClassName, this);
4750            } else if (EVENT_UPDATE.equals(eventCode)) {
4751                //Add will automatically put it in the cache, which, in turn
4752
//will clear out related values
4753
cacheUtils.addToCache(this);
4754            } else if (EVENT_DELETE.equals(eventCode)) {
4755                //Removal will clear out related values
4756
cs.removeItem(myClassName, this);
4757            } else {
4758                throw new DBException("Unknown Notification Code: " + eventCode);
4759            }
4760
4761            /* Must clear the entire valid values cache */
4762            //Should now be taken care of via cache listening listening
4763
// cs.clear(myClassName + ".validValues");
4764

4765        } catch (CacheException ce) {
4766            log.error("Unable to clear caches correctly", ce);
4767        }
4768
4769    } /* notifyListeners(String) */
4770
4771
4772    /**
4773     * populateDefaultValues is called by the schema object to allow a table to
4774     * populate itself with any desired values. <p>
4775     * The base class implementation does nothing. Override this method in derived
4776     * classes to achieve your custom behavior. <p>
4777     * <p/>
4778     * The calling routine, DBTool.populateTables(), will set dbName on each
4779     * object, so you can get it from getDataContext(), and you can assume that the
4780     * user for the population action is Admin.
4781     *
4782     * @throws DBException Upon add error, never if it isn't overridden.
4783     */

4784    public synchronized void populateDefaultValues()
4785            throws DBException {
4786
4787        //Base class version does nothing.
4788
} /* populateDefaultValues() */
4789
4790
4791    /**
4792     * Used when reading object from initialization stream.
4793     *
4794     * @param stream The object input stream
4795     * @throws IOException upon communication failure
4796     * @see com.jcorporate.expresso.core.dbobj.DBObject#writeObject
4797     */

4798    private void readObject(ObjectInputStream JavaDoc stream)
4799            throws IOException JavaDoc {
4800        try {
4801            myClassName = getClass().getName();
4802            initialize();
4803            stream.defaultReadObject();
4804            if (fieldData != null) {
4805                DataObjectMetaData metadata = getMetaData();
4806                for (Iterator JavaDoc i = fieldData.keySet().iterator(); i.hasNext();) {
4807                    String JavaDoc fieldName = (String JavaDoc) i.next();
4808                    DataField dataField = (DataField) fieldData.get(fieldName);
4809                    if (dataField.getOwner() == null) {
4810                        dataField.setOwner(this);
4811                    }
4812
4813                    if (dataField.getFieldMetaData() == null) {
4814                        dataField.setFieldMetaData(metadata.getFieldMetadata(fieldName));
4815                    }
4816                }
4817            }
4818        } catch (DBException dbe) {
4819            dbe.printStackTrace();
4820            throw new IOException JavaDoc("Error initializing deserialized DBObject. " +
4821                    dbe.getMessage());
4822        } catch (Throwable JavaDoc t) {
4823            t.printStackTrace();
4824            log.error("Error instantiating dbobject from stream", t);
4825            throw new IOException JavaDoc("Error initializing deserialized DBObject. " +
4826                    t.getMessage());
4827        }
4828    }
4829
4830    /**
4831     * Writes a DBObject to the stream. We write only a minimal data to the
4832     * stream since serialization would otherwise be expensive. Specifically
4833     * we only write the data values, and nothing else about the class.
4834     *
4835     * @param stream The stream to write to
4836     * @throws IOException upon communication error
4837     */

4838    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
4839        stream.defaultWriteObject();
4840    }
4841
4842
4843    /**
4844     * The reverse of the checkRef method - if this object is referred to
4845     * by some other object, and we are changing our own key (only allowed
4846     * by deleting)
4847     * then check the other table to make sure our key is not being used by it!
4848     * <p>This method should by called by subclass delete() methods
4849     * (which then call super.delete() to handle
4850     * the actual deletion)</p>
4851     *
4852     * @param refObject An instance of the DBObject that refers to us
4853     * @param foreignKeyNames A semicolon-delimited list of foreign
4854     * key names from the other object
4855     * @param errorMessage The error message to throw if the key is in use
4856     * @throws DBException If it is not possible to register the
4857     * given referential constraint
4858     */

4859    protected synchronized void referredToBy(DBObject refObject,
4860                                             String JavaDoc foreignKeyNames,
4861                                             String JavaDoc errorMessage)
4862            throws DBException {
4863        refObject.setDataContext(getDataContext());
4864
4865        Vector JavaDoc foreignKeys = new Vector JavaDoc();
4866
4867        /* Make a list from the foreignKeyNames */
4868        StringTokenizer JavaDoc stk = new StringTokenizer JavaDoc(foreignKeyNames, ";");
4869
4870        while (stk.hasMoreTokens()) {
4871            foreignKeys.addElement(stk.nextToken());
4872        }
4873
4874        /* Get the list of key fields from the refObject */
4875        Enumeration JavaDoc fKeys = foreignKeys.elements();
4876
4877        for (Iterator JavaDoc myKeys = getKeyFieldListIterator(); myKeys.hasNext();) {
4878            String JavaDoc OneMyKey;
4879// Commented out by MR - the only case where I can see this would be a problem
4880
//is in a race condition.
4881
// if (myKeys.hasNext()) {
4882
OneMyKey = (String JavaDoc) myKeys.next();
4883// } else {
4884
// throw new DBException("(" + myClassName +
4885
// ") Wrong number of key elements");
4886
// }
4887

4888            String JavaDoc OneRefKey = (String JavaDoc) fKeys.nextElement();
4889            refObject.setField(OneRefKey, getField(OneMyKey));
4890        }
4891        /* Call retrieve on the ref object & catch any error */
4892        try {
4893            refObject.search();
4894
4895            if (refObject.getFoundCount() != 0) {
4896                throw new DBException("(" + myClassName +
4897                        "):No record found");
4898            }
4899        } catch (DBException de) {
4900            //If the target table doesn't exist, we have not relational problem
4901
//here ;)
4902
if (de.getMessage().indexOf("does not exist") > -1) {
4903                return;
4904            }
4905            throw new DBException("(" + myClassName + ")" +
4906                    errorMessage, de);
4907        }
4908    } /* referredToBy(DBObject, String, String) */
4909
4910
4911    /**
4912     * Remove a specific object from that object's cache. It will then be re-read
4913     * when a new object is requested by a client program
4914     * <p/>
4915     * Creation date: (4/18/00 1:49:46 PM)
4916     *
4917     * @param theDBObj com.jcorporate.expresso.core.dbobj.DBObject The object to
4918     * be removed
4919     * @throws DBException upon error.
4920     */

4921    public synchronized void removeFromCache(DBObject theDBObj)
4922            throws DBException {
4923        try {
4924            CacheManager.getInstance();
4925            CacheManager.removeItem(theDBObj.getDataContext(),
4926                    theDBObj.myClassName, theDBObj);
4927        } catch (CacheException ce) {
4928            throw new DBException(ce);
4929        }
4930    } /* removeFromCache(DBObject) */
4931
4932
4933    /**
4934     * Removes an attribute from this particular database object.
4935     *
4936     * @param attributeName The name of the attribute to remove.
4937     */

4938    public void removeAttribute(String JavaDoc attributeName) {
4939        attributes.remove(attributeName);
4940    }
4941
4942
4943    /**
4944     * Get a particular record from the database into this object's fields
4945     * Key fields for this object must be set; throws otherwise
4946     *
4947     * @throws DBRecordNotFoundException If the record could not be retrieved.
4948     * @throws DBException [Really DBRecordNotFoundException]
4949     * @see #find()
4950     */

4951    public void retrieve()
4952            throws DBException {
4953
4954
4955        if (!haveAllKeys()) {
4956            throw new DBRecordNotFoundException("(" +
4957                    myClassName +
4958                    ") does not have key fields set.");
4959        }
4960
4961        if (getExecutor().retrieve(this) == false) {
4962            throw new DBRecordNotFoundException("(" +
4963                    myClassName +
4964                    ") No such record" +
4965                    forKey());
4966        }
4967
4968        if (getDef().isLoggingEnabled()) {
4969            myUpdates = null;
4970        }
4971
4972        // after retrieve, we know that 'current' values are now the baseline for comparison
4973
cacheIsChangedComparison();
4974        setStatus(BaseDataObject.STATUS_CURRENT);
4975
4976        if (isCached() && !anyFieldsToRetrieve) {
4977            if (log.isDebugEnabled()) {
4978                log.debug("Adding cached " + myClassName +
4979                        " key " + getKey());
4980            }
4981
4982            cacheUtils.addUnmodifiedToCache(this);
4983        } else {
4984            if (log.isDebugEnabled()) {
4985                log.debug("Not adding " + myClassName +
4986                        " to the cache - we don't cache it");
4987            }
4988        }
4989
4990
4991    } /* retrieve() */
4992
4993
4994    /**
4995     * Retrieve this object from cache, if possible.
4996     *
4997     * @return true if the cache supplied this item, false
4998     * otherwise
4999     * @throws DBException upon error.
5000     */

5001    public boolean retrieveFromCache()
5002            throws DBException {
5003
5004        CacheManager.getInstance();
5005        CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
5006        if (cs == null) {
5007            return false;
5008        }
5009
5010        if (retrieveFields == null) {
5011            retrieveFields = new HashMap(getMetaData().getFieldListArray().size());
5012        }
5013
5014        DBField oneField = null;
5015
5016        try {
5017            ConfigJdbc myConfig = ConfigManager.getJdbcRequired(getDataContext());
5018
5019            if (isCached()) {
5020                if (myConfig.dbStats()) {
5021                    String JavaDoc statName = myClassName + "|" + getDataContext();
5022                    synchronized (sCacheStats) {
5023                        CacheStatEntry ce = (CacheStatEntry) sCacheStats.get(statName);
5024
5025                        if (ce == null) {
5026                            ce = new CacheStatEntry(myClassName,
5027                                    getDataContext());
5028                            //Synchronization is not necessary because the ConcurrentReaderHashMap
5029
//takes care of synchronization for us.
5030
sCacheStats.put(statName, ce);
5031                        }
5032
5033                        ce.incCounts(true);
5034                    }
5035                }
5036
5037                if (log.isDebugEnabled()) {
5038                    log.debug("Looking for " + myClassName + "cache");
5039                }
5040
5041                if (cs.existsCache(myClassName)) {
5042                    DBObject cachedObject = (DBObject) cs.getItem(myClassName,
5043                            getKey());
5044
5045                    if (cachedObject != null) {
5046                        JDBCObjectMetaData metadata = getJDBCMetaData();
5047                        if (anyFieldsToRetrieve) {
5048                            String JavaDoc oneFieldName = null;
5049
5050                            for (Iterator JavaDoc i = getFieldsToRetrieveIterator();
5051                                 i.hasNext();) {
5052                                oneFieldName = (String JavaDoc) i.next();
5053                                DataFieldMetaData vField = getFieldMetaData(oneFieldName);
5054                                if (!vField.isLongBinaryType()) {
5055                                    setField(oneFieldName,
5056                                            cachedObject.getField(oneFieldName));
5057                                } else {
5058                                    setField(oneFieldName,
5059                                            cachedObject.getFieldByteArray(oneFieldName));
5060                                }
5061                            } /* for each field */
5062
5063                            for (Iterator JavaDoc i = metadata.getAllKeysMap().values().iterator();
5064                                 i.hasNext();) {
5065                                oneField = (DBField) i.next();
5066                                String JavaDoc fieldName = oneField.getName();
5067                                if (!retrieveFields.containsKey(fieldName)) {
5068                                    setField(fieldName, cachedObject.getField(fieldName));
5069                                }
5070                            }
5071                        } else { /* for each key field */
5072
5073                            for (Iterator JavaDoc it = metadata.getAllFieldsMap().values().iterator();
5074                                 it.hasNext();) {
5075                                oneField = (DBField) it.next();
5076                                String JavaDoc fieldName = oneField.getName();
5077
5078                                if (!oneField.isVirtual() && !oneField.isBinaryObjectType() && !oneField.isLongBinaryType()) {
5079                                    set(fieldName, cachedObject.getDataField(fieldName)
5080                                            .asString());
5081                                } else if (oneField.isLongBinaryType()) {
5082                                    set(fieldName, cachedObject.getFieldByteArray(fieldName));
5083                                }
5084                            } /* for each field */
5085
5086                        } /* if anyFieldsToRetrieve */
5087
5088
5089                        // we know that 'current' values are now the baseline for comparison
5090
cacheIsChangedComparison();
5091
5092                        //
5093
//Set it to current since the Cache contains
5094
//a current copy.
5095
//
5096
setStatus(BaseDataObject.STATUS_CURRENT);
5097                        return true;
5098                    } /* if the object from the cache was not null */
5099
5100                } /* if the cache was initialized */
5101
5102            } /* if we do caching for this object */
5103
5104            if (myConfig.dbStats()) {
5105                synchronized (sCacheStats) {
5106                    String JavaDoc statName = myClassName + "|" + getDataContext();
5107
5108                    CacheStatEntry ce = (CacheStatEntry) sCacheStats.get(statName);
5109
5110                    if (ce == null) {
5111                        ce = new CacheStatEntry(myClassName, getDataContext());
5112                        sCacheStats.put(statName, ce);
5113                    }
5114
5115                    ce.incCounts(false);
5116                }
5117            }
5118        } catch (ConfigurationException ce) {
5119            throw new DBException(ce);
5120        }
5121
5122        return false;
5123    } /* retrieveFromCache() */
5124
5125
5126    /**
5127     * Find a set of keys of all of the objects that match the current
5128     * search critieria in the fields. Search assumes that the fields are
5129     * populated with search criteria instead of data
5130     * NOTE: Criteria in 'text' type colums is ignored. After the search, the
5131     * foundKeys vector is populated with the keys of the records found by the search.
5132     *
5133     * @throws DBException if unable to retreive records due to a database problem
5134     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
5135     */

5136    public synchronized void search()
5137            throws DBException {
5138        boolean needComma = false;
5139
5140
5141        if (getDef().isLoggingEnabled()) {
5142            myUpdates = null;
5143        }
5144
5145        /* Clear the previous result set */
5146        foundKeys = null;
5147
5148        FastStringBuffer myStatement = new FastStringBuffer(128);
5149        myStatement.append("SELECT ");
5150
5151        if (anyFieldsToRetrieve) {
5152            String JavaDoc oneFieldName = null;
5153
5154            for (Iterator JavaDoc i = getFieldsToRetrieveIterator(); i.hasNext();) {
5155                oneFieldName = (String JavaDoc) i.next();
5156
5157                if (needComma) {
5158                    myStatement.append(", ");
5159                }
5160
5161                myStatement.append(selectFieldString(oneFieldName));
5162                needComma = true;
5163            }
5164        } else { /* for each field */
5165            for (Iterator JavaDoc e = getJDBCMetaData().getKeyFieldListArray().iterator(); e.hasNext();) {
5166                if (needComma) {
5167                    myStatement.append(", ");
5168                }
5169
5170                String JavaDoc fieldName = (String JavaDoc) e.next();
5171                myStatement.append(selectFieldString(fieldName));
5172                needComma = true;
5173            } /* for */
5174
5175        } /* if anyFieldsToRetrieve */
5176
5177
5178        myStatement.append(" FROM ");
5179        myStatement.append(getJDBCMetaData().getTargetSQLTable(this.getDataContext()));
5180
5181        if (customWhereClause != null) {
5182            myStatement.append(customWhereClause);
5183            customWhereClause = null;
5184        } else {
5185            myStatement.append(buildWhereClause(true));
5186        }
5187        /* Add the ORDER BY clause if any sortKeys are specified */
5188        if (sortKeys != null && sortKeys.size() > 0) {
5189            myStatement.append(" ORDER BY ");
5190
5191            boolean needComma2 = false;
5192
5193            for (Iterator JavaDoc it = sortKeys.iterator(); it.hasNext();) {
5194                if (needComma2) {
5195                    myStatement.append(", ");
5196                }
5197
5198                myStatement.append((String JavaDoc) it.next());
5199                needComma2 = true;
5200            }
5201        }
5202
5203        DBConnection myConnection = null;
5204
5205        try {
5206            if (localConnection != null) {
5207                myConnection = localConnection;
5208            } else {
5209                myConnection = getConnectionPool().getConnection(myClassName);
5210            }
5211
5212            myConnection.execute(myStatement.toString());
5213
5214            if (foundKeys == null) {
5215                foundKeys = new ArrayList JavaDoc();
5216            }
5217            while (myConnection.next()) {
5218                String JavaDoc oneKeyString = ("");
5219                int allKeysSize = getMetaData().getAllKeysMap().size();
5220
5221                for (int ind = 1; ind <= allKeysSize; ind++) {
5222                    if (ind > 1) {
5223                        oneKeyString = oneKeyString + "/";
5224                    }
5225                    try {
5226                        if (myConnection.isStringNotTrim()) {
5227                            oneKeyString = oneKeyString +
5228                                    StringUtil.notNull(myConnection.getStringNoTrim(ind));
5229                        } else {
5230                            oneKeyString = oneKeyString +
5231                                    StringUtil.notNull(myConnection.getString(ind));
5232                        }
5233                    } catch (DBException de) {
5234                        throw new DBException("Error retrieving field " +
5235                                "at index " + ind + ":" +
5236                                de.getMessage());
5237                    }
5238                } /* for each key field */
5239
5240
5241                foundKeys.add(oneKeyString);
5242            } /* while */
5243
5244        } catch (DBException de) {
5245            throw de;
5246        } finally {
5247            if (localConnection == null && myConnection != null) {
5248                myConnection.release();
5249            }
5250        }
5251    } /* search() */
5252
5253
5254    /**
5255     * Second form of search: takes a list of sort keys & calls regular search.
5256     * The records retrieved are in the specified order
5257     *
5258     * @param sortKeyString A string containing field names,
5259     * seperated by pipes, that indicate in what order the objects
5260     * are retrieved
5261     * @throws DBException upon error.
5262     */

5263    public synchronized void search(String JavaDoc sortKeyString)
5264            throws DBException {
5265        setSortKey(sortKeyString);
5266        search();
5267    } /* search(String) */
5268
5269
5270    /**
5271     * Find a set of records of all of the objects that match the current
5272     * search critieria in the fields
5273     * and retrieve the list of all records that match this criteria
5274     * NOTE: Criteria in 'text' type colums is ignored (SQL Server limitation)
5275     * <p/>
5276     * SIDE-EFFECT: custom 'where' clause is set to null.
5277     *
5278     * @return Vector A vector of new database objects containing the results
5279     * of the search
5280     * @throws DBException If the search could not be completed
5281     */

5282    public synchronized ArrayList JavaDoc searchAndRetrieveList()
5283            throws DBException {
5284
5285        ArrayList JavaDoc retrievedFieldList = new ArrayList JavaDoc();
5286
5287        DBConnection myConnection = null;
5288        try {
5289            myConnection = createAndExecuteSearch(retrievedFieldList);
5290
5291            int recordCount = 0;
5292            int retrieveCount = 0;
5293
5294            while (myConnection.next()) {
5295                recordCount++;
5296                retrieveCount++;
5297
5298                //If there's limitation syntax on, then the first record will be the
5299
//maximum record.
5300
if (retrieveCount < offsetRecord && offsetRecord > 0 &&
5301                        myConnection.getLimitationPosition() == DBConnection.LIMITATION_DISABLED) {
5302                    continue;
5303                } else if (retrieveCount == offsetRecord && offsetRecord > 0 &&
5304                        myConnection.getLimitationPosition() == DBConnection.LIMITATION_DISABLED) {
5305                    recordCount = 0; //Reset count for counting for max records.
5306
continue; //Skip this record... next one, we will start loading.
5307
}
5308                if ((recordCount > maxRecords) && (maxRecords > 0)) {
5309                    setAttribute("More Records", "Y");
5310                    break;
5311                }
5312
5313                //Only allocate if we're gonna load this record
5314
DBObject myObj = newInstance();
5315                loadFromConnection(myObj, myConnection, retrievedFieldList);
5316                recordSet.add(myObj);
5317            }
5318        } catch (DBException de) {
5319            log.error("Error performing searchAndRetrieveList", de);
5320            throw new DBException(de);
5321        } catch (Throwable JavaDoc t) {
5322            log.error("Error performing searchAndRetrieveList", t);
5323            throw new DBException("Error performing searchAndRetrieveList", t);
5324        } finally {
5325            if (localConnection == null) {
5326                if (myConnection != null) {
5327                    getConnectionPool().release(myConnection);
5328                }
5329            }
5330        }
5331
5332        return recordSet;
5333    } /* searchAndRetrieveList() */
5334
5335
5336    /**
5337     * Search and retrieve in a particular order
5338     *
5339     * @param sortKeyString A pipe-delimited list of key fields to sort
5340     * the returned set by
5341     * @return Vector A vector of new database objects retrieved by the search
5342     * @throws DBException If the search could not be completed
5343     */

5344    public synchronized ArrayList JavaDoc searchAndRetrieveList(String JavaDoc sortKeyString)
5345            throws DBException {
5346        setSortKey(sortKeyString);
5347        return searchAndRetrieveList();
5348    } /* searchAndRetrieve(String) */
5349
5350
5351    /**
5352     * Set an attribute. Attributes are temporary (e.g. not stored in the DBMS) values
5353     * associated with this particular DB object instance.
5354     *
5355     * @param attribName The name of the attribute being defined
5356     * @param attribValue The object to store under this attribute name
5357     */

5358    public synchronized void setAttribute(String JavaDoc attribName,
5359                                          Object JavaDoc attribValue) {
5360        if (attributes == null) {
5361            attributes = new HashMap();
5362        }
5363
5364        attributes.put(attribName, attribValue);
5365    } /* setAttribute(String, Object) */
5366
5367
5368    /**
5369     * Read the configuration table to determine the max size
5370     * of the cache for this db object. The way we go about it is like so:
5371     * <ol>
5372     * <li>Check if setCacheSize(int) has been set before. If so, return that</li>
5373     * <li>Check if a specific entry exists for this object in the ControllerDefaults
5374     * table if so return that</li>
5375     * <li>Try to read the default entries in the table. If it doesn't exist you're
5376     * going to see a log warning. Return that value</li>
5377     * <li>And finally if that all fails, set the default cache size to zero</li>
5378     * <ol>
5379     */

5380    public synchronized void setCacheSize() {
5381
5382        synchronized (DBObject.class) {
5383            //
5384
//Cache size has already been set.
5385
//
5386
if (myCacheSize >= 0) {
5387                return;
5388            }
5389
5390            //
5391
//Check to see if we already have a value in the metadata
5392
//
5393
myCacheSize = getDef().getCacheSize();
5394            if (myCacheSize >= 0) {
5395                return;
5396            }
5397
5398            CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
5399            if (cs == null) {
5400                myCacheSize = 0;
5401                return;
5402            }
5403            /* Check if caching is enabled at all */
5404            try {
5405                ConfigJdbc myConfig = ConfigManager.getJdbcRequired(getDataContext());
5406
5407                if (!myConfig.cache()) {
5408                    if (log.isDebugEnabled()) {
5409                        log.debug("We don't cache at all");
5410                    }
5411
5412                    myCacheSize = 0;
5413                }
5414            } catch (ConfigurationException ce) {
5415                log.error(ce);
5416                myCacheSize = 0;
5417                return;
5418            }
5419
5420            try {
5421                if (cs != null) {
5422                    if (!cs.existsCache("com.jcorporate.expresso.services.dbobj.DBObjLimit")) {
5423                        cs.createCache("com.jcorporate.expresso.services.dbobj.DBObjLimit",
5424                                false);
5425                    }
5426                }
5427
5428                DBObjLimit dbl = (DBObjLimit) cs.getItem(
5429                        com.jcorporate.expresso.services.dbobj.DBObjLimit.class.getName(),
5430                        myClassName);
5431
5432                if (dbl == null) {
5433                    dbl = new DBObjLimit();
5434                    dbl.setDataContext(getDataContext());
5435                    dbl.setField("DBObjectName", myClassName);
5436
5437                    try {
5438                        dbl.retrieve();
5439                    } catch (DBRecordNotFoundException de) {
5440                        //
5441
//Get the default caching value
5442
//
5443
dbl.setField("DBObjectName",
5444                                "com.jcorporate.expresso.services.dbobj.ControllerDefault");
5445                        try {
5446                            dbl.retrieve();
5447                        } catch (DBRecordNotFoundException dbe) {
5448                            log.warn("Unable to locate default Controller Parameters for data caches for object" +
5449                                    myClassName +
5450                                    ". Setting default cache sizes");
5451                            dbl.setField("DBObjectName", myClassName);
5452                            dbl.setField("CacheSize", "50");
5453                            dbl.setField("PageLimit", "0");
5454                            dbl.setField("TTL", "10");
5455                            dbl.add();
5456                        }
5457
5458                    } catch (DBException de) {
5459                        log.warn("Unable to locate default Controller Parameters for data caches for object" +
5460                                myClassName +
5461                                " Setting cache size to zero");
5462                        dbl.setField("DBObjectName", myClassName);
5463                        dbl.setField("CacheSize", "0");
5464                        dbl.setField("PageLimit", "0");
5465                        dbl.setField("TTL", "10");
5466                    }
5467
5468                    if (cs != null) {
5469                        cs.addItem("com.jcorporate.expresso.services.dbobj.DBObjLimit",
5470                                dbl, DBObject.DBOBJLIMIT_CACHE_TTL);
5471                    }
5472                }
5473
5474                if (!dbl.getDataField("CacheSize").isNull()) {
5475                    myCacheSize = dbl.getFieldInt("CacheSize");
5476                    getDef().setCacheSize(myCacheSize);
5477                } else {
5478                    myCacheSize = 50;
5479                    getDef().setCacheSize(myCacheSize);
5480                }
5481                setAttribute("TTL", new Integer JavaDoc(dbl.getFieldInt("TTL")));
5482            } catch (CacheException ce) {
5483                log.error("Unable to set cache size - using zero", ce);
5484                myCacheSize = 0;
5485                setAttribute("TTL", new Integer JavaDoc(60 * 1000 * 10));
5486            } catch (DBException de) {
5487                log.error("Database exception setting cache size - using zero", de);
5488                myCacheSize = 0;
5489                setAttribute("TTL", new Integer JavaDoc(60 * 1000 * 10));
5490            } catch (Throwable JavaDoc t) {
5491                log.error("Unknown exception setting cache size - using zero", t);
5492                myCacheSize = 0;
5493                setAttribute("TTL", new Integer JavaDoc(60 * 1000 * 10));
5494            }
5495            getDef().setCacheSize(myCacheSize);
5496
5497        }
5498    } /* setCacheSize() */
5499
5500    /**
5501     * Set a characterset for a particular field.
5502     * for more information on Filters and implementing Filters for your own
5503     * characterset.
5504     * <p/>
5505     * Sets the characterset expected for a this DB object. A Table's default
5506     * is &quot;ISO-8859-1&quot;
5507     *
5508     * @param newCharSet The name of the characterset that you will filter against.
5509     * For Western-Latin character sets use &quot;ISO-8859-1&quot; as the parameter.
5510     * Currently, no other charactersets are implemented. <br>
5511     * @throws DBException upon error.
5512     * @see com.jcorporate.expresso.core.security.filters.FilterManager
5513     * @see com.jcorporate.expresso.core.security.filters.Filter
5514     * @see com.jcorporate.expresso.core.security.filters.ISO_8859_1
5515     */

5516    public synchronized void setCharset(String JavaDoc newCharSet)
5517            throws DBException {
5518        getDef().setCharset(newCharSet);
5519    } /* setCharset(String) */
5520
5521
5522    /**
5523     * Gets the check zero update flags for this DBObject.
5524     *
5525     * @return boolean value that denotes if check zero update is on or off.
5526     * @throws DBException upon error.
5527     * @see #getCheckZeroUpdate
5528     * @see com.jcorporate.expresso.core.misc.ConfigJdbc#checkZeroUpdate()
5529     * @see com.jcorporate.expresso.core.db.DBConnectionPool#getCheckZeroUpdate()
5530     */

5531    public synchronized boolean getCheckZeroUpdate()
5532            throws DBException {
5533        return getDef().checkZeroUpdate();
5534    } /* setCheckZeroUpdate(boolean) */
5535
5536
5537    /**
5538     * Turn on or off the facility to verify that when an update is made that at
5539     * least one record got updated. If this flag is on, and no records get updated,
5540     * the update() method throws an Exception. Note that for some databases, if the
5541     * existing record is not changed (e.g. it was already identical to what
5542     * was being updated) this counts "no update" (notably, mySQL does this).
5543     *
5544     * @param newFlag True to turn on checking, false to turn it off
5545     * @throws DBException upon error.
5546     */

5547    public synchronized void setCheckZeroUpdate(boolean newFlag)
5548            throws DBException {
5549        getDef().setCheckZeroUpdate(newFlag);
5550    } /* setCheckZeroUpdate(boolean) */
5551
5552
5553    /**
5554     * Set a specific DB connection for use with this db object. If you do not set
5555     * a connection, the db object will request it's own connection from the
5556     * appropriate connection pool & release it again after every operation (e.g.
5557     * add, update, etc). It is important to use your own explicit connection when
5558     * dealing with a database transactional environment (e.g. commit(), rollback()).
5559     *
5560     * @param newConnection The new DBConnection object to be used by this DB Object
5561     * @throws DBException upon error.
5562     */

5563    public synchronized void setConnection(DBConnection newConnection)
5564            throws DBException {
5565        setConnection(newConnection, newConnection.getDataContext());
5566    } /* setConnection(DBConnection) */
5567
5568    /**
5569     * <p/>
5570     * Set a specific DB connection for use with this db object. If you do not set
5571     * a connection, the db object will request it's own connection from the
5572     * appropriate connection pool & release it again after every operation (e.g.
5573     * add, update, etc). It is important to use your own explicit connection when
5574     * dealing with a database transactional environment (e.g. commit(), rollback()).
5575     * </p>
5576     * <p>The difference between this and setConnection(DBConnection) is that this
5577     * is used for using otherDB capabilities within a transaction. So you use
5578     * a dbconnection from your other pool, but the setup tables are in a different
5579     * context</p>
5580     *
5581     * @param newConnection The new DBConnection object to be used by this DB Object
5582     * @param setupTablesContext the data context that is used for the expresso setup tables.
5583     * @throws DBException upon error.
5584     * @see #setConnection(DBConnection)
5585     */

5586    public synchronized void setConnection(DBConnection newConnection,
5587                                           String JavaDoc setupTablesContext) throws DBException {
5588        localConnection = newConnection;
5589        setDataContext(setupTablesContext);
5590    }
5591
5592
5593    /**
5594     * Specify a custom "where" clause for the SQL used to retrieve records for this
5595     * object. The where clause 'reset' after each call to searchAndRetrieve() or
5596     * other retrieval methods, so it must be set just before the call to retrieve
5597     * the records is made. If no custom where clause is specified by this method, the
5598     * where clause is built from the field values in the object.
5599     *
5600     * @param newCustomWhere java.lang.String string with clause. Do NOT add 'WHERE' keyword--this is prepended by system, so that multiple calls to append can be made
5601     */

5602    public synchronized void setCustomWhereClause(String JavaDoc newCustomWhere) {
5603        setCustomWhereClause(newCustomWhere, false);
5604    } /* setCustomWhereClause(String) */
5605
5606    /**
5607     * Allows us to specify a custom WHERE clause and have it appended to the
5608     * one built from the field values in the object
5609     *
5610     * @param newCustomWhere java.lang.String string with clause. Do NOT add 'WHERE' keyword--this is prepended by system, so that multiple calls to append can be made
5611     * @param append if true, custom WHERE clause is appended
5612     */

5613    public synchronized void setCustomWhereClause(String JavaDoc newCustomWhere, boolean append) {
5614
5615        if (append) {
5616            customWhereClause = " AND " + newCustomWhere;
5617        } else {
5618            if (newCustomWhere == null || newCustomWhere.length() == 0) {
5619                customWhereClause = null;
5620            } else {
5621                customWhereClause = WHERE_KEYWORD + newCustomWhere;
5622            }
5623        }
5624        appendCustomWhere = append;
5625    } /* setCustomWhereClause(String) */
5626
5627    /**
5628     * Allows us to query the custom where clause (if any). ' WHERE ' has been prepended to the clause.
5629     * You may want to remove this prepended info for reuse, e.g. getCustomWhereClause().substring(WHERE_KEYWORD.length())
5630     *
5631     * @return java.lang.String or null if no custom where clause has been set.
5632     */

5633    public String JavaDoc getCustomWhereClause() {
5634        return customWhereClause;
5635    }
5636
5637
5638    /**
5639     * Define a "default" value for a field - to be used for the field
5640     * when the user does not specify a value.
5641     *
5642     * @param fieldName the field to set
5643     * @param fieldValue the default value to set
5644     * @throws DBException upon error.
5645     */

5646    protected synchronized void setDefaultValue(String JavaDoc fieldName,
5647                                                String JavaDoc fieldValue)
5648            throws DBException {
5649        getDef().setDefaultValue(fieldName, fieldValue);
5650    }
5651
5652    /**
5653     * Set the description of this object
5654     *
5655     * @param newDescription A description of this database object
5656     * @throws DBException upon error.
5657     */

5658    public synchronized void setDescription(String JavaDoc newDescription)
5659            throws DBException {
5660        getDef().setDescription(newDescription);
5661    } /* setDescription(String) */
5662
5663
5664    /**
5665     * Byte primitive integer Typesafe version of setField.
5666     * Convenience method to set the field values.
5667     *
5668     * @param fieldName The name to set
5669     * @param fieldValue the byte integer field value to set
5670     * @throws DBException upon error.
5671     */

5672    public synchronized void setField(String JavaDoc fieldName, byte fieldValue)
5673            throws DBException {
5674        setField(fieldName, Byte.toString(fieldValue));
5675    }
5676
5677    /**
5678     * Byte primitive integer Typesafe version of setField.
5679     * Convenience method to set the field values.
5680     *
5681     * @param fieldName The name to set
5682     * @param fieldValue the byte integer field value to set
5683     * @throws DBException upon error.
5684     */

5685    public synchronized void setField(String JavaDoc fieldName, byte[] fieldValue)
5686            throws DBException {
5687        StringUtil.assertNotBlank(fieldName, "Field name may not be blank");
5688
5689        if (getStatus() == null) {
5690            throw new DBException("Status is null");
5691        }
5692        if (getStatus().equals(BaseDataObject.STATUS_CURRENT)) {
5693            setStatus(BaseDataObject.STATUS_UPDATED);
5694        }
5695
5696        DataFieldMetaData oneField = getFieldMetaData(fieldName);
5697
5698        if (!oneField.isLongBinaryType()) {
5699            throw new DBException("(" + this.myClassName + ") Field " +
5700                    fieldName +
5701                    " is not a longvarbinary field. You should not be" +
5702                    " calling setField directly");
5703        }
5704
5705        if (oneField.isVirtual()) {
5706            throw new DBException("(" + this.myClassName + ") Field " +
5707                    fieldName +
5708                    " is a virtual field. Database object should " +
5709                    "extend setField method to handle requests for this field");
5710        }
5711
5712        clearError(fieldName);
5713        if (fieldValue != null) {
5714            logChange((DBField) oneField, fieldValue.toString());
5715        }
5716
5717        setFieldData(oneField.getName(), fieldValue);
5718    }
5719
5720
5721    /**
5722     * Short primitive integer Typesafe version of setField.
5723     * Convenience method to set the field values.
5724     *
5725     * @param fieldName The name to set
5726     * @param fieldValue the short integer field value to set
5727     * @throws DBException upon error.
5728     */

5729    public synchronized void setField(String JavaDoc fieldName, short fieldValue)
5730            throws DBException {
5731        setField(fieldName, Short.toString(fieldValue));
5732    }
5733
5734    /**
5735     * Integer primitive Typesafe version of setField.
5736     * Convenience method to set the field value.
5737     *
5738     * @param fieldName The name to set
5739     * @param fieldValue the integer field value to set
5740     * @throws DBException upon invalid parameters.
5741     */

5742    public synchronized void setField(String JavaDoc fieldName, int fieldValue)
5743            throws DBException {
5744        setField(fieldName, Integer.toString(fieldValue));
5745    }
5746
5747    /**
5748     * Long primitive Typesafe version of <code>setField</code>.
5749     * Convenience method to set the field value.
5750     *
5751     * @param fieldName The name to set
5752     * @param fieldValue the long integer field value to set
5753     * @throws DBException upon invalid parameters.
5754     */

5755    public synchronized void setField(String JavaDoc fieldName, long fieldValue)
5756            throws DBException {
5757        setField(fieldName, Long.toString(fieldValue));
5758    }
5759
5760    /**
5761     * Double primitive Typesafe version of <code>setField</code>.
5762     * Convenience method to set the field values.
5763     *
5764     * @param fieldName The name to set
5765     * @param fieldValue the double field value to set
5766     * @throws DBException upon invalid values.
5767     */

5768    public synchronized void setField(String JavaDoc fieldName, double fieldValue)
5769            throws DBException {
5770        setField(fieldName, Double.toString(fieldValue));
5771    }
5772
5773    /**
5774     * BigDecimal object Typesafe version of <code>setField</code>.
5775     * Convenience method to set the field values.
5776     *
5777     * @param fieldName The name to set
5778     * @param fieldValue the big decimal value to set
5779     * @throws DBException upon error.
5780     */

5781    public synchronized void setField(String JavaDoc fieldName, BigDecimal JavaDoc fieldValue)
5782            throws DBException {
5783        setField(fieldName, fieldValue.toString());
5784    }
5785
5786
5787    /**
5788     * Boolean typesafe version of setField
5789     *
5790     * @param fieldName The field name to set
5791     * @param fieldValue the new boolean Field Value to set
5792     * @throws DBException upon error.
5793     */

5794    public synchronized void setField(String JavaDoc fieldName, boolean fieldValue)
5795            throws DBException {
5796        if (fieldValue) {
5797            setField(fieldName, "true");
5798        } else {
5799            setField(fieldName, "false");
5800        }
5801    }
5802
5803    /**
5804     * Internal refactoring for getting what a boolean field should be set to.
5805     *
5806     * @param fieldValue The target value
5807     * @return the string value
5808     * @throws DBException upon error.
5809     */

5810    protected String JavaDoc getBooleanFieldValue(boolean fieldValue)
5811            throws DBException {
5812        try {
5813            boolean nativeBoolean = ConfigManager.getContext(getDataContext()).getJdbc().isNativeBool();
5814
5815            if (fieldValue == true) {
5816                if (nativeBoolean) {
5817                    return "true";
5818                } else {
5819                    return "Y";
5820                }
5821            } else {
5822                if (nativeBoolean) {
5823                    return "false";
5824                } else {
5825                    return "N";
5826                }
5827            }
5828        } catch (ConfigurationException ce) {
5829            throw new DBException(ce);
5830        }
5831    }
5832
5833    /**
5834     * Set the given field to a given value. Call checkField
5835     * before setting the value to verify that it is allowed for this field
5836     * The subclass must override checkField as required
5837     *
5838     * @param fieldName The name of the field
5839     * @param fieldValue The value to set the field
5840     * @throws DBException If the given field does not exist in this object or
5841     * the value supplied is not allowed for this field
5842     */

5843    public synchronized void setField(String JavaDoc fieldName, String JavaDoc fieldValue)
5844            throws DBException {
5845        StringUtil.assertNotBlank(fieldName, "Field name may not be blank");
5846
5847        if (getStatus() == null) {
5848            throw new DBException("Status is null");
5849        }
5850        if (getStatus().equals(BaseDataObject.STATUS_CURRENT)) {
5851            setStatus(BaseDataObject.STATUS_UPDATED);
5852        }
5853
5854        DataFieldMetaData oneField = getFieldMetaData(fieldName);
5855
5856        if (oneField.isBinaryObjectType()) {
5857            throw new DBException("(" + myClassName + ") Field " +
5858                    fieldName +
5859                    " is a binary object field. You should not be" +
5860                    " calling setField directly");
5861        }
5862
5863        if (oneField.isVirtual()) {
5864            throw new DBException("(" + myClassName + ") Field " +
5865                    fieldName +
5866                    " is a virtual field. Database object should " +
5867                    "extend setField method to handle requests for this field");
5868        }
5869        //
5870
//If there was an error associated with this field before, then
5871
//remove the error attribute.
5872
//
5873
clearError(fieldName);
5874
5875        if (!(oneField.allowsNull() && (fieldValue == null))) {
5876            // truncate strings if necessary
5877
// we don't truncate for query expressions such as
5878
// "BETWEEN 3 AND 5"
5879
if (oneField.isQuotedTextType()
5880                    && (oneField.getLengthInt() > 0)
5881                    && (fieldValue != null)
5882                    && (fieldValue.length() > oneField.getLengthInt())
5883                    && (denotesRange(fieldValue) == null)
5884            ) {
5885                fieldValue = fieldValue.substring(0, oneField.getLengthInt());
5886            }
5887
5888            /**
5889             * if it's not a text field and it has newlines or returns embedded,
5890             * take 'em out
5891             * @todo this doesn't seem right for varchar
5892             */

5893            if (fieldValue != null) {
5894                /* Check for returns or newlines in key fields - remove if found */
5895                if (oneField.getTypeString().equalsIgnoreCase("varchar")) {
5896                    fieldValue = noNewLine(fieldValue);
5897                } /* if not a text field */
5898
5899                if (oneField.getTypeString().equalsIgnoreCase("char")) {
5900                    fieldValue = noNewLine(fieldValue);
5901                } /* if not a text field */
5902
5903                //Used to help mesh boolean values with checkboxes in html
5904
//fields
5905
if (oneField.isBooleanType()) {
5906                    if (fieldValue.length() == 0) {
5907                        if (log.isDebugEnabled()) {
5908                            log.debug("Zero length boolean. Skipping modification");
5909                        }
5910                    } else if (fieldValue.equalsIgnoreCase("Y") ||
5911                            fieldValue.equalsIgnoreCase("t") ||
5912                            fieldValue.equalsIgnoreCase("true")) {
5913                        fieldValue = getBooleanFieldValue(true);
5914                    } else {
5915                        fieldValue = getBooleanFieldValue(false);
5916                    }
5917                }
5918            } /* if field was null */
5919
5920        } /* field was not null and allowed to be */
5921
5922        logChange((DBField) oneField, fieldValue);
5923
5924        setFieldData(oneField.getName(), fieldValue);
5925    } /* setField(String, String) */
5926
5927
5928    /**
5929     * Date object Typesafe version of <code>setField</code>.
5930     * Convenience method to set the field values.
5931     *
5932     * @param fieldName The name to set
5933     * @param fieldValue the Java date value to set
5934     * @throws DBException upon error.
5935     */

5936    public void setField(String JavaDoc fieldName, java.util.Date JavaDoc fieldValue)
5937            throws DBException {
5938        DataFieldMetaData fieldMetadata = getFieldMetaData(fieldName);
5939        if (fieldMetadata.isDateOnlyType()) {
5940            setField(fieldName, DateTime.getDateForDB(fieldValue, getDataContext()));
5941        } else if (fieldMetadata.isTimeType()) {
5942            setField(fieldName, DateTime.getTimeForDB(fieldValue, getDataContext()));
5943        } else {
5944            //Format as datetime
5945
setField(fieldName, DateTime.getDateTimeForDB(fieldValue, getDataContext()));
5946        }
5947    }
5948
5949
5950    /**
5951     * Helper Function:
5952     * If Change Logging is enabled, then this logs what has changed
5953     *
5954     * @param oneField The field to set the data to.
5955     * @param fieldValue The value to set the field to.
5956     */

5957    protected void logChange(DBField oneField, String JavaDoc fieldValue) {
5958        if (getDef().isLoggingEnabled()) {
5959
5960            /* Record this setField */
5961            FieldUpdate myUpdate = new FieldUpdate();
5962            myUpdate.fieldName = oneField.getName();
5963            myUpdate.oldValue = getFieldData(myUpdate.fieldName);
5964            myUpdate.newValue = fieldValue;
5965
5966            /* Put this update in the list of updates since the last retrieve */
5967            if (myUpdates == null) {
5968                myUpdates = new ArrayList JavaDoc(5);
5969            }
5970
5971            myUpdates.add(myUpdate);
5972        } /* if change logging is enabled */
5973    }
5974
5975
5976    /**
5977     * Helper function that doesn't fire all the processing... just set's the
5978     * field data in raw form and forgets it.
5979     *
5980     * @param fieldName The name of the field to set
5981     * @param fieldValue the value to set it to.
5982     */

5983    protected synchronized void setFieldData(String JavaDoc fieldName,
5984                                             String JavaDoc fieldValue) {
5985        if (fieldData == null) {
5986            fieldData = new HashMap();
5987        }
5988
5989        DataField df = (DataField) fieldData.get(fieldName);
5990        if (df == null) {
5991            df = DefaultDataField.getInstance(getFieldMetaData(fieldName), this);
5992        }
5993
5994        df.setValue(fieldValue);
5995        fieldData.put(fieldName, df);
5996    }
5997
5998
5999    /**
6000     * Helper function that doesn't fire all the processing... just set's the
6001     * field data in raw form and forgets it.
6002     *
6003     * @param fieldName The name of the field to set
6004     * @param fieldValue the value to set it to.
6005     */

6006    protected synchronized void setFieldData(String JavaDoc fieldName,
6007                                             byte[] fieldValue) {
6008        if (fieldData == null) {
6009            fieldData = new HashMap();
6010        }
6011
6012        DataField df = (DataField) fieldData.get(fieldName);
6013        if (df == null) {
6014            df = DefaultDataField.getInstance(this.getFieldMetaData(fieldName), this);
6015        }
6016
6017        df.setValue(fieldValue);
6018        fieldData.put(fieldName, df);
6019    }
6020
6021    /**
6022     * Convenience method to set a field to be distinct or not
6023     * within this database object.
6024     *
6025     * @param fieldName the name of the field
6026     * @param flag boolean value true if the field is set distinct.
6027     * @throws DBException If the operation could not be completed
6028     * <p/>
6029     * author Peter Pilgrim <peter.pilgrim@db.com>
6030     * @see #isFieldDistinct
6031     * @see #isDistinct()
6032     */

6033    public void setFieldDistinct(String JavaDoc fieldName, boolean flag)
6034            throws DBException {
6035
6036        DataFieldMetaData oneField = getFieldMetaData(fieldName);
6037
6038        if (oneField.isVirtual()) {
6039            throw new DBException("(" + myClassName + ") Field " +
6040                    fieldName +
6041                    " is a virtual field. Database object should extend " +
6042                    "getField method to handle requests for this field");
6043        }
6044
6045        if (oneField.isBinaryObjectType() && flag) {
6046            throw new DBException("(" + myClassName + ") Field " +
6047                    fieldName +
6048                    " is a BLOB field. It should not be retrieved under normal queries");
6049        }
6050
6051        if (oneField.isCharacterLongObjectType()) {
6052            log.warn("You are selecting a DISTINCT row based upon a Long Character Object." +
6053                    " Although the database may work with it, it will be a highly " +
6054                    "inefficient query.");
6055        }
6056
6057
6058        if (distinctFields == null) {
6059            distinctFields = new HashMap();
6060        }
6061
6062        distinctFields.put(oneField.getName(), oneField.getName());
6063        anyFieldsDistinct = true;
6064    } /* setFieldDistinct(String, boolean) */
6065
6066
6067    /**
6068     * Convenience method to set the fields to be retrieved
6069     * within this database object.
6070     * <b>NOTE AS OF EXPRESSO 5.0</b> setFieldsToRetrieve() could cause you
6071     * some problems if you're attempting to retrieve long objects since
6072     * everything will be converted to a STRING. Please use caution :).
6073     *
6074     * @param fieldNames contain the name of the fields separate by "|"
6075     * @throws DBException If the operation could not be completed
6076     * <p/>
6077     * author Yves Henri Amaizo <amy_amaizo@compuserve.com>
6078     * @see #isFieldsToRetrieve()
6079     */

6080    public void setFieldsToRetrieve(String JavaDoc fieldNames)
6081            throws DBException {
6082
6083        if (fieldNames == null) {
6084            // Allows DBObject to be used within a MultiDBObject for join purposes
6085
// when no fields are required from this DBObject
6086
anyFieldsToRetrieve = true;
6087            anyFieldsToRetrieveMulti = true;
6088            return;
6089        }
6090
6091        if (retrieveFields == null) {
6092            retrieveFields = new HashMap();
6093        }
6094
6095        String JavaDoc tempString = null;
6096        StringTokenizer JavaDoc stk = new StringTokenizer JavaDoc(fieldNames, "|");
6097
6098        while (stk.hasMoreTokens()) {
6099            tempString = stk.nextToken();
6100
6101            //
6102
//Will throw a DBException if the field doesn't exist.
6103
//
6104
DataFieldMetaData oneField = getFieldMetaData(tempString);
6105
6106            if (!oneField.isCharacterLongObjectType() && !oneField.isLongBinaryType() && oneField.isLongObjectType()) {
6107                log.warn("Ignoring field to retrieve for field name: " + tempString +
6108                        " the field type is considered a binary large object and" +
6109                        " should be retrieved separately");
6110
6111                continue;
6112            }
6113
6114            retrieveFields.put(oneField.getName(), oneField.getName());
6115            anyFieldsToRetrieve = true;
6116            anyFieldsToRetrieveMulti = true; // ADDED HERE BY ABHI
6117
}
6118    } /* setFieldsToRetrieve(String) */
6119
6120
6121    /**
6122     * Set the values for each of the key fields of this object from
6123     * a /-delimited string
6124     *
6125     * @param keyValues The string containing one value for each key
6126     * field in the object
6127     * @throws DBException If the key fields cannot be set
6128     */

6129    public synchronized void setKeys(String JavaDoc keyValues)
6130            throws DBException {
6131        StringTokenizer JavaDoc stk = new StringTokenizer JavaDoc(keyValues, "/");
6132        ArrayList JavaDoc keysToSet = new ArrayList JavaDoc();
6133
6134        while (stk.hasMoreTokens()) {
6135            keysToSet.add(stk.nextToken());
6136        }
6137
6138        ArrayList JavaDoc keys = getJDBCMetaData().getKeyFieldListArray();
6139
6140        if (keys.size() != keysToSet.size()) {
6141            throw new DBException("(" + myClassName + ") There are " +
6142                    keys.size() +
6143                    " key fields in this table, but " +
6144                    keyValues + " only contains " +
6145                    keysToSet.size() + " values");
6146        }
6147
6148        Iterator JavaDoc l = keysToSet.iterator();
6149
6150        for (Iterator JavaDoc i = keys.iterator(); i.hasNext();) {
6151            setField((String JavaDoc) i.next(), (String JavaDoc) l.next());
6152        }
6153    } /* setKeys(String) */
6154
6155
6156    /**
6157     * Set a field's lookup object - this is the name of another database
6158     * object that can be used to look up valid values for this object. This value
6159     * is used by the DBMaint servlet to provide automatic lookup ability for
6160     * related fields. Side-effect: adds a listener for this object to cache manager
6161     * in order to maintain caches for valid values
6162     *
6163     * @param fieldName The field name that this lookup is associated with
6164     * @param objectName The db object used to look up valid values for this field.
6165     * @throws DBException upon error.
6166     */

6167    public synchronized void setLookupObject(String JavaDoc fieldName,
6168                                             String JavaDoc objectName)
6169            throws DBException {
6170        DataFieldMetaData oneField = getFieldMetaData(fieldName);
6171        oneField.setLookupObject(objectName);
6172    } /* setLookupObject(String, String) */
6173
6174    /**
6175     * Sets the lookup field name. This field allows automatic lookups to
6176     * be performed by matching the field name given with the primary key of the
6177     * lookup object. Note to use this feature, the lookup object can only
6178     * have one and only one primary key field at this time.
6179     *
6180     * @param fieldName the name to set the lookup field value for.
6181     * @param lookupFieldName the field name in the lookup object to map.
6182     */

6183    public synchronized void setLookupField(String JavaDoc fieldName,
6184                                            String JavaDoc lookupFieldName) {
6185        getDef().getDBField(fieldName).setLookupField(lookupFieldName);
6186    }
6187
6188    /**
6189     * Set a "mask", or regular expresso to be matched, for the named field.
6190     * This regular expression is then checked whenever the field is validated
6191     * <p/>
6192     * Note: Masks are used to validate a field's data type to prevent sql injections. So, when
6193     * setting your own mask, make sure it excludes thwarts sql injections.
6194     *
6195     * @param fieldName The field name to set
6196     * @param newMask the regular expression to apply to this field
6197     * @throws DBException upon error.
6198     */

6199    protected synchronized void setMask(String JavaDoc fieldName, String JavaDoc newMask)
6200            throws DBException {
6201        getDef().setMask(fieldName, newMask);
6202    } /* setMask(String, String) */
6203
6204
6205    /**
6206     * Specify a maximum number of records to be retrieved in any subsequent
6207     * searchAndRetrieve() call. Records will be retrieved (in the specified
6208     * sort order) until the specified maximum is reached, then the remainder
6209     * of the result set is discarded. Specifying zero indicates that all
6210     * records are to be retrieved.
6211     *
6212     * @param newMax The maximum number of records to retrieve.
6213     * @throws DBException If the max number is less than 0
6214     */

6215    public synchronized void setMaxRecords(int newMax)
6216            throws DBException {
6217        if (maxRecords < 0) {
6218            throw new DBException("Max records can't be less than 0");
6219        }
6220
6221        maxRecords = newMax;
6222    } /* setMaxRecords(int) */
6223
6224
6225    /**
6226     * Set this field to be multi-valued - e.g. there is a specific list of
6227     * values that are valid for this field. There should be code in the
6228     * getValidValues method that returns the valid values for the specified
6229     * multi-valued field.
6230     *
6231     * @param fieldName The name of the field to specify as multi-valued
6232     * @throws DBException If the field name given is not valid, or is a virtual field
6233     */

6234    protected synchronized void setMultiValued(String JavaDoc fieldName)
6235            throws DBException {
6236        DataFieldMetaData oneField = getFieldMetaData(fieldName);
6237
6238        if (oneField.isVirtual()) {
6239            throw new DBException("(" + myClassName + ") Field '" +
6240                    fieldName +
6241                    "' is a virtual field - not suitable for " +
6242                    "setting as multi-valued");
6243        }
6244
6245        oneField.setMultiValued(true);
6246    } /* setMultiValued(String) */
6247
6248
6249    /**
6250     * Set the name of this object - this name is used to identify the db object
6251     * with a more human-readable description
6252     *
6253     * @param theName New name for this object
6254     * @throws DBException upon error.
6255     */

6256    protected synchronized void setName(String JavaDoc theName)
6257            throws DBException {
6258        getDef().setName(theName);
6259    } /* setName(String) */
6260
6261
6262    /**
6263     * Specifies the number of records that should be skipped over
6264     * before any data from the <code>ResultSet</code>
6265     * is retrieved in any subsequent
6266     * searchAndRetrieve() call. Records will be skipped over (in the specified
6267     * sort order) until the record counts is equal to or greater
6268     * than the offset record. Specifying zero indicates that no
6269     * records should be skipped over and the
6270     * <code>ResultSet</code> immediately from the start.
6271     *
6272     * @param newOffset The maximum number of records to retrieve.
6273     * @throws DBException If the max number is less than 0
6274     * <p/>
6275     * author Peter Pilgrim <peterp at xenonsoft dot demon dot co dot uk>
6276     */

6277    public synchronized void setOffsetRecord(int newOffset)
6278            throws DBException {
6279        if (newOffset < 0) {
6280            throw new DBException("Offset records can't be less than 0");
6281        }
6282
6283        offsetRecord = newOffset;
6284    } /* setOffsetRecord(int) */
6285
6286
6287    /**
6288     * Set a field as read-only - these fields are not offered for update
6289     * when a form is produced by the generic database maintenance servlet
6290     * (DBMaint). It does not otherwise affect the use of the field.
6291     * <p>Typical uses are for serial-numbered fields and timestamps</p>
6292     *
6293     * @param fieldName The name of the field to be reserved as normally read-only.
6294     * @throws DBException If there is no such field.
6295     */

6296    public synchronized void setReadOnly(String JavaDoc fieldName)
6297            throws DBException {
6298        DataFieldMetaData oneField = getFieldMetaData(fieldName);
6299        oneField.setReadOnly();
6300    } /* setReadOnly(String) */
6301
6302
6303    /**
6304     * Specify which schema this DB object belongs to. This is
6305     * necessary when using the local-langauge strings in the
6306     * DB object for field names, descriptions, etc. The schema
6307     * will be assumed to be Expresso's schema if not otherwise
6308     * set.
6309     * From expresso 5.3, this method is called in ConfigManager at startup. You probably do
6310     * not need to call it. (Previously, developers had to call it within each DBObject's setup().)
6311     *
6312     * @param schema The schema to set
6313     * @throws DBException upon error.
6314     */

6315    public synchronized void setSchema(Schema schema)
6316            throws DBException {
6317        getDef().setSchema(schema.getClass().getName());
6318    } /* setSchema(String) */
6319
6320
6321    /**
6322     * Set a field as 'secret' - these fields are not shown
6323     * when a list is produced by the generic database maintenance servlet. In
6324     * this way, only users with update permission to the object can view the
6325     * values of "secret" fields.
6326     *
6327     * @param fieldName The name of the field to set as 'secret'
6328     * @throws DBException I fthe field does not exist
6329     */

6330    public synchronized void setSecret(String JavaDoc fieldName)
6331            throws DBException {
6332        DataFieldMetaData oneField = getFieldMetaData(fieldName);
6333        oneField.setSecret();
6334    } /* setSecret(String) */
6335
6336
6337    /**
6338     * Set a filter for a particular field, permanently for all instances of this class.
6339     * for a means to set a filter on just a single instance, see setFilterClass
6340     *
6341     * @param fieldName The name of the field to set a particular filter for.
6342     * @param filterMethod The name of the filter method to use when calling a filter.
6343     * Can be one of three values:<br> See {@link
6344     * com.jcorporate.expresso.core.security.filters.FilterManager#filterString} for
6345     * more information on this parameter.
6346     * @return old filter just replaced
6347     * @throws DBException if: fieldName doesn't exist, or filterType String
6348     * isn't a valid value.
6349     * @see com.jcorporate.expresso.core.security.filters.FilterManager
6350     * @see #setFilterClass(com.jcorporate.expresso.core.security.filters.Filter) for a means to set a filter on just an instance
6351     */

6352    public synchronized String JavaDoc setStringFilter(String JavaDoc fieldName,
6353                                               String JavaDoc filterMethod)
6354            throws DBException {
6355
6356        //
6357
//Parameter Check
6358
//
6359
if (!(filterMethod.equalsIgnoreCase("standardFilter") || filterMethod.equalsIgnoreCase("stripFilter") ||
6360                filterMethod.equalsIgnoreCase("rawFilter"))) {
6361            throw new DBException("Undefined Filter Method: " + filterMethod);
6362        }
6363
6364        DataFieldMetaData oneField = getFieldMetaData(fieldName);
6365        return ((DBField) oneField).setFilterMethod(filterMethod);
6366    } /* setStringFilter(String, String) */
6367
6368    /**
6369     * get the current filter for a particular field. This is the 'static', permanent filter.
6370     *
6371     * @param fieldName The name of the field to set a particular filter for.
6372     * @return java.lang.String the current filter method.
6373     * @throws DBException if: fieldName doesn't exist, or filterType String
6374     * isn't a valid value.
6375     * @see #getFilterClass() for a means to get a filter on just an instance
6376     * @see com.jcorporate.expresso.core.security.filters.FilterManager
6377     */

6378    public synchronized String JavaDoc getStringFilter(String JavaDoc fieldName)
6379            throws DBException {
6380
6381        DataFieldMetaData oneField = getFieldMetaData(fieldName);
6382        return ((DBField) oneField).getFilterMethod();
6383    } /* getStringFilter(String) */
6384
6385
6386    /**
6387     * Set the target table for this DBObject. Note that an object
6388     * can span tables by the use of virtual fields, but that this table
6389     * is the default table for the object.
6390     *
6391     * @param theTable Table for this object
6392     * @throws DBException upon error.
6393     */

6394    public synchronized void setTargetTable(String JavaDoc theTable)
6395            throws DBException {
6396        getDef().setTargetTable(theTable);
6397    } /* setTargetTable(String) */
6398
6399    /**
6400     * Set the target table for this DBObject. Note that an object
6401     * can span tables by the use of virtual fields, but that this table
6402     * is the default table for the object.
6403     *
6404     * @param theDbSchema Table for this object
6405     * @throws DBException upon error.
6406     */

6407    public synchronized void setTargetDbSchema(String JavaDoc theDbSchema)
6408            throws DBException {
6409        getDef().setTargetDbSchema(theDbSchema);
6410    } /* setTargetDbSchema(String) */
6411
6412
6413    /**
6414     * Method to set up the fields for this database object. This method should
6415     * be defined in the implementing object and should make calls to addField,
6416     * addKey, setMultiValued, etc. as required to define the content of
6417     * the DBObject. Each setupFields method should call "super.setupFields()"
6418     * so that field definitions can be "inheritcable"
6419     *
6420     * @throws DBException If there is an error setting up the fields
6421     * as requested. For example, if a field allowing null is requested
6422     * as part of the key
6423     */

6424    protected void setupFields()
6425            throws DBException {
6426    }
6427
6428    /**
6429     * This allows the invocation of the SQL AVG, MIN, MAX and SUM
6430     * aggregate functions on one of the DB's columns (the DBObject
6431     * fieldname is supplied).
6432     * This function is not meant to be used directly, but is here
6433     * as the common/shared code used by the following methods in DBObject:
6434     * avg(String), min(String), max(String), sum(String).
6435     *
6436     * @param func String The SQL aggregate function - must be one of AVG, MIN, MAX, SUM
6437     * @param fieldName String The DBObject fieldName to use for the aggregate function
6438     * @return double The result of the SQL function invocation
6439     * @throws DBException If the function could not be completed
6440     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
6441     */

6442    protected synchronized double sqlAggrFunction(String JavaDoc func,
6443                                                  String JavaDoc fieldName)
6444            throws DBException {
6445        FastStringBuffer myStatement = FastStringBuffer.getInstance();
6446        String JavaDoc statementSQL = null;
6447        try {
6448            myStatement.append("SELECT " + func);
6449            myStatement.append("(" + fieldName + ") FROM ");
6450            myStatement.append(this.getJDBCMetaData().getTargetSQLTable(this.getDataContext()));
6451            FastStringBuffer fsb = FastStringBuffer.getInstance();
6452            try {
6453                if (customWhereClause != null) {
6454                    if (appendCustomWhere) {
6455                        myStatement.append(buildWhereClauseBuffer(true, fsb));
6456                        myStatement.append(customWhereClause);
6457                        appendCustomWhere = false;
6458                    } else {
6459                        myStatement.append(customWhereClause);
6460                    }
6461                } else {
6462                    myStatement.append(buildWhereClauseBuffer(true, fsb));
6463                }
6464            } finally {
6465                fsb.release();
6466            }
6467            statementSQL = myStatement.toString();
6468        } finally {
6469            myStatement.release();
6470            myStatement = null;
6471        }
6472        DBConnection myConnection = null;
6473        try {
6474            if (localConnection != null) {
6475                myConnection = localConnection;
6476            } else {
6477                myConnection = getConnectionPool().getConnection(myClassName);
6478            }
6479
6480            myConnection.execute(statementSQL);
6481
6482            if (myConnection.next()) {
6483                return myConnection.getDouble(1);
6484            }
6485        } catch (DBException de) {
6486            throw de;
6487        } finally {
6488            if (localConnection == null) {
6489                myConnection.release();
6490            }
6491        }
6492
6493        return 0.0;
6494    } /* sqlAggrFunction() */
6495
6496
6497    /**
6498     * Find the sum of the values in the specified field of records
6499     * selected by the DBObject selection criteria.
6500     *
6501     * @param fieldName String DBObject fieldName to average
6502     * @return double Sum of the records matching the criteria
6503     * @throws DBException If the search could not be completed
6504     */

6505    public double sum(String JavaDoc fieldName)
6506            throws DBException {
6507        return sqlAggrFunction("SUM", fieldName);
6508    } /* sum() */
6509
6510    /**
6511     * Update the database with the new info. Update affects only one record,
6512     * and uses the current field values to write to the DBMS.
6513     * <p/>
6514     * by default, updates all fields each time.
6515     * <p/>
6516     * for efficiency, developers who are confident that their code does not have any
6517     * 'blind updates' can set the Setup value UPDATE_CHANGED_ONLY to true
6518     * (a blind update is where the object is not retieved before values in it are reset)
6519     *
6520     * @throws DBException if the update to the database fails due to
6521     * a database error
6522     * @see #update(boolean)
6523     */

6524    public void update()
6525            throws DBException {
6526
6527        /*
6528        * for efficiency, developers who are confident that their code does not have any
6529        * 'blind updates' can set the Setup value UPDATE_CHANGED_ONLY to true
6530        * (a blind update is where the object is not retieved before values in it are reset)
6531        */

6532        String JavaDoc onlychanged = Setup.getValueUnrequired(getDataContext(), UPDATE_CHANGED_ONLY);
6533        if (StringUtil.toBoolean(onlychanged)) {
6534            update(true);
6535        } else {
6536            update(false);
6537        }
6538
6539    }
6540
6541    /**
6542     * Update the database with the new info. Update affects only one record,
6543     * and uses the current field values to write to the DBMS.
6544     * <p/>
6545     * if param updateChangedFieldsOnly is true, only changed fields are updated.
6546     * this assumes that the entire object has been retrieved from DB prior
6547     * to resetting any field.
6548     *
6549     * @param updateChangedFieldsOnly if true only modified fields (isChanged = true)
6550     * will be included in the update
6551     * @throws DBException if the update to the database fails due to
6552     * a database error
6553     */

6554    public void update(boolean updateChangedFieldsOnly)
6555            throws DBException {
6556        getExecutor().update(this, updateChangedFieldsOnly);
6557
6558        /* Now log the change if we are logging */
6559        if (getDef().isLoggingEnabled()) {
6560            ChangeLog myChangeLog = new ChangeLog();
6561            myChangeLog.setDataContext(getDataContext());
6562            myChangeLog.setField("ObjectChanged", myClassName);
6563            myChangeLog.setField("RecordKey", getMyKeys());
6564            myChangeLog.setField("Operation", EVENT_UPDATE);
6565
6566            if (myUpdates != null) {
6567                for (Iterator JavaDoc i = myUpdates.iterator(); i.hasNext();) {
6568
6569                    FieldUpdate myUpdate = (FieldUpdate) i.next();
6570
6571                    if (!myUpdate.oldValue.equals(myUpdate.newValue)) {
6572                        myChangeLog.setField("ChangedField",
6573                                myUpdate.fieldName);
6574                        myChangeLog.setField("OldValue", myUpdate.oldValue);
6575                        myChangeLog.setField("NewValue", myUpdate.newValue);
6576                        myChangeLog.add();
6577                    }
6578                } /* for */
6579
6580            }
6581
6582            /* We're done tracking changes */
6583            myUpdates = null;
6584        } /* if */
6585
6586
6587        // after update, we know that 'current' values are now the baseline for comparison
6588
cacheIsChangedComparison();
6589        setStatus(BaseDataObject.STATUS_CURRENT);
6590        notifyListeners(EVENT_UPDATE);
6591    } /* update() */
6592
6593    /**
6594     * Generate a debuggable string for suitable printing out in an
6595     * interactive Java IDE. Write the string in PERL hash structure
6596     * style. Primary key field are denoted with (*) asterisks.
6597     * <p/>
6598     * <pre>
6599     * com.bar.foo.AcmePayroll@1234BABE{
6600     * id* =&gt; 7, fullname =&gt; Peter Pilgrim,
6601     * city =&gt; country =&gt; England }
6602     * </pre>
6603     * <p/>
6604     * <p> This is a method is not the standard
6605     * <code>toString()</code> because some times a data object used
6606     * professionally can have dozens of data fields. We also dont
6607     * want to overwhelm you with useless information!
6608     *
6609     * @return java.lang.String
6610     */

6611    public String JavaDoc toDebugString() {
6612        FastStringBuffer fsb = FastStringBuffer.getInstance();
6613        String JavaDoc returnValue = null;
6614
6615        try {
6616            // Retrieve all the field names
6617
DataObjectMetaData metaData = getMetaData();
6618            String JavaDoc fieldNames[] = metaData.getFields();
6619
6620            fsb.append(getClass().getName());
6621            fsb.append("@");
6622            fsb.append(Integer.toHexString(hashCode()));
6623            fsb.append("{ ");
6624
6625            for (int k = 0; k < fieldNames.length; ++k) {
6626                if (k > 0) {
6627                    fsb.append(',');
6628                }
6629                fsb.append(fieldNames[k]);
6630                if (metaData.getFieldMetadata(fieldNames[k]).isKey()) {
6631                    fsb.append('*');
6632                }
6633                fsb.append(" = ");
6634                fsb.append(getField(fieldNames[k]));
6635                fsb.append(" ");
6636            }
6637            fsb.append("}");
6638
6639            returnValue = fsb.toString(); /*!*/
6640        } catch (Exception JavaDoc ignored) {
6641        } finally {
6642            fsb.release();
6643            fsb = null;
6644        }
6645
6646        return returnValue;
6647    }
6648
6649
6650    /**
6651     * Update all of the records specified by the current search criteria. If
6652     * you use a blank DBObject, then all the records in the table will be
6653     * deleted
6654     *
6655     * @throws DBException upon error
6656     */

6657    public synchronized void updateAll()
6658            throws DBException {
6659
6660        //Build an object that we're going to query with.
6661
DBObject testObject = newInstance();
6662        DataObjectMetaData metadata = this.getMetaData();
6663        for (Iterator JavaDoc i = this.getMetaData().getFieldListArray().iterator();
6664             i.hasNext();) {
6665            String JavaDoc fieldName = (String JavaDoc) i.next();
6666            DataFieldMetaData fieldMetadata = metadata.getFieldMetadata(fieldName);
6667            if (fieldMetadata.isLongObjectType() || fieldMetadata.isVirtual()) {
6668                if (log.isDebugEnabled()) {
6669                    ChangeLog myChangeLog = new ChangeLog();
6670                    myChangeLog.setDataContext(this.getDataContext());
6671                    myChangeLog.setField("ObjectChanged", this.myClassName);
6672                    myChangeLog.setField("RecordKey", this.getMyKeys());
6673                    myChangeLog.setField("Operation", "U");
6674
6675                    if (myUpdates != null) {
6676                        for (Iterator JavaDoc j = myUpdates.iterator(); j.hasNext();) {
6677
6678                            FieldUpdate myUpdate = (FieldUpdate) j.next();
6679                            if (!myUpdate.oldValue.equals(myUpdate.newValue)) {
6680                                myChangeLog.setField("ChangedField", myUpdate.fieldName);
6681                                myChangeLog.setField("OldValue", myUpdate.oldValue);
6682                                myChangeLog.setField("NewValue", myUpdate.newValue);
6683                                myChangeLog.add();
6684                            }
6685                        } /* for */
6686                    }
6687                }
6688            } else {
6689                testObject.setField(fieldName, this.getField(fieldName));
6690            }
6691        }
6692
6693        if (customWhereClause != null) {
6694            testObject.setCustomWhereClause(getCustomWhereClause(), appendCustomWhere);
6695        }
6696
6697        testObject.setMaxRecords(100);
6698
6699        //
6700
//Iterate for each 100 items. So we don't load more records at once
6701
//than memory can handle
6702
//
6703
int num = 0;
6704        ArrayList JavaDoc al = new ArrayList JavaDoc(100);
6705        do {
6706            al = testObject.searchAndRetrieveList();
6707            num = al.size();
6708            for (Iterator JavaDoc i = al.iterator(); i.hasNext();) {
6709                DBObject oneObject = (DBObject) i.next();
6710                oneObject.update(true);
6711            }
6712
6713            al.clear();
6714        } while (num >= 100);
6715    } /* updateteAll() */
6716
6717
6718    /**
6719     * Update the database with the new info. Update affects all records,
6720     * and uses the current field values to write to the DBMS.
6721     *
6722     * @param oneByOne if true the records will be modifed one by one and
6723     * will be included in the cache
6724     * @throws DBException if the update to the database fails due to
6725     * a database error
6726     */

6727    public void updateAll(boolean oneByOne)
6728            throws DBException {
6729        if (oneByOne) {
6730            updateAll();
6731        } else {
6732            doUpdateAll(true);
6733        }
6734    } /* updateAll(boolean) */
6735
6736    /**
6737     * Takes a <code>DataObject</code> and updates all to the underlying data source
6738     *
6739     * @param updateChangedCache if true only modified fields (isChanged = true)
6740     * will be included in the update
6741     * @throws DataException upon error updating the object to the data source
6742     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
6743     * @todo move code here to impl. of updateAll(), using logic to figure out whether efficient technique below can be used; i.e., whether dbobject has any dependent details.
6744     */

6745    private void doUpdateAll(boolean updateChangedCache)
6746            throws DataException {
6747
6748        boolean needComma = false;
6749
6750        try {
6751            //
6752
//If myPool isn't set yet, then we need to use getDBName to
6753
//reset. FIXME: Have the system throw exceptions if DBName isn't
6754
//set instead.
6755
//
6756
DBConnectionPool myPool = DBConnectionPool.getInstance(getMappedDataContext());
6757            DBConnection localConnection = this.getLocalConnection();
6758
6759            List JavaDoc lobFieldOrder = null;
6760            List JavaDoc lobUpdates = null;
6761            DataFieldMetaData oneField = null;
6762            FastStringBuffer sqlCommand = FastStringBuffer.getInstance();
6763            String JavaDoc theSqlString = null;
6764            boolean fieldsUpdated = false;
6765            try {
6766                sqlCommand.append("UPDATE ");
6767                sqlCommand.append(this.getJDBCMetaData().getTargetSQLTable(this.getDataContext()));
6768                sqlCommand.append(" SET ");
6769
6770
6771                for (Iterator JavaDoc i = this.getMetaData().getFieldListArray().iterator(); i.hasNext();) {
6772                    String JavaDoc oneFieldName = (String JavaDoc) i.next();
6773                    oneField = this.getFieldMetaData(oneFieldName);
6774
6775                    /* We skip any key fields in the update part (not allowed to */
6776                    /* update them). Also skip any virtual fields */
6777                    if ((!oneField.isKey()) && (!oneField.isVirtual()) &&
6778                            (!oneField.isAutoIncremented()) && (!oneField.isBinaryObjectType())) {
6779                        // Only include changed fields in the update if required
6780
if (this.getDataField(oneFieldName).isValueSet() && this.getStatus().equals(
6781                                DataObject.STATUS_NEW)) {
6782                            ;
6783                        } else {
6784                            if (!this.getDataField(oneFieldName).isChanged()) {
6785                                continue;
6786                            }
6787                        }
6788
6789                        this.checkField(oneFieldName, this.getField(oneFieldName));
6790
6791                        if (needComma) {
6792                            sqlCommand.append(", ");
6793                        }
6794                        if (!oneField.isKey()) {
6795                            fieldsUpdated = true;
6796                            sqlCommand.append(oneField.getName());
6797                            sqlCommand.append(" = ");
6798
6799                            if (oneField.isDateType()) {
6800                                Object JavaDoc tmpData = this.get(oneField.getName());
6801                                String JavaDoc data;
6802                                //
6803
//FIXME allow for appropriate support of other data types.
6804
//
6805
if (tmpData == null) {
6806                                    data = null;
6807                                } else if (tmpData instanceof String JavaDoc) {
6808                                    data = (String JavaDoc) tmpData;
6809                                } else {
6810                                    data = tmpData.toString();
6811                                }
6812                                if (data == null || data.length() == 0) {
6813                                    sqlCommand.append("null");
6814                                } else {
6815                                    sqlCommand.append(JDBCUtil.getInstance()
6816                                            .formatDateTime(this, oneField.getName()));
6817                                }
6818                            } else if (oneField.isCharacterLongObjectType()) {
6819                                //
6820
//If we have a CLOB object, then we will go ahead and process
6821
//it, but we execute it in a separate update
6822
//
6823
if (lobUpdates == null) {
6824                                    lobUpdates = new ArrayList JavaDoc();
6825                                    lobFieldOrder = new ArrayList JavaDoc();
6826                                }
6827                                Object JavaDoc tmpData = this.get(oneField.getName());
6828                                String JavaDoc data;
6829                                //
6830
//FIXME allow for appropriate support of other data types.
6831
//
6832
if (tmpData == null) {
6833                                    data = null;
6834                                } else if (tmpData instanceof String JavaDoc) {
6835                                    data = (String JavaDoc) tmpData;
6836                                } else {
6837                                    data = tmpData.toString();
6838                                }
6839
6840                                lobUpdates.add(data);
6841                                lobFieldOrder.add(oneFieldName);
6842
6843                                sqlCommand.append("?");
6844
6845                            } else {
6846                                sqlCommand.append(this.quoteIfNeeded(oneField.getName(),
6847                                        null));
6848                            }
6849
6850                            sqlCommand.append(" ");
6851                            needComma = true;
6852                        }
6853                    } /* if it's not a key field */
6854
6855                }
6856
6857                if (!fieldsUpdated) {
6858                    return;
6859                }
6860
6861                String JavaDoc whereClause;
6862
6863                if (customWhereClause != null) {
6864                    if (appendCustomWhere) {
6865                        whereClause = buildWhereClause(true) + customWhereClause;
6866                        appendCustomWhere = false;
6867                    } else {
6868                        whereClause = customWhereClause;
6869                    }
6870                    sqlCommand.append(whereClause);
6871                    customWhereClause = null;
6872                } else {
6873                    whereClause = buildWhereClause(true);
6874                    sqlCommand.append(whereClause);
6875                }
6876
6877                theSqlString = sqlCommand.toString();
6878
6879            } finally {
6880                sqlCommand.release();
6881                sqlCommand = null;
6882            }
6883
6884            DBConnection myConnection = null;
6885
6886            try {
6887                if (localConnection != null) {
6888                    myConnection = localConnection;
6889                } else {
6890                    myConnection = myPool.getConnection(getClass().getName());
6891                }
6892
6893
6894                if (lobUpdates != null) {
6895
6896                    java.sql.PreparedStatement JavaDoc prepStatement = null;
6897                    try {
6898                        prepStatement = myConnection.createPreparedStatement(theSqlString);
6899
6900                        int size = lobFieldOrder.size();
6901                        for (int i = 0; i < size; i++) {
6902                            String JavaDoc value = (String JavaDoc) lobUpdates.get(i);
6903                            try {
6904                                prepStatement.setString(i + 1, value);
6905                            } catch (SQLException JavaDoc ex) {
6906                                String JavaDoc key = (String JavaDoc) lobFieldOrder.get(i);
6907                                log.error("Error executing LOB set Character Stream", ex);
6908                                throw new DataException("Error setting character stream for field: "
6909                                        + key + " with value: " + value + " for DBObject: "
6910                                        + getClass().getName(), ex);
6911                            }
6912                        }
6913                        myConnection.executeUpdate(null);
6914                    } finally {
6915                        myConnection.clearPreparedStatement();
6916                    }
6917                } else {
6918                    myConnection.executeUpdate(theSqlString);
6919                }
6920
6921                if ((myConnection.getUpdateCount() == 0) &&
6922                        (this.checkZeroUpdate())) {
6923                    log.debug("No record updated for SQL '" +
6924                            theSqlString + "'");
6925                }
6926            } catch (NullPointerException JavaDoc npe) {
6927                npe.printStackTrace();
6928                throw new DataException(npe);
6929            } finally {
6930                if (localConnection == null) {
6931                    if (myPool != null && myConnection != null) {
6932                        myPool.release(myConnection);
6933                    }
6934                }
6935            }
6936        } catch (DBException ex) {
6937            String JavaDoc msg = "doUpdateAll(DataObject) error";
6938            log.error(msg, ex);
6939            throw new DataException(msg, ex);
6940        }
6941
6942    } /* doUpdateAll */
6943
6944
6945    /**
6946     * Verify that this object is working correctly
6947     * by selecting all records, running a validation on each of them. The
6948     * validation consists of verifying the referential integrity against all
6949     * objects defined as being referred to by this object in the checkAllRefs method
6950     *
6951     * @throws DBException If the validation fails
6952     */

6953    public void verify()
6954            throws DBException {
6955        DBObject list = newInstance();
6956        list.setDataContext(getDataContext());
6957
6958        DBObject oneObject = null;
6959
6960        for (Iterator JavaDoc i = list.searchAndRetrieveList().iterator();
6961             i.hasNext();) {
6962            oneObject = (DBObject) i.next();
6963            oneObject.checkAllRefs();
6964        } /* each object */
6965
6966    }
6967
6968    /**
6969     * Equals implementation. this == parm1 If and only if: <br/>
6970     * 1 - The two classes are of the same classname <br />
6971     * 2 - All the data fields in the two classes are the same <br />
6972     * 3 - The data contexts are the same<br />
6973     *
6974     * @param parm1 The object to compare to
6975     * @return true if the two objects being compared are equal
6976     */

6977    public boolean equals(Object JavaDoc parm1) {
6978        if (parm1 == null) {
6979            return false;
6980        }
6981
6982        //False if other object isn't at least a DBObject. [Keeps us from
6983
//throwing ClassCastException later on.
6984
if (!(parm1 instanceof DBObject)) {
6985            return false;
6986        }
6987
6988        DBObject otherObj = (DBObject) parm1;
6989
6990        //
6991
//True if both objects have no field data.
6992
//
6993
if (fieldData == null && otherObj.fieldData == null) {
6994            return true;
6995            //
6996
//False if one or the other have data but not both.
6997
//
6998
} else if (!(fieldData != null && otherObj.fieldData != null)) {
6999            return false;
7000        }
7001
7002        //
7003
//At this point we are guaranteed that both fielddata have values
7004
//Now return false if the data sizes do not agree.
7005
//
7006
if (fieldData.size() != otherObj.fieldData.size()) {
7007            return false;
7008        }
7009
7010        //--------------------------------
7011
//Although the comparisons above this line might seem more suited until
7012
//after we have validated the classnames. The truth of the matter is
7013
//that we have to do string comparisons for the classes below, which
7014
//is computationally more expensive than integer comparisons. Thus
7015
//we do those first to see if we can eliminate something right off the
7016
//bat.
7017
//
7018

7019        //
7020
//False if the classnames aren't identical since derived classes must
7021
//point to separate tables.
7022
//
7023
if (!myClassName.equals(parm1.getClass().getName())) {
7024            return false;
7025        }
7026
7027        //
7028
//False if the DBObjects are pointing to different data contexts.
7029
//
7030
if (!getDataContext().equals(otherObj.getDataContext())) {
7031            return false;
7032        }
7033
7034        //
7035
//Ok, we've done all the non-computationally extensive comparisons,
7036
//lets try the field by field comparison
7037
//
7038

7039        try {
7040            //
7041
//Ok, we've done all the non-computationally extensive comparisons,
7042
//lets try the field by field comparison
7043
//
7044
for (Iterator JavaDoc i = fieldData.keySet().iterator(); i.hasNext();) {
7045                String JavaDoc key = (String JavaDoc) i.next();
7046                DataField value2 = null;
7047                try {
7048                    value2 = (DataField) otherObj.fieldData.get(key);
7049                } catch (ClassCastException JavaDoc ex) {
7050                    return false;
7051                }
7052
7053                DataField thisField = (DataField) fieldData.get(key);
7054
7055                //
7056
//Check for one or the other being null.
7057
//
7058
if ((value2 == null && thisField != null) ||
7059                        (thisField == null && value2 != null)) {
7060                    return false;
7061                }
7062
7063                if (!thisField.getFieldMetaData().getTypeString()
7064                        .equals(value2.getFieldMetaData().getTypeString())) {
7065                    //Different data types... definitely won't match :)
7066
return false;
7067                }
7068
7069                //
7070
//If one field value is null and not the other, then they're different
7071
//
7072
if ((thisField.getValue() == null ^ value2.getValue() == null)) {
7073                    return false;
7074                }
7075
7076                if (thisField.getValue() == null && value2.getValue() == null) {
7077                    continue;
7078                }
7079
7080                if (value2.getValue().equals(thisField.getValue())) {
7081                    //We're ok... they're equal
7082
continue;
7083                }
7084
7085                if (thisField.getFieldMetaData().isNumericType()) {
7086                    //We could have differences due to string formatting, but the
7087
//actual value could be the same. Examples are: 72 vs 72.00
7088
BigDecimal JavaDoc bd1 = thisField.asBigDecimal();
7089                    BigDecimal JavaDoc bd2 = value2.asBigDecimal();
7090
7091                    if (bd1.equals(bd2)) {
7092                        continue;
7093                    }
7094                }
7095
7096                return false;
7097
7098            }
7099        } catch (Throwable JavaDoc ex) {
7100            log.error("Error comparing DBOBjects", ex);
7101            return false;
7102        }
7103
7104
7105        //Ok, by now we've checked all fields individually, and we're still at
7106
//a match. We think they're equal at this point.
7107

7108        return true;
7109    }
7110
7111    /**
7112     * Same as getField, but works with the DataObject interface for now. Will
7113     * eventually return something other than Strings. For example, dates, times,
7114     * etc
7115     *
7116     * @param fieldName name of the field to get the object for.
7117     * @return DataField or null if the field doesn't exist yet.
7118     * @throws DBException upon error.
7119     */

7120    public DataField getDataField(String JavaDoc fieldName) throws DBException {
7121        if (fieldData == null) {
7122            fieldData = new HashMap();
7123        }
7124        DataField oneField = (DataField) fieldData.get(fieldName);
7125        if (oneField == null) {
7126            oneField = DefaultDataField.getInstance(getFieldMetaData(fieldName), this);
7127            fieldData.put(fieldName, oneField);
7128        }
7129
7130        return oneField;
7131
7132    }
7133
7134    /**
7135     * Directly gets the DataField Data without having to deal with the DataField
7136     * itself
7137     *
7138     * @param fieldName the name of the field to get
7139     * @return Object or null.
7140     * @throws DataException upon error.
7141     */

7142    public Object JavaDoc get(String JavaDoc fieldName) throws DataException {
7143        if (fieldData == null) {
7144            return null;
7145        } else {
7146            DataField df = (DataField) fieldData.get(fieldName);
7147            if (df == null) {
7148                DataField nullField = DefaultDataField.getInstance(getFieldMetaData(fieldName), this);
7149                fieldData.put(fieldName, nullField);
7150                return nullField.getValue();
7151            } else {
7152                return df.getValue();
7153            }
7154        }
7155    }
7156
7157    /**
7158     * sets the field value by using 'instanceof' operator; convenient for template methods, but less efficient
7159     * than calling setField()
7160     *
7161     * @param fieldName the name of the field to set
7162     * @param o the object value to set it to.
7163     * @throws DataException upon error.
7164     * @see #setField
7165     */

7166    public void set(String JavaDoc fieldName, Object JavaDoc o) throws DataException {
7167        try {
7168            //
7169
//For now this is a backwards compatability hack to get the interface
7170
//out the door. It will eventually be the bulk of it's own
7171
//work
7172
//
7173
if (o == null) {
7174                setField(fieldName, (String JavaDoc) null);
7175            } else if (o instanceof java.lang.String JavaDoc) {
7176                setField(fieldName, (String JavaDoc) o);
7177            } else if (o instanceof java.lang.Integer JavaDoc) {
7178                setField(fieldName, ((Integer JavaDoc) o).intValue());
7179            } else if (o instanceof java.lang.Boolean JavaDoc) {
7180                setField(fieldName, ((Boolean JavaDoc) o).booleanValue());
7181            } else {
7182                throw new IllegalArgumentException JavaDoc("DBObject.setFieldValue(String," +
7183                        "Object) must currently have o only of " +
7184                        "type String, Integer, or Boolean");
7185
7186            }
7187        } catch (DBException ex) {
7188            throw new DataException(ex);
7189        }
7190    }
7191
7192    /**
7193     * Same as setField, but works with the DataObject Interface
7194     *
7195     * @param fieldName the name of the field to set.
7196     * @param o the object to set the field to... currently should be
7197     * java.lang.Strings only.
7198     * @throws DataException upon error.
7199     * @since Expresso 5.0
7200     */

7201    public void setDataField(String JavaDoc fieldName, DataField o) throws DataException {
7202        if (fieldData == null) {
7203            fieldData = new HashMap();
7204        }
7205
7206        fieldData.put(fieldName, o);
7207    }
7208
7209    /**
7210     * Sets the data context that this object is residing in. This is identical
7211     * to the pre-Expresso 5.0 <code>setDBName()</code>
7212     *
7213     * @param newContext the new name of the data context.
7214     * @throws IllegalArgumentException if the DataContext does not exist
7215     */

7216    public void setDataContext(String JavaDoc newContext) {
7217        try {
7218            setDBName(newContext);
7219        } catch (DBException ex) {
7220            String JavaDoc errorMessage = "Invalid newContext name: "
7221                    + newContext + ": "
7222                    + ex.getMessage();
7223            log.error(errorMessage, ex);
7224            throw new IllegalArgumentException JavaDoc(errorMessage);
7225        }
7226    }
7227
7228    /**
7229     * Returns the name of the currently set DataContext
7230     *
7231     * @return java.lang.String
7232     */

7233    public String JavaDoc getDataContext() {
7234
7235        // set from RequestRegistry if this is first access
7236
if (dbKey == null || dbKey.length() == 0) {
7237            try {
7238                this.setDataContext(RequestRegistry.getDataContext());
7239            } catch (Exception JavaDoc ex) {
7240                getLogger().warn("Problem getting data context from Registry: " +
7241                        ex.getClass().getName() + ", msg: " + ex.getMessage());
7242                try {
7243                    setDBName(DBConnection.DEFAULT_DB_CONTEXT_NAME);
7244                } catch (DBException de) {
7245                    getLogger().error("Unable to set database to 'default'", de);
7246                }
7247            }
7248
7249        }
7250
7251        return dbKey;
7252    }
7253
7254
7255    /**
7256     * Returns a <b>Read Only</b> Map containing all the current attributes set
7257     * for this particular data object instance.
7258     *
7259     * @return Read Only <code>java.util.Map</code>
7260     */

7261    public Map getAllAttributes() {
7262        if (attributes == null) {
7263            return new HashMap();
7264        } else {
7265            return Collections.unmodifiableMap(attributes);
7266        }
7267    }
7268
7269    /**
7270     * Private class that defines errors for fields.
7271     */

7272    public class FieldError {
7273        private String JavaDoc fieldName = null;
7274        private String JavaDoc errorMessage = null;
7275
7276        /**
7277         * Default Constructor
7278         */

7279        public FieldError() {
7280
7281        }
7282
7283        /**
7284         * Constructor that sets all fields.
7285         *
7286         * @param fldName The fieldName to associate with
7287         * @param message The appropriate message
7288         */

7289        public FieldError(String JavaDoc fldName, String JavaDoc message) {
7290            fieldName = fldName;
7291            errorMessage = message;
7292        }
7293
7294        /**
7295         * Returns the fieldName that this error message belongs to.
7296         *
7297         * @return java.lang.String the field name
7298         */

7299        public String JavaDoc getFieldName() {
7300            return fieldName;
7301        }
7302
7303        /**
7304         * Returns the error message associated with this FieldError class.
7305         *
7306         * @return the error message
7307         */

7308        public String JavaDoc getErrorMessage() {
7309            return errorMessage;
7310        }
7311
7312
7313        /**
7314         * Sets the field name for this error class
7315         *
7316         * @param newValue The new field name value
7317         */

7318        public void setFieldName(String JavaDoc newValue) {
7319            fieldName = newValue;
7320        }
7321
7322        /**
7323         * Sets the Error Message for this class.
7324         *
7325         * @param newValue the new Error message for this object
7326         */

7327        public void setErrorMessage(String JavaDoc newValue) {
7328            errorMessage = newValue;
7329        }
7330
7331        /**
7332         * Overrides default toString() behavior. to return a friendly message
7333         *
7334         * @return java.lang.String
7335         */

7336        public String JavaDoc toString() {
7337
7338            FastStringBuffer fsb = FastStringBuffer.getInstance();
7339            String JavaDoc returnValue = null;
7340            try {
7341                if (fieldName != null) {
7342                    fsb.append("Error in field: ");
7343                    fsb.append(fieldName);
7344                }
7345
7346                if (errorMessage != null) {
7347                    fsb.append("Message: ");
7348                    fsb.append(errorMessage);
7349                }
7350                returnValue = fsb.toString();
7351            } finally {
7352                fsb.release();
7353                fsb = null;
7354            }
7355
7356            return returnValue;
7357        }
7358    }
7359
7360    ///////////////////////////////////////////////////////////////////////////
7361
// embedded object
7362
/////////////////////////////////////////////////////////////////////////
7363

7364    /**
7365     * Private class used to track field updates
7366     */

7367    public class FieldUpdate {
7368        public String JavaDoc fieldName;
7369        public String JavaDoc oldValue;
7370        public String JavaDoc newValue;
7371
7372        /**
7373         * Constructor
7374         */

7375        public FieldUpdate() {
7376            fieldName = ("");
7377            oldValue = ("");
7378            newValue = ("");
7379        } /* FieldUpdate() */
7380
7381    } /* FieldUpdate */
7382
7383    /**
7384     * set string filters to the given filter on ALL fields that are quoted text fields
7385     *
7386     * @param filter filter to set
7387     * @throws DBException upon error
7388     */

7389    public void setStringFiltersOnAll(String JavaDoc filter) throws DBException {
7390        Iterator JavaDoc iter = getMetaData().getAllFieldsMap().values().iterator();
7391        while (iter.hasNext()) {
7392            DBField field = (DBField) iter.next();
7393            if (field.isQuotedTextType()) {
7394                setStringFilter(field.getName(), filter);
7395            }
7396        }
7397    }
7398
7399
7400    /**
7401     * the INSTANCE value of filter class that will be used for all string filtering for THIS INSTANCE.
7402     * defaults to null, which means that there is no instance filter, so static filter will be used.
7403     * If this instance returns null, static value in
7404     * DBField.getFilterClass() filter class will be used. This method only
7405     * returns the local data member, not the static value from DBField
7406     *
7407     * @return the current filter (a subclass of Filter), or null to indicate default (HTML) filter
7408     * @see DBField#getFilterClass
7409     * @see com.jcorporate.expresso.core.security.filters.HtmlFilter
7410     */

7411    public Class JavaDoc getFilterClass() {
7412        return mFilter;
7413    }
7414
7415
7416    /**
7417     * set the filter class that will be used for all string filtering
7418     * for this object instance ONLY.
7419     *
7420     * @param filter class which is subclass of Filter
7421     * @return old Class that was just replaced
7422     * @see com.jcorporate.expresso.core.security.filters.Filter
7423     * @see DBField#setFilterClass
7424     * @deprecated use setFilterClass(Filter)
7425     */

7426    public Class JavaDoc setFilterClass(Class JavaDoc filter) {
7427        Class JavaDoc old = mFilter;
7428        if (filter == null) {
7429            mFilter = null;
7430        } else {
7431            mFilter = filter;
7432        }
7433        return old;
7434    }
7435
7436    /**
7437     * set the filter class that will be used for all string filtering
7438     * for this object instance ONLY.
7439     *
7440     * @param filter which is subclass of Filter; we will persist only the class name, NOT the instance passed in
7441     * @return old filter that was just replaced; can be null
7442     * @see #setStringFilter(java.lang.String, java.lang.String) for a means to set "permanenent" filters in metadata for all objects of this class
7443     * @see DBField#setFilterClass
7444     */

7445    public Filter setFilterClass(Filter filter) {
7446        Class JavaDoc old = mFilter;
7447        if (filter == null) {
7448            mFilter = null;
7449        } else {
7450            mFilter = filter.getClass();
7451        }
7452
7453        Filter result = null;
7454        if (old != null) {
7455            try {
7456                result = (Filter) old.newInstance();
7457            } catch (Exception JavaDoc e) {
7458                // should not happen since we were passed in a good filter
7459
log.error(e);
7460            }
7461        }
7462
7463        return result;
7464    }
7465
7466    /**
7467     * convenience for getting logger for current (sub) class
7468     *
7469     * @return Category for logging
7470     */

7471    public Logger getLogger() {
7472        if (slog == null) {
7473            slog = Logger.getLogger(getClass().getName());
7474        }
7475
7476        return slog;
7477    }
7478
7479    /**
7480     * summarize DataField.isChanged() for all fields.
7481     * isChanged() per field means that setField() has been called twice for that field,
7482     * once presumably by retrieval from DB, once to change a value (with
7483     * new value != former value)
7484     *
7485     * @return true if any field has been changed--i.e., setField() for that field has been called twice, once presumably by retrieval from DB, once to change a value
7486     * @throws DBException upon error.
7487     * @see DataField#isChanged()
7488     */

7489    public boolean isChanged() throws DBException {
7490        boolean result = false;
7491        for (Iterator JavaDoc iterator = getMetaData().getAllFieldsMap().values().iterator(); iterator.hasNext();) {
7492            DBField field = (DBField) iterator.next();
7493            DataField df = getDataField(field.getName());
7494            if (df.isChanged()) {
7495                result = true;
7496                break;
7497            }
7498        }
7499        return result;
7500    }
7501
7502
7503} // dbobject
7504
Popular Tags