Source Code Cross Referenced for DBObject.java in  » J2EE » Expresso » com » jcorporate » expresso » core » dbobj » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » Expresso » com.jcorporate.expresso.core.dbobj 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* ====================================================================
0002:         * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
0003:         *
0004:         * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
0005:         *
0006:         * Redistribution and use in source and binary forms, with or without
0007:         * modification, are permitted provided that the following conditions
0008:         * are met:
0009:         *
0010:         * 1. Redistributions of source code must retain the above copyright
0011:         *    notice, this list of conditions and the following disclaimer.
0012:         *
0013:         * 2. Redistributions in binary form must reproduce the above copyright
0014:         *    notice, this list of conditions and the following disclaimer in
0015:         *    the documentation and/or other materials provided with the
0016:         *    distribution.
0017:         *
0018:         * 3. The end-user documentation included with the redistribution,
0019:         *    if any, must include the following acknowledgment:
0020:         *       "This product includes software developed by Jcorporate Ltd.
0021:         *        (http://www.jcorporate.com/)."
0022:         *    Alternately, this acknowledgment may appear in the software itself,
0023:         *    if and wherever such third-party acknowledgments normally appear.
0024:         *
0025:         * 4. "Jcorporate" and product names such as "Expresso" must
0026:         *    not be used to endorse or promote products derived from this
0027:         *    software without prior written permission. For written permission,
0028:         *    please contact info@jcorporate.com.
0029:         *
0030:         * 5. Products derived from this software may not be called "Expresso",
0031:         *    or other Jcorporate product names; nor may "Expresso" or other
0032:         *    Jcorporate product names appear in their name, without prior
0033:         *    written permission of Jcorporate Ltd.
0034:         *
0035:         * 6. No product derived from this software may compete in the same
0036:         *    market space, i.e. framework, without prior written permission
0037:         *    of Jcorporate Ltd. For written permission, please contact
0038:         *    partners@jcorporate.com.
0039:         *
0040:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0041:         * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0042:         * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0043:         * DISCLAIMED.  IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
0044:         * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0045:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
0046:         * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0047:         * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0048:         * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0049:         * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0050:         * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0051:         * SUCH DAMAGE.
0052:         * ====================================================================
0053:         *
0054:         * This software consists of voluntary contributions made by many
0055:         * individuals on behalf of the Jcorporate Ltd. Contributions back
0056:         * to the project(s) are encouraged when you make modifications.
0057:         * Please send them to support@jcorporate.com. For more information
0058:         * on Jcorporate Ltd. and its products, please see
0059:         * <http://www.jcorporate.com/>.
0060:         *
0061:         * Portions of this software are based upon other open source
0062:         * products and are subject to their respective licenses.
0063:         */
0064:
0065:        package com.jcorporate.expresso.core.dbobj;
0066:
0067:        import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
0068:        import com.jcorporate.expresso.core.cache.CacheException;
0069:        import com.jcorporate.expresso.core.cache.CacheManager;
0070:        import com.jcorporate.expresso.core.cache.CacheSystem;
0071:        import com.jcorporate.expresso.core.cache.Cacheable;
0072:        import com.jcorporate.expresso.core.controller.Transition;
0073:        import com.jcorporate.expresso.core.dataobjects.BaseDataObject;
0074:        import com.jcorporate.expresso.core.dataobjects.DataException;
0075:        import com.jcorporate.expresso.core.dataobjects.DataField;
0076:        import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
0077:        import com.jcorporate.expresso.core.dataobjects.DataObject;
0078:        import com.jcorporate.expresso.core.dataobjects.DataObjectMetaData;
0079:        import com.jcorporate.expresso.core.dataobjects.DataTransferObject;
0080:        import com.jcorporate.expresso.core.dataobjects.DefaultDataField;
0081:        import com.jcorporate.expresso.core.dataobjects.jdbc.FieldRangeParser;
0082:        import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCDataObject;
0083:        import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData;
0084:        import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCUtil;
0085:        import com.jcorporate.expresso.core.db.DBConnection;
0086:        import com.jcorporate.expresso.core.db.DBConnectionPool;
0087:        import com.jcorporate.expresso.core.db.DBException;
0088:        import com.jcorporate.expresso.core.db.exception.DBRecordNotFoundException;
0089:        import com.jcorporate.expresso.core.misc.ConfigJdbc;
0090:        import com.jcorporate.expresso.core.misc.ConfigManager;
0091:        import com.jcorporate.expresso.core.misc.ConfigurationException;
0092:        import com.jcorporate.expresso.core.misc.DateTime;
0093:        import com.jcorporate.expresso.core.misc.StringUtil;
0094:        import com.jcorporate.expresso.core.registry.RequestRegistry;
0095:        import com.jcorporate.expresso.core.security.filters.Filter;
0096:        import com.jcorporate.expresso.kernel.util.ClassLocator;
0097:        import com.jcorporate.expresso.kernel.util.FastStringBuffer;
0098:        import com.jcorporate.expresso.services.dbobj.ChangeLog;
0099:        import com.jcorporate.expresso.services.dbobj.DBObjLimit;
0100:        import com.jcorporate.expresso.services.dbobj.Setup;
0101:        import org.apache.log4j.Logger;
0102:        import org.apache.oro.text.regex.Pattern;
0103:        import org.apache.oro.text.regex.PatternMatcher;
0104:        import org.apache.oro.text.regex.Perl5Matcher;
0105:
0106:        import java.io.IOException;
0107:        import java.io.InputStream;
0108:        import java.io.ObjectInputStream;
0109:        import java.io.ObjectOutputStream;
0110:        import java.lang.reflect.Method;
0111:        import java.math.BigDecimal;
0112:        import java.sql.SQLException;
0113:        import java.text.DecimalFormat;
0114:        import java.text.NumberFormat;
0115:        import java.text.ParseException;
0116:        import java.util.ArrayList;
0117:        import java.util.Collections;
0118:        import java.util.Enumeration;
0119:        import java.util.HashMap;
0120:        import java.util.Iterator;
0121:        import java.util.List;
0122:        import java.util.Locale;
0123:        import java.util.Map;
0124:        import java.util.StringTokenizer;
0125:        import java.util.Vector;
0126:
0127:        /**
0128:         * <p>DBObjects are the core of Expresso's object-relational mapping.  They are object-oriented
0129:         * wrappers for some sort of data.  They are generally expected to be used in a single thread, like
0130:         * in processing a web query.</p>
0131:         * <p>When making your own application, you derive your classes from DBObject or
0132:         * SecuredDBObject.</p>
0133:         *
0134:         * @author Michael Nash
0135:         * @see com.jcorporate.expresso.core.dbobj.SecuredDBObject
0136:         * @see com.jcorporate.expresso.core.db.DBException
0137:         * @see com.jcorporate.expresso.core.db.DBConnection
0138:         * @since Expresso 1.0
0139:         */
0140:        public abstract class DBObject extends JDBCDataObject implements 
0141:                Cacheable, LookupInterface {
0142:
0143:            /**
0144:             * setup code for update() policy:
0145:             * for efficiency, developers who are confident that their code does not have any
0146:             * 'blind updates' can set the Setup value UPDATE_CHANGED_ONLY to true
0147:             * (a blind update is where the object is not retieved before values in it are reset)
0148:             */
0149:            public static final String UPDATE_CHANGED_ONLY = "UPDATE_CHANGED_ONLY";
0150:
0151:            /**
0152:             * Attribute String for if there's an error with the field.
0153:             */
0154:            public static final String ATTRIBUTE_ERROR = "error";
0155:
0156:            /**
0157:             * Attribute String for what message to display if there's an error
0158:             * with thie field.
0159:             */
0160:            public static final String ATTRIBUTE_ERROR_MESSAGE = "error-message";
0161:
0162:            /**
0163:             * Attribute for what is the limit to retrieve from searchAndRetrieve operations
0164:             */
0165:            public static final String ATTRIBUTE_PAGE_LIMIT = "pageLimit";
0166:
0167:            /**
0168:             * Event 'Add' code
0169:             */
0170:            public static final String EVENT_ADD = "A";
0171:
0172:            /**
0173:             * Event 'Delete' Code
0174:             */
0175:            public static final String EVENT_DELETE = "D";
0176:
0177:            /**
0178:             * Event 'Update' Code
0179:             */
0180:            public static final String EVENT_UPDATE = "U";
0181:
0182:            /**
0183:             * A static zero BIG DECIMAL object
0184:             * <p/>
0185:             * author Peter Pilgrim <peterp@xenonsoft.demon.co.uk>
0186:             *
0187:             * @see #getFieldBigDecimal
0188:             */
0189:            transient protected static final BigDecimal BIG_DECIMAL_ZERO = new BigDecimal(
0190:                    0.0);
0191:
0192:            /* the logs
0193:             * @todo move this static variable outside DBObject so we can wrap it for
0194:             * EJBs
0195:             */
0196:            transient private static Logger log = Logger
0197:                    .getLogger(DBObject.class);
0198:
0199:            /**
0200:             * 30 minute DBObjLimit cache tty
0201:             */
0202:            transient private static final long DBOBJLIMIT_CACHE_TTL = 60 * 1000 * 30;
0203:
0204:            /**
0205:             * Statistics for Cache entries.
0206:             */
0207:            transient private static ConcurrentReaderHashMap sCacheStats = new ConcurrentReaderHashMap();
0208:
0209:            /**
0210:             * Cache Util object for bridging DBObject &lt;-&gt; CacheManager
0211:             */
0212:            transient final private static CacheUtils cacheUtils = new CacheUtils();
0213:
0214:            /**
0215:             * A Pattern Matcher for examining fields vs validation regular expressions
0216:             * It has been modified for thead local instantiation to reduce synchronization.
0217:             */
0218:            transient private static ThreadLocal patternMatcher = new ThreadLocal() {
0219:                protected synchronized Object initialValue() {
0220:                    return new Perl5Matcher();
0221:                }
0222:            };
0223:            private static Logger slog = null;
0224:            public static final String WHERE_KEYWORD = " WHERE ";
0225:
0226:            /**
0227:             * Retrieve a thread local instance of the Perl5 pattern matcher.  Allows
0228:             * for optimization of # of instances of pattern matcher vs synchronization.
0229:             *
0230:             * @return PatternMatcher
0231:             */
0232:            protected PatternMatcher getPatternMatcher() {
0233:                return (PatternMatcher) patternMatcher.get();
0234:            }
0235:
0236:            /**
0237:             * Field Range Parser verifies range values to make sure there isn't things
0238:             * like SQL injection getting passed into the range.
0239:             */
0240:            transient private static FieldRangeParser rangeVerifier = new FieldRangeParser();
0241:
0242:            ///////////////////////////////////////////////////////////////////////////////////////
0243:            // instance vars
0244:            ///////////////////////////////////////////////////////////////////////////////////////
0245:
0246:            /**
0247:             * string filter class; null defaults to mean HtmlFilter
0248:             * this should be stored here, in the object instance, rather than a setting
0249:             * in metadata for all objects of this type:  consider a use-case where
0250:             * one object is getting rendered in HTML while another is simultaneously
0251:             * rendered in XML.
0252:             */
0253:            private Class mFilter = null;
0254:
0255:            /**
0256:             * A DBObject instance often refers to a single row, and this hash map contains
0257:             * the row data for this instance.  This Map specifically contains a list of
0258:             * <code>DataField</code> objects that in turn contain the actual field data.
0259:             */
0260:            private Map fieldData = null;
0261:
0262:            /**
0263:             * Contains a map of DBObject.FieldError classes describing the errors
0264:             * set by checkField().
0265:             */
0266:            private HashMap fieldErrors = null;
0267:
0268:            /* This is the locale of the DBObject. Originally this attribute
0269:             * was part of the subclass <code>SecuredDBObject</code>. However
0270:             * there is no reason that explains why all DBObjects should not
0271:             * support internationalisation (i18n).
0272:             * Modification by Peter Pilgrim Wed Jan 01 18:27:14 GMT 2003
0273:             *
0274:             * @see #getLocale
0275:             * @see #setLocale( Locale )
0276:             */
0277:            private Locale myLocale = Locale.getDefault();
0278:
0279:            /**
0280:             * Keys that have been found in the last retrieve.
0281:             */
0282:            private ArrayList foundKeys = null;
0283:
0284:            /**
0285:             * Attributes of this DB Object
0286:             */
0287:            private HashMap attributes = null;
0288:
0289:            /* The cache size of this particular DB object */
0290:            private int myCacheSize = -2;
0291:
0292:            /**
0293:             * Very Similar to  "anyFieldsToRetrieve" already present on the DBObject.
0294:             * author ABHI
0295:             */
0296:            boolean anyFieldsToRetrieveMulti = false;
0297:
0298:            /**
0299:             * Integer Regular Expression for easy reference
0300:             */
0301:            public static final String INT_MASK = "^[+-]?[0-9]+";
0302:
0303:            /**
0304:             * Floating point regular expression syntax for easy reference.
0305:             */
0306:            public static final String FLOAT_MASK = "^([+-]?)(?=\\d|\\.\\d)\\d*(\\.\\d*)?([Ee]([+-]?\\d+))?$";
0307:
0308:            /**
0309:             * Email Regular Expression Constant.
0310:             */
0311:            public static final String EMAIL_MASK = "^[A-Za-z0-9\\-\\.\\_]+"
0312:                    + "@" + "[A-Za-z0-9\\-\\.\\_]+" + "\\." + "[a-zA-Z]{2,6}$"; // 6 chars in "museum" TLD
0313:            public static final String IS_CHECK_RELATIONAL_INTEGRITY = "isCheckRelationalIntegrity";
0314:
0315:            /**
0316:             * Default Constructor. This allows a DB object to be dynamically
0317:             * instantiated (e.g. loaded
0318:             * with Class.forName()) and does all of the required initializations.
0319:             *
0320:             * @throws DBException upon error.
0321:             */
0322:            public DBObject() throws DBException {
0323:                initialize();
0324:            } /* DBObject() */
0325:
0326:            /**
0327:             * Constructor that sets a connection as the object is created - typically
0328:             * this is used when a particular DBConnection is required for the purposes of
0329:             * maintaining a database transaction. If a specific connection is not used,
0330:             * there is no way to use commit() and rollback() in the event of failure, as a
0331:             * different DBConnection might be used for each phase of the transaction.
0332:             * Critial sections should therefore explicity request a DBConnection from the
0333:             * connection pool and pass it to each of the DB objects in that section.
0334:             *
0335:             * @param newConnection The DBConnection to utilize
0336:             * @throws DBException upon error.
0337:             */
0338:            public DBObject(DBConnection newConnection) throws DBException {
0339:                this (newConnection, newConnection.getDataContext());
0340:            } /* DBObject(DBConnection) */
0341:
0342:            /**
0343:             * Constructor that sets a connection as the object is created - typically
0344:             * this is used when a particular DBConnection is required for the purposes of
0345:             * maintaining a database transaction. If a specific connection is not used,
0346:             * there is no way to use commit() and rollback() in the event of failure, as a
0347:             * different DBConnection might be used for each phase of the transaction.
0348:             * Critial sections should therefore explicity request a DBConnection from the
0349:             * connection pool and pass it to each of the DB objects in that section.
0350:             * <p>This constructor is neceesary to work with otherDBMap and transaction
0351:             * capabilities</p>
0352:             *
0353:             * @param newConnection      The DBConnection to utilize
0354:             * @param setupTablesContext The data context that contains the setup (and
0355:             *                           security) tables for this object
0356:             * @throws DBException upon error.
0357:             * @since Expresso 5.0.1
0358:             */
0359:            public DBObject(DBConnection newConnection,
0360:                    String setupTablesContext) throws DBException {
0361:                this ();
0362:                setConnection(newConnection, setupTablesContext);
0363:            } /* DBObject(DBConnection) */
0364:
0365:            /**
0366:             * For using DBObjects within Controllers.  Initializes based upon
0367:             * the current locale and the requested db context. There is no
0368:             * current user login id set in this method. If you need the user id
0369:             * then use you should use the subclass <code>SecuredDBObject</code>
0370:             * type instead.
0371:             *
0372:             * @param request - The controller request handed to you by the framework.
0373:             * @throws DBException if there's an error constructing the SecuredDBObject
0374:             */
0375:            public DBObject(RequestContext request) throws DBException {
0376:                this ();
0377:                setDataContext(request.getDBName());
0378:                setLocale(request.getLocale());
0379:            }
0380:
0381:            /**
0382:             * Get the current locale for this dbobject
0383:             *
0384:             * @return The currently set locale or null if there is no locale set.
0385:             * @since Expresso 5.0.1
0386:             */
0387:            public Locale getLocale() {
0388:                return myLocale;
0389:            }
0390:
0391:            /**
0392:             * Sets the locale to be used with this DBObject
0393:             *
0394:             * @param newLocale The new Locale to use with this object
0395:             * @since Expresso 5.0.1
0396:             */
0397:            public void setLocale(Locale newLocale) {
0398:                myLocale = newLocale;
0399:            }
0400:
0401:            /**
0402:             * Initialize this DBObject and set the db/context to the specified key
0403:             *
0404:             * @param newdbKey The database Context name
0405:             * @throws DBException upon error.
0406:             */
0407:            public DBObject(String newdbKey) throws DBException {
0408:                this ();
0409:                setDataContext(newdbKey);
0410:            } /* DBObject(String) */
0411:
0412:            /**
0413:             * Add a new record to the target table.
0414:             * Assumes that the fields of this object are populated with data for the new
0415:             * record. All key fields at least must be supplied with values, and all fields
0416:             * that are specified as "no nulls". This method also validates all referential
0417:             * integrity constraints specified by the object.
0418:             *
0419:             * @throws DBException If the record cannot be added - this includes if the
0420:             *                     record has a duplicate key
0421:             */
0422:            public void add() throws DBException {
0423:
0424:                getExecutor().add(this );
0425:
0426:                /* Now log the change if we are logging */
0427:                if (getDef().isLoggingEnabled()) {
0428:                    ChangeLog myChangeLog = new ChangeLog();
0429:                    myChangeLog.setDataContext(getDataContext());
0430:                    myChangeLog.setField("ObjectChanged", myClassName);
0431:                    myChangeLog.setField("RecordKey", getMyKeys());
0432:                    myChangeLog.setField("Operation", EVENT_ADD);
0433:                    myChangeLog.setField("ChangedField", "ALL");
0434:                    myChangeLog.add();
0435:
0436:                    /* We're done tracking changes */
0437:                    myUpdates = null;
0438:                } /* if */
0439:
0440:                // after add(), we know that 'current' values are now the baseline for comparison
0441:                cacheIsChangedComparison();
0442:                setStatus(BaseDataObject.STATUS_CURRENT);
0443:                notifyListeners(EVENT_ADD);
0444:            } /* add() */
0445:
0446:            /**
0447:             * Hand a dbobject a connection that contains fields corresponding to
0448:             * what the dbobject expects, and it'll set itself.
0449:             * <p/>
0450:             * Does not increment the result set in the DBConnection.
0451:             *
0452:             * @param connection The connection that currently has a dbobject ready to
0453:             *                   be read in it's result set.
0454:             * @return The number of fields read.  Depending on the SQL you sent to the connection
0455:             *         the DBObject might not have all fields in existence.
0456:             * @throws DBException upon error.
0457:             */
0458:            public synchronized int loadFromConnection(DBConnection connection)
0459:                    throws DBException {
0460:                String oneFieldName = null;
0461:                Object tmpData = null;
0462:                int fieldCount = 0;
0463:                JDBCObjectMetaData metadata = getJDBCMetaData();
0464:                for (Iterator it = metadata.getFieldListArray().iterator(); it
0465:                        .hasNext();) {
0466:                    oneFieldName = (String) it.next();
0467:
0468:                    try {
0469:                        DataFieldMetaData oneField = metadata
0470:                                .getFieldMetadata(oneFieldName);
0471:                        if (oneField.isDateType()) {
0472:                            tmpData = getCustomStringFieldValue(connection,
0473:                                    oneFieldName);
0474:                        } else {
0475:                            if (!oneField.isLongBinaryType()
0476:                                    && !oneField.isLongCharacterType()) {
0477:                                if (connection.isStringNotTrim()) {
0478:                                    tmpData = connection
0479:                                            .getStringNoTrim(oneFieldName);
0480:                                } else {
0481:                                    tmpData = connection
0482:                                            .getString(oneFieldName);
0483:                                }
0484:                            } else {
0485:                                if (oneField.isLongBinaryType()) {
0486:                                    tmpData = null;
0487:                                    InputStream is = connection
0488:                                            .getBinaryStream(oneFieldName);
0489:                                    if (is != null) {
0490:                                        byte[] bstr = new byte[LONGBINARY_READ_DEFAULT_SIZE];
0491:                                        int j = is.read(bstr);
0492:                                        if (j > 0) {
0493:                                            byte[] content = new byte[j];
0494:                                            System.arraycopy(bstr, 0, content,
0495:                                                    0, j);
0496:                                            tmpData = content;
0497:                                        }
0498:                                    }
0499:                                } else {
0500:                                    tmpData = connection
0501:                                            .getStringNoTrim(oneFieldName);
0502:                                }
0503:                            }
0504:
0505:                            //					if (connection.isStringNotTrimmed()) {
0506:                            //                    	oneFieldValue = connection.getStringNoTrim(oneFieldName);
0507:                            //					} else {
0508:                            //						oneFieldValue = connection.getString(oneFieldName);
0509:                            //					}
0510:                        }
0511:
0512:                        //				this.setField(oneFieldName, oneFieldValue);
0513:                        this .set(oneFieldName, tmpData);
0514:                        fieldCount++;
0515:                    } catch (DBException de) {
0516:                        if (log.isDebugEnabled()) {
0517:                            log.debug("Failed to load field.", de);
0518:                        }
0519:
0520:                        //Failed to load this field, that's fine
0521:                    } catch (Exception e) {
0522:                        if (log.isDebugEnabled()) {
0523:                            log.debug("Failed to load field.", e);
0524:                        }
0525:
0526:                        //Failed to load this field, that's fine
0527:                    }
0528:
0529:                } /* for each retrieved field name */
0530:
0531:                setDataContext(getDataContext());
0532:                cacheIsChangedComparison();
0533:                setStatus(BaseDataObject.STATUS_CURRENT);
0534:
0535:                return fieldCount;
0536:            }
0537:
0538:            /**
0539:             * reset 'original' value and isChanged flags on all fields, establishing a baseline for comparison.
0540:             * call when add(), retrieve, or update() has occurred, and currentValue of data fields should be
0541:             * considered 'original value' for purposes of determining 'isChanged'
0542:             *
0543:             * @throws DBException upon error.
0544:             */
0545:            public void cacheIsChangedComparison() throws DBException {
0546:                for (Iterator i = getMetaData().getFieldListArray().iterator(); i
0547:                        .hasNext();) {
0548:                    String oneFieldName = (String) i.next();
0549:                    DataField field = getDataField(oneFieldName);
0550:                    field.cacheIsChangedComparison();
0551:                }
0552:            }
0553:
0554:            /**
0555:             * This is used internally by JDBC Exceutor's and JDBC Query when dealing
0556:             * with queries to the underlying datasource.  Under normal conditions you
0557:             * would not used this function directly.
0558:             *
0559:             * @param fieldName the name of the fieldname found.
0560:             */
0561:            public void addFoundKeys(String fieldName) {
0562:                if (foundKeys == null) {
0563:                    foundKeys = new ArrayList();
0564:                }
0565:
0566:                foundKeys.add(fieldName);
0567:            }
0568:
0569:            /**
0570:             * Specify a new "detail" db object, and the fields in this object
0571:             * they specify the fields in the related object
0572:             *
0573:             * @param objName          The class name of the related object. There is assumed to be
0574:             *                         a one to one or one to many relationship from this object to the specified object
0575:             * @param keyFieldsLocal   A pipe-delimited list of field names in this object
0576:             * @param keyFieldsForeign A pipe-delimieted list of field names in the other object
0577:             * @throws DBException upon error.
0578:             */
0579:            protected synchronized void addDetail(String objName,
0580:                    String keyFieldsLocal, String keyFieldsForeign)
0581:                    throws DBException {
0582:                getDef().addDetail(objName, keyFieldsLocal, keyFieldsForeign);
0583:            }
0584:
0585:            /**
0586:             * Add a field with more details: This version allows the user to specify a
0587:             * precision, for fields that use both a size and precision.
0588:             *
0589:             * @param fieldName        Name of the field
0590:             * @param fieldType        Type of the field - this is the internal Expresso type,
0591:             *                         mapping in DBField to a specific database data type.
0592:             * @param fieldSize        Size of the field
0593:             * @param fieldPrecision   The precision of the field
0594:             * @param allowNull        Does this field allow nulls?
0595:             * @param fieldDescription A longer description of this field
0596:             *                         (user-understandable hopefully!)
0597:             * @throws DBException upon error.
0598:             */
0599:            protected synchronized void addField(String fieldName,
0600:                    String fieldType, int fieldSize, int fieldPrecision,
0601:                    boolean allowNull, String fieldDescription)
0602:                    throws DBException {
0603:                getDef().addField(fieldName, fieldType, fieldSize,
0604:                        fieldPrecision, allowNull, fieldDescription);
0605:            } /* addField(String, String, int, int, boolean, String) */
0606:
0607:            /**
0608:             * Add a field with more details: This version of addfield supplies
0609:             * the allowNull flags and a description of the field to be used
0610:             * when reporting errors to the user. This method is only used by the class that
0611:             * extends DB object, and typically only in the setupFields() method.
0612:             *
0613:             * @param fieldName        Name of the field
0614:             * @param fieldType        Type of the field - this is the "internal" Expresso type,
0615:             *                         and is mapped to a specific type for the database depending on the
0616:             *                         mappings in the properties file (if any). The DBField object contains
0617:             *                         the default mappings.
0618:             * @param fieldSize        Size of this field, if specified for this type of field. For
0619:             *                         fields that do not use a size (such as "date"), specify 0 for the size.
0620:             * @param allowNull        Does this field allow nulls?
0621:             * @param fieldDescription A longer description of this field
0622:             *                         (user-understandable hopefully!)
0623:             * @throws DBException upon error.
0624:             */
0625:            protected synchronized void addField(String fieldName,
0626:                    String fieldType, int fieldSize, boolean allowNull,
0627:                    String fieldDescription) throws DBException {
0628:                getDef().addField(fieldName, fieldType, fieldSize, allowNull,
0629:                        fieldDescription);
0630:            } /* addField(String, String, int, boolean, String) */
0631:
0632:            /**
0633:             * Determine if a record with this key exists already - if
0634:             * not, add a new record. Note that this method uses just the key
0635:             * fields to determine if the record already exists.
0636:             *
0637:             * @throws DBException upon error.
0638:             */
0639:            public synchronized void addIfNeeded() throws DBException {
0640:
0641:                DBObject searchObj = newInstance();
0642:                searchObj.setDataContext(getDataContext());
0643:
0644:                String oneFieldName = null;
0645:
0646:                for (Iterator i = getKeyFieldListIterator(); i.hasNext();) {
0647:                    oneFieldName = (String) i.next();
0648:                    DataFieldMetaData oneField = getFieldMetaData(oneFieldName);
0649:                    String value = getField(oneFieldName);
0650:
0651:                    // warn user if they should not be using this method
0652:                    if (value.length() == 0) {
0653:                        log
0654:                                .warn("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.");
0655:                    }
0656:
0657:                    if (!oneField.isVirtual()) {
0658:                        searchObj.setField(oneFieldName, value);
0659:                    }
0660:                }
0661:                if (!searchObj.find()) {
0662:                    add();
0663:                }
0664:            } /* addOrUpdate() */
0665:
0666:            /**
0667:             * Use this in your derived checkField() class to add error messages to
0668:             * be associated with various fields.
0669:             *
0670:             * @param fieldName    The field name to add the error to
0671:             * @param errorMessage The custom error message to associate when there's
0672:             *                     a problem with this field
0673:             */
0674:            protected void addFieldError(String fieldName, String errorMessage) {
0675:                if (fieldErrors == null) {
0676:                    fieldErrors = new HashMap(5);
0677:                }
0678:
0679:                fieldErrors.put(fieldName, new DBObject.FieldError(fieldName,
0680:                        errorMessage));
0681:            }
0682:
0683:            /**
0684:             * Retrieve the error message associated with this field.
0685:             *
0686:             * @param fieldName the fieldName to get the associated error message
0687:             * @return A string containing the field error message or NULL if there is
0688:             *         no error for this field. or POSSIBLY if no error message has been set
0689:             *         for this field.
0690:             */
0691:            public String getFieldErrorMessage(String fieldName) {
0692:                if (fieldErrors == null) {
0693:                    return null;
0694:                }
0695:
0696:                DBObject.FieldError fe = (DBObject.FieldError) fieldErrors
0697:                        .get(fieldName);
0698:                if (fe == null) {
0699:                    return null;
0700:                }
0701:
0702:                return fe.getErrorMessage();
0703:            }
0704:
0705:            /**
0706:             * Use this to check if a field is in error.
0707:             *
0708:             * @param fieldName Check if there's an error set for this field.
0709:             * @return true if an error is set for this field.
0710:             */
0711:            public boolean hasError(String fieldName) {
0712:                try {
0713:                    DataField df = getDataField(fieldName);
0714:                    if (df.getAttribute(ATTRIBUTE_ERROR) != null) {
0715:                        return true;
0716:                    }
0717:                } catch (DBException ex) {
0718:                    log.error("Invalid field name: " + fieldName, ex);
0719:                    return false;
0720:                }
0721:
0722:                if (fieldErrors == null) {
0723:                    return false;
0724:                }
0725:
0726:                DBObject.FieldError fe = (DBObject.FieldError) fieldErrors
0727:                        .get(fieldName);
0728:                if (fe == null) {
0729:                    return false;
0730:                }
0731:
0732:                return true;
0733:            }
0734:
0735:            /**
0736:             * Use this to check if any fields are in error.
0737:             *
0738:             * @return true if an error is set.
0739:             */
0740:            public boolean hasErrors() {
0741:                if (fieldErrors == null || fieldErrors.isEmpty()) {
0742:                    return false;
0743:                }
0744:
0745:                return true;
0746:            }
0747:
0748:            /**
0749:             * Used to clear field error flags.
0750:             *
0751:             * @param fieldName the name of the field to clear the error flag
0752:             */
0753:            protected void clearError(String fieldName) {
0754:                if (fieldErrors == null) {
0755:                    return;
0756:                }
0757:
0758:                fieldErrors.remove(fieldName);
0759:            }
0760:
0761:            /**
0762:             * Add an index to the table.
0763:             *
0764:             * @param indexName  the name to give the index in the table; MUST CONTAIN NO SPACES--use underscores instead
0765:             * @param fieldNames A comma delimited list of all fields in the index.
0766:             * @param isUnique   - True if this field is a unique index.
0767:             * @throws IllegalArgumentException of fieldName is null or doesn't exist
0768:             *                                  or if indexName is null
0769:             * @throws DBException              upon error.
0770:             */
0771:            protected void addIndex(String indexName, String fieldNames,
0772:                    boolean isUnique) throws IllegalArgumentException,
0773:                    DBException {
0774:                getDef().addIndex(indexName, fieldNames, isUnique);
0775:            }
0776:
0777:            /**
0778:             * Add a new field to the list of fields that are part of this
0779:             * object's key. Called after all of the "addField" calls in the setupFields()
0780:             * method to specify which fields make up the primary key of this object.
0781:             *
0782:             * @param keyFieldName The name of the field to add as part of the key
0783:             * @throws DBException if the field name is not valid or the field
0784:             *                     allows nulls
0785:             */
0786:            protected synchronized void addKey(String keyFieldName)
0787:                    throws DBException {
0788:                getDef().addKey(keyFieldName);
0789:            } /* addKey(String) */
0790:
0791:            /**
0792:             * Determine if a record with these fields exists already - if so, update. If
0793:             * not, add a new record.
0794:             *
0795:             * @throws DBException upon error.
0796:             */
0797:            public synchronized void addOrUpdate() throws DBException {
0798:                DBObject searchObj = newInstance();
0799:                // we must have all key fields for retrieve & update to work
0800:                boolean canUpdate = true;
0801:                String oneFieldName = null;
0802:                JDBCObjectMetaData metadata = getJDBCMetaData();
0803:                for (Iterator i = metadata.getKeyFieldListArray().iterator(); i
0804:                        .hasNext();) {
0805:                    oneFieldName = (String) i.next();
0806:                    DataField df = getDataField(oneFieldName);
0807:                    if (df == null || df.isNull()) {
0808:                        canUpdate = false; // we do not have all keys--cannot update
0809:                        break;
0810:                    }
0811:
0812:                    searchObj.setField(oneFieldName, getField(oneFieldName));
0813:                }
0814:
0815:                if (!canUpdate) {
0816:                    add();
0817:                    return;
0818:                }
0819:
0820:                try {
0821:                    searchObj.retrieve(); // will throw if not found
0822:
0823:                    // special case: if there are no fields besides key
0824:                    // fields, then do nothing since key fields never update this way
0825:                    if (metadata.getKeyFieldListArray().size() == metadata
0826:                            .getFieldListArray().size()) {
0827:                        if (log.isDebugEnabled()) {
0828:                            log
0829:                                    .debug("No fields other than keys.  Skipping update");
0830:                        }
0831:                        // do nothing
0832:                        // @todo add logic for virtual fields
0833:                    } else {
0834:                        update();
0835:                    }
0836:                } catch (DBRecordNotFoundException e) {
0837:                    add();
0838:                }
0839:            } /* addOrUpdate() */
0840:
0841:            /**
0842:             * ?????
0843:             *
0844:             * @param t unknown
0845:             * @throws DBException upon metadata retrieval error
0846:             */
0847:            protected void addTransition(Transition t) throws DBException {
0848:                getDef().addTransition(t);
0849:            }
0850:
0851:            /**
0852:             * Add a field with more details: This version allows the user to specify a
0853:             * precision, for fields that use both a size and precision.
0854:             *
0855:             * @param fieldName        Name of the field
0856:             * @param fieldType        Type of the field - this is the internal Expresso type,
0857:             *                         mapping in DBField to a specific database data type.
0858:             * @param fieldSize        Size of the field
0859:             * @param fieldPrecision   The precision of the field
0860:             * @param allowNull        Does this field allow nulls?
0861:             * @param fieldDescription A longer description of this field
0862:             *                         (user-understandable hopefully!)
0863:             * @throws DBException upon error.
0864:             */
0865:            protected synchronized void addVirtualField(String fieldName,
0866:                    String fieldType, int fieldSize, int fieldPrecision,
0867:                    boolean allowNull, String fieldDescription)
0868:                    throws DBException {
0869:                getDef().addVirtualField(fieldName, fieldType, fieldSize,
0870:                        fieldPrecision, allowNull, fieldDescription);
0871:            } /* addField(String, String, int, int, boolean, String) */
0872:
0873:            /**
0874:             * Add a field with more details: This version allows the user to specify a
0875:             * precision, for fields that use both a size and precision.
0876:             *
0877:             * @param fieldName        Name of the field
0878:             * @param fieldType        Type of the field - this is the internal Expresso type,
0879:             *                         mapping in DBField to a specific database data type.
0880:             * @param fieldSize        Size of the field
0881:             * @param fieldPrecision   The precision of the field
0882:             * @param allowNull        Does this field allow nulls?
0883:             * @param descripKey       The key in the local language file for the
0884:             *                         description of this field
0885:             * @param fieldDescription A longer description of this field
0886:             *                         (user-understandable hopefully!)
0887:             * @throws DBException upon error.
0888:             */
0889:            protected synchronized void addVirtualField(String fieldName,
0890:                    String fieldType, int fieldSize, int fieldPrecision,
0891:                    boolean allowNull, String descripKey,
0892:                    String fieldDescription) throws DBException {
0893:                getDef()
0894:                        .addVirtualField(fieldName, fieldType, fieldSize,
0895:                                fieldPrecision, allowNull, descripKey,
0896:                                fieldDescription);
0897:            } /* addField(String, String, int, int, boolean, String, String) */
0898:
0899:            /**
0900:             * Add a new virtual field to the definition of this object.
0901:             * A virtual field is just like a regular one, except it's
0902:             * not stored in the target table
0903:             * A normal call to getField or setField on a virtual field will throw
0904:             * an exception - getField and setField should be extended to handle the virtual
0905:             * fields for a particular object correctly.
0906:             *
0907:             * @param fieldName        Name of the field
0908:             * @param fieldType        Database type of the field
0909:             * @param fieldSize        Size of the field in characters
0910:             * @param fieldDescription A longer description of this field
0911:             *                         (user-understandable hopefully!)
0912:             * @throws DBException upon error.
0913:             */
0914:            protected synchronized void addVirtualField(String fieldName,
0915:                    String fieldType, int fieldSize, String fieldDescription)
0916:                    throws DBException {
0917:                getDef().addVirtualField(fieldName, fieldType, fieldSize,
0918:                        fieldDescription);
0919:            } /* addVirtualField(String, String, int, String) */
0920:
0921:            /**
0922:             * Parses the sort key string into useful values.  This version forces full
0923:             * parsing of the input values to prevent SQL injection attacks.
0924:             *
0925:             * @param sortKeyString the pipe delimited string of field names with the optional 'ASC or DESC' keyword afterwords; null indicates no sorting
0926:             */
0927:            protected void setSortKey(String sortKeyString) {
0928:                if (sortKeyString == null) {
0929:                    if (sortKeys != null) {
0930:                        sortKeys.clear();
0931:                    }
0932:                    return;
0933:                }
0934:
0935:                if (sortKeys == null) {
0936:                    sortKeys = new ArrayList();
0937:                } else {
0938:                    sortKeys.clear();
0939:                }
0940:
0941:                StringTokenizer stk = new StringTokenizer(sortKeyString, "|");
0942:
0943:                while (stk.hasMoreTokens()) {
0944:                    String key = stk.nextToken().trim();
0945:                    String fieldName;
0946:                    String sortOrder = null;
0947:                    int spaceLoc = key.indexOf(" ");
0948:                    boolean ascending = true;
0949:
0950:                    if (spaceLoc > 0) {
0951:                        fieldName = key.substring(0, spaceLoc);
0952:                        sortOrder = key.substring(spaceLoc + 1).trim();
0953:
0954:                        if (!(sortOrder.equalsIgnoreCase("ASC") || sortOrder
0955:                                .equalsIgnoreCase("DESC"))) {
0956:                            log.error("Invalid set sort key: FieldName: "
0957:                                    + fieldName + " sortOrder: " + sortOrder);
0958:                            throw new IllegalArgumentException(
0959:                                    "Must specify a proper "
0960:                                            + "field name and either ASC or DESC only as the sort order");
0961:                        }
0962:
0963:                        if (sortOrder.equalsIgnoreCase("DESC")) {
0964:                            ascending = false;
0965:                        }
0966:                    } else {
0967:                        fieldName = key;
0968:                    }
0969:
0970:                    addSortKey(fieldName, ascending);
0971:                }
0972:            }
0973:
0974:            /**
0975:             * Add a field to be sorted in a search
0976:             *
0977:             * @param fieldString the name of the field to sort on
0978:             * @param ascending   true if using ascending sort order, false if descending
0979:             *                    sort order.
0980:             * @throws IllegalArgumentException if the fieldString does not exist
0981:             */
0982:            public void addSortKey(String fieldString, boolean ascending) {
0983:                if (sortKeys == null) {
0984:                    sortKeys = new ArrayList();
0985:                }
0986:
0987:                //
0988:                //We do this call because getFieldMetadata throws IllegalArgumentException
0989:                //if the field name does not exist.  Thus guarding against potential
0990:                //SQL injection attacks.
0991:                //
0992:                getFieldMetaData(fieldString);
0993:
0994:                if (ascending) {
0995:                    sortKeys.add(fieldString + " ASC");
0996:                } else {
0997:                    sortKeys.add(fieldString + " DESC");
0998:                }
0999:            }
1000:
1001:            /**
1002:             * Clear the sort keys
1003:             *
1004:             * @see #addSortKey
1005:             */
1006:            public void clearSortKeys() {
1007:                sortKeys = null;
1008:            }
1009:
1010:            /**
1011:             * Add a new virtual field to the definition of this object.
1012:             * A virtual field is just like a regular one, except it's
1013:             * not stored in the target table
1014:             * A normal call to getField or setField on a virtual field will throw
1015:             * an exception - getField and setField should be extended to handle the virtual
1016:             * fields for a particular object correctly.
1017:             *
1018:             * @param fieldName        Name of the field
1019:             * @param fieldType        Database type of the field
1020:             * @param fieldSize        Size of the field in characters
1021:             * @param descripKey       Key into the local langauge file for the
1022:             *                         description of this field
1023:             * @param fieldDescription A longer description of this field
1024:             *                         (user-understandable hopefully!)
1025:             * @throws DBException upon error.
1026:             */
1027:            protected synchronized void addVirtualField(String fieldName,
1028:                    String fieldType, int fieldSize, String descripKey,
1029:                    String fieldDescription) throws DBException {
1030:                getDef().addVirtualField(fieldName, fieldType, fieldSize,
1031:                        descripKey, fieldDescription);
1032:            } /* addVirtualField(String, String, int, String) */
1033:
1034:            /**
1035:             * Find the average of the values in the specified field of records
1036:             * selected by the DBObject selection criteria.
1037:             *
1038:             * @param fieldName String DBObject fieldName to average
1039:             * @return double Average of the records matching the criteria
1040:             * @throws DBException If the search could not be completed
1041:             */
1042:            public double average(String fieldName) throws DBException {
1043:                return sqlAggrFunction("AVG", fieldName);
1044:            } /* average() */
1045:
1046:            /**
1047:             * <p/>
1048:             * A &quot;simplified&quote; version of add that can be used to do a basic
1049:             * INSERT statement into this table. Often used with data import
1050:             * facilities, when referential integrity and other validations
1051:             * must be temporarily bypassed.  </p>
1052:             * <p><b>NOTE</b> BLOB Objects are not added with this function!
1053:             * <p><b>NOTE</b> this function does NOT handle auto-increment fields!
1054:             *
1055:             * @throws DBException If the record cannot be added - this includes if the
1056:             *                     record has a duplicate key
1057:             *                     <p/>
1058:             *                     Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1059:             */
1060:            public synchronized void basicAdd() throws DBException {
1061:                boolean needComma = false;
1062:
1063:                if (!haveAllKeys()) {
1064:                    throw new DBException(
1065:                            "("
1066:                                    + myClassName
1067:                                    + ") All key fields not present - cannot add new record");
1068:                }
1069:
1070:                JDBCObjectMetaData metadata = getJDBCMetaData();
1071:                DBField oneField = null;
1072:                FastStringBuffer sqlCommand = new FastStringBuffer(128);
1073:                sqlCommand.append("INSERT INTO ");
1074:                sqlCommand.append(metadata.getTargetSQLTable(getDataContext()));
1075:                sqlCommand.append(" (");
1076:
1077:                for (Iterator i = metadata.getAllFieldsMap().values()
1078:                        .iterator(); i.hasNext();) {
1079:                    oneField = (DBField) i.next();
1080:
1081:                    if (!oneField.isVirtual()) {
1082:                        if (isFieldNull(oneField.getName())) {
1083:                            continue;
1084:                        }
1085:                        if (oneField.isBinaryObjectType()) {
1086:                            continue;
1087:                        }
1088:                        if (needComma) {
1089:                            sqlCommand.append(", ");
1090:                        }
1091:
1092:                        sqlCommand.append(oneField.getName());
1093:                        needComma = true;
1094:                    }
1095:                } /* for each field */
1096:
1097:                sqlCommand.append(") VALUES (");
1098:                needComma = false;
1099:
1100:                for (Iterator i = metadata.getAllFieldsMap().values()
1101:                        .iterator(); i.hasNext();) {
1102:                    oneField = (DBField) i.next();
1103:
1104:                    if (!oneField.isVirtual()) {
1105:                        if (isFieldNull(oneField.getName())) {
1106:                            continue;
1107:                        }
1108:                        if (oneField.isBinaryObjectType()) {
1109:                            continue;
1110:                        }
1111:
1112:                        String stringToAdd = null;
1113:                        if (oneField.isDateType()) {
1114:                            if (getFieldData(oneField.getName()) == null
1115:                                    || (getFieldData(oneField.getName())
1116:                                            .length() == 0)) {
1117:                                stringToAdd = "null";
1118:                            } else {
1119:                                try {
1120:                                    stringToAdd = JDBCUtil.getInstance()
1121:                                            .formatDateTime(this ,
1122:                                                    oneField.getName());
1123:                                    //                            stringToAdd = formatDateTime(oneField.getName());
1124:                                } catch (DBException e) {
1125:                                    log
1126:                                            .warn(
1127:                                                    "Error in basic add, setting field to null",
1128:                                                    e);
1129:                                    stringToAdd = "null";
1130:                                }
1131:                            }
1132:                        }
1133:
1134:                        if (needComma) {
1135:                            sqlCommand.append(", ");
1136:                        }
1137:
1138:                        if (stringToAdd == null) {
1139:                            sqlCommand.append(quoteIfNeeded(oneField.getName(),
1140:                                    null));
1141:                        } else {
1142:                            sqlCommand.append(stringToAdd);
1143:                        }
1144:                        needComma = true;
1145:                    } /* if field is not virtual */
1146:
1147:                } /* for each field */
1148:
1149:                sqlCommand.append(")");
1150:
1151:                DBConnection myConnection = null;
1152:
1153:                try {
1154:                    if (localConnection != null) {
1155:                        myConnection = localConnection;
1156:                    } else {
1157:                        myConnection = getConnectionPool().getConnection(
1158:                                myClassName);
1159:                    }
1160:
1161:                    myConnection.executeUpdate(sqlCommand.toString());
1162:                } catch (DBException de) {
1163:                    throw new DBException("(" + myClassName
1164:                            + ") Unable to add record to database" + forKey()
1165:                            + ":" + de.getMessage() + " (" + sqlCommand + ")",
1166:                            de.getDBMessage());
1167:                } finally {
1168:                    if (localConnection == null) {
1169:                        myConnection.release();
1170:                    }
1171:                }
1172:                /* Now log the change if we are logging */
1173:                if (getDef().isLoggingEnabled()) {
1174:                    ChangeLog myChangeLog = new ChangeLog();
1175:                    myChangeLog.setField("ObjectChanged", myClassName);
1176:                    myChangeLog.setField("RecordKey", getMyKeys());
1177:                    myChangeLog.setField("Operation", EVENT_ADD);
1178:                    myChangeLog.setField("ChangedField", "ALL");
1179:                    myChangeLog.add();
1180:
1181:                    /* We're done tracking changes */
1182:                    myUpdates = null;
1183:                } /* if */
1184:
1185:                cacheIsChangedComparison();
1186:                setStatus(BaseDataObject.STATUS_CURRENT);
1187:                notifyListeners(EVENT_ADD);
1188:                NextNumber.getInstance().reset(getDataContext(), this );
1189:            } /* basicAdd() */
1190:
1191:            /**
1192:             * No security is applied at the DBObject level, only at the level
1193:             * of SecuredDBObjects. The method is still here, however, so that
1194:             * it can be called by the cascading delete and any other methods
1195:             * as required
1196:             *
1197:             * @param function The appropriate function code.  This is mainly used
1198:             *                 in objects derived from SecuredDBObject
1199:             * @return true if this function is alloed
1200:             * @throws DBException upon error.
1201:             */
1202:            public boolean checkAllowed(String function) throws DBException {
1203:                if (log.isDebugEnabled()) {
1204:                    log.debug("checkAllowed.  Function requested: " + function
1205:                            + " default return value is true");
1206:                }
1207:                return true;
1208:            }
1209:
1210:            /**
1211:             * Extended by subclasses to make calls to ReferredToBy as needed. This method
1212:             * implements the basic referential integrity of an object by specifying all of
1213:             * the object that refer to this object as a foreign key. This method is called
1214:             * when a delete is about to occurr to verify that removing the current object
1215:             * would not create invalid references in the "referring" objects - if it would,
1216:             * an exception is thrown, preventing the delete.
1217:             *
1218:             * @throws DBException If the referential integrity is not correct
1219:             */
1220:            protected synchronized void checkAllReferredToBy()
1221:                    throws DBException {
1222:            } /* checkAllReferredToBy() */
1223:
1224:            /**
1225:             * Extended by subclasses to make calls to checkRef as needed. Thie method
1226:             * implements (along with checkAllReferredToBy()) basic referential integrity
1227:             * for this object. Whenever an update or add is about to be made, this method is
1228:             * called to see if the changed record is referring to invalid foreign keys - e.g.
1229:             * lookup tables that do not have an appropriate related record. If so, an
1230:             * Exception is thrown. See the checkRef method for the calls that should be made
1231:             * in this method.
1232:             *
1233:             * @throws DBException If the update/add operation would result in an invalid
1234:             *                     foreign key reference.
1235:             */
1236:            protected synchronized void checkAllRefs() throws DBException {
1237:            } /* checkAllRefs() */
1238:
1239:            /**
1240:             * Public interface to checkAllRefs()
1241:             *
1242:             * @throws DBException upon error.
1243:             */
1244:            public synchronized void checkAllRefsPublic() throws DBException {
1245:                checkAllRefs();
1246:            }
1247:
1248:            /**
1249:             * Check that a given value is valid for a given field.
1250:             * This method is overriden by specific DBObjects to do their own field-level
1251:             * validations - they should also call super in order to do the
1252:             * standard stuff. Every field is automatically checked by this method before the
1253:             * database is updated.
1254:             *
1255:             * @param fieldName  Name of the field to verify
1256:             * @param fieldValue Value of the field to be evaluated
1257:             * @throws DBException If the value is not valid
1258:             */
1259:            public synchronized void checkField(String fieldName,
1260:                    String fieldValue) throws DBException {
1261:
1262:                DataObjectMetaData objMetadata = getMetaData();
1263:                DataFieldMetaData oneField = getDef().getDBField(fieldName);
1264:
1265:                if (oneField == null) {
1266:                    FastStringBuffer fsb = FastStringBuffer.getInstance();
1267:                    String stringValue = null;
1268:                    try {
1269:                        fsb.append("No such field as '");
1270:                        fsb.append(objMetadata.getDescription(getLocale(),
1271:                                fieldName));
1272:                        fsb.append("'");
1273:                        stringValue = fsb.toString();
1274:                    } finally {
1275:                        fsb.release();
1276:                        fsb = null;
1277:                    }
1278:
1279:                    throw new DBException(stringValue);
1280:                }
1281:
1282:                if (oneField.isAutoIncremented()) {
1283:                    return;
1284:                }
1285:
1286:                //Allow autoinc fields through since they're added last minute.
1287:                if ((!oneField.allowsNull()) && (fieldValue == null)) {
1288:                    getDataField(fieldName).setAttribute(ATTRIBUTE_ERROR, "Y");
1289:
1290:                    String msg = (String) oneField
1291:                            .getAttribute(DBObject.ATTRIBUTE_ERROR_MESSAGE);
1292:                    if (msg == null) {
1293:                        FastStringBuffer fsb = FastStringBuffer.getInstance();
1294:                        try {
1295:                            fsb.append("Field '");
1296:                            fsb.append(objMetadata.getDescription(getLocale(),
1297:                                    fieldName));
1298:                            fsb.append("' cannot accept a null value ");
1299:                            fsb.append(forKey());
1300:                            msg = fsb.toString();
1301:                        } finally {
1302:                            fsb.release();
1303:                            fsb = null;
1304:                        }
1305:                    }
1306:
1307:                    addFieldError(fieldName, msg);
1308:                    throw new DBException(msg);
1309:
1310:                }
1311:
1312:                /**
1313:                 * Modification done for avoid empty content check for read only
1314:                 * field in Add state when you use DBMaint or sevlet like DBMaint to fill table
1315:                 * The field is checked before it's filled in add method within it's own DBObject.
1316:                 * @author 		Yves Henri AMAIZO
1317:                 */
1318:                /* Check for null and not read only column*/
1319:                if ((!oneField.allowsNull() && !oneField.isReadOnly())
1320:                        && fieldValue.length() == 0) {
1321:                    getDataField(fieldName).setAttribute(ATTRIBUTE_ERROR, "Y");
1322:                    String msg = (String) oneField
1323:                            .getAttribute(DBObject.ATTRIBUTE_ERROR_MESSAGE);
1324:                    if (msg == null) {
1325:                        FastStringBuffer fsb = FastStringBuffer.getInstance();
1326:                        try {
1327:                            fsb.append("Field '");
1328:                            fsb.append(objMetadata.getDescription(getLocale(),
1329:                                    fieldName));
1330:                            fsb.append("' cannot be empty ");
1331:                            fsb.append(forKey());
1332:                            msg = fsb.toString();
1333:                        } finally {
1334:                            fsb.release();
1335:                            fsb = null;
1336:                        }
1337:                    }
1338:
1339:                    addFieldError(fieldName, msg);
1340:                    throw new DBException(msg);
1341:                }
1342:
1343:                if (fieldValue != null && fieldValue.length() > 0) {
1344:                    Pattern mask = oneField.getMask();
1345:
1346:                    if (mask != null) {
1347:                        if (!getPatternMatcher().matches(fieldValue, mask)) {
1348:                            getDataField(fieldName).setAttribute(
1349:                                    ATTRIBUTE_ERROR, "Y");
1350:                            String msg = (String) oneField
1351:                                    .getAttribute(DBObject.ATTRIBUTE_ERROR_MESSAGE);
1352:                            if (msg == null) {
1353:                                FastStringBuffer fsb = FastStringBuffer
1354:                                        .getInstance();
1355:                                try {
1356:                                    fsb.append("Value '");
1357:                                    fsb.append(fieldValue);
1358:                                    fsb.append("' supplied for field '");
1359:                                    fsb.append(objMetadata.getDescription(
1360:                                            getLocale(), fieldName));
1361:                                    fsb
1362:                                            .append("' does not match the regular expression specified for this field.");
1363:                                    fsb.append(forKey());
1364:                                    msg = fsb.toString();
1365:                                } finally {
1366:                                    fsb.release();
1367:                                    fsb = null;
1368:                                }
1369:
1370:                            }
1371:
1372:                            addFieldError(fieldName, msg);
1373:                            throw new DBException(msg);
1374:                        }
1375:                    }
1376:                }
1377:
1378:                /* For multi-valued fields, the value specified must be one of the valid values
1379:                   Note: I have changed this behavior slightly from previous versions.
1380:                   If a field has been marked as "allowsNull", it should pass the check below,
1381:                   even if the field has also been marked as "multiValued". In other words,
1382:                   a mutlivalued, allowsnull field should pass the checkField method if it has a
1383:                   null value.
1384:                    - Adam
1385:                 */
1386:
1387:                // check for configuration (Setup) switch which turns off all relational integrity checking
1388:                String isCheckRelationalIntegrity = Setup.getValueUnrequired(
1389:                        getDataContext(), IS_CHECK_RELATIONAL_INTEGRITY);
1390:                boolean isCheck = true; // default is true for legacy; this is only a switch used by a minority of developers
1391:                if (isCheckRelationalIntegrity != null) {
1392:                    isCheck = StringUtil.toBoolean(isCheckRelationalIntegrity);
1393:                }
1394:
1395:                if (isCheck && oneField.isMultiValued()
1396:                        && (!fieldValue.equals(""))) {
1397:                    Vector values = getValidValues(oneField.getName());
1398:
1399:                    if (values == null) {
1400:                        throw new DBException(
1401:                                "("
1402:                                        + myClassName
1403:                                        + ") '"
1404:                                        + objMetadata.getDescription(
1405:                                                getLocale(), fieldName)
1406:                                        + "' is set as multi-valued, but there are no defined "
1407:                                        + "valid values" + forKey());
1408:                    }
1409:
1410:                    ValidValue oneValue = null;
1411:
1412:                    for (Enumeration e = values.elements(); e.hasMoreElements();) {
1413:                        oneValue = (ValidValue) e.nextElement();
1414:
1415:                        if (oneValue.getKey().equals(fieldValue)) {
1416:                            return;
1417:                        }
1418:                    } /* for each valid value */
1419:
1420:                    oneField.setAttribute(ATTRIBUTE_ERROR, "Y");
1421:                    throw new DBException("("
1422:                            + myClassName
1423:                            + ") '"
1424:                            + fieldValue
1425:                            + "' is not a valid value for field '"
1426:                            + objMetadata
1427:                                    .getDescription(getLocale(), fieldName)
1428:                            + "' " + forKey());
1429:                } /* if field is multi-valued */
1430:
1431:            } /* checkField(String, String) */
1432:
1433:            /**
1434:             * Convenience method for checking a reference where blank is *not* allowed
1435:             * in the foreign key;
1436:             * This test can be turned off if you add
1437:             * an Expresso setup (configuration) entry, 'isCheckRelationalIntegrity' set to false
1438:             *
1439:             * @param foreignKeyNames names of the foreign key fields
1440:             * @param refObject       The foreign DBObject to check against
1441:             * @param errorMessage    The custom error message to display if check fails
1442:             * @throws DBException if check fails
1443:             */
1444:            protected synchronized void checkRef(String foreignKeyNames,
1445:                    DBObject refObject, String errorMessage) throws DBException {
1446:                // respect transaction if any
1447:                if (getLocalConnection() != null) {
1448:                    refObject.setConnection(getLocalConnection());
1449:                }
1450:                checkRef(foreignKeyNames, refObject, errorMessage, false);
1451:            }
1452:
1453:            /**
1454:             * Verify referential integrity from the given list of fields to the
1455:             * key fields in the given DB Object. Calls to this method are made in the
1456:             * checkAllRefs method only. checkAllRefs is automatically called before any
1457:             * database updates are made to validate the referential integrity of the update.
1458:             * Use of this method (and the checkAllRefs method) allow the referential
1459:             * constraints to be "declared", then automatically maintained for a dbobject.
1460:             *
1461:             * @param foreignKeyNames A semicolon-sperated list of foreign
1462:             *                        key fields from this object
1463:             * @param refObject       Another DBObject that we are to refer to
1464:             * @param errorMessage    Error string to throw if the check fails
1465:             * @param allowBlank      true if blank fields are allowed
1466:             * @throws DBException If the referential integrity is not correct
1467:             */
1468:            protected synchronized void checkRef(String foreignKeyNames,
1469:                    DBObject refObject, String errorMessage, boolean allowBlank)
1470:                    throws DBException {
1471:
1472:                // check for configuration (Setup) switch which turns off all relational integrity checking
1473:                String isCheckRelationalIntegrity = Setup.getValueUnrequired(
1474:                        getDataContext(), IS_CHECK_RELATIONAL_INTEGRITY);
1475:                boolean isCheck = true; // default is true for legacy; this is only a switch used by a minority of developers
1476:                if (isCheckRelationalIntegrity != null) {
1477:                    isCheck = StringUtil.toBoolean(isCheckRelationalIntegrity);
1478:                }
1479:
1480:                if (!isCheck) {
1481:                    return;
1482:                }
1483:
1484:                refObject.setDataContext(getDataContext());
1485:
1486:                ArrayList foreignKeys = new ArrayList();
1487:                String fieldValue = null;
1488:
1489:                /* do ALL of the key fields listed have a blank/empty/null value */
1490:                boolean allBlank = true;
1491:
1492:                /* Make a list from the foreignKeyNames */
1493:                StringTokenizer stk = new StringTokenizer(foreignKeyNames, ";");
1494:
1495:                while (stk.hasMoreTokens()) {
1496:                    foreignKeys.add(stk.nextToken());
1497:                }
1498:
1499:                /* Get the list of key fields from the refObject */
1500:                Iterator myKeys = foreignKeys.iterator();
1501:
1502:                for (Iterator refKeys = refObject.getJDBCMetaData()
1503:                        .getKeyFieldListArray().iterator(); refKeys.hasNext();) {
1504:                    String oneMyKey;
1505:
1506:                    if (myKeys.hasNext()) {
1507:                        oneMyKey = (String) myKeys.next();
1508:                    } else {
1509:                        throw new DBException(
1510:                                "("
1511:                                        + myClassName
1512:                                        + ") Wrong number of key elements - the call to "
1513:                                        + "checkRef must refer to the same number"
1514:                                        + " of key elements as the referred-to object has");
1515:                    }
1516:
1517:                    String oneRefKey = (String) refKeys.next();
1518:                    fieldValue = getField(oneMyKey);
1519:
1520:                    if (!StringUtil.notNull(fieldValue).equals("")) {
1521:                        allBlank = false;
1522:                    }
1523:
1524:                    refObject.setField(oneRefKey, fieldValue);
1525:                }
1526:                /* if we allow blanks, and all are blank, then return */
1527:                if (allowBlank && allBlank) {
1528:                    return;
1529:                }
1530:                /* Call retrieve on the ref object & catch any error */
1531:                if (!refObject.find()) {
1532:                    throw new DBException(errorMessage);
1533:                }
1534:            } /* checkRef(String, DBObject, String) */
1535:
1536:            /**
1537:             * Set all fields to empty value & clear the last result set & clear sort keys and customWhereClause
1538:             *
1539:             * @throws DBException If the fields cannot be cleared
1540:             */
1541:            public synchronized void clear() throws DBException {
1542:
1543:                if (fieldData != null) {
1544:                    DBField oneField = null;
1545:                    for (Iterator i = getMetaData().getAllFieldsMap().values()
1546:                            .iterator(); i.hasNext();) {
1547:                        oneField = (DBField) i.next();
1548:                        if (!oneField.isVirtual()) {
1549:                            // avoid use of setField() here
1550:                            //setField(oneField.getName(), "");
1551:                            if (getDef().isLoggingEnabled()) {
1552:                                // make this new method
1553:                                logChange(oneField, null);
1554:                            }
1555:
1556:                            // reset underlying hashmap
1557:                            fieldData.remove(oneField.getName());
1558:                        }
1559:                    }
1560:                }
1561:
1562:                foundKeys = null;
1563:                clearSortKeys();
1564:                customWhereClause = null;
1565:            } /* clear() */
1566:
1567:            /**
1568:             * This convenience method clears all the distinct flags of
1569:             * <code>DBFields</code> belonging to this
1570:             * <code>DBObject</code>
1571:             * <p/>
1572:             * author Peter Pilgrim <peter.pilgrim@db.com>
1573:             *
1574:             * @throws DBException upon error.
1575:             * @see #getDistinctFieldCount()
1576:             * @see #isDistinct
1577:             */
1578:            public synchronized void clearDistinctFields() throws DBException {
1579:                distinctFields = null;
1580:                anyFieldsDistinct = false;
1581:            } /* clearDistinctFields() */
1582:
1583:            /**
1584:             * This convenience method clears all the fileds to be retrieved
1585:             * <code>DBFields</code> belonging to this
1586:             * <code>DBObject</code>
1587:             * <p/>
1588:             * author Yves Henri Amaizo <amy_amaizo@compuserve.com>
1589:             *
1590:             * @throws DBException upon error.
1591:             * @see #isFieldsToRetrieve()
1592:             */
1593:            public synchronized void clearFieldsToRetrieve() throws DBException {
1594:                if (retrieveFields == null) {
1595:                    return;
1596:                } else {
1597:                    retrieveFields = null;
1598:                    anyFieldsToRetrieve = false;
1599:                }
1600:            } /* clearFieldsToRetrieve() */
1601:
1602:            /**
1603:             * See if this field value contains wild cards (e.g. pattern matching
1604:             * criteria for the database). The wild cards can be configured via the
1605:             * properties file.
1606:             *
1607:             * @param fieldValue The field value to check for wild cards
1608:             * @return True if the string does contain wild cards, False if it does not
1609:             * @throws DBException upon error.
1610:             */
1611:            protected synchronized boolean containsWildCards(String fieldValue)
1612:                    throws DBException {
1613:
1614:                return getJDBCUtil().containsWildCards(this , fieldValue);
1615:            } /* containsWildCards(String) */
1616:
1617:            /**
1618:             * Just like find, but only retrieves the count, not the records themselves.
1619:             *
1620:             * @return integer Count of the records matching the criteria
1621:             * @throws DBException If the search could not be completed
1622:             */
1623:            public synchronized int count() throws DBException {
1624:                foundKeys = null;
1625:
1626:                String sqlString = null;
1627:                ArrayList keyFields = getMetaData().getKeyFieldListArray();
1628:                FastStringBuffer myStatement = FastStringBuffer.getInstance();
1629:                try {
1630:
1631:                    //
1632:                    //Performance improvement.  If we only have ony key field, then
1633:                    //we count the field itself instead of (*).  On some databases, this
1634:                    //is significantly faster.
1635:                    //TODO: What about performance where there is more than one key?
1636:                    //
1637:                    if (keyFields == null || keyFields.size() > 1
1638:                            || keyFields.size() == 0) {
1639:                        myStatement.append("SELECT");
1640:                        myStatement.append(" COUNT(*) FROM ");
1641:                    } else {
1642:                        DataFieldMetaData keymetadata = this 
1643:                                .getFieldMetaData((String) keyFields.get(0));
1644:                        myStatement.append("SELECT ");
1645:                        myStatement.append("COUNT( " + keymetadata.getName()
1646:                                + ") FROM ");
1647:                    }
1648:
1649:                    myStatement.append(getJDBCMetaData().getTargetSQLTable(
1650:                            getDataContext()));
1651:
1652:                    if (customWhereClause != null) {
1653:                        myStatement.append(customWhereClause);
1654:                    } else {
1655:                        FastStringBuffer fsb = FastStringBuffer.getInstance();
1656:                        try {
1657:                            myStatement
1658:                                    .append(buildWhereClauseBuffer(true, fsb));
1659:                        } catch (IllegalArgumentException ex) {
1660:                            log.error("Illegal Argument Exception", ex);
1661:                            throw ex;
1662:                        } finally {
1663:                            fsb.release();
1664:                            fsb = null;
1665:                        }
1666:                    }
1667:
1668:                    sqlString = myStatement.toString();
1669:                } catch (Throwable t) {
1670:                    log.error("Error constructing count query", t);
1671:                    throw new DBException(t.getMessage());
1672:                } finally {
1673:                    myStatement.release();
1674:                    myStatement = null;
1675:                }
1676:
1677:                DBConnection myConnection = null;
1678:
1679:                try {
1680:                    if (localConnection != null) {
1681:                        myConnection = localConnection;
1682:                    } else {
1683:                        myConnection = getConnectionPool().getConnection(
1684:                                myClassName);
1685:                    }
1686:
1687:                    myConnection.execute(sqlString);
1688:
1689:                    if (myConnection.next()) {
1690:                        return myConnection.getInt(1);
1691:                    }
1692:                } catch (DBException de) {
1693:                    log.error("DBException Error", de);
1694:                    throw de;
1695:                } catch (Throwable t) {
1696:                    log.error("Error counting field", t);
1697:                    throw new DBException("Error counting field", t);
1698:                } finally {
1699:                    if (localConnection == null) {
1700:                        if (myConnection != null) {
1701:                            myConnection.release();
1702:                        }
1703:                    }
1704:                }
1705:
1706:                return 0;
1707:            } /* count() */
1708:
1709:            /**
1710:             * Delete this record from the target table. The current field values for the key
1711:             * of this object tell us which record to delete. This default version
1712:             * of delete also deletes associated detail records (e.g. performs a
1713:             * "cascading delete" of details.
1714:             *
1715:             * @throws DBException If the delete fails or if the referential integrity
1716:             *                     would be violated by the delete
1717:             * @see DBObject#deleteAll to delete objects identified by non-key fields
1718:             */
1719:            public void delete() throws DBException {
1720:                delete(true);
1721:            }
1722:
1723:            /**
1724:             * Delete this record, optionally deleting any associated detail
1725:             * records.
1726:             *
1727:             * @param deleteDetails Delete associated detail records if true
1728:             * @throws DBException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1729:             * @see DBObject#deleteAll to delete objects identified by non-key fields
1730:             */
1731:            public synchronized void delete(boolean deleteDetails)
1732:                    throws DBException {
1733:
1734:                if (!haveAllKeys()) {
1735:                    throw new DBException(
1736:                            "("
1737:                                    + myClassName
1738:                                    + getKey()
1739:                                    + ") All key fields not present - cannot delete record");
1740:                }
1741:
1742:                /* check referential integrity */
1743:                checkAllReferredToBy();
1744:
1745:                FastStringBuffer sqlCommand = new FastStringBuffer(128);
1746:                sqlCommand.append("DELETE FROM ");
1747:                sqlCommand.append(getJDBCMetaData().getTargetTable());
1748:
1749:                if (customWhereClause != null) {
1750:                    sqlCommand.append(customWhereClause);
1751:                } else {
1752:                    FastStringBuffer fsb = FastStringBuffer.getInstance();
1753:                    try {
1754:                        // does this object have key fields?
1755:                        if (getKeyFieldListIterator().hasNext()) {
1756:                            // just use key fields to identify
1757:                            sqlCommand
1758:                                    .append(buildWhereClauseBuffer(false, fsb));
1759:                        } else {
1760:                            // use all fields to identify
1761:                            sqlCommand
1762:                                    .append(buildWhereClauseBuffer(true, fsb));
1763:                        }
1764:                    } finally {
1765:                        fsb.release();
1766:                    }
1767:                }
1768:
1769:                String commandToExecute = sqlCommand.toString();
1770:                DBConnection myConnection = null;
1771:                boolean detailsRequireCommittment = false;
1772:
1773:                try {
1774:                    if (localConnection != null) {
1775:                        myConnection = localConnection;
1776:                    } else {
1777:                        myConnection = getConnectionPool().getConnection(
1778:                                myClassName);
1779:                    }
1780:
1781:                    if (myConnection == null) {
1782:                        throw new DBException("null DB connection");
1783:                    }
1784:
1785:                    if (deleteDetails) {
1786:                        /**
1787:                         * if possible, details are deleted as part of a transaction,
1788:                         * (if DB supports transactions). if part of transaction,
1789:                         *  commit will happen either
1790:                         * 1) when parent record is committed in this routine (if auto-commit is true) or
1791:                         * 2) when calling routine commits
1792:                         */
1793:                        boolean originalCommitState = myConnection
1794:                                .getAutoCommit();
1795:                        try {
1796:                            if (myConnection.supportsTransactions()) {
1797:                                if (originalCommitState) {
1798:                                    myConnection.setAutoCommit(false);
1799:                                }
1800:                                deleteDetails(myConnection);
1801:                                detailsRequireCommittment = true;
1802:
1803:                            } else {
1804:                                deleteDetails(null);
1805:                            }
1806:                        } finally {
1807:                            if (originalCommitState) {
1808:                                myConnection.setAutoCommit(originalCommitState);
1809:                            }
1810:                        }
1811:                    } /* if we delete the details */
1812:
1813:                    // this will commit details too if auto-commit is true;
1814:                    // otherwise, it is up to the calling routine to commit
1815:                    myConnection.executeUpdate(commandToExecute);
1816:
1817:                } catch (DBException e) {
1818:                    /**
1819:                     * @todo will rollback here prevent rollback by calling routine?
1820:                     */
1821:
1822:                    // roll back any details deletion if we are in control of
1823:                    // the db connection, i.e., if there was no localConnection
1824:                    // provided to us
1825:                    if (detailsRequireCommittment && localConnection == null
1826:                            && myConnection.getAutoCommit() == false) {
1827:                        myConnection.rollback();
1828:                    }
1829:
1830:                    throw ((DBException) e.fillInStackTrace());
1831:                } finally {
1832:                    if (localConnection == null) {
1833:                        myConnection.release();
1834:                    }
1835:                }
1836:
1837:                if (myConnection.getUpdateCount() == 0) {
1838:                    log.error("No record(s) deleted for SQL '"
1839:                            + sqlCommand.toString() + "'");
1840:                    throw new DBException("No record(s) deleted.");
1841:                }
1842:                if (isCached()) {
1843:                    removeFromCache(this );
1844:                }
1845:
1846:                /* Now log the change if we are logging */
1847:                if (getDef().isLoggingEnabled()) {
1848:                    ChangeLog myChangeLog = new ChangeLog();
1849:                    myChangeLog.setDataContext(getDataContext());
1850:                    myChangeLog.setField("ObjectChanged", myClassName);
1851:                    myChangeLog.setField("RecordKey", getMyKeys());
1852:                    myChangeLog.setField("Operation", EVENT_DELETE);
1853:                    myChangeLog.setField("ChangedField", "ALL");
1854:                    myChangeLog.add();
1855:
1856:                    /* We're done tracking changes */
1857:                    myUpdates = null;
1858:                } /* if */
1859:
1860:                setStatus(BaseDataObject.STATUS_DELETED);
1861:                notifyListeners(EVENT_DELETE);
1862:            } /* delete() */
1863:
1864:            /**
1865:             * Delete all of the records specified by the current search criteria.  If
1866:             * you use a blank DBObject, then all the records in the table will be
1867:             * deleted
1868:             *
1869:             * @throws DBException upon error
1870:             */
1871:            public synchronized void deleteAll() throws DBException {
1872:
1873:                // Old Memory intensive way of doing it :)
1874:                //        ArrayList list = searchAndRetrieveList();
1875:                //        for (Iterator iterator = list.iterator(); iterator.hasNext();) {
1876:                //            DBObject object = (DBObject) iterator.next();
1877:                //            object.delete();
1878:                //        }
1879:
1880:                //Build an object that we're going to query with.
1881:                DBObject testObject = newInstance();
1882:                DataObjectMetaData metadata = getMetaData();
1883:                for (Iterator i = getMetaData().getFieldListArray().iterator(); i
1884:                        .hasNext();) {
1885:                    String fieldName = (String) i.next();
1886:                    DataFieldMetaData fieldMetadata = metadata
1887:                            .getFieldMetadata(fieldName);
1888:                    if (fieldMetadata.isLongObjectType()
1889:                            || fieldMetadata.isVirtual()) {
1890:                        if (log.isDebugEnabled()) {
1891:                            log.debug("Skipping field type" + fieldName);
1892:                        }
1893:                    } else {
1894:                        // does local field have a value set?
1895:                        DataField datafield = getDataField(fieldName);
1896:                        // ignore this field if no setting
1897:                        if (!datafield.isValueSet()) {
1898:                            continue;
1899:                        }
1900:
1901:                        Object valObj = datafield.getValue();
1902:                        String valStr = null;
1903:                        if (valObj != null) {
1904:                            valStr = valObj.toString();
1905:                        }
1906:                        if (valStr != null && valStr.length() > 0) {
1907:                            // use 'raw' value in data field
1908:                            testObject.setField(fieldName, datafield.getValue()
1909:                                    .toString());
1910:                        }
1911:                    }
1912:                }
1913:
1914:                testObject.setMaxRecords(100);
1915:
1916:                //
1917:                //Iterate for each 100 items.  So we don't load more records at once
1918:                //than memory can handle
1919:                //
1920:                int num = 0;
1921:                ArrayList al = new ArrayList(100);
1922:                do {
1923:                    if (customWhereClause != null) {
1924:                        testObject.setCustomWhereClause(getCustomWhereClause()
1925:                                .substring(WHERE_KEYWORD.length()),
1926:                                appendCustomWhere);
1927:                    }
1928:                    al = testObject.searchAndRetrieveList(); // will clear custom where clause
1929:                    num = al.size();
1930:                    for (Iterator i = al.iterator(); i.hasNext();) {
1931:                        DBObject oneObject = (DBObject) i.next();
1932:                        oneObject.delete();
1933:                    }
1934:
1935:                    al.clear(); // help gc
1936:                } while (num >= 100);
1937:
1938:                //        ArrayList retrievedFieldList = new ArrayList();
1939:                //
1940:                //        DBConnection listingConnection = null;
1941:                //        try {
1942:                //            listingConnection = createAndExecuteSearch(retrievedFieldList);
1943:                //            //Only allocate if we're gonna load this record
1944:                //            DBObject myObj = newInstance();
1945:                //
1946:                //
1947:                //            while (listingConnection.next()) {
1948:                //                myObj.clear();
1949:                //                loadFromConnection(myObj,listingConnection,retrievedFieldList);
1950:                //                myObj.delete();
1951:                //            }
1952:                //        } catch (DBException de) {
1953:                //            log.error("Error performing deleteAll", de);
1954:                //            throw new DBException(de);
1955:                //        } catch (Throwable t) {
1956:                //            log.error("Error performing deleteAll", t);
1957:                //            throw new DBException("Error performing deleteAll", t);
1958:                //        } finally {
1959:                //            if (localConnection == null) {
1960:                //                if (listingConnection != null) {
1961:                //                    getConnectionPool().release(listingConnection);
1962:                //                }
1963:                //            }
1964:                //        }
1965:            }
1966:
1967:            /**
1968:             * Delete all of the records specified by the current search criteria.  If
1969:             * you use a blank DBObject, then all the records in the table will be
1970:             * deleted
1971:             *
1972:             * @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)
1973:             * @throws DBException upon error
1974:             */
1975:            public synchronized void deleteAll(boolean oneByOne)
1976:                    throws DBException {
1977:
1978:                if (oneByOne) {
1979:                    deleteAll();
1980:                } else {
1981:                    doDeleteAll(true);
1982:                }
1983:            } /* deleteAll(boolean) */
1984:
1985:            /**
1986:             * Delete all records inside the database.
1987:             * 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)
1988:             *
1989:             * @param deleteAllRecords Delete associated detail records if true
1990:             * @throws DBException Modify
1991:             * @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.
1992:             * @see DBObject#deleteAll to delete objects identified by non-key fields
1993:             */
1994:            private synchronized void doDeleteAll(boolean deleteAllRecords)
1995:                    throws DBException {
1996:
1997:                FastStringBuffer sqlCommand = new FastStringBuffer(128);
1998:                sqlCommand.append("DELETE FROM ");
1999:                sqlCommand.append(getJDBCMetaData().getTargetSQLTable(
2000:                        getDataContext()));
2001:
2002:                String whereClause;
2003:
2004:                if (customWhereClause != null) {
2005:                    if (appendCustomWhere) {
2006:                        whereClause = buildWhereClause(true)
2007:                                + customWhereClause;
2008:                        appendCustomWhere = false;
2009:                    } else {
2010:                        whereClause = customWhereClause;
2011:                    }
2012:                    sqlCommand.append(whereClause);
2013:                    customWhereClause = null;
2014:                } else {
2015:                    FastStringBuffer fsb = FastStringBuffer.getInstance();
2016:                    try {
2017:                        // does this object have key fields?
2018:                        if (this .getKeyFieldListIterator().hasNext()) {
2019:                            // just use key fields to identify
2020:                            sqlCommand
2021:                                    .append(buildWhereClauseBuffer(false, fsb));
2022:                        } else {
2023:                            // use all fields to identify
2024:                            sqlCommand
2025:                                    .append(buildWhereClauseBuffer(true, fsb));
2026:                        }
2027:                    } finally {
2028:                        fsb.release();
2029:                    }
2030:                }
2031:
2032:                String commandToExecute = sqlCommand.toString();
2033:                DBConnection myConnection = null;
2034:
2035:                try {
2036:                    if (localConnection != null) {
2037:                        myConnection = localConnection;
2038:                    } else {
2039:                        myConnection = this .getConnectionPool().getConnection(
2040:                                this .myClassName);
2041:                    }
2042:
2043:                    if (myConnection == null) {
2044:                        throw new DBException("null DB connection");
2045:                    }
2046:
2047:                    // this will commit details too if auto-commit is true;
2048:                    // otherwise, it is up to the calling routine to commit
2049:                    myConnection.executeUpdate(commandToExecute);
2050:
2051:                } catch (DBException e) {
2052:                    /**
2053:                     * @todo will rollback here prevent rollback by calling routine?
2054:                     */
2055:
2056:                    throw ((DBException) e.fillInStackTrace());
2057:                } finally {
2058:                    if (localConnection == null) {
2059:                        myConnection.release();
2060:                    }
2061:                }
2062:
2063:                if (myConnection.getUpdateCount() == 0) {
2064:                    log.debug("No record(s) deleted for SQL '"
2065:                            + sqlCommand.toString() + "'");
2066:                    //			throw new DBException("No record(s) deleted.");
2067:                }
2068:                //		if (isCached()) {
2069:                //			removeFromCache(this);
2070:                //		}
2071:
2072:                /* Now log the change if we are logging */
2073:                if (getDef().isLoggingEnabled()) {
2074:                    ChangeLog myChangeLog = new ChangeLog();
2075:                    myChangeLog.setDataContext(getDataContext());
2076:                    myChangeLog.setField("ObjectChanged", this .myClassName);
2077:                    myChangeLog.setField("RecordKey", this .getMyKeys());
2078:                    myChangeLog.setField("Operation", "D");
2079:                    myChangeLog.setField("ChangedField", "ALL");
2080:                    myChangeLog.add();
2081:
2082:                    /* We're done tracking changes */
2083:                    myUpdates = null;
2084:                } /* if */
2085:
2086:                //		setStatus(BaseDataObject.STATUS_DELETED);
2087:                //		notifyListeners("D");
2088:            } /* doDeleteAll() */
2089:
2090:            /**
2091:             * If this DB object has associated detail objects,
2092:             * locate the appropriate related detail records and
2093:             * delete them as well. This is a "cascading delete" process.
2094:             *
2095:             * @param detailConnection the DBConnection to use while retrieving details
2096:             * @throws DBException upon error.
2097:             */
2098:            protected void deleteDetails(DBConnection detailConnection)
2099:                    throws DBException {
2100:                String oneDet = null;
2101:
2102:                for (Enumeration ee = getDetails(); ee.hasMoreElements();) {
2103:                    oneDet = (String) ee.nextElement();
2104:
2105:                    DBObject detailObj = null;
2106:
2107:                    try {
2108:                        detailObj = (DBObject) ClassLocator.loadClass(oneDet)
2109:                                .newInstance();
2110:                    } catch (Exception e) {
2111:                        throw new DBException("Unable to instantiate "
2112:                                + "detail db object '" + oneDet + "'", e);
2113:                    }
2114:                    copyAttributes(detailObj); // copy userId, data context
2115:                    // use given connection
2116:                    if (detailConnection != null) {
2117:                        detailObj.setConnection(detailConnection);
2118:                    } else {
2119:                        detailObj.setDataContext(getDataContext());
2120:                    }
2121:
2122:                    checkDeleteDetailPerm(detailObj);
2123:
2124:                    StringTokenizer stkLocal = new StringTokenizer(
2125:                            getJDBCMetaData().getDetailFieldsLocal(oneDet), "|");
2126:                    StringTokenizer stkForeign = new StringTokenizer(
2127:                            getJDBCMetaData().getDetailFieldsForeign(oneDet),
2128:                            "|");
2129:
2130:                    boolean foundValues = false;
2131:                    while (stkLocal.hasMoreTokens()) {
2132:                        String localField = stkLocal.nextToken();
2133:                        String foreignField = stkForeign.nextToken();
2134:
2135:                        // does local field have a value set?
2136:                        DataField datafield = getDataField(localField);
2137:                        if (datafield == null || !datafield.isValueSet()) {
2138:                            continue;
2139:                        }
2140:
2141:                        // only set field if we have a value
2142:                        Object valObj = datafield.getValue();
2143:                        String value = null;
2144:                        if (valObj != null) {
2145:                            value = valObj.toString();
2146:                        }
2147:                        if (value != null && value.length() > 0) {
2148:                            detailObj.setField(foreignField, value);
2149:                            foundValues = true;
2150:                        }
2151:                    }
2152:
2153:                    // do not delete if empty, since all the items in the detail table would be deleted
2154:                    // if we have no criteria for a detail
2155:                    if (foundValues) {
2156:                        try {
2157:                            detailObj.deleteAll();
2158:                        } catch (DBException ex) {
2159:                            log.error("Error deleting detail object", ex);
2160:                        }
2161:                    }
2162:                } /* for each detail */
2163:
2164:            } /* deleteDetails */
2165:
2166:            /**
2167:             * extracted for subclasses checking
2168:             *
2169:             * @param obj the dbobject to check it's permission
2170:             * @throws DBException upon error
2171:             */
2172:            protected void checkDeleteDetailPerm(DBObject obj)
2173:                    throws DBException {
2174:                obj.checkAllowed("D");
2175:            }
2176:
2177:            /**
2178:             * Does a given field value denote a range?
2179:             *
2180:             * @param fieldValue The field value to check against.
2181:             * @return The "range" string if the value starts with a range indicator, null if not
2182:             */
2183:            protected String denotesRange(String fieldValue) {
2184:                return rangeVerifier.denotesRange(fieldValue);
2185:            } /* denotesRange(String) */
2186:
2187:            /**
2188:             * A lot like retrieve, but works with any fields, not just the key field. Does not
2189:             * throw an exception if the record is not found, just returns false.
2190:             * Finds only first record matching the criteria. The current fields in this object
2191:             * are populated with the data in the record found after the call to find() if the
2192:             * find is successful.
2193:             *
2194:             * @return boolean If the search resulted in a record
2195:             * @throws DBException If the search could not be completed
2196:             *                     Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
2197:             */
2198:            public boolean find() throws DBException {
2199:
2200:                //
2201:                //If we have all the keys, then use retrieve which is much faster
2202:                //than find.
2203:                //
2204:                boolean haveAllKeys = false;
2205:                com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData metadata = getJDBCMetaData();
2206:
2207:                ArrayList keyfields = metadata.getKeyFieldListArray();
2208:                int keysize = keyfields.size();
2209:                for (int i = 0; i < keysize; i++) {
2210:                    String oneFieldName = (String) keyfields.get(i);
2211:                    DataField df = getDataField(oneFieldName);
2212:                    if (df == null || df.isNull()) {
2213:                        haveAllKeys = false;
2214:                        break;
2215:                    }
2216:                    haveAllKeys = true;
2217:                }
2218:
2219:                //        for (Iterator i = getAllKeysIterator(); i.hasNext();) {
2220:                //            DBField oneField = (DBField) i.next();
2221:                //
2222:                //            DataField df = getDataField(oneField.getName());
2223:                //            if (df == null || df.isNull()) {
2224:                //                haveAllKeys = false;
2225:                //                break;
2226:                //            }
2227:                //            haveAllKeys = true;
2228:                //        }
2229:
2230:                //Release the reference.
2231:                keyfields = null;
2232:
2233:                if (haveAllKeys && (!anyFieldsDistinct)) {
2234:                    try {
2235:                        retrieve();
2236:                        return true;
2237:                    } catch (DBRecordNotFoundException ex) {
2238:                        return false;
2239:                    }
2240:                }
2241:
2242:                boolean needComma = false;
2243:                DataFieldMetaData oneField = null;
2244:                foundKeys = null;
2245:
2246:                //Pre-allocate to the size of the field list.
2247:                ArrayList retrievedFieldList = new ArrayList(metadata
2248:                        .getFieldListArray().size());
2249:                FastStringBuffer myStatement = FastStringBuffer.getInstance();
2250:                String sqlStatement = null;
2251:                try {
2252:                    myStatement.append("SELECT ");
2253:
2254:                    if (anyFieldsDistinct) {
2255:                        String oneFieldName = null;
2256:                        myStatement.append(" "
2257:                                + getConnectionPool()
2258:                                        .getDistinctRowsetKeyword() + " ");
2259:
2260:                        for (Iterator i = getDistinctFieldArrayList()
2261:                                .iterator(); i.hasNext();) {
2262:                            oneFieldName = (String) i.next();
2263:                            retrievedFieldList.add(oneFieldName);
2264:
2265:                            if (needComma) {
2266:                                myStatement.append(", ");
2267:                            }
2268:
2269:                            myStatement.append(selectFieldString(oneFieldName));
2270:                            needComma = true;
2271:                        }
2272:                    } else if (anyFieldsToRetrieve) { /* for */
2273:                        String oneFieldName = null;
2274:
2275:                        for (Iterator i = getFieldsToRetrieveIterator(); i
2276:                                .hasNext();) {
2277:                            oneFieldName = (String) i.next();
2278:
2279:                            if (needComma) {
2280:                                myStatement.append(", ");
2281:                            }
2282:
2283:                            retrievedFieldList.add(oneFieldName);
2284:                            myStatement.append(selectFieldString(oneFieldName));
2285:                            needComma = true;
2286:                        }
2287:                    } else { /* for each field */
2288:                        for (Iterator i = metadata.getAllFieldsMap().values()
2289:                                .iterator(); i.hasNext();) {
2290:                            oneField = (DBField) i.next();
2291:
2292:                            if (!oneField.isVirtual()
2293:                                    && !oneField.isBinaryObjectType()) {
2294:                                if (needComma) {
2295:                                    myStatement.append(", ");
2296:                                }
2297:
2298:                                retrievedFieldList.add(oneField.getName());
2299:                                myStatement.append(selectFieldString(oneField
2300:                                        .getName()));
2301:                                needComma = true;
2302:                            } /* if field is not virtual */
2303:
2304:                        } /* for */
2305:                    }
2306:
2307:                    myStatement.append(" FROM ");
2308:                    myStatement.append(metadata.getTargetSQLTable(this 
2309:                            .getDataContext()));
2310:
2311:                    if (customWhereClause != null) {
2312:                        myStatement.append(customWhereClause);
2313:                        customWhereClause = null;
2314:                    } else {
2315:                        FastStringBuffer fsb = FastStringBuffer.getInstance();
2316:                        try {
2317:                            myStatement
2318:                                    .append(buildWhereClauseBuffer(true, fsb));
2319:                        } finally {
2320:                            fsb.release();
2321:                        }
2322:                    }
2323:
2324:                    sqlStatement = myStatement.toString();
2325:                } finally {
2326:                    myStatement.release();
2327:                    myStatement = null;
2328:
2329:                }
2330:
2331:                DBConnection myConnection = null;
2332:
2333:                try {
2334:                    if (localConnection != null) {
2335:                        myConnection = localConnection;
2336:                    } else {
2337:                        myConnection = getConnectionPool().getConnection(
2338:                                myClassName);
2339:                    }
2340:
2341:                    myConnection.execute(sqlStatement);
2342:
2343:                    String oneFieldName = null;
2344:                    String oneFieldValue = ("");
2345:                    if (foundKeys == null) {
2346:                        //Pre-allocate to the size of the key fields for this object.
2347:                        foundKeys = new ArrayList(metadata
2348:                                .getKeyFieldListArray().size());
2349:                    }
2350:                    if (myConnection.next()) {
2351:                        String theKeyString = null;
2352:                        FastStringBuffer oneKeyString = FastStringBuffer
2353:                                .getInstance();
2354:                        try {
2355:
2356:                            int i = 1;
2357:
2358:                            for (Iterator it = retrievedFieldList.iterator(); it
2359:                                    .hasNext();) {
2360:                                oneFieldName = (String) 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(
2368:                                                myConnection, oneField
2369:                                                        .getName());
2370:                                    } else {
2371:                                        //								if (!oneField.isLongBinaryType() && !oneField.isLongCharacterType()) {
2372:                                        //									if (myConnection.isStringNotTrimmed()) {
2373:                                        //										tmpData =  myConnection.getStringNoTrim(oneFieldName);
2374:                                        //									} else {
2375:                                        //										tmpData = myConnection.getString(oneFieldName);
2376:                                        //									}
2377:                                        //								} else {
2378:                                        //									if (oneField.isLongBinaryType()) {
2379:                                        //										tmpData = null;
2380:                                        //										InputStream is = myConnection.getBinaryStream(oneFieldName);
2381:                                        //										if (is != null) {
2382:                                        //											byte[] bstr = new byte[LONGBINARY_READ_DEFAULT_SIZE];
2383:                                        //											int j = is.read(bstr);
2384:                                        //											if (j > 0) {
2385:                                        //												byte[] content = new byte[j];
2386:                                        //												System.arraycopy(bstr, 0, content, 0, j);
2387:                                        //												tmpData = content;
2388:                                        //											}
2389:                                        //										}
2390:                                        //									} else {
2391:                                        //										tmpData = myConnection.getStringNoTrim(oneFieldName);
2392:                                        //									  }
2393:                                        //								}
2394:
2395:                                        if (myConnection.isStringNotTrim()) {
2396:                                            oneFieldValue = myConnection
2397:                                                    .getStringNoTrim(i);
2398:                                        } else {
2399:                                            oneFieldValue = myConnection
2400:                                                    .getString(i);
2401:                                        }
2402:                                    }
2403:                                } catch (DBException de1) {
2404:                                    throw new DBException("(" + myClassName
2405:                                            + ") Error retrieving field '"
2406:                                            + oneFieldName + "' index " + i
2407:                                            + ":" + de1.getMessage(), de1
2408:                                            .getDBMessage());
2409:                                } catch (Exception e1) {
2410:                                    throw new DBException("("
2411:                                            + this .myClassName
2412:                                            + ") Error retrieving field '"
2413:                                            + oneFieldName + "' index " + i
2414:                                            + ":" + e1.getMessage(), e1
2415:                                            .getMessage());
2416:                                }
2417:
2418:                                i++;
2419:                                setField(oneFieldName, oneFieldValue);
2420:                                //                        set(oneFieldName, tmpData);
2421:
2422:                                if (oneField.isKey()) {
2423:                                    if (i > 1) {
2424:                                        oneKeyString.append("/");
2425:                                    }
2426:
2427:                                    oneKeyString.append(oneFieldValue);
2428:                                } /* if field is key */
2429:
2430:                            }
2431:                            theKeyString = oneKeyString.toString();
2432:                        } finally {
2433:                            oneKeyString.release();
2434:                            oneKeyString = null;
2435:                        }
2436:
2437:                        foundKeys.add(theKeyString);
2438:                    } else { /* for each field */
2439:                        return false;
2440:                    }
2441:                    if (getDef().isLoggingEnabled()) {
2442:                        myUpdates = null;
2443:                    }
2444:
2445:                    cacheIsChangedComparison();
2446:                    setStatus(BaseDataObject.STATUS_CURRENT);
2447:
2448:                    if (isCached() && !anyFieldsToRetrieve) {
2449:                        //Doesn't stale anything, drop it in the cache as is.
2450:                        cacheUtils.addUnmodifiedToCache(this );
2451:                    }
2452:
2453:                    return true;
2454:                } catch (NullPointerException npe) {
2455:                    npe.printStackTrace();
2456:                    throw npe;
2457:                } catch (ClassCastException cce) {
2458:                    log.error("Fatal Error in find: " + cce.getMessage(), cce);
2459:                    throw cce;
2460:                } catch (DBException de) {
2461:                    de.printStackTrace();
2462:                    log.error("Error in find: " + de.getMessage(), de);
2463:                    throw de;
2464:                } finally {
2465:                    if (localConnection == null) {
2466:                        if (myConnection != null) {
2467:                            myConnection.release();
2468:                        }
2469:                    }
2470:                }
2471:            } /* find() */
2472:
2473:            /**
2474:             * Return a string we can use in error messages to indicate the record
2475:             * that had the problem by including the key and the current database/context
2476:             *
2477:             * @return String: A formatted string showing the key of this record
2478:             */
2479:            public String forKey() {
2480:
2481:                FastStringBuffer keyString = FastStringBuffer.getInstance();
2482:                String returnValue = null;
2483:                try {
2484:                    keyString.append(" for record with key '");
2485:                    boolean needSlash = false;
2486:                    boolean blankKey = true;
2487:
2488:                    try {
2489:                        for (Iterator i = getJDBCMetaData()
2490:                                .getKeyFieldListArray().iterator(); i.hasNext();) {
2491:                            String fieldValue = getField((String) i.next());
2492:
2493:                            if (fieldValue == null || fieldValue.length() == 0) {
2494:                                continue;
2495:                            }
2496:
2497:                            blankKey = false;
2498:                            if (needSlash) {
2499:                                keyString.append("/");
2500:                            }
2501:                            keyString.append(fieldValue);
2502:
2503:                            needSlash = true;
2504:                        }
2505:                    } catch (DBException de) {
2506:                        keyString.append(" -- UNABLE TO GET FIELD LIST --");
2507:                        log.error(de);
2508:                    }
2509:
2510:                    if (blankKey) {
2511:                        return "";
2512:                    }
2513:
2514:                    keyString
2515:                            .append("' in database '" + getDataContext() + "'");
2516:                    returnValue = keyString.toString();
2517:                } finally {
2518:                    keyString.release();
2519:                }
2520:
2521:                return returnValue;
2522:            } /* forKey() */
2523:
2524:            /**
2525:             * Given the value of a date/time or date/time field, return the value formatted
2526:             * as appropriate for the current DBMS. Can be configured using property
2527:             * file values.
2528:             *
2529:             * @param fieldName java.lang.String The value for the date/time field.
2530:             * @return java.lang.String The formatted date time, ready for use in the DBMS
2531:             * @throws DBException upon error.
2532:             */
2533:            public String formatDateTime(String fieldName) throws DBException {
2534:                return JDBCUtil.getInstance().formatDateTime(this , fieldName);
2535:            } /* formatDateTime(String) */
2536:
2537:            /**
2538:             * Return an "attribute". Attributes are temporary (e.g. not stored in the DBMS)
2539:             * values associated with this particular DB object instance.
2540:             *
2541:             * @param attribName The attribute name to check
2542:             * @return the object associated with this attribute
2543:             */
2544:            public Object getAttribute(String attribName) {
2545:                if (attributes != null) {
2546:                    return attributes.get(attribName);
2547:                } else {
2548:                    return null;
2549:                }
2550:            } /* getAttribute(String) */
2551:
2552:            /**
2553:             * Get an iterator for all of the attributes specified for a field
2554:             *
2555:             * @param fieldName The field name to get the attirbutes for
2556:             * @return the Iterator for all attributes associated with this field
2557:             * @throws DBException upon error.
2558:             */
2559:            public Iterator getAttributesIterator(String fieldName)
2560:                    throws DBException {
2561:                return getDef().getAttributesIterator(fieldName);
2562:            }
2563:
2564:            /**
2565:             * Gets the set size of the cache for this DBOBject
2566:             *
2567:             * @return The number of cache objects available for this object.
2568:             */
2569:            public int getCacheSize() {
2570:
2571:                /* Special case for the DBObjLimit object */
2572:                if (myClassName.equals(DBObjLimit.class.getName())) {
2573:                    return 0;
2574:                }
2575:                if (myCacheSize == -2) {
2576:                    if (log.isDebugEnabled()) {
2577:                        log.debug("Cache size not set for " + myClassName
2578:                                + " - setting");
2579:                    }
2580:
2581:                    setCacheSize();
2582:                }
2583:                if (log.isDebugEnabled()) {
2584:                    log.debug("Cache size for " + myClassName + " is "
2585:                            + myCacheSize);
2586:                }
2587:
2588:                return myCacheSize;
2589:            } /* getCacheSize() */
2590:
2591:            /**
2592:             * @return a HashMap of all cache stats
2593:             */
2594:            public synchronized static HashMap getCacheStatsMap() {
2595:                return new HashMap(sCacheStats);
2596:            }
2597:
2598:            /**
2599:             * Returns the metadata for the specified field.
2600:             *
2601:             * @param fieldName The name of the field to get
2602:             * @return The DBField for this fieldName
2603:             */
2604:            public DataFieldMetaData getFieldMetaData(String fieldName) {
2605:                StringUtil.assertNotBlank(fieldName,
2606:                        "Field name may not be blank");
2607:                DBField oneField = (DBField) getMetaData().getFieldMetadata(
2608:                        fieldName);
2609:
2610:                if (oneField == null) {
2611:                    throw new IllegalArgumentException("(" + myClassName
2612:                            + ") No such field as '" + fieldName
2613:                            + "' in object '" + myClassName + "'");
2614:                }
2615:
2616:                return oneField;
2617:            } /* getDBField(String) */
2618:
2619:            /**
2620:             * Return the name of the context/database connection that this DB object is using.
2621:             * If none is set, then we are using the "default" database/context.
2622:             *
2623:             * @return a String containing the name of the DBName to use.
2624:             * @deprecated since Expresso 5.1
2625:             *             Use getDataContext() instead
2626:             */
2627:            public synchronized String getDBName() {
2628:                return getDataContext();
2629:            } /* getDBName() */
2630:
2631:            /**
2632:             * Get a list of all db objects that are specified as being "details"
2633:             * to this db object. Returns an empty enumeration if there are no details
2634:             *
2635:             * @return java.util.Enumeration containing all detail objects
2636:             * @throws DBException upon error.
2637:             */
2638:            public Enumeration getDetails() throws DBException {
2639:                return getDef().getDetails();
2640:            }
2641:
2642:            /**
2643:             * This convenience method counts
2644:             * <code>DBFields</code> belonging to this
2645:             * <code>DBObject</code> that are set to <b>distinct</b>.
2646:             *
2647:             * @return int number of distinct fields.
2648:             *         <p/>
2649:             *         author Peter Pilgrim <peter.pilgrim@db.com>
2650:             * @throws DBException upon error.
2651:             */
2652:            public synchronized int getDistinctFieldCount() throws DBException {
2653:                if (distinctFields == null) {
2654:                    return 0;
2655:                }
2656:
2657:                int num = 0;
2658:
2659:                for (Iterator i = getMetaData().getAllFieldsMap().values()
2660:                        .iterator(); i.hasNext();) {
2661:                    DBField oneField = (DBField) i.next();
2662:
2663:                    if (distinctFields.containsKey(oneField.getName())) {
2664:                        ++num;
2665:                    }
2666:                }
2667:
2668:                return (num);
2669:            } /* getDistinctFieldCount() */
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[] getDistinctFields() throws DBException {
2688:                ArrayList al = new ArrayList();
2689:
2690:                if (distinctFields == null) {
2691:                    return (String[]) al.toArray();
2692:                }
2693:                for (Iterator it = getMetaData().getFieldListArray().iterator(); it
2694:                        .hasNext();) {
2695:                    String fieldName = (String) 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[]) al.toArray();
2709:            } /* getDistinctFields() */
2710:
2711:            /**
2712:             * Get the string value of a field in this object as a string
2713:             *
2714:             * @param fieldName Name of the field to fetch
2715:             * @return The value of the given field as a string - if the field is null,
2716:             *         an empty string is returned.
2717:             * @throws DBException If there is no such field or it's value    cannot be accessed
2718:             */
2719:            public synchronized String getField(String fieldName)
2720:                    throws DBException {
2721:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
2722:
2723:                if (oneField == null) {
2724:                    throw new DBException("(" + myClassName
2725:                            + ") No such field as '" + fieldName + "'");
2726:                }
2727:                if (oneField.isVirtual()) {
2728:                    throw new DBException(
2729:                            "("
2730:                                    + myClassName
2731:                                    + ") Field "
2732:                                    + fieldName
2733:                                    + " is a virtual field. Database object should extend "
2734:                                    + "getField method to handle requests for this field");
2735:                }
2736:
2737:                String returnValue = StringUtil.notNull(getFieldData(oneField
2738:                        .getName()));
2739:
2740:                if (returnValue.length() == 0) {
2741:                    return returnValue;
2742:                }
2743:
2744:                if (oneField.isBooleanType()) {
2745:
2746:                    if (returnValue.equals("true") || returnValue.equals("t")
2747:                            || returnValue.equalsIgnoreCase("y")
2748:                            || returnValue.equals("1")) {
2749:                        return "true";
2750:                    } else {
2751:                        return "false";
2752:                    }
2753:                }
2754:
2755:                /**
2756:                 * If the record is not yet stored in the database, don't filter the
2757:                 * value being returned - it might be a search criteria
2758:                 */
2759:                if (getStatus().equals(BaseDataObject.STATUS_NEW)) {
2760:                    return returnValue;
2761:                }
2762:
2763:                Class filterclass = getFilterClass(); // instance setting
2764:                if (filterclass == null) {
2765:                    // use static field info
2766:                    filterclass = ((DBField) oneField).getFilterClass();
2767:                }
2768:
2769:                try {
2770:                    return getDef().getFilterManager().filterString(
2771:                            returnValue, filterclass,
2772:                            ((DBField) oneField).getFilterMethod());
2773:                } catch (Exception ex) {
2774:                    throw new DBException("(" + myClassName
2775:                            + ") Error Filtering Field: " + fieldName, ex);
2776:                }
2777:            } /* getField(String) */
2778:
2779:            /**
2780:             * Internal mechanism for getting the raw field data
2781:             *
2782:             * @param fieldName the name of the field to retrieve the data for
2783:             * @return java.lang.String or null if the fieldData doesn't exist in the map
2784:             */
2785:            protected String getFieldData(String fieldName) {
2786:                if (fieldData == null) {
2787:                    return null;
2788:                }
2789:
2790:                DataField df = (DataField) fieldData.get(fieldName);
2791:                if (df == null) {
2792:                    return null;
2793:                }
2794:
2795:                Object o = df.getValue();
2796:                if (o == null) {
2797:                    return null;
2798:                }
2799:
2800:                return o.toString();
2801:            }
2802:
2803:            /**
2804:             * Boolean typesafe getField
2805:             *
2806:             * @param fieldName to retrieve
2807:             * @return boolean true false
2808:             * @throws DBException upon error.
2809:             * @todo - Do databases localize "true and false" values in strings?
2810:             */
2811:            public boolean getFieldBoolean(String fieldName) throws DBException {
2812:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
2813:
2814:                if (oneField == null) {
2815:                    throw new DBException("(" + myClassName
2816:                            + ") No such field as " + fieldName);
2817:                }
2818:
2819:                if (oneField.getAttribute("checkbox") == null
2820:                        && !oneField.isBooleanType()) {
2821:                    throw new DBException("(" + myClassName + ") Field "
2822:                            + fieldName + " is not a boolean field");
2823:                }
2824:
2825:                String strVal = getFieldData(oneField.getName());
2826:
2827:                if (strVal == null) {
2828:                    return false;
2829:                }
2830:
2831:                if (strVal.equals("true") || strVal.equals("t")
2832:                        || strVal.equalsIgnoreCase("y") || strVal.equals("1")) {
2833:                    return true;
2834:                } else {
2835:                    return false;
2836:                }
2837:            }
2838:
2839:            /**
2840:             * Return the value of a field as a Date object
2841:             *
2842:             * @param fieldName The field to be retrieved
2843:             * @return The Date object equivilant to this field's value
2844:             * @throws DBException If the field does not exist or it's value
2845:             *                     is not a date or cannot be converted to a date
2846:             */
2847:            public java.util.Date getFieldDate(String fieldName)
2848:                    throws DBException {
2849:                return getJDBCUtil().getDateField(this , fieldName);
2850:
2851:            } /* getFieldDate(String) */
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 getFieldDecimalFormatted(String fieldName,
2883:                    String formatPattern) throws DBException {
2884:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
2885:
2886:                if (oneField == null) {
2887:                    throw new DBException("(" + myClassName
2888:                            + ") No such field as " + fieldName);
2889:                }
2890:
2891:                String strVal = getFieldData(oneField.getName());
2892:
2893:                if (strVal == null) {
2894:                    return null;
2895:                }
2896:                if (strVal.equals("")) {
2897:                    return null;
2898:                }
2899:
2900:                Double myDouble = new Double(strVal);
2901:                DecimalFormat df = null;
2902:
2903:                if (formatPattern == null) {
2904:                    df = new DecimalFormat();
2905:                } else {
2906:                    df = new DecimalFormat(formatPattern);
2907:                }
2908:
2909:                String returnValue = df.format(myDouble);
2910:
2911:                return returnValue;
2912:            } /* getFieldDecimalFormatted(String, String) */
2913:
2914:            /**
2915:             * Get the primitive <code>float</code> value of a field in this object. we use the locale specified
2916:             * in the properties file directly to determine how to parse the field
2917:             *
2918:             * @param fieldName Name of the field to be retrieved
2919:             * @return float The value of the field as a float
2920:             * @throws DBException If there is no such field or it's value
2921:             *                     cannot be converted to a float
2922:             * @see #setField(String,double)
2923:             */
2924:            public float getFieldFloat(String fieldName) throws DBException {
2925:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
2926:                String strVal = getFieldData(oneField.getName());
2927:                if (strVal == null) {
2928:                    return 0;
2929:                }
2930:
2931:                Locale myLocale = Locale.getDefault();
2932:
2933:                try {
2934:                    myLocale = new Locale(ConfigManager.getContext(
2935:                            getDataContext()).getLanguage(), ConfigManager
2936:                            .getContext(getDataContext()).getCountry());
2937:                } catch (ConfigurationException ce) {
2938:                    throw new DBException(ce);
2939:                }
2940:
2941:                NumberFormat formatter = NumberFormat.getInstance(myLocale);
2942:                Float returnFloat = null;
2943:
2944:                try {
2945:                    returnFloat = new Float(formatter.parse(strVal)
2946:                            .floatValue());
2947:                } catch (IllegalArgumentException ie) {
2948:                    throw new DBException("(" + myClassName
2949:                            + ") Unable to parse a float value from field '"
2950:                            + fieldName + "' which contained '" + strVal + "'",
2951:                            ie);
2952:                } catch (ParseException pe) {
2953:                    throw new DBException("(" + myClassName
2954:                            + ") Unable to parse a float value from field '"
2955:                            + fieldName + "' which contained '" + strVal + "'",
2956:                            pe);
2957:                }
2958:                if (returnFloat == null) {
2959:                    throw new DBException("(" + myClassName
2960:                            + ") Unable to get float value from field '"
2961:                            + fieldName + "', value was '" + strVal + "'");
2962:                }
2963:
2964:                return returnFloat.floatValue();
2965:            } /* getFieldFloat(String) */
2966:
2967:            /**
2968:             * Get the primitive <code>double</code> value of a field in this object. we use the locale specified
2969:             * in the properties file directly to determine how to parse the field
2970:             *
2971:             * @param fieldName Name of the field to be retrieved
2972:             * @return float The value of the field as a float
2973:             * @throws DBException If there is no such field or it's value
2974:             *                     cannot be converted to a float
2975:             * @see #setField(String,double)
2976:             */
2977:            public double getFieldDouble(String fieldName) throws DBException {
2978:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
2979:                String strVal = getFieldData(oneField.getName());
2980:                if (strVal == null) {
2981:                    return 0;
2982:                }
2983:
2984:                Locale myLocale = Locale.getDefault();
2985:
2986:                try {
2987:                    myLocale = new Locale(ConfigManager.getContext(
2988:                            getDataContext()).getLanguage(), ConfigManager
2989:                            .getContext(getDataContext()).getCountry());
2990:                } catch (ConfigurationException ce) {
2991:                    throw new DBException(ce);
2992:                }
2993:
2994:                NumberFormat formatter = NumberFormat.getInstance(myLocale);
2995:                Double returnDouble = null;
2996:
2997:                try {
2998:                    returnDouble = new Double(formatter.parse(strVal)
2999:                            .doubleValue());
3000:                } catch (IllegalArgumentException ie) {
3001:                    throw new DBException("(" + getClass().getName()
3002:                            + ") Unable to parse a double value from field '"
3003:                            + fieldName + "' which contained '" + strVal + "'",
3004:                            ie);
3005:                } catch (ParseException pe) {
3006:                    throw new DBException("(" + getClass().getName()
3007:                            + ") Unable to parse a double value from field '"
3008:                            + fieldName + "' which contained '" + strVal + "'",
3009:                            pe);
3010:                }
3011:                if (returnDouble == null) {
3012:                    throw new DBException("(" + getClass().getName()
3013:                            + ") Unable to get double value from field '"
3014:                            + fieldName + "', value was '" + strVal + "'");
3015:                }
3016:
3017:                return returnDouble.doubleValue();
3018:            } /* getFieldDouble(String) */
3019:
3020:            /**
3021:             * Get the <code>BigDecimal</code> value of a field in this object.
3022:             * We use the locale specified
3023:             * in the properties file directly to determine how to parse the field
3024:             * <p/>
3025:             * <p/>
3026:             * NB: Corresponds to <code>java.sql.Type.DECIMAL</code> or <code>java.sql.Type.NUMERIC</code>
3027:             * </p>
3028:             *
3029:             * @param fieldName Name of the field to be retrieved
3030:             * @return BigDecimal The value of the field as a BigDecimal object
3031:             * @throws DBException If there is no such field or it's value
3032:             *                     cannot be converted to a BigDecimal
3033:             *                     <p/>
3034:             *                     author Peter Pilgrim <peterp@xenonsoft.demon.co.uk>
3035:             * @see "'JDBC API Tutorial and Reference', Second Edition, pg 944, by Catell, Hamilton et al; published by Addison Wesley"
3036:             * @see #setField(String,BigDecimal)
3037:             */
3038:            public BigDecimal getFieldBigDecimal(String fieldName)
3039:                    throws DBException {
3040:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3041:                String strVal = getFieldData(oneField.getName());
3042:                if (strVal == null) {
3043:                    return BIG_DECIMAL_ZERO;
3044:                }
3045:
3046:                BigDecimal returnDecimal = null;
3047:
3048:                try {
3049:                    returnDecimal = new BigDecimal(strVal);
3050:                } catch (NumberFormatException nfe) {
3051:                    throw new DBException(
3052:                            "("
3053:                                    + getClass().getName()
3054:                                    + ") Unable to parse a BigDecimal value from field '"
3055:                                    + fieldName + "' which contained '"
3056:                                    + strVal + "'", nfe);
3057:                }
3058:                if (returnDecimal == null) {
3059:                    throw new DBException("(" + getClass().getName()
3060:                            + ") Unable to get BigDecimal value from field '"
3061:                            + fieldName + "', value was '" + strVal + "'");
3062:                }
3063:
3064:                return returnDecimal;
3065:            } /* getFieldBigDecimal(String) */
3066:
3067:            /**
3068:             * Get the primitive <code>byte</code> value of a field in this object.
3069:             * A convenience method for <code>getField</code>
3070:             *
3071:             * @param fieldName Name of a field in this object
3072:             * @return int  The value of the field as a int
3073:             * @throws DBException if there is no such field or it's value cannot be
3074:             *                     converted to a byte integer.
3075:             * @see #setField(String,byte)
3076:             */
3077:            public byte getFieldByte(String fieldName) throws DBException {
3078:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3079:                String strVal = "";
3080:                try {
3081:                    strVal = getFieldData(oneField.getName());
3082:                    if (strVal == null) {
3083:                        return 0;
3084:                    }
3085:                    return Byte.parseByte(strVal);
3086:                } catch (NumberFormatException ex) {
3087:                    throw new DBException(
3088:                            "("
3089:                                    + myClassName
3090:                                    + ") Unable to parse an byte integer value from field "
3091:                                    + fieldName + " which contained '" + strVal
3092:                                    + "'", ex);
3093:                }
3094:            }
3095:
3096:            /**
3097:             * Get the primitive <code>byteArray</code> value of a field in this object.
3098:             * A convenience method for <code>getField</code>
3099:             *
3100:             * @param fieldName Name of a field in this object
3101:             * @return int  The value of the field as a byte{]
3102:             * @throws DBException if there is no such field or it's value cannot be
3103:             *                     get to a byte array.
3104:             * @see #setField(String,byte[])
3105:             */
3106:            public byte[] getFieldByteArray(String fieldName)
3107:                    throws DBException {
3108:                String strVal = null;
3109:                try {
3110:                    if (fieldData == null) {
3111:                        //					  fieldData = new HashMap();
3112:                        return null;
3113:                    }
3114:
3115:                    DataField df = (DataField) fieldData.get(fieldName);
3116:                    if (df == null) {
3117:                        return null;
3118:                    }
3119:
3120:                    Object o = df.getValue();
3121:                    if (o == null) {
3122:                        return null;
3123:                    }
3124:                    return (byte[]) o;
3125:                } catch (Exception ex) {
3126:                    throw new DBException("(" + this .myClassName
3127:                            + ") Unable to get a byte array value from field "
3128:                            + fieldName + " which contained '" + strVal + "'",
3129:                            ex.getMessage());
3130:                }
3131:            }
3132:
3133:            /**
3134:             * Get the primitive <code>integer</code> value of a field in this object.
3135:             * A convenience method for <code>getField</code>
3136:             *
3137:             * @param fieldName Name of a field in this object
3138:             * @return int  The value of the field as a int
3139:             * @throws DBException if there is no such field or it's value cannot be
3140:             *                     converted to a short integer.
3141:             * @see #setField(String,short)
3142:             */
3143:            public short getFieldShort(String fieldName) throws DBException {
3144:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3145:                String strVal = "";
3146:                try {
3147:                    strVal = getFieldData(oneField.getName());
3148:                    if (strVal == null) {
3149:                        return 0;
3150:                    }
3151:                    return Short.parseShort(strVal);
3152:                } catch (NumberFormatException ex) {
3153:                    throw new DBException(
3154:                            "("
3155:                                    + myClassName
3156:                                    + ") Unable to parse an short integer value from field "
3157:                                    + fieldName + " which contained '" + strVal
3158:                                    + "'", ex);
3159:                }
3160:            }
3161:
3162:            /**
3163:             * Get the primitive <code>integer</code> value of a field in this object.
3164:             * A convenience method for <code>getField</code>
3165:             *
3166:             * @param fieldName Name of a field in this object
3167:             * @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;
3168:             * @throws DBException if there is no such field or it's value cannot be
3169:             *                     converted to an integer.
3170:             * @see #setField(String,int)
3171:             * @see #isFieldNull(java.lang.String) in order to know if 0 is 'real' or because of an underlying null
3172:             */
3173:            public int getFieldInt(String fieldName) throws DBException {
3174:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3175:                String strVal = "";
3176:
3177:                try {
3178:                    strVal = getFieldData(oneField.getName());
3179:
3180:                    if (strVal == null) {
3181:                        return 0;
3182:                    }
3183:
3184:                    return Integer.parseInt(strVal);
3185:                } catch (NumberFormatException ex) {
3186:                    throw new DBException("(" + myClassName
3187:                            + ") Unable to parse an integer value from field "
3188:                            + fieldName + " which contained '" + strVal + "'",
3189:                            ex);
3190:                }
3191:            } /* getFieldInt(String) */
3192:
3193:            /**
3194:             * Get the prmitive <code>long</code> value of a field in this object.
3195:             * A convenience method for <code>getField</code>
3196:             *
3197:             * @param fieldName Name of a field in this object
3198:             * @return long  The value of the field as a long
3199:             * @throws DBException if there is no such field or it's value cannot be
3200:             *                     converted to a long integer.
3201:             * @see #setField(String,long)
3202:             */
3203:            public long getFieldLong(String fieldName) throws DBException {
3204:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3205:                String 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 ex) {
3214:                    throw new DBException(
3215:                            "("
3216:                                    + myClassName
3217:                                    + ") Unable to parse an long integer value from field "
3218:                                    + fieldName + " which contained '" + strVal
3219:                                    + "'", ex);
3220:                }
3221:            } /* getFieldLong(String) */
3222:
3223:            /**
3224:             * Sets a byte array to the underlying data as a blob
3225:             *
3226:             * @param fieldName    The name of the field to save.
3227:             * @param incomingData the stream to write to the database table.
3228:             * @throws DBException upon error
3229:             * @deprecated since Expresso 5.6.  Use LobField directly or
3230:             *             com.jcorporate.expresso.services.dbobj.MediaDBObject for BLOB storage since
3231:             *             they all require much less memory as well as provides more dynamic database
3232:             *             support.
3233:             */
3234:            public void saveBinaryField(String fieldName, byte[] incomingData)
3235:                    throws DBException {
3236:                DBConnectionPool connectionPool = null;
3237:                DBConnection myConnection = localConnection;
3238:
3239:                if (localConnection == null) {
3240:                    connectionPool = DBConnectionPool
3241:                            .getInstance(getDataContext());
3242:                    myConnection = connectionPool.getConnection();
3243:                }
3244:
3245:                try {
3246:                    LOBSupport.getInstance().setBLOB(this , fieldName,
3247:                            incomingData, myConnection);
3248:                } finally {
3249:                    if (localConnection == null) {
3250:                        if (connectionPool != null) {
3251:                            connectionPool.release(myConnection);
3252:                        }
3253:                    }
3254:                }
3255:            }
3256:
3257:            /**
3258:             * This convenience method counts
3259:             * <code>DBFields</code> belonging to this
3260:             * <code>DBObject</code> that are set to <b>retrieve</b>.
3261:             *
3262:             * @return int number of retrieve fields.
3263:             * @throws DBException upon error.
3264:             */
3265:            public synchronized int getFieldsToRetrieveCount()
3266:                    throws DBException {
3267:                if (retrieveFields == null) {
3268:                    return 0;
3269:                } else {
3270:                    return retrieveFields.size();
3271:                }
3272:            } /* getFieldsToRetrieveCount() */
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:             * Return the Array of keys (in the form field/field/field) that
3290:             * was found in the last search() call
3291:             *
3292:             * @return An array containing keys as strings
3293:             */
3294:            public Object[] getFoundKeysArray() {
3295:                if (foundKeys == null) {
3296:                    return new ArrayList().toArray();
3297:                } else {
3298:                    return foundKeys.toArray();
3299:                }
3300:            }
3301:
3302:            /**
3303:             * Get an array of DBIndex objects for purpose of
3304:             * creating them through the schema.  Return type
3305:             * should actually be DBIndex. <i>Please Note</i> This may cause an
3306:             * exception to be thrown if indexList is null.  Call hasIndex() first
3307:             * before calling getIndexArray
3308:             * To be soon deprecated
3309:             *
3310:             * @return an object containing all the DBOBjects
3311:             * @throws DBException upon error.
3312:             * @deprecated since 5.6, Use ((DBObjectDef)getMetaData()).getIndexArray() instead
3313:             */
3314:            public synchronized Object[] getIndexArray() throws DBException {
3315:                return ((DBObjectDef) getMetaData()).getIndexArray();
3316:            }
3317:
3318:            /**
3319:             * Get a string consisting of the values of each key field for this object
3320:             * appended together with a | between them.
3321:             *
3322:             * @return Value of all keys appended with a | between
3323:             */
3324:            public String getKey() {
3325:                try {
3326:                    return getMyKeys();
3327:                } catch (DBException de) {
3328:                    log.error(de);
3329:
3330:                    return null;
3331:                }
3332:            } /* getKey() */
3333:
3334:            /**
3335:             * Get a list of all of the fields in this object This iterator is not thread
3336:             * safe.
3337:             *
3338:             * @return An iterator of the fieldNamesInOrder array list
3339:             * @throws DBException If the list cannot be retrieved
3340:             */
3341:            public Iterator getKeyFieldListIterator() throws DBException {
3342:                return getDef().getKeyFieldListIterator();
3343:            }
3344:
3345:            /**
3346:             * Return the length of a field
3347:             *
3348:             * @param fieldName The name of the field
3349:             * @return String: The length of the field
3350:             * @throws DBException If there is no such field in this object
3351:             */
3352:            public String getLength(String fieldName) throws DBException {
3353:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3354:
3355:                return Integer.toString(oneField.getLengthInt());
3356:            } /* getLength(String) */
3357:
3358:            /**
3359:             * Get the length of this field as an integer
3360:             *
3361:             * @param fieldName to check
3362:             * @return length of field
3363:             * @throws DBException upon error.
3364:             */
3365:            public int getLengthInt(String fieldName) throws DBException {
3366:                return new Integer(getLength(fieldName)).intValue();
3367:            }
3368:
3369:            /**
3370:             * Get a field's lookup object - this is the name of another database
3371:             * object that can be used to look up valid values for this object. The lookup
3372:             * object for a field is set in the db objects setupFields method, and is used
3373:             * by the DBMaint servlet to provide automatic lookup links for fields.
3374:             *
3375:             * @param fieldName The fieldname to look up
3376:             * @return A String containing the classname of the lookup dbobject
3377:             * @throws DBException If the specified field does not exist.
3378:             */
3379:            public String getLookupObject(String fieldName) throws DBException {
3380:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3381:
3382:                return oneField.getLookupObject();
3383:            } /* getLookupObject(String) */
3384:
3385:            /**
3386:             * Returns the local connection currently associated with this DBObject...
3387:             * may be null if no local connection has ever been set.
3388:             *
3389:             * @return com.jcorporate.expresso.core.db.DBConnection or null
3390:             */
3391:            public DBConnection getLocalConnection() {
3392:                return localConnection;
3393:            }
3394:
3395:            /**
3396:             * Get the Maximum value in the table of a particular field
3397:             * <p/>
3398:             * Contributed by Madan Mohanrao Kulkarni [kulsmadya@rediffmail.com]
3399:             *
3400:             * @param fieldName   the Fieldname to get the max value for.
3401:             * @param whereClause Use a custom whereclause?
3402:             * @return a String containing the maximum value for this field name
3403:             *         Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
3404:             * @throws DBException upon error.
3405:             */
3406:            public String getMax(String fieldName, boolean whereClause)
3407:                    throws DBException {
3408:
3409:                DBConnection myConnection = null;
3410:                String returnValue = null;
3411:
3412:                try {
3413:                    if (localConnection != null) {
3414:                        myConnection = localConnection;
3415:                    } else {
3416:                        myConnection = getConnectionPool().getConnection(
3417:                                myClassName);
3418:                    }
3419:                    if (!whereClause) {
3420:                        myConnection.execute("SELECT MAX("
3421:                                + fieldName
3422:                                + ") FROM "
3423:                                + getJDBCMetaData().getTargetSQLTable(
3424:                                        this .getDataContext()));
3425:                    } else {
3426:                        FastStringBuffer sqlCommand = new FastStringBuffer(60);
3427:                        sqlCommand.append("SELECT MAX("
3428:                                + fieldName
3429:                                + ") FROM "
3430:                                + getJDBCMetaData().getTargetSQLTable(
3431:                                        this .getDataContext()) + " ");
3432:
3433:                        if (customWhereClause != null) {
3434:                            sqlCommand.append(customWhereClause);
3435:                        } else {
3436:                            FastStringBuffer fsb = FastStringBuffer
3437:                                    .getInstance();
3438:                            try {
3439:                                sqlCommand.append(buildWhereClauseBuffer(true,
3440:                                        fsb));
3441:                            } finally {
3442:                                fsb.release();
3443:                            }
3444:                        }
3445:
3446:                        myConnection.execute(sqlCommand.toString());
3447:                    }
3448:                    if (myConnection.next()) {
3449:                        returnValue = myConnection.getString(1);
3450:                    }
3451:                } catch (DBException de) {
3452:                    throw de;
3453:                } finally {
3454:                    if (localConnection == null) {
3455:                        myConnection.release();
3456:                    }
3457:                }
3458:
3459:                return returnValue;
3460:            } /* getMax(String) */
3461:
3462:            /**
3463:             * Get the Maximum value in the table of a particular field
3464:             * <p/>
3465:             * Contributed by Madan Mohanrao Kulkarni [kulsmadya@rediffmail.com]
3466:             *
3467:             * @param fieldName The fieldName to check against
3468:             * @return The maximum value for this field in the table.
3469:             * @throws DBException upon error.
3470:             */
3471:            public String getMax(String fieldName) throws DBException {
3472:                return getMax(fieldName, false);
3473:            } /* getMax(String) */
3474:
3475:            /**
3476:             * A DB Object can be told to only retrieve a certain number of records. If a
3477:             * "max records" value has been specified, this method provides access to it.
3478:             *
3479:             * @return The maximum number of records that should be retrieved, or zero
3480:             *         if no max has been set
3481:             */
3482:            public int getMaxRecords() {
3483:                return maxRecords;
3484:            } /* getMaxRecords() */
3485:
3486:            /**
3487:             * Get a string consisting of the values of each key field for this object
3488:             * appended together with a | between them.
3489:             *
3490:             * @return Value of all keys appended with a | between
3491:             * @throws DBException If the key list cannot be built.
3492:             */
3493:            public String getMyKeys() throws DBException {
3494:                FastStringBuffer myKeys = FastStringBuffer.getInstance();
3495:                String returnValue = null;
3496:                try {
3497:                    boolean needPipe = false;
3498:
3499:                    for (Iterator i = getMetaData().getKeyFieldListArray()
3500:                            .iterator(); i.hasNext();) {
3501:                        if (needPipe) {
3502:                            myKeys.append("|");
3503:                        }
3504:
3505:                        myKeys.append(getField((String) i.next()));
3506:                        needPipe = true;
3507:                    }
3508:
3509:                    returnValue = myKeys.toString();
3510:                } finally {
3511:                    myKeys.release();
3512:                }
3513:
3514:                return returnValue;
3515:            } /* getMyKeys() */
3516:
3517:            /**
3518:             * Return the array of updates (see the inner class) that specify what changes
3519:             * were just made to this object.
3520:             *
3521:             * @return Logged Updates in an Object Array
3522:             */
3523:            protected Object[] getMyUpdatesArray() {
3524:                if (myUpdates == null) {
3525:                    myUpdates = new ArrayList();
3526:                }
3527:
3528:                return myUpdates.toArray();
3529:            } /* getMyUpdatesArray() */
3530:
3531:            /**
3532:             * Gets the number of records that be skipped. The offset records.
3533:             * A DB Object can be told to skip a certain number of
3534:             * records, before reading records from the <code>ResultSet</code>.
3535:             * <p/>
3536:             * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
3537:             *
3538:             * @return The maximum number of records that should be
3539:             *         skipped over before reading the data records.
3540:             * @see #setOffsetRecord(int)
3541:             */
3542:            public int getOffsetRecord() {
3543:                return offsetRecord;
3544:            } /* getOffsetRecord() */
3545:
3546:            /**
3547:             * Get the precision of thie field, if one was specified
3548:             *
3549:             * @param fieldName The fieldname to get the precision for
3550:             * @return length of precision  as integer
3551:             * @throws DBException upon error.
3552:             */
3553:            public int getPrecision(String fieldName) throws DBException {
3554:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
3555:
3556:                return oneField.getPrecision();
3557:            } /* getPrecision(String) */
3558:
3559:            /**
3560:             * This function is called whenever the DBField is about to be written to
3561:             * the database.  It may do additional processing such as encryption depending
3562:             * on the field attributes.
3563:             *
3564:             * @param theField A DBField object
3565:             * @return the value to write to the data source.
3566:             * @throws DBException upon error.
3567:             * @todo This is not completely implemented yet, and this responsibility for
3568:             * formating a field should probably be
3569:             * put into DBField for better object orientation.
3570:             */
3571:            public String getSerializedForm(DBField theField)
3572:                    throws DBException {
3573:
3574:                return super .getSerialForm(theField);
3575:            } /* getSerializedForm() */
3576:
3577:            /**
3578:             * This will return a new object of the type of the subclass.
3579:             * <p/>
3580:             * OVERRIDE this method with implentation like
3581:             * <p>return new MyObject()</p>
3582:             * if you want greatest efficiency. For example: A DBObject called "Customer"
3583:             * should return a new "Customer" object.
3584:             * <p/>
3585:             * the implementation in DBObject uses getClass().newInstance(), which seems to be about
3586:             * 50% slower than "new MyObject()" calls on JDK 1.4
3587:             * <p/>
3588:             * Note that this method should never be called directly. It should only
3589:             * be called by newInstance()
3590:             *
3591:             * @return DBObject A newly allocated object of the subclass's class
3592:             * @throws DBException upon error.
3593:             * @see #getThisDBObj
3594:             */
3595:            protected DBObject getThisDBObj() throws DBException {
3596:                try {
3597:
3598:                    DBObject returnObj = (DBObject) getClass().newInstance();
3599:                    return returnObj;
3600:
3601:                } catch (InstantiationException ie) {
3602:                    throw new DBException("DBObject object '" + myClassName
3603:                            + "' cannot be instantiated", ie);
3604:                } catch (IllegalAccessException iae) {
3605:                    throw new DBException(
3606:                            "llegal access loading "
3607:                                    + "DBObject object '"
3608:                                    + myClassName
3609:                                    + "'  You must have a default public constructor for your DBObject",
3610:                            iae);
3611:                }
3612:            } /* getThisDBObj() */
3613:
3614:            /**
3615:             * get a new instance of this object, with some basic attributes copied,
3616:             * like the local connection of the transaction
3617:             * <p/>
3618:             * Generally speaking, DO NOT OVERRIDE THIS METHOD.  Instead, to improve efficiency,
3619:             * most subclasses will override the method getThisDBObj()
3620:             *
3621:             * @return DBObject A newly allocated object of the subclass's class
3622:             * @throws DBException upon error.
3623:             * @see #getThisDBObj
3624:             */
3625:            public DBObject newInstance() throws DBException {
3626:                DBObject returnObj = getThisDBObj();
3627:                copyAttributes(returnObj);
3628:                return returnObj;
3629:            } /* newInstance() */
3630:
3631:            /**
3632:             * called by newInstance(), this method should make sure that the newly created
3633:             * object is properly initialized
3634:             *
3635:             * @param returnObj the object to set the attributes to.
3636:             * @throws DBException upon error
3637:             */
3638:            protected void copyAttributes(DBObject returnObj)
3639:                    throws DBException {
3640:                if (localConnection != null) {
3641:                    returnObj.setConnection(localConnection);
3642:                } else {
3643:                    returnObj.setDataContext(getDataContext());
3644:                }
3645:            }
3646:
3647:            /**
3648:             * Constructor method that takes a DataTransferObject and builds a full fledged
3649:             * DBObject out of it.
3650:             *
3651:             * @param dto The DataTransferObject
3652:             * @return a constructed DBObject that still needs DataContext and record
3653:             *         ownership to be set.
3654:             * @throws DBException upon error
3655:             */
3656:            public static synchronized DBObject getThisDBbj(
3657:                    DataTransferObject dto) throws DBException {
3658:                DBObject newObj = null;
3659:                try {
3660:
3661:                    Class clazz = ClassLocator.loadClass(dto
3662:                            .getObjectClassName());
3663:                    newObj = (DBObject) clazz.newInstance();
3664:                    newObj.setDataTransferObject(dto);
3665:                } catch (Throwable e) {
3666:                    throw new DBException("Error instantiating dbobject: "
3667:                            + dto.getObjectClassName(), e);
3668:                }
3669:
3670:                return newObj;
3671:
3672:            }
3673:
3674:            /**
3675:             * Gets the data transfer object usually for serialization purposes
3676:             *
3677:             * @return The built DataTransferObject
3678:             * @throws DBException upon error.
3679:             */
3680:            public synchronized DataTransferObject getDataTransferObject()
3681:                    throws DBException {
3682:
3683:                DataTransferObject dto = new DataTransferObject();
3684:                dto.setDataContext(getDataContext());
3685:                dto.setObjectClassName(myClassName);
3686:                HashMap data = new HashMap(fieldData.size());
3687:                for (Iterator i = fieldData.keySet().iterator(); i.hasNext();) {
3688:                    String key = (String) i.next();
3689:                    DataField df = (DataField) fieldData.get(key);
3690:                    data.put(key, df.getValue());
3691:                }
3692:                dto.setTableFields(data);
3693:                return dto;
3694:            }
3695:
3696:            /**
3697:             * fill fields with values found in dto; REPLACES any data field already present.
3698:             *
3699:             * @param dto The filled out data transfer object
3700:             * @throws DBException upon error.
3701:             */
3702:            public synchronized void setDataTransferObject(
3703:                    DataTransferObject dto) throws DBException {
3704:                //
3705:                //Attempt to set the dbname as before, HOWEVER, if it's impossible [very likely
3706:                //across enterprise systems, then we ignore the error and move on.
3707:                //
3708:                try {
3709:                    setDBName(dto.getDataContext());
3710:                } catch (DBException dbe) {
3711:                    if (log.isDebugEnabled()) {
3712:                        log.debug("Error setting data context", dbe);
3713:                    }
3714:                }
3715:
3716:                //
3717:                //Set the field data  - we have to go through the long process here
3718:                //because we don't want to be serializing all the parents and metadata
3719:                //with each serialization of the data transfer object. [Very Messy]
3720:                //
3721:                Map dataTransferFields = dto.getTableFields();
3722:                fieldData = new HashMap(dataTransferFields.size());
3723:                for (Iterator i = dataTransferFields.keySet().iterator(); i
3724:                        .hasNext();) {
3725:                    String keyName = (String) i.next();
3726:                    Object newValue = dataTransferFields.get(keyName);
3727:                    DataField df = DefaultDataField.getInstance(
3728:                            getFieldMetaData(keyName), this );
3729:                    df.setValue(newValue);
3730:                    fieldData.put(keyName, df);
3731:                }
3732:                //
3733:                //        fieldData = new HashMap(dto.getTableFields());
3734:                cacheIsChangedComparison();
3735:                setStatus(BaseDataObject.STATUS_CURRENT);
3736:            }
3737:
3738:            /**
3739:             * This is a convenience method which will return a ValidValue description
3740:             * for a multi-valued field.
3741:             * (Contributed by Adam Rossi)
3742:             *
3743:             * @param fieldName java.lang.String
3744:             * @return java.lang.String The description of this multi-valued field
3745:             * @throws DBException If there is no such field, or the values cannot be retrieved.
3746:             */
3747:            public String getValidValueDescrip(String fieldName)
3748:                    throws DBException {
3749:                if (!getMetaData().getFieldMetadata(fieldName).isMultiValued()) {
3750:                    throw new DBException("This getValidValueDescrip method "
3751:                            + "cannot be invoked on a non-multivalued field");
3752:                }
3753:
3754:                Vector v = getValidValues(fieldName);
3755:
3756:                if (v == null) {
3757:                    throw new DBException("No valid values could be "
3758:                            + "found for this field.");
3759:                }
3760:
3761:                ValidValue onevv = null;
3762:                String fieldValue = getField(fieldName);
3763:
3764:                for (Enumeration e = v.elements(); e.hasMoreElements();) {
3765:                    onevv = (ValidValue) e.nextElement();
3766:
3767:                    if (fieldValue.equals(onevv.getValue())) {
3768:                        return onevv.getDescription();
3769:                    }
3770:                }
3771:
3772:                return null;
3773:            } /* getValidValueDescrip(String) */
3774:
3775:            /**
3776:             * Retrieve a list of valid value object for this particular dbobject
3777:             *
3778:             * @param fieldName the name of the field to get the valid values list for
3779:             * @return java.util.List of ValidValue objects
3780:             * @throws DBException upon error
3781:             */
3782:            public java.util.List getValidValuesList(String fieldName)
3783:                    throws DBException {
3784:                return getValidValues(fieldName);
3785:            }
3786:
3787:            /**
3788:             * New method to replace getValues with a structure of valid values
3789:             * and descriptions. Database objects should extend this method to
3790:             * return Vectors of ValidValue objects for multi-valued fields. A
3791:             * specific object can return it's own list of ValidValues, or it
3792:             * can call this super method to use the lookup object to get the
3793:             * list of valid values instead.
3794:             *
3795:             * @param fieldName The name of the fields for which a value set is requested
3796:             * @return A Vector of ValidValue objects
3797:             * @throws DBException upon error.
3798:             */
3799:            public synchronized Vector getValidValues(String fieldName)
3800:                    throws DBException {
3801:                if (getMetaData().getFieldMetadata(fieldName).isMultiValued()) {
3802:                    String lookupObj = getLookupObject(fieldName);
3803:
3804:                    if (log.isDebugEnabled()) {
3805:                        log.debug("Lookup is " + lookupObj);
3806:                    }
3807:                    if (StringUtil.notNull(lookupObj).equals("")) {
3808:                        if (log.isDebugEnabled()) {
3809:                            log.debug("No lookup");
3810:                        }
3811:
3812:                        return null;
3813:                    } else {
3814:                        try {
3815:                            Class c = ClassLocator.loadClass(lookupObj);
3816:                            Object o = c.newInstance();
3817:
3818:                            //CALL setDBName()  ONLY if it exists in the class.  It
3819:                            //might not be necessary across contexts.
3820:                            if (o instanceof  DataObject) {
3821:                                DataObject dao = (DataObject) o;
3822:                                dao.setDataContext(getDataContext());
3823:                            }
3824:
3825:                            if (o instanceof  DBObject) {
3826:                                DBObject dbo = (DBObject) o;
3827:                                if (localConnection != null) {
3828:                                    dbo.setConnection(localConnection);
3829:                                }
3830:                            } else if (o instanceof  MultiDBObject) {
3831:                                MultiDBObject mdo = (MultiDBObject) o;
3832:                                mdo.setDBName(getDataContext());
3833:                            } else {
3834:                                try {
3835:                                    Class[] params = { java.lang.String.class };
3836:                                    Method m = c.getMethod("setDBName", params);
3837:
3838:                                    if (m != null) {
3839:                                        Object[] paramValues = { getDataContext() };
3840:                                        m.invoke(o, paramValues);
3841:                                    }
3842:                                } catch (NoSuchMethodException nsme) {
3843:                                    if (log.isDebugEnabled()) {
3844:                                        log.debug(
3845:                                                "No setDBName for this object",
3846:                                                nsme);
3847:                                    }
3848:
3849:                                    //Do nothing, the method just doesn't exist.
3850:                                } catch (SecurityException se) {
3851:                                    throw new DBException(
3852:                                            "Security Exception trying to call"
3853:                                                    + " setDBName in loading lookup object",
3854:                                            se);
3855:                                } catch (java.lang.reflect.InvocationTargetException ive) {
3856:                                    ive.printStackTrace();
3857:                                    log
3858:                                            .error(
3859:                                                    "Error Invoking setDBName() dynamically",
3860:                                                    ive);
3861:                                    throw new DBException(
3862:                                            "Error calling setDBName in "
3863:                                                    + "loading lookup object",
3864:                                            ive.getTargetException());
3865:                                } catch (IllegalAccessException ilae) {
3866:                                    if (log.isDebugEnabled()) {
3867:                                        log
3868:                                                .debug(
3869:                                                        "setDBName() exists, but is not a public method",
3870:                                                        ilae);
3871:                                    }
3872:
3873:                                    //Do nothing, setDBName is not public
3874:                                }
3875:
3876:                            }
3877:                            if (log.isDebugEnabled()) {
3878:                                log.debug("Returning values from " + lookupObj);
3879:                            }
3880:
3881:                            return ((LookupInterface) o).getValues();
3882:                        } catch (ClassNotFoundException cn) {
3883:                            throw new DBException("Lookup object not found", cn);
3884:                        } catch (InstantiationException ie) {
3885:                            throw new DBException(
3886:                                    "Lookup object cannot be instantiated", ie);
3887:                        } catch (IllegalAccessException iae) {
3888:                            throw new DBException(
3889:                                    "llegal access loading Lookup object", iae);
3890:                        }
3891:                    }
3892:                } else { /* if */
3893:                    throw new DBException(
3894:                            "Field '"
3895:                                    + fieldName
3896:                                    + "' in object '"
3897:                                    + myClassName
3898:                                    + "' is not specified as multi-valued, so you cannot "
3899:                                    + "call getValidValues for this field");
3900:                }
3901:            } /* getValidValues(String) */
3902:
3903:            /**
3904:             * Method to return a Vector of ValidValue
3905:             * Template method--not implemented in this superclass.
3906:             * This method may be implemented by objects that want to provide a
3907:             * list of valid values for other DB objects. It is strongly recommended
3908:             * that the valid value list be cached (via the CacheManager) for performance.
3909:             * The naming convention used in Expresso is to store the ValidValue list with
3910:             * a cache name the same as the db objects class name with ".validValues" appended
3911:             * TODO: This should be converted to array List versions
3912:             *
3913:             * @return java.util.Vector of valid values
3914:             * @throws DBException upon error.
3915:             * @see #getValidValues
3916:             */
3917:            public Vector getValues() throws DBException {
3918:                throw new DBException("This object: " + myClassName
3919:                        + " does not have valid values defined.");
3920:            } /* getValues() */
3921:
3922:            /**
3923:             * Basic version of getValidValues that stores/retrieves the valid
3924:             * values in a Cache. This method does <em>not</em> support
3925:             * internationalisation (i18n).
3926:             * <p/>
3927:             * <p/>
3928:             * Valid values are store inside cache with a key name that equals
3929:             * <code>myClassName+".validValues"</code>
3930:             * The method creates <code>ValidValue</code> object instances and
3931:             * stores them in the cache.
3932:             * </p>
3933:             *
3934:             * @param valueField   java.lang.String
3935:             * @param descripField java.lang.String
3936:             * @return java.util.Vector
3937:             * @throws DBException if a database error occurs
3938:             *                     <p/>
3939:             *                     #see #getValuesDefault(String valueField, String descripField, String whereClause)
3940:             *                     #see #getISOValuesDefault(String valueField, String descripField)
3941:             */
3942:            protected Vector getValuesDefault(String valueField,
3943:                    String descripField) throws DBException {
3944:                return getValuesDefault(valueField, descripField, "");
3945:            }
3946:
3947:            /**
3948:             * Basic filtered version of getValidValues that stores/retrieves the valid
3949:             * values in a Cache. This method does <em>not</em> support
3950:             * internationalisation (i18n).
3951:             * <p/>
3952:             * <p/>
3953:             * Valid values are store inside cache with a key name that equals
3954:             * <code>myClassName+".validValues"</code>
3955:             * The method creates <code>ValidValue</code> object instances and
3956:             * stores them in the cache.
3957:             * </p>
3958:             *
3959:             * @param valueField   java.lang.String
3960:             * @param descripField java.lang.String
3961:             * @param whereClause  the where clause that will be pass
3962:             *                     to setCustomWhereClause.
3963:             * @return java.util.Vector
3964:             * @throws DBException if a database error occurs
3965:             *                     <p/>
3966:             *                     #see #setCustomWhereClause(String newCustomWhere)
3967:             *                     #see #getISOValuesDefault(String valueField, String descripField)
3968:             */
3969:            protected Vector getValuesDefault(String valueField,
3970:                    String descripField, String whereClause) throws DBException {
3971:                return getValuesDefault(valueField, descripField, whereClause,
3972:                        "");
3973:            }
3974:
3975:            /**
3976:             * Basic version of getValidValues that stores/retrieves the valid values
3977:             * in a Cache. This method retrieves the valid values in the order
3978:             * specified by the sortKeyString, and filters them with the given where
3979:             * clause. Note, it does <em>not</em> support internationalisation (i18n).
3980:             * <p/>
3981:             * <p/>
3982:             * Valid values are store inside cache with a key name that equals
3983:             * <code>myClassName+".validValues"</code>
3984:             * The method creates <code>ValidValue</code> object instances and
3985:             * stores them in the cache.
3986:             * </p>
3987:             *
3988:             * @param valueField    java.lang.String
3989:             * @param descripField  java.lang.String
3990:             * @param whereClause   the where clause that will be pass
3991:             *                      to setCustomWhereClause.
3992:             * @param sortKeyString the pipe delimited string of field names with the
3993:             *                      optional 'ASC or DESC' keyword afterwords; pass in null to indicate no sorting
3994:             * @return java.util.Vector
3995:             * @throws DBException if a database error occurs
3996:             *                     <p/>
3997:             *                     #see #setCustomWhereClause(String newCustomWhere)
3998:             *                     #see #setSortKey(String sortKeyString)
3999:             *                     #see #getISOValuesDefault(String valueField, String descripField)
4000:             */
4001:            protected Vector getValuesDefault(String valueField,
4002:                    String descripField, String whereClause,
4003:                    String sortKeyString) throws DBException {
4004:                try {
4005:                    DBObject this Obj = newInstance();
4006:
4007:                    if (!this Obj.myClassName.equals(myClassName)) {
4008:                        throw new DBException("The newInstance() method in "
4009:                                + myClassName + " returned " + " a "
4010:                                + this Obj.myClassName + " object instead of a "
4011:                                + myClassName
4012:                                + " object. Please correct the method.");
4013:                    }
4014:
4015:                    String cacheName = myClassName + ".valueField:"
4016:                            + valueField + "|descrip:" + descripField
4017:                            + "|where:" + whereClause + "|sort:"
4018:                            + sortKeyString;
4019:                    CacheManager.getInstance();
4020:                    CacheSystem cs = CacheManager
4021:                            .getCacheSystem(getDataContext());
4022:                    if (cs != null) {
4023:                        if (!cs.existsCache(cacheName)) {
4024:                            cs.createCache(cacheName, true);
4025:                            //Clear the valid values when the main object class is
4026:                            //modified.
4027:                            cs.addListener(cacheName, myClassName);
4028:                        }
4029:                    }
4030:
4031:                    Vector myValues = null;
4032:
4033:                    //
4034:                    // Do not use cache if in Transaction. and a localconnection has been set
4035:                    //
4036:                    if (localConnection == null
4037:                            || localConnection.getAutoCommit() == false) {
4038:                        java.util.List temp = cs.getItems(cacheName);
4039:                        if (temp != null) {
4040:                            if (temp instanceof  Vector) {
4041:                                myValues = (Vector) temp;
4042:                            } else {
4043:                                myValues = new Vector(temp);
4044:                            }
4045:                        }
4046:                    }
4047:
4048:                    if (myValues == null) {
4049:                        myValues = new Vector();
4050:                        this Obj.setDataContext(getDataContext());
4051:
4052:                        if (sortKeyString != null) {
4053:                            String sortField = descripField;
4054:                            if (this Obj.getMetaData().getFieldMetadata(
4055:                                    descripField).isVirtual()) {
4056:                                sortField = valueField;
4057:                            }
4058:
4059:                            if (sortKeyString.length() > 1) {
4060:                                sortField = sortKeyString;
4061:                            }
4062:                            this Obj.setSortKey(sortField);
4063:                        }
4064:
4065:                        // Retrieve valid values from the database, and
4066:                        // attempt to localise the `description' attribute by
4067:                        // automatic canonisation. *PP* 27/12/2002
4068:                        if (whereClause != null && whereClause.length() > 1) {
4069:                            this Obj.setCustomWhereClause(whereClause);
4070:                        }
4071:
4072:                        DBObject oneObj = null;
4073:                        for (Iterator oset = this Obj.searchAndRetrieveList()
4074:                                .iterator(); oset.hasNext();) {
4075:                            oneObj = (DBObject) oset.next();
4076:                            myValues.addElement(new ValidValue(oneObj
4077:                                    .getField(valueField), oneObj
4078:                                    .getField(descripField)));
4079:                        }
4080:
4081:                        //
4082:                        //Default expiration of valid values = 15 minutes
4083:                        //
4084:                        long expiration = 60 * 1000 * 15;
4085:                        DBObjLimit limit = new DBObjLimit(
4086:                                SecuredDBObject.SYSTEM_ACCOUNT);
4087:                        limit.setDataContext(getDataContext());
4088:                        limit.setField(DBObjLimit.DB_OBJECT_NAME,
4089:                                this Obj.myClassName);
4090:                        if (limit.find()) {
4091:                            String ttl = limit.getField(DBObjLimit.TTL);
4092:                            if (ttl != null && ttl.length() > 0) {
4093:                                try {
4094:                                    expiration = Long.parseLong(ttl) * 60 * 1000;
4095:                                } catch (NumberFormatException ex) {
4096:                                    log.error("Invalid TTL value: " + ttl);
4097:                                }
4098:                            }
4099:                        }
4100:                        if (cs != null) {
4101:                            cs.setItems(cacheName, myValues, expiration);
4102:                            //Add a listener to the cache so that when the target class
4103:                            //disappears, this cache dies too.
4104:                            cs.addListener(cacheName, this Obj.myClassName);
4105:                        }
4106:                    }
4107:
4108:                    return myValues;
4109:                } catch (CacheException ce) {
4110:                    throw new DBException(ce);
4111:                }
4112:            } /* getValuesDefault(String, String) */
4113:
4114:            /**
4115:             * Basic version of getValidValues with supports
4116:             * <em>internationalisation</code> (i18n) that stores/retrieves
4117:             * the valid values in a locale dependent Caches
4118:             * <p/>
4119:             * <p/>
4120:             * <p/>
4121:             * Valid values are store inside cache with a key name that equals
4122:             * <code>myClassName+"."+oneLocale.getString()+".validValues"</code>.
4123:             * For example the key cache could be:
4124:             * <pre>
4125:             * "com.acme.test.Fruits.en_gb.validValues"
4126:             * "com.acme.test.Fruits.de_de.validValues"
4127:             * "com.acme.test.Fruits.es_es.validValues"
4128:             * </pre>
4129:             * <p/>
4130:             * The method creates <code>ISOValidValue</code> object instances and
4131:             * stores them in the cache.
4132:             * <p/>
4133:             * </p>
4134:             *
4135:             * @param valueField   java.lang.String
4136:             * @param descripField java.lang.String
4137:             * @return java.util.Vector
4138:             * @throws DBException if a database error occurs
4139:             *                     <p/>
4140:             *                     #see #getValuesDefault(String valueField, String descripField)
4141:             */
4142:            protected Vector getISOValuesDefault(String valueField,
4143:                    String descripField) throws DBException {
4144:                return getISOValuesDefault(valueField, descripField, "");
4145:            }
4146:
4147:            /**
4148:             * Basic filtered version of getValidValues with supports
4149:             * <em>internationalisation</code> (i18n) that stores/retrieves
4150:             * the valid values in a locale dependent Caches
4151:             * <p/>
4152:             * <p/>
4153:             * <p/>
4154:             * Valid values are store inside cache with a key name that equals
4155:             * <code>myClassName+"."+oneLocale.getString()+".validValues"</code>.
4156:             * For example the key cache could be:
4157:             * <pre>
4158:             * "com.acme.test.Fruits.en_gb.validValues"
4159:             * "com.acme.test.Fruits.de_de.validValues"
4160:             * "com.acme.test.Fruits.es_es.validValues"
4161:             * </pre>
4162:             * <p/>
4163:             * The method creates <code>ISOValidValue</code> object instances and
4164:             * stores them in the cache.
4165:             * <p/>
4166:             * </p>
4167:             *
4168:             * @param valueField   java.lang.String
4169:             * @param descripField java.lang.String
4170:             * @param whereClause  the where clause that will be pass
4171:             *                     to setCustomWhereClause.
4172:             * @return java.util.Vector
4173:             * @throws DBException if a database error occurs
4174:             *                     <p/>
4175:             *                     #see #getValuesDefault(String valueField, String descripField, String whereClause)
4176:             */
4177:            protected Vector getISOValuesDefault(String valueField,
4178:                    String descripField, String whereClause) throws DBException {
4179:                return getISOValuesDefault(valueField, descripField,
4180:                        whereClause, "");
4181:            }
4182:
4183:            /**
4184:             * Basic version of getValidValues with supports
4185:             * <em>internationalisation</code> (i18n) that stores/retrieves the valid
4186:             * values in a locale dependent Caches. This method retrieves the valid
4187:             * values in the order specified by the sortKeyString, and filters them
4188:             * with the given where clause.
4189:             * <p/>
4190:             * <p/>
4191:             * <p/>
4192:             * Valid values are store inside cache with a key name that equals
4193:             * <code>myClassName+"."+oneLocale.getString()+".validValues"</code>.
4194:             * For example the key cache could be:
4195:             * <pre>
4196:             * "com.acme.test.Fruits.en_gb.validValues"
4197:             * "com.acme.test.Fruits.de_de.validValues"
4198:             * "com.acme.test.Fruits.es_es.validValues"
4199:             * </pre>
4200:             * <p/>
4201:             * The method creates <code>ISOValidValue</code> object instances and
4202:             * stores them in the cache.
4203:             * <p/>
4204:             * </p>
4205:             *
4206:             * @param valueField    java.lang.String
4207:             * @param descripField  java.lang.String
4208:             * @param whereClause   the where clause that will be pass
4209:             *                      to setCustomWhereClause.
4210:             * @param sortKeyString the pipe delimited string of field names with the
4211:             *                      optional 'ASC or DESC' keyword afterwords
4212:             * @return java.util.Vector
4213:             * @throws DBException if a database error occurs
4214:             *                     <p/>
4215:             *                     #see #getValuesDefault(String valueField, String descripField, String whereClause, String sortKeyString)
4216:             */
4217:            protected Vector getISOValuesDefault(String valueField,
4218:                    String descripField, String whereClause,
4219:                    String sortKeyString) throws DBException {
4220:                try {
4221:                    DBObject this Obj = newInstance();
4222:
4223:                    if (!this Obj.myClassName.equals(myClassName)) {
4224:                        throw new DBException("The newInstance() method in "
4225:                                + myClassName + " returned " + " a "
4226:                                + this Obj.myClassName + " object instead of a "
4227:                                + myClassName
4228:                                + " object. Please correct the method.");
4229:                    }
4230:
4231:                    Locale oneLocale = Locale.getDefault();
4232:
4233:                    String cacheName = myClassName + "." + oneLocale.toString()
4234:                            + ".valueField:" + valueField + "|descrip:"
4235:                            + descripField + "|where:" + whereClause + "|sort:"
4236:                            + sortKeyString;
4237:                    CacheManager.getInstance();
4238:                    CacheSystem cs = CacheManager
4239:                            .getCacheSystem(getDataContext());
4240:
4241:                    if (cs != null && !cs.existsCache(cacheName)) {
4242:                        cs.createCache(cacheName, true);
4243:                    }
4244:
4245:                    Vector myValues = null;
4246:
4247:                    //
4248:                    // Do not use cache if in Transaction. and a localconnection has been set
4249:                    //
4250:                    if (localConnection == null
4251:                            || localConnection.getAutoCommit() == false) {
4252:                        if (cs != null) {
4253:                            java.util.List temp = cs.getItems(cacheName);
4254:                            if (!(temp instanceof  Vector)) {
4255:                                myValues = new Vector(temp);
4256:                            } else {
4257:                                myValues = (Vector) temp;
4258:                            }
4259:                        }
4260:                    }
4261:
4262:                    if (myValues == null) {
4263:                        myValues = new Vector();
4264:                        this Obj.setDataContext(getDataContext());
4265:
4266:                        DBObject oneObj = null;
4267:                        String sortField = descripField;
4268:
4269:                        if (this Obj.getMetaData()
4270:                                .getFieldMetadata(descripField).isVirtual()) {
4271:                            sortField = valueField;
4272:                        }
4273:
4274:                        if (sortKeyString.length() > 1) {
4275:                            sortField = sortKeyString;
4276:                        }
4277:                        this Obj.setSortKey(sortField);
4278:
4279:                        // Get schema and the prefix
4280:                        String oneSchema = this Obj.getJDBCMetaData()
4281:                                .getSchema();
4282:                        String prefix = this Obj.myClassName;
4283:
4284:                        // Retrieve valid values from the database, and
4285:                        // attempt to localise the `description' attribute by
4286:                        // automatic canonisation. *PP* 27/12/2002
4287:                        if (whereClause.length() > 1) {
4288:                            this Obj.setCustomWhereClause(whereClause);
4289:                        }
4290:                        for (Iterator oset = this Obj.searchAndRetrieveList(
4291:                                sortField).iterator(); oset.hasNext();) {
4292:                            oneObj = (DBObject) oset.next();
4293:                            myValues.addElement(new ISOValidValue(oneSchema,
4294:                                    oneLocale, prefix, oneObj
4295:                                            .getField(valueField), oneObj
4296:                                            .getField(descripField)));
4297:                        }
4298:
4299:                        //
4300:                        //Default expiration of valid values = 15 minutes
4301:                        //
4302:                        long expiration = 60 * 1000 * 15;
4303:                        DBObjLimit limit = new DBObjLimit(
4304:                                SecuredDBObject.SYSTEM_ACCOUNT);
4305:                        limit.setField("DBObjectName", this Obj.myClassName);
4306:                        if (limit.find()) {
4307:                            String ttl = limit.getField("TTL");
4308:                            if (ttl != null && ttl.length() > 0) {
4309:                                try {
4310:                                    expiration = Long.parseLong(ttl) * 60 * 1000;
4311:                                } catch (NumberFormatException ex) {
4312:                                    log.error("Invalid TTL value: " + ttl);
4313:                                }
4314:                            }
4315:                        }
4316:                        if (cs != null) {
4317:                            cs.setItems(cacheName, myValues, expiration);
4318:                            //Add a listener to the cache so that when the target class
4319:                            //disappears, this cache dies too.
4320:                            cs.addListener(cacheName, this Obj.myClassName);
4321:                        }
4322:
4323:                    }
4324:
4325:                    return myValues;
4326:                } catch (CacheException ce) {
4327:                    throw new DBException(ce);
4328:                }
4329:            } /* getISOValuesDefault(String, String) */
4330:
4331:            /**
4332:             * See if we have a value for each of the key fields
4333:             *
4334:             * @return True if all key fields have a value, false if not
4335:             * @throws DBException upon error.
4336:             */
4337:            public boolean haveAllKeys() throws DBException {
4338:                DBField oneField = null;
4339:
4340:                for (Iterator i = getJDBCMetaData().getAllKeysMap().values()
4341:                        .iterator(); i.hasNext();) {
4342:                    oneField = (DBField) i.next();
4343:                    DataField df = getDataField(oneField.getName());
4344:                    if (df == null || df.isNull()) {
4345:                        return false;
4346:                    }
4347:                }
4348:
4349:                return true;
4350:            } /* haveAllKeys() */
4351:
4352:            /**
4353:             * Sets up metadata for the dbobject via call to setupFields().
4354:             * it is important within this method to provide STATIC synchronization
4355:             * to enable static (meta)data members to be setup by the first
4356:             * instantiation of a given class. Two threads
4357:             * can simultaneously create the first instance of different classes,
4358:             * and so a simple object-level synchronization alone will not suffice here,
4359:             * though it is useful to block out calls from the same class.
4360:             * <p/>
4361:             * author Adam Rossi, PlatinumSolutions
4362:             * author Larry Hamel, CodeGuild.com
4363:             *
4364:             * @throws DBException The exception description.
4365:             */
4366:            protected synchronized void initialize() throws DBException {
4367:                synchronized (sMetadataMap) {
4368:                    if (getMetaData() == null) {
4369:
4370:                        DBObjectDef myDef = new DBObjectDef();
4371:                        myDef.setName(myClassName);
4372:
4373:                        // get static synchronization, since 2 threads with 2 different classes
4374:                        // could be competing, and sMetadata needs sync for put()
4375:                        sMetadataMap.put(myClassName, myDef);
4376:
4377:                        //  note that DBObjectDef is
4378:                        // NOT threadsafe during its initialization during setupFields,
4379:                        // so it is imperitive that it be protected here in this static
4380:                        // context to protect against 2 threads trying to initialize 2
4381:                        // different instantiations of the same object; however, we can
4382:                        // release the sync lock to allow other kinds of objects to
4383:                        // finish
4384:                        setupFields();
4385:
4386:                        try {
4387:                            //We have to force default here because no data context has been
4388:                            //set.  So checkZeroUpdate is determined
4389:                            //by the default pool only; or false if cannot find that context
4390:                            DBConnectionPool tempPool = DBConnectionPool
4391:                                    .getInstance("default");
4392:                            if (tempPool != null) {
4393:                                getDef().setCheckZeroUpdate(
4394:                                        tempPool.getCheckZeroUpdate());
4395:                            }
4396:                        } catch (DBException e) {
4397:                            getDef().setCheckZeroUpdate(false);
4398:                        }
4399:                    }
4400:                }
4401:            } /* initialize() */
4402:
4403:            /**
4404:             * Is this object using internal caching?
4405:             * If the cache value is set to other than zero, it is using caching
4406:             *
4407:             * @return True if internal caching is enabled, else false
4408:             */
4409:            public boolean isCached() {
4410:                if (getCacheSize() > 0) {
4411:                    return true;
4412:                }
4413:
4414:                return false;
4415:            } /* isCached() */
4416:
4417:            /**
4418:             * This method iterates through all the <code>DBFields</code> belonging
4419:             * <code>DBObject</code> returns <code>true</code> if any of them are
4420:             * set a <b>distinct</b>.
4421:             * <p/>
4422:             * author Peter Pilgrim <peter.pilgrim@db.com>
4423:             *
4424:             * @return true if this is a distinct field
4425:             * @throws DBException upon error.
4426:             * @see #isFieldDistinct(String)
4427:             * @see #getDistinctFields()
4428:             * @see #getDistinctFieldCount()
4429:             */
4430:            public synchronized boolean isDistinct() throws DBException {
4431:                if (distinctFields == null) {
4432:                    return false;
4433:                }
4434:
4435:                for (Iterator i = getMetaData().getAllFieldsMap().values()
4436:                        .iterator(); i.hasNext();) {
4437:                    DBField oneField = (DBField) i.next();
4438:
4439:                    if (distinctFields.containsKey(oneField.getName())) {
4440:
4441:                        // Ok, we found at least one!
4442:                        return true;
4443:                    }
4444:                }
4445:
4446:                return false;
4447:            } /* isDistinct() */
4448:
4449:            /**
4450:             * Tells whether a particular field is null or not.  <b><i>Note:</i></b>
4451:             * To maintain backwards compatibility, if you have a virtual field then
4452:             * isFieldNull will always return false.  You should override this method
4453:             * for virtual fields if you want isNull support.
4454:             *
4455:             * @param fieldName The name of the field to check for isFieldNull()
4456:             * @return true if the field is null
4457:             * @throws DBException if the field doesn't exist for the particular
4458:             *                     DBObject.
4459:             */
4460:            public boolean isFieldNull(String fieldName) throws DBException {
4461:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
4462:
4463:                if (oneField == null) {
4464:                    throw new DBException("(" + myClassName
4465:                            + ") No such field as '" + fieldName + "'");
4466:                }
4467:
4468:                if (oneField.isVirtual()) {
4469:                    log
4470:                            .warn("("
4471:                                    + myClassName
4472:                                    + ") Field "
4473:                                    + fieldName
4474:                                    + " is a virtual field. Database object should extend "
4475:                                    + "getField method to handle requests for this field");
4476:                    return false;
4477:                }
4478:
4479:                String returnValue = getFieldData(oneField.getName());
4480:
4481:                return (returnValue == null);
4482:            }
4483:
4484:            /**
4485:             * Return true if every field in this object is empty or null. Tests only
4486:             * "real" fields, not virtual ones
4487:             *
4488:             * @return boolean: True if the record is "empty" (all fields blank),
4489:             *         False if not.
4490:             * @throws DBException If the list of fields cannot be traversed
4491:             */
4492:            public synchronized boolean isEmpty() throws DBException {
4493:                String oneName = null;
4494:
4495:                for (Iterator i = getJDBCMetaData().getFieldListArray()
4496:                        .iterator(); i.hasNext();) {
4497:                    oneName = (String) i.next();
4498:
4499:                    // only concern ourselves with real fields stored in this object,
4500:                    // not virtual fields
4501:                    DataFieldMetaData oneField = getFieldMetaData(oneName);
4502:                    if (oneField != null && !oneField.isVirtual()) {
4503:                        String value = getFieldData(oneField.getName());
4504:                        if (value != null && value.length() > 0) {
4505:                            return false;
4506:                        }
4507:                    }
4508:                } /* for each field */
4509:
4510:                return true;
4511:            } /* isEmpty() */
4512:
4513:            /**
4514:             * Convenience method to check if a field is distinct or not
4515:             * within this database object.
4516:             *
4517:             * @param fieldName the name of the field
4518:             * @return boolean value true if the field is set distinct.
4519:             * @throws DBException If the operation could not be completed
4520:             *                     <p/>
4521:             *                     author Peter Pilgrim <peter.pilgrim@db.com>
4522:             * @see #getDistinctFields()
4523:             * @see #getDistinctFieldCount()
4524:             * @see #setFieldDistinct
4525:             */
4526:            public boolean isFieldDistinct(String fieldName) throws DBException {
4527:                if (distinctFields == null) {
4528:                    return false;
4529:                }
4530:
4531:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
4532:
4533:                if (oneField.isVirtual()) {
4534:                    throw new DBException(
4535:                            "("
4536:                                    + myClassName
4537:                                    + ") Field "
4538:                                    + fieldName
4539:                                    + " is a virtual field. Database object should extend "
4540:                                    + "getField method to handle requests for this field");
4541:                }
4542:
4543:                return distinctFields.containsKey(oneField.getName());
4544:            } /* isFieldDistinct(String) */
4545:
4546:            /**
4547:             * This method iterates through all the <code>DBFields</code> belonging
4548:             * <code>DBObject</code> returns <code>true</code> if any of them are
4549:             * between the <b>retrieve</b> fields.
4550:             * <p/>
4551:             * author Yves Henri Amaizo <amy_amaizo@compuserve.com>
4552:             *
4553:             * @return true if there are specific fields to retrieve
4554:             * @throws DBException upon error.
4555:             * @see #isFieldToRetrieve(String)
4556:             */
4557:            public synchronized boolean isFieldsToRetrieve() throws DBException {
4558:                if (retrieveFields == null) {
4559:                    return false;
4560:                } else if (retrieveFields.size() > 0) {
4561:                    return true;
4562:                } else {
4563:                    return false;
4564:                }
4565:            } /* isFieldsToRetrieve() */
4566:
4567:            /**
4568:             * Convenience method to check if a field is field to be retrieve or not
4569:             * within this database object.
4570:             *
4571:             * @param fieldName the name of the field
4572:             * @return boolean value true if the field is set distinct.
4573:             * @throws DBException If the operation could not be completed
4574:             *                     <p/>
4575:             *                     author Yves Henri Amaizo <amy_amaizo@compuserve.com>
4576:             * @see #setFieldsToRetrieve( String )
4577:             */
4578:            public boolean isFieldToRetrieve(String fieldName)
4579:                    throws DBException {
4580:                if (retrieveFields == null) {
4581:                    return false;
4582:                }
4583:
4584:                return retrieveFields.containsKey(fieldName);
4585:            } /* isFieldToretrieve(String) */
4586:
4587:            /**
4588:             * Method called to determine if a particular field is multi-valued,
4589:             * that is does it have a set of specific values and descriptions
4590:             *
4591:             * @param fieldName Name of the field
4592:             * @return boolean True if the field is multi-valued, false if not
4593:             * @throws DBException If there is no such field
4594:             * @deprecated since 5.6, use getJDBCMetaData().isMultiValued(String)
4595:             */
4596:            public synchronized boolean isMultiValued(String fieldName)
4597:                    throws DBException {
4598:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
4599:
4600:                return oneField.isMultiValued();
4601:            } /* isMultiValued(String) */
4602:
4603:            /**
4604:             * Is a given field readOnly - these fields are not offered for entry
4605:             * when a form is produced by the generic database maintenance servlet
4606:             *
4607:             * @param fieldName The field name to check
4608:             * @return True of the field is "read only", false if it is not
4609:             * @throws DBException Ff there is no such field
4610:             * @deprecated since 5.6, use getJDBCMetaData().isReadOnly(String)
4611:             */
4612:            public boolean isReadOnly(String fieldName) throws DBException {
4613:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
4614:
4615:                if (oneField == null) {
4616:                    throw new DBException("(" + myClassName
4617:                            + ") No such field '" + fieldName + "'");
4618:                }
4619:
4620:                return oneField.isReadOnly();
4621:            } /* isReadOnly(String) */
4622:
4623:            /**
4624:             * Is a given field 'secret' - these fields are not shown
4625:             * when a list is produced by the generic database maintenance servlet (DBMaint).
4626:             * This means that only users with update permission to the record can see the
4627:             * value of the specified field.
4628:             *
4629:             * @param fieldName The name of the field to check
4630:             * @return True if the field is 'secret', false if it is not
4631:             * @throws DBException If there is no such field.
4632:             * @see #setSecret(String)
4633:             * @deprecated since 5.6, use getJDBCMetaData().isSecret(String)
4634:             */
4635:            public boolean isSecret(String fieldName) throws DBException {
4636:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
4637:
4638:                return oneField.isSecret();
4639:            } /* isSecret(String) */
4640:
4641:            /**
4642:             * Is a given field virtual? A virtual field is not stored in the target table
4643:             * for this object - it may be computed, or stored in another table.
4644:             *
4645:             * @param fieldName The name of the field to check
4646:             * @return True of the field is virtual, false if it is not
4647:             * @throws DBException If there is no such field
4648:             * @see #addVirtualField(String, String, int, String)
4649:             * @deprecated since 5.6, use getJDBCMetaData().isVirtual(String)
4650:             */
4651:            public boolean isVirtual(String fieldName) throws DBException {
4652:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
4653:
4654:                return oneField.isVirtual();
4655:            } /* isVirtual(String) */
4656:
4657:            /**
4658:             * Find the maximum of the values in the specified field of records
4659:             * se;lected by the DBObject selection criteria.
4660:             *
4661:             * @param fieldName String DBObject fieldName to average
4662:             * @return double Maximum of the records matching the criteria
4663:             * @throws DBException If the search could not be completed
4664:             */
4665:            public double max(String fieldName) throws DBException {
4666:                return sqlAggrFunction("MAX", fieldName);
4667:            } /* max() */
4668:
4669:            /**
4670:             * Find the minimum of the values in the specified field of records
4671:             * selected by the DBObject selection criteria.
4672:             *
4673:             * @param fieldName String DBObject fieldName to average
4674:             * @return double Minimum of the records matching the criteria
4675:             * @throws DBException If the search could not be completed
4676:             */
4677:            public double min(String fieldName) throws DBException {
4678:                return sqlAggrFunction("MIN", fieldName);
4679:            } /* min() */
4680:
4681:            /**
4682:             * Strip out the newlines out of a string
4683:             *
4684:             * @param fieldValue the Value to strip of newlines
4685:             * @return the data with newlines stripped
4686:             */
4687:            protected String noNewLine(String fieldValue) {
4688:                String returnValue = fieldValue;
4689:
4690:                if (returnValue.indexOf("\n") != 0) {
4691:                    returnValue = StringUtil.replace(returnValue, "\n", "");
4692:                }
4693:                if (returnValue.indexOf("\r") != 0) {
4694:                    returnValue = StringUtil.replace(returnValue, "\r", "");
4695:                }
4696:
4697:                return returnValue;
4698:            } /* noNewLine(String) */
4699:
4700:            /**
4701:             * Utility method to return a string with all single quotes replaced with a pair of
4702:             * single quotes, and all double quotes also replaced with a pair of single quotes
4703:             *
4704:             * @param oldString The original string
4705:             * @return The string modified as above
4706:             */
4707:            protected String noQuotes(String oldString) {
4708:                String newString = StringUtil.replace(oldString, "'", "''");
4709:                String newString2 = StringUtil.replace(newString, "\"", "''");
4710:
4711:                return newString2;
4712:            } /* noQuotes(String) */
4713:
4714:            /**
4715:             * 'Pseudo' factory method to retrieve the caching utility class instance.
4716:             *
4717:             * @return CacheUtils instance
4718:             */
4719:            protected CacheUtils getCacheUtil() {
4720:                return cacheUtils;
4721:            }
4722:
4723:            /**
4724:             * Used by internal caching to ensure that caches are cleared when an update is
4725:             * made. Notifies all of the objects registered to receive "update events" on
4726:             * this object of the fact that an update has occurred. The "listener" then
4727:             * clears or updates it's cache as appropriate.
4728:             *
4729:             * @param eventCode The "code" for the event that has just occurred
4730:             * @throws DBException If an error occurrs trying to send all of the event notices
4731:             */
4732:            protected synchronized void notifyListeners(String eventCode)
4733:                    throws DBException {
4734:
4735:                /* Tell the cache manager to clear our db object cache and */
4736:                /*  valid value cache */
4737:                try {
4738:                    CacheManager.getInstance();
4739:                    CacheSystem cs = CacheManager
4740:                            .getCacheSystem(getDataContext());
4741:                    //Caching is not enabled if cs == null
4742:                    if (cs == null) {
4743:                        return;
4744:                    }
4745:
4746:                    if (EVENT_ADD.equals(eventCode)) {
4747:                        //Add will automatically put it in the cache, which, in turn
4748:                        //will clear out related values
4749:                        //While it may seem odd that we're removing it when an item
4750:                        //is added, right now, auto-inc fields won't necessarily
4751:                        //get transferred properly into the cache.
4752:                        cs.removeItem(myClassName, this );
4753:                    } else if (EVENT_UPDATE.equals(eventCode)) {
4754:                        //Add will automatically put it in the cache, which, in turn
4755:                        //will clear out related values
4756:                        cacheUtils.addToCache(this );
4757:                    } else if (EVENT_DELETE.equals(eventCode)) {
4758:                        //Removal will clear out related values
4759:                        cs.removeItem(myClassName, this );
4760:                    } else {
4761:                        throw new DBException("Unknown Notification Code: "
4762:                                + eventCode);
4763:                    }
4764:
4765:                    /* Must clear the entire valid values cache */
4766:                    //Should now be taken care of via cache listening listening
4767:                    //            cs.clear(myClassName + ".validValues");
4768:                } catch (CacheException ce) {
4769:                    log.error("Unable to clear caches correctly", ce);
4770:                }
4771:
4772:            } /* notifyListeners(String) */
4773:
4774:            /**
4775:             * populateDefaultValues is called by the schema object to allow a table to
4776:             * populate itself with any desired values.  <p>
4777:             * The base class implementation does nothing.  Override this method in derived
4778:             * classes to achieve your custom behavior. <p>
4779:             * <p/>
4780:             * The calling routine, DBTool.populateTables(), will set  dbName on each
4781:             * object, so you can get it from getDataContext(), and you can assume that the
4782:             * user for the population action is Admin.
4783:             *
4784:             * @throws DBException Upon add error, never if it isn't overridden.
4785:             */
4786:            public synchronized void populateDefaultValues() throws DBException {
4787:
4788:                //Base class version does nothing.
4789:            } /* populateDefaultValues() */
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 stream)
4799:                    throws IOException {
4800:                try {
4801:                    myClassName = getClass().getName();
4802:                    initialize();
4803:                    stream.defaultReadObject();
4804:                    if (fieldData != null) {
4805:                        DataObjectMetaData metadata = getMetaData();
4806:                        for (Iterator i = fieldData.keySet().iterator(); i
4807:                                .hasNext();) {
4808:                            String fieldName = (String) i.next();
4809:                            DataField dataField = (DataField) fieldData
4810:                                    .get(fieldName);
4811:                            if (dataField.getOwner() == null) {
4812:                                dataField.setOwner(this );
4813:                            }
4814:
4815:                            if (dataField.getFieldMetaData() == null) {
4816:                                dataField.setFieldMetaData(metadata
4817:                                        .getFieldMetadata(fieldName));
4818:                            }
4819:                        }
4820:                    }
4821:                } catch (DBException dbe) {
4822:                    dbe.printStackTrace();
4823:                    throw new IOException(
4824:                            "Error initializing deserialized DBObject. "
4825:                                    + dbe.getMessage());
4826:                } catch (Throwable t) {
4827:                    t.printStackTrace();
4828:                    log.error("Error instantiating dbobject from stream", t);
4829:                    throw new IOException(
4830:                            "Error initializing deserialized DBObject. "
4831:                                    + t.getMessage());
4832:                }
4833:            }
4834:
4835:            /**
4836:             * Writes a DBObject to the stream.  We write only a minimal data to the
4837:             * stream since serialization would otherwise be expensive.  Specifically
4838:             * we only write the data values, and nothing else about the class.
4839:             *
4840:             * @param stream The stream to write to
4841:             * @throws IOException upon communication error
4842:             */
4843:            private void writeObject(ObjectOutputStream stream)
4844:                    throws IOException {
4845:                stream.defaultWriteObject();
4846:            }
4847:
4848:            /**
4849:             * The reverse of the checkRef method - if this object is referred to
4850:             * by some other object, and we are changing our own key (only allowed
4851:             * by deleting)
4852:             * then check the other table to make sure our key is not being used by it!
4853:             * <p>This method should by called by subclass delete() methods
4854:             * (which then call super.delete() to handle
4855:             * the actual deletion)</p>
4856:             *
4857:             * @param refObject       An instance of the DBObject that refers to us
4858:             * @param foreignKeyNames A semicolon-delimited list of foreign
4859:             *                        key names from the other object
4860:             * @param errorMessage    The error message to throw if the key is in use
4861:             * @throws DBException If it is not possible to register the
4862:             *                     given referential constraint
4863:             */
4864:            protected synchronized void referredToBy(DBObject refObject,
4865:                    String foreignKeyNames, String errorMessage)
4866:                    throws DBException {
4867:                refObject.setDataContext(getDataContext());
4868:
4869:                Vector foreignKeys = new Vector();
4870:
4871:                /* Make a list from the foreignKeyNames */
4872:                StringTokenizer stk = new StringTokenizer(foreignKeyNames, ";");
4873:
4874:                while (stk.hasMoreTokens()) {
4875:                    foreignKeys.addElement(stk.nextToken());
4876:                }
4877:
4878:                /* Get the list of key fields from the refObject */
4879:                Enumeration fKeys = foreignKeys.elements();
4880:
4881:                for (Iterator myKeys = getKeyFieldListIterator(); myKeys
4882:                        .hasNext();) {
4883:                    String OneMyKey;
4884:                    // Commented out by MR - the only case where I can see this would be a problem
4885:                    //is in a race condition.
4886:                    //            if (myKeys.hasNext()) {
4887:                    OneMyKey = (String) myKeys.next();
4888:                    //            } else {
4889:                    //                throw new DBException("(" + myClassName +
4890:                    //                                      ") Wrong number of key elements");
4891:                    //            }
4892:
4893:                    String OneRefKey = (String) fKeys.nextElement();
4894:                    refObject.setField(OneRefKey, getField(OneMyKey));
4895:                }
4896:                /* Call retrieve on the ref object & catch any error */
4897:                try {
4898:                    refObject.search();
4899:
4900:                    if (refObject.getFoundCount() != 0) {
4901:                        throw new DBException("(" + myClassName
4902:                                + "):No record found");
4903:                    }
4904:                } catch (DBException de) {
4905:                    //If the target table doesn't exist, we have not relational problem
4906:                    //here ;)
4907:                    if (de.getMessage().indexOf("does not exist") > -1) {
4908:                        return;
4909:                    }
4910:                    throw new DBException("(" + myClassName + ")"
4911:                            + errorMessage, de);
4912:                }
4913:            } /* referredToBy(DBObject, String, String) */
4914:
4915:            /**
4916:             * Remove a specific object from that object's cache. It will then be re-read
4917:             * when a new object is requested by a client program
4918:             * <p/>
4919:             * Creation date: (4/18/00 1:49:46 PM)
4920:             *
4921:             * @param theDBObj com.jcorporate.expresso.core.dbobj.DBObject The object to
4922:             *                 be removed
4923:             * @throws DBException upon error.
4924:             */
4925:            public synchronized void removeFromCache(DBObject theDBObj)
4926:                    throws DBException {
4927:                try {
4928:                    CacheManager.getInstance();
4929:                    CacheManager.removeItem(theDBObj.getDataContext(),
4930:                            theDBObj.myClassName, theDBObj);
4931:                } catch (CacheException ce) {
4932:                    throw new DBException(ce);
4933:                }
4934:            } /* removeFromCache(DBObject) */
4935:
4936:            /**
4937:             * Removes an attribute from this particular database object.
4938:             *
4939:             * @param attributeName The name of the attribute to remove.
4940:             */
4941:            public void removeAttribute(String attributeName) {
4942:                attributes.remove(attributeName);
4943:            }
4944:
4945:            /**
4946:             * Get a particular record from the database into this object's fields
4947:             * Key fields for this object must be set; throws otherwise
4948:             *
4949:             * @throws DBRecordNotFoundException If the record could not be retrieved.
4950:             * @throws DBException               [Really DBRecordNotFoundException]
4951:             * @see #find()
4952:             */
4953:            public void retrieve() throws DBException {
4954:
4955:                if (!haveAllKeys()) {
4956:                    throw new DBRecordNotFoundException("(" + myClassName
4957:                            + ") does not have key fields set.");
4958:                }
4959:
4960:                if (getExecutor().retrieve(this ) == false) {
4961:                    throw new DBRecordNotFoundException("(" + myClassName
4962:                            + ") No such record" + forKey());
4963:                }
4964:
4965:                if (getDef().isLoggingEnabled()) {
4966:                    myUpdates = null;
4967:                }
4968:
4969:                // after retrieve, we know that 'current' values are now the baseline for comparison
4970:                cacheIsChangedComparison();
4971:                setStatus(BaseDataObject.STATUS_CURRENT);
4972:
4973:                if (isCached() && !anyFieldsToRetrieve) {
4974:                    if (log.isDebugEnabled()) {
4975:                        log.debug("Adding cached " + myClassName + " key "
4976:                                + getKey());
4977:                    }
4978:
4979:                    cacheUtils.addUnmodifiedToCache(this );
4980:                } else {
4981:                    if (log.isDebugEnabled()) {
4982:                        log.debug("Not adding " + myClassName
4983:                                + " to the cache - we don't cache it");
4984:                    }
4985:                }
4986:
4987:            } /* retrieve() */
4988:
4989:            /**
4990:             * Retrieve this object from cache, if possible.
4991:             *
4992:             * @return true if the cache supplied this item, false
4993:             *         otherwise
4994:             * @throws DBException upon error.
4995:             */
4996:            public boolean retrieveFromCache() throws DBException {
4997:
4998:                CacheManager.getInstance();
4999:                CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
5000:                if (cs == null) {
5001:                    return false;
5002:                }
5003:
5004:                if (retrieveFields == null) {
5005:                    retrieveFields = new HashMap(getMetaData()
5006:                            .getFieldListArray().size());
5007:                }
5008:
5009:                DBField oneField = null;
5010:
5011:                try {
5012:                    ConfigJdbc myConfig = ConfigManager
5013:                            .getJdbcRequired(getDataContext());
5014:
5015:                    if (isCached()) {
5016:                        if (myConfig.dbStats()) {
5017:                            String statName = myClassName + "|"
5018:                                    + getDataContext();
5019:                            synchronized (sCacheStats) {
5020:                                CacheStatEntry ce = (CacheStatEntry) sCacheStats
5021:                                        .get(statName);
5022:
5023:                                if (ce == null) {
5024:                                    ce = new CacheStatEntry(myClassName,
5025:                                            getDataContext());
5026:                                    //Synchronization is not necessary because the ConcurrentReaderHashMap
5027:                                    //takes care of synchronization for us.
5028:                                    sCacheStats.put(statName, ce);
5029:                                }
5030:
5031:                                ce.incCounts(true);
5032:                            }
5033:                        }
5034:
5035:                        if (log.isDebugEnabled()) {
5036:                            log.debug("Looking for " + myClassName + "cache");
5037:                        }
5038:
5039:                        if (cs.existsCache(myClassName)) {
5040:                            DBObject cachedObject = (DBObject) cs.getItem(
5041:                                    myClassName, getKey());
5042:
5043:                            if (cachedObject != null) {
5044:                                JDBCObjectMetaData metadata = getJDBCMetaData();
5045:                                if (anyFieldsToRetrieve) {
5046:                                    String oneFieldName = null;
5047:
5048:                                    for (Iterator i = getFieldsToRetrieveIterator(); i
5049:                                            .hasNext();) {
5050:                                        oneFieldName = (String) i.next();
5051:                                        DataFieldMetaData vField = getFieldMetaData(oneFieldName);
5052:                                        if (!vField.isLongBinaryType()) {
5053:                                            setField(oneFieldName, cachedObject
5054:                                                    .getField(oneFieldName));
5055:                                        } else {
5056:                                            setField(
5057:                                                    oneFieldName,
5058:                                                    cachedObject
5059:                                                            .getFieldByteArray(oneFieldName));
5060:                                        }
5061:                                    } /* for each field */
5062:
5063:                                    for (Iterator i = metadata.getAllKeysMap()
5064:                                            .values().iterator(); i.hasNext();) {
5065:                                        oneField = (DBField) i.next();
5066:                                        String fieldName = oneField.getName();
5067:                                        if (!retrieveFields
5068:                                                .containsKey(fieldName)) {
5069:                                            setField(fieldName, cachedObject
5070:                                                    .getField(fieldName));
5071:                                        }
5072:                                    }
5073:                                } else { /* for each key field */
5074:
5075:                                    for (Iterator it = metadata
5076:                                            .getAllFieldsMap().values()
5077:                                            .iterator(); it.hasNext();) {
5078:                                        oneField = (DBField) it.next();
5079:                                        String fieldName = oneField.getName();
5080:
5081:                                        if (!oneField.isVirtual()
5082:                                                && !oneField
5083:                                                        .isBinaryObjectType()
5084:                                                && !oneField.isLongBinaryType()) {
5085:                                            set(fieldName, cachedObject
5086:                                                    .getDataField(fieldName)
5087:                                                    .asString());
5088:                                        } else if (oneField.isLongBinaryType()) {
5089:                                            set(
5090:                                                    fieldName,
5091:                                                    cachedObject
5092:                                                            .getFieldByteArray(fieldName));
5093:                                        }
5094:                                    } /* for each field */
5095:
5096:                                } /* if anyFieldsToRetrieve */
5097:
5098:                                // we know that 'current' values are now the baseline for comparison
5099:                                cacheIsChangedComparison();
5100:
5101:                                //
5102:                                //Set it to current since the Cache contains
5103:                                //a current copy.
5104:                                //
5105:                                setStatus(BaseDataObject.STATUS_CURRENT);
5106:                                return true;
5107:                            } /* if the object from the cache was not null */
5108:
5109:                        } /* if the cache was initialized */
5110:
5111:                    } /* if we do caching for this object */
5112:
5113:                    if (myConfig.dbStats()) {
5114:                        synchronized (sCacheStats) {
5115:                            String statName = myClassName + "|"
5116:                                    + getDataContext();
5117:
5118:                            CacheStatEntry ce = (CacheStatEntry) sCacheStats
5119:                                    .get(statName);
5120:
5121:                            if (ce == null) {
5122:                                ce = new CacheStatEntry(myClassName,
5123:                                        getDataContext());
5124:                                sCacheStats.put(statName, ce);
5125:                            }
5126:
5127:                            ce.incCounts(false);
5128:                        }
5129:                    }
5130:                } catch (ConfigurationException ce) {
5131:                    throw new DBException(ce);
5132:                }
5133:
5134:                return false;
5135:            } /* retrieveFromCache() */
5136:
5137:            /**
5138:             * Find a set of keys of all of the objects that match the current
5139:             * search critieria in the fields. Search assumes that the fields are
5140:             * populated with search criteria instead of data
5141:             * NOTE: Criteria in 'text' type colums is ignored. After the search, the
5142:             * foundKeys vector is populated with the keys of the records found by the search.
5143:             *
5144:             * @throws DBException if unable to retreive records due to a database problem
5145:             *                     Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
5146:             */
5147:            public synchronized void search() throws DBException {
5148:                boolean needComma = false;
5149:
5150:                if (getDef().isLoggingEnabled()) {
5151:                    myUpdates = null;
5152:                }
5153:
5154:                /* Clear the previous result set */
5155:                foundKeys = null;
5156:
5157:                FastStringBuffer myStatement = new FastStringBuffer(128);
5158:                myStatement.append("SELECT ");
5159:
5160:                if (anyFieldsToRetrieve) {
5161:                    String oneFieldName = null;
5162:
5163:                    for (Iterator i = getFieldsToRetrieveIterator(); i
5164:                            .hasNext();) {
5165:                        oneFieldName = (String) i.next();
5166:
5167:                        if (needComma) {
5168:                            myStatement.append(", ");
5169:                        }
5170:
5171:                        myStatement.append(selectFieldString(oneFieldName));
5172:                        needComma = true;
5173:                    }
5174:                } else { /* for each field */
5175:                    for (Iterator e = getJDBCMetaData().getKeyFieldListArray()
5176:                            .iterator(); e.hasNext();) {
5177:                        if (needComma) {
5178:                            myStatement.append(", ");
5179:                        }
5180:
5181:                        String fieldName = (String) e.next();
5182:                        myStatement.append(selectFieldString(fieldName));
5183:                        needComma = true;
5184:                    } /* for */
5185:
5186:                } /* if anyFieldsToRetrieve */
5187:
5188:                myStatement.append(" FROM ");
5189:                myStatement.append(getJDBCMetaData().getTargetSQLTable(
5190:                        this .getDataContext()));
5191:
5192:                if (customWhereClause != null) {
5193:                    myStatement.append(customWhereClause);
5194:                    customWhereClause = null;
5195:                } else {
5196:                    myStatement.append(buildWhereClause(true));
5197:                }
5198:                /* Add the ORDER BY clause if any sortKeys are specified */
5199:                if (sortKeys != null && sortKeys.size() > 0) {
5200:                    myStatement.append(" ORDER BY ");
5201:
5202:                    boolean needComma2 = false;
5203:
5204:                    for (Iterator it = sortKeys.iterator(); it.hasNext();) {
5205:                        if (needComma2) {
5206:                            myStatement.append(", ");
5207:                        }
5208:
5209:                        myStatement.append((String) it.next());
5210:                        needComma2 = true;
5211:                    }
5212:                }
5213:
5214:                DBConnection myConnection = null;
5215:
5216:                try {
5217:                    if (localConnection != null) {
5218:                        myConnection = localConnection;
5219:                    } else {
5220:                        myConnection = getConnectionPool().getConnection(
5221:                                myClassName);
5222:                    }
5223:
5224:                    myConnection.execute(myStatement.toString());
5225:
5226:                    if (foundKeys == null) {
5227:                        foundKeys = new ArrayList();
5228:                    }
5229:                    while (myConnection.next()) {
5230:                        String oneKeyString = ("");
5231:                        int allKeysSize = getMetaData().getAllKeysMap().size();
5232:
5233:                        for (int ind = 1; ind <= allKeysSize; ind++) {
5234:                            if (ind > 1) {
5235:                                oneKeyString = oneKeyString + "/";
5236:                            }
5237:                            try {
5238:                                if (myConnection.isStringNotTrim()) {
5239:                                    oneKeyString = oneKeyString
5240:                                            + StringUtil.notNull(myConnection
5241:                                                    .getStringNoTrim(ind));
5242:                                } else {
5243:                                    oneKeyString = oneKeyString
5244:                                            + StringUtil.notNull(myConnection
5245:                                                    .getString(ind));
5246:                                }
5247:                            } catch (DBException de) {
5248:                                throw new DBException("Error retrieving field "
5249:                                        + "at index " + ind + ":"
5250:                                        + de.getMessage());
5251:                            }
5252:                        } /* for each key field */
5253:
5254:                        foundKeys.add(oneKeyString);
5255:                    } /* while */
5256:
5257:                } catch (DBException de) {
5258:                    throw de;
5259:                } finally {
5260:                    if (localConnection == null && myConnection != null) {
5261:                        myConnection.release();
5262:                    }
5263:                }
5264:            } /* search() */
5265:
5266:            /**
5267:             * Second form of search: takes a list of sort keys & calls regular search.
5268:             * The records retrieved are in the specified order
5269:             *
5270:             * @param sortKeyString A string containing field names,
5271:             *                      seperated by pipes, that indicate in what order the objects
5272:             *                      are retrieved
5273:             * @throws DBException upon error.
5274:             */
5275:            public synchronized void search(String sortKeyString)
5276:                    throws DBException {
5277:                setSortKey(sortKeyString);
5278:                search();
5279:            } /* search(String) */
5280:
5281:            /**
5282:             * Find a set of records of all of the objects that match the current
5283:             * search critieria in the fields
5284:             * and retrieve the list of all records that match this criteria
5285:             * NOTE: Criteria in 'text' type colums is ignored (SQL Server limitation)
5286:             * <p/>
5287:             * SIDE-EFFECT: custom 'where' clause is set to null.
5288:             *
5289:             * @return Vector A vector of new database objects containing the results
5290:             *         of the search
5291:             * @throws DBException If the search could not be completed
5292:             */
5293:            public synchronized ArrayList searchAndRetrieveList()
5294:                    throws DBException {
5295:
5296:                ArrayList retrievedFieldList = new ArrayList();
5297:
5298:                DBConnection myConnection = null;
5299:                try {
5300:                    myConnection = createAndExecuteSearch(retrievedFieldList);
5301:
5302:                    int recordCount = 0;
5303:                    int retrieveCount = 0;
5304:
5305:                    while (myConnection.next()) {
5306:                        recordCount++;
5307:                        retrieveCount++;
5308:
5309:                        //If there's limitation syntax on, then the first record will be the
5310:                        //maximum record.
5311:                        if (retrieveCount < offsetRecord
5312:                                && offsetRecord > 0
5313:                                && myConnection.getLimitationPosition() == DBConnection.LIMITATION_DISABLED) {
5314:                            continue;
5315:                        } else if (retrieveCount == offsetRecord
5316:                                && offsetRecord > 0
5317:                                && myConnection.getLimitationPosition() == DBConnection.LIMITATION_DISABLED) {
5318:                            recordCount = 0; //Reset count for counting for max records.
5319:                            continue; //Skip this record... next one, we will start loading.
5320:                        }
5321:                        if ((recordCount > maxRecords) && (maxRecords > 0)) {
5322:                            setAttribute("More Records", "Y");
5323:                            break;
5324:                        }
5325:
5326:                        //Only allocate if we're gonna load this record
5327:                        DBObject myObj = newInstance();
5328:                        loadFromConnection(myObj, myConnection,
5329:                                retrievedFieldList);
5330:                        recordSet.add(myObj);
5331:                    }
5332:                } catch (DBException de) {
5333:                    log.error("Error performing searchAndRetrieveList", de);
5334:                    throw new DBException(de);
5335:                } catch (Throwable t) {
5336:                    log.error("Error performing searchAndRetrieveList", t);
5337:                    throw new DBException(
5338:                            "Error performing searchAndRetrieveList", t);
5339:                } finally {
5340:                    if (localConnection == null) {
5341:                        if (myConnection != null) {
5342:                            getConnectionPool().release(myConnection);
5343:                        }
5344:                    }
5345:                }
5346:
5347:                return recordSet;
5348:            } /* searchAndRetrieveList() */
5349:
5350:            /**
5351:             * Search and retrieve in a particular order
5352:             *
5353:             * @param sortKeyString A pipe-delimited list of key fields to sort
5354:             *                      the returned set by
5355:             * @return Vector A vector of new database objects retrieved by the search
5356:             * @throws DBException If the search could not be completed
5357:             */
5358:            public synchronized ArrayList searchAndRetrieveList(
5359:                    String sortKeyString) throws DBException {
5360:                setSortKey(sortKeyString);
5361:                return searchAndRetrieveList();
5362:            } /* searchAndRetrieve(String) */
5363:
5364:            /**
5365:             * Set an attribute. Attributes are temporary (e.g. not stored in the DBMS) values
5366:             * associated with this particular DB object instance.
5367:             *
5368:             * @param attribName  The name of the attribute being defined
5369:             * @param attribValue The object to store under this attribute name
5370:             */
5371:            public synchronized void setAttribute(String attribName,
5372:                    Object attribValue) {
5373:                if (attributes == null) {
5374:                    attributes = new HashMap();
5375:                }
5376:
5377:                attributes.put(attribName, attribValue);
5378:            } /* setAttribute(String, Object) */
5379:
5380:            /**
5381:             * Read the configuration table to determine the max size
5382:             * of the cache for this db object.  The way we go about it is like so:
5383:             * <ol>
5384:             * <li>Check if setCacheSize(int) has been set before.  If so, return that</li>
5385:             * <li>Check if a specific entry exists for this object in the ControllerDefaults
5386:             * table if so return that</li>
5387:             * <li>Try to read the default entries in the table.  If it doesn't exist you're
5388:             * going to see a log warning.  Return that value</li>
5389:             * <li>And finally if that all fails, set the default cache size to zero</li>
5390:             * <ol>
5391:             */
5392:            public synchronized void setCacheSize() {
5393:
5394:                synchronized (DBObject.class) {
5395:                    //
5396:                    //Cache size has already been set.
5397:                    //
5398:                    if (myCacheSize >= 0) {
5399:                        return;
5400:                    }
5401:
5402:                    //
5403:                    //Check to see if we already have a value in the metadata
5404:                    //
5405:                    myCacheSize = getDef().getCacheSize();
5406:                    if (myCacheSize >= 0) {
5407:                        return;
5408:                    }
5409:
5410:                    CacheSystem cs = CacheManager
5411:                            .getCacheSystem(getDataContext());
5412:                    if (cs == null) {
5413:                        myCacheSize = 0;
5414:                        return;
5415:                    }
5416:                    /* Check if caching is enabled at all */
5417:                    try {
5418:                        ConfigJdbc myConfig = ConfigManager
5419:                                .getJdbcRequired(getDataContext());
5420:
5421:                        if (!myConfig.cache()) {
5422:                            if (log.isDebugEnabled()) {
5423:                                log.debug("We don't cache at all");
5424:                            }
5425:
5426:                            myCacheSize = 0;
5427:                        }
5428:                    } catch (ConfigurationException ce) {
5429:                        log.error(ce);
5430:                        myCacheSize = 0;
5431:                        return;
5432:                    }
5433:
5434:                    try {
5435:                        if (cs != null) {
5436:                            if (!cs
5437:                                    .existsCache("com.jcorporate.expresso.services.dbobj.DBObjLimit")) {
5438:                                cs
5439:                                        .createCache(
5440:                                                "com.jcorporate.expresso.services.dbobj.DBObjLimit",
5441:                                                false);
5442:                            }
5443:                        }
5444:
5445:                        DBObjLimit dbl = (DBObjLimit) cs
5446:                                .getItem(
5447:                                        com.jcorporate.expresso.services.dbobj.DBObjLimit.class
5448:                                                .getName(), myClassName);
5449:
5450:                        if (dbl == null) {
5451:                            dbl = new DBObjLimit();
5452:                            dbl.setDataContext(getDataContext());
5453:                            dbl.setField("DBObjectName", myClassName);
5454:
5455:                            try {
5456:                                dbl.retrieve();
5457:                            } catch (DBRecordNotFoundException de) {
5458:                                //
5459:                                //Get the default caching value
5460:                                //
5461:                                dbl
5462:                                        .setField("DBObjectName",
5463:                                                "com.jcorporate.expresso.services.dbobj.ControllerDefault");
5464:                                try {
5465:                                    dbl.retrieve();
5466:                                } catch (DBRecordNotFoundException dbe) {
5467:                                    log
5468:                                            .warn("Unable to locate default Controller Parameters for data caches for object"
5469:                                                    + myClassName
5470:                                                    + ".  Setting default cache sizes");
5471:                                    dbl.setField("DBObjectName", myClassName);
5472:                                    dbl.setField("CacheSize", "50");
5473:                                    dbl.setField("PageLimit", "0");
5474:                                    dbl.setField("TTL", "10");
5475:                                    dbl.add();
5476:                                }
5477:
5478:                            } catch (DBException de) {
5479:                                log
5480:                                        .warn("Unable to locate default Controller Parameters for data caches for object"
5481:                                                + myClassName
5482:                                                + " Setting cache size to zero");
5483:                                dbl.setField("DBObjectName", myClassName);
5484:                                dbl.setField("CacheSize", "0");
5485:                                dbl.setField("PageLimit", "0");
5486:                                dbl.setField("TTL", "10");
5487:                            }
5488:
5489:                            if (cs != null) {
5490:                                cs
5491:                                        .addItem(
5492:                                                "com.jcorporate.expresso.services.dbobj.DBObjLimit",
5493:                                                dbl,
5494:                                                DBObject.DBOBJLIMIT_CACHE_TTL);
5495:                            }
5496:                        }
5497:
5498:                        if (!dbl.getDataField("CacheSize").isNull()) {
5499:                            myCacheSize = dbl.getFieldInt("CacheSize");
5500:                            getDef().setCacheSize(myCacheSize);
5501:                        } else {
5502:                            myCacheSize = 50;
5503:                            getDef().setCacheSize(myCacheSize);
5504:                        }
5505:                        setAttribute("TTL", new Integer(dbl.getFieldInt("TTL")));
5506:                    } catch (CacheException ce) {
5507:                        log.error("Unable to set cache size - using zero", ce);
5508:                        myCacheSize = 0;
5509:                        setAttribute("TTL", new Integer(60 * 1000 * 10));
5510:                    } catch (DBException de) {
5511:                        log
5512:                                .error(
5513:                                        "Database exception setting cache size - using zero",
5514:                                        de);
5515:                        myCacheSize = 0;
5516:                        setAttribute("TTL", new Integer(60 * 1000 * 10));
5517:                    } catch (Throwable t) {
5518:                        log
5519:                                .error(
5520:                                        "Unknown exception setting cache size - using zero",
5521:                                        t);
5522:                        myCacheSize = 0;
5523:                        setAttribute("TTL", new Integer(60 * 1000 * 10));
5524:                    }
5525:                    getDef().setCacheSize(myCacheSize);
5526:
5527:                }
5528:            } /* setCacheSize() */
5529:
5530:            /**
5531:             * Set a characterset for a particular field.
5532:             * for more information on Filters and implementing Filters for your own
5533:             * characterset.
5534:             * <p/>
5535:             * Sets the characterset expected for a this DB object.  A Table's default
5536:             * is &quot;ISO-8859-1&quot;
5537:             *
5538:             * @param newCharSet The name of the characterset that you will filter against.
5539:             *                   For Western-Latin character sets use &quot;ISO-8859-1&quot; as the parameter.
5540:             *                   Currently, no other charactersets are implemented.  <br>
5541:             * @throws DBException upon error.
5542:             * @see com.jcorporate.expresso.core.security.filters.FilterManager
5543:             * @see com.jcorporate.expresso.core.security.filters.Filter
5544:             * @see com.jcorporate.expresso.core.security.filters.ISO_8859_1
5545:             */
5546:            public synchronized void setCharset(String newCharSet)
5547:                    throws DBException {
5548:                getDef().setCharset(newCharSet);
5549:            } /* setCharset(String) */
5550:
5551:            /**
5552:             * Gets the check zero update flags for this DBObject.
5553:             *
5554:             * @return boolean value that denotes if check zero update is on or off.
5555:             * @throws DBException upon error.
5556:             * @see #getCheckZeroUpdate
5557:             * @see com.jcorporate.expresso.core.misc.ConfigJdbc#checkZeroUpdate()
5558:             * @see com.jcorporate.expresso.core.db.DBConnectionPool#getCheckZeroUpdate()
5559:             */
5560:            public synchronized boolean getCheckZeroUpdate() throws DBException {
5561:                return getDef().checkZeroUpdate();
5562:            } /* setCheckZeroUpdate(boolean) */
5563:
5564:            /**
5565:             * Turn on or off the facility to verify that when an update is made that at
5566:             * least one record got updated. If this flag is on, and no records get updated,
5567:             * the update() method throws an Exception. Note that for some databases, if the
5568:             * existing record is not changed (e.g. it was already identical to what
5569:             * was being updated) this counts "no update" (notably, mySQL does this).
5570:             *
5571:             * @param newFlag True to turn on checking, false to turn it off
5572:             * @throws DBException upon error.
5573:             */
5574:            public synchronized void setCheckZeroUpdate(boolean newFlag)
5575:                    throws DBException {
5576:                getDef().setCheckZeroUpdate(newFlag);
5577:            } /* setCheckZeroUpdate(boolean) */
5578:
5579:            /**
5580:             * Set a specific DB connection for use with this db object. If you do not set
5581:             * a connection, the db object will request it's own connection from the
5582:             * appropriate connection pool & release it again after every operation (e.g.
5583:             * add, update, etc). It is important to use your own explicit connection when
5584:             * dealing with a database transactional environment (e.g. commit(), rollback()).
5585:             *
5586:             * @param newConnection The new DBConnection object to be used by this DB Object
5587:             * @throws DBException upon error.
5588:             */
5589:            public synchronized void setConnection(DBConnection newConnection)
5590:                    throws DBException {
5591:                setConnection(newConnection, newConnection.getDataContext());
5592:            } /* setConnection(DBConnection) */
5593:
5594:            /**
5595:             * <p/>
5596:             * Set a specific DB connection for use with this db object. If you do not set
5597:             * a connection, the db object will request it's own connection from the
5598:             * appropriate connection pool & release it again after every operation (e.g.
5599:             * add, update, etc). It is important to use your own explicit connection when
5600:             * dealing with a database transactional environment (e.g. commit(), rollback()).
5601:             * </p>
5602:             * <p>The difference between this and setConnection(DBConnection) is that this
5603:             * is used for using otherDB capabilities within a transaction.  So you use
5604:             * a dbconnection from your other pool, but the setup tables are in a different
5605:             * context</p>
5606:             *
5607:             * @param newConnection      The new DBConnection object to be used by this DB Object
5608:             * @param setupTablesContext the data context that is used for the expresso setup tables.
5609:             * @throws DBException upon error.
5610:             * @see #setConnection(DBConnection)
5611:             */
5612:            public synchronized void setConnection(DBConnection newConnection,
5613:                    String setupTablesContext) throws DBException {
5614:                localConnection = newConnection;
5615:                setDataContext(setupTablesContext);
5616:            }
5617:
5618:            /**
5619:             * Specify a custom "where" clause for the SQL used to retrieve records for this
5620:             * object. The where clause 'reset' after each call to searchAndRetrieve() or
5621:             * other retrieval methods, so it must be set just before the call to retrieve
5622:             * the records is made. If no custom where clause is specified by this method, the
5623:             * where clause is built from the field values in the object.
5624:             *
5625:             * @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
5626:             */
5627:            public synchronized void setCustomWhereClause(String newCustomWhere) {
5628:                setCustomWhereClause(newCustomWhere, false);
5629:            } /* setCustomWhereClause(String) */
5630:
5631:            /**
5632:             * Allows us to specify a custom WHERE clause and have it appended to the
5633:             * one built from the field values in the object
5634:             *
5635:             * @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
5636:             * @param append         if true, custom WHERE clause is appended
5637:             */
5638:            public synchronized void setCustomWhereClause(
5639:                    String newCustomWhere, boolean append) {
5640:
5641:                if (append) {
5642:                    customWhereClause = " AND " + newCustomWhere;
5643:                } else {
5644:                    if (newCustomWhere == null || newCustomWhere.length() == 0) {
5645:                        customWhereClause = null;
5646:                    } else {
5647:                        customWhereClause = WHERE_KEYWORD + newCustomWhere;
5648:                    }
5649:                }
5650:                appendCustomWhere = append;
5651:            } /* setCustomWhereClause(String) */
5652:
5653:            /**
5654:             * Allows us to query the custom where clause (if any). ' WHERE ' has been prepended to the clause.
5655:             * You may want to remove this prepended info for reuse, e.g. getCustomWhereClause().substring(WHERE_KEYWORD.length())
5656:             *
5657:             * @return java.lang.String or null if no custom where clause has been set.
5658:             */
5659:            public String getCustomWhereClause() {
5660:                return customWhereClause;
5661:            }
5662:
5663:            /**
5664:             * Define a "default" value for a field - to be used for the field
5665:             * when the user does not specify a value.
5666:             *
5667:             * @param fieldName  the field to set
5668:             * @param fieldValue the default value to set
5669:             * @throws DBException upon error.
5670:             */
5671:            protected synchronized void setDefaultValue(String fieldName,
5672:                    String fieldValue) throws DBException {
5673:                getDef().setDefaultValue(fieldName, fieldValue);
5674:            }
5675:
5676:            /**
5677:             * Set the description of this object
5678:             *
5679:             * @param newDescription A description of this database object
5680:             * @throws DBException upon error.
5681:             */
5682:            public synchronized void setDescription(String newDescription)
5683:                    throws DBException {
5684:                getDef().setDescription(newDescription);
5685:            } /* setDescription(String) */
5686:
5687:            /**
5688:             * Byte primitive integer Typesafe version of setField.
5689:             * Convenience method to set the field values.
5690:             *
5691:             * @param fieldName  The name to set
5692:             * @param fieldValue the byte integer field value to set
5693:             * @throws DBException upon error.
5694:             */
5695:            public synchronized void setField(String fieldName, byte fieldValue)
5696:                    throws DBException {
5697:                setField(fieldName, Byte.toString(fieldValue));
5698:            }
5699:
5700:            /**
5701:             * Byte primitive integer Typesafe version of setField.
5702:             * Convenience method to set the field values.
5703:             *
5704:             * @param fieldName  The name to set
5705:             * @param fieldValue the byte integer field value to set
5706:             * @throws DBException upon error.
5707:             */
5708:            public synchronized void setField(String fieldName,
5709:                    byte[] fieldValue) throws DBException {
5710:                StringUtil.assertNotBlank(fieldName,
5711:                        "Field name may not be blank");
5712:
5713:                if (getStatus() == null) {
5714:                    throw new DBException("Status is null");
5715:                }
5716:                if (getStatus().equals(BaseDataObject.STATUS_CURRENT)) {
5717:                    setStatus(BaseDataObject.STATUS_UPDATED);
5718:                }
5719:
5720:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
5721:
5722:                if (!oneField.isLongBinaryType()) {
5723:                    throw new DBException(
5724:                            "("
5725:                                    + this .myClassName
5726:                                    + ") Field "
5727:                                    + fieldName
5728:                                    + " is not a longvarbinary field. You should not be"
5729:                                    + " calling setField directly");
5730:                }
5731:
5732:                if (oneField.isVirtual()) {
5733:                    throw new DBException(
5734:                            "("
5735:                                    + this .myClassName
5736:                                    + ") Field "
5737:                                    + fieldName
5738:                                    + " is a virtual field. Database object should "
5739:                                    + "extend setField method to handle requests for this field");
5740:                }
5741:
5742:                clearError(fieldName);
5743:                if (fieldValue != null) {
5744:                    logChange((DBField) oneField, fieldValue.toString());
5745:                }
5746:
5747:                setFieldData(oneField.getName(), fieldValue);
5748:            }
5749:
5750:            /**
5751:             * Short primitive integer Typesafe version of setField.
5752:             * Convenience method to set the field values.
5753:             *
5754:             * @param fieldName  The name to set
5755:             * @param fieldValue the short integer field value to set
5756:             * @throws DBException upon error.
5757:             */
5758:            public synchronized void setField(String fieldName, short fieldValue)
5759:                    throws DBException {
5760:                setField(fieldName, Short.toString(fieldValue));
5761:            }
5762:
5763:            /**
5764:             * Integer primitive Typesafe version of setField.
5765:             * Convenience method to set the field value.
5766:             *
5767:             * @param fieldName  The name to set
5768:             * @param fieldValue the integer field value to set
5769:             * @throws DBException upon invalid parameters.
5770:             */
5771:            public synchronized void setField(String fieldName, int fieldValue)
5772:                    throws DBException {
5773:                setField(fieldName, Integer.toString(fieldValue));
5774:            }
5775:
5776:            /**
5777:             * Long primitive Typesafe version of <code>setField</code>.
5778:             * Convenience method to set the field value.
5779:             *
5780:             * @param fieldName  The name to set
5781:             * @param fieldValue the long integer field value to set
5782:             * @throws DBException upon invalid parameters.
5783:             */
5784:            public synchronized void setField(String fieldName, long fieldValue)
5785:                    throws DBException {
5786:                setField(fieldName, Long.toString(fieldValue));
5787:            }
5788:
5789:            /**
5790:             * Double primitive Typesafe version of <code>setField</code>.
5791:             * Convenience method to set the field values.
5792:             *
5793:             * @param fieldName  The name to set
5794:             * @param fieldValue the double field value to set
5795:             * @throws DBException upon invalid values.
5796:             */
5797:            public synchronized void setField(String fieldName,
5798:                    double fieldValue) throws DBException {
5799:                setField(fieldName, Double.toString(fieldValue));
5800:            }
5801:
5802:            /**
5803:             * BigDecimal object Typesafe version of <code>setField</code>.
5804:             * Convenience method to set the field values.
5805:             *
5806:             * @param fieldName  The name to set
5807:             * @param fieldValue the big decimal value to set
5808:             * @throws DBException upon error.
5809:             */
5810:            public synchronized void setField(String fieldName,
5811:                    BigDecimal fieldValue) throws DBException {
5812:                setField(fieldName, fieldValue.toString());
5813:            }
5814:
5815:            /**
5816:             * Boolean typesafe version of setField
5817:             *
5818:             * @param fieldName  The field name to set
5819:             * @param fieldValue the new boolean Field Value to set
5820:             * @throws DBException upon error.
5821:             */
5822:            public synchronized void setField(String fieldName,
5823:                    boolean fieldValue) throws DBException {
5824:                if (fieldValue) {
5825:                    setField(fieldName, "true");
5826:                } else {
5827:                    setField(fieldName, "false");
5828:                }
5829:            }
5830:
5831:            /**
5832:             * Internal refactoring for getting what a boolean field should be set to.
5833:             *
5834:             * @param fieldValue The target value
5835:             * @return the string value
5836:             * @throws DBException upon error.
5837:             */
5838:            protected String getBooleanFieldValue(boolean fieldValue)
5839:                    throws DBException {
5840:                try {
5841:                    boolean nativeBoolean = ConfigManager.getContext(
5842:                            getDataContext()).getJdbc().isNativeBool();
5843:
5844:                    if (fieldValue == true) {
5845:                        if (nativeBoolean) {
5846:                            return "true";
5847:                        } else {
5848:                            return "Y";
5849:                        }
5850:                    } else {
5851:                        if (nativeBoolean) {
5852:                            return "false";
5853:                        } else {
5854:                            return "N";
5855:                        }
5856:                    }
5857:                } catch (ConfigurationException ce) {
5858:                    throw new DBException(ce);
5859:                }
5860:            }
5861:
5862:            /**
5863:             * Set the given field to a given value. Call checkField
5864:             * before setting the value to verify that it is allowed for this field
5865:             * The subclass must override checkField as required
5866:             *
5867:             * @param fieldName  The name of the field
5868:             * @param fieldValue The value to set the field
5869:             * @throws DBException If the given field does not exist in this object or
5870:             *                     the value supplied is not allowed for this field
5871:             */
5872:            public synchronized void setField(String fieldName,
5873:                    String fieldValue) throws DBException {
5874:                StringUtil.assertNotBlank(fieldName,
5875:                        "Field name may not be blank");
5876:
5877:                if (getStatus() == null) {
5878:                    throw new DBException("Status is null");
5879:                }
5880:                if (getStatus().equals(BaseDataObject.STATUS_CURRENT)) {
5881:                    setStatus(BaseDataObject.STATUS_UPDATED);
5882:                }
5883:
5884:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
5885:
5886:                if (oneField.isBinaryObjectType()) {
5887:                    throw new DBException("(" + myClassName + ") Field "
5888:                            + fieldName
5889:                            + " is a binary object field. You should not be"
5890:                            + " calling setField directly");
5891:                }
5892:
5893:                if (oneField.isVirtual()) {
5894:                    throw new DBException(
5895:                            "("
5896:                                    + myClassName
5897:                                    + ") Field "
5898:                                    + fieldName
5899:                                    + " is a virtual field. Database object should "
5900:                                    + "extend setField method to handle requests for this field");
5901:                }
5902:                //
5903:                //If there was an error associated with this field before, then
5904:                //remove the error attribute.
5905:                //
5906:                clearError(fieldName);
5907:
5908:                if (!(oneField.allowsNull() && (fieldValue == null))) {
5909:                    // truncate strings if necessary
5910:                    // we don't truncate for query expressions such as
5911:                    // "BETWEEN 3 AND 5"
5912:                    if (oneField.isQuotedTextType()
5913:                            && (oneField.getLengthInt() > 0)
5914:                            && (fieldValue != null)
5915:                            && (fieldValue.length() > oneField.getLengthInt())
5916:                            && (denotesRange(fieldValue) == null)) {
5917:                        fieldValue = fieldValue.substring(0, oneField
5918:                                .getLengthInt());
5919:                    }
5920:
5921:                    /**
5922:                     * if it's not a text field and it has newlines or returns embedded,
5923:                     * take 'em out
5924:                     * @todo this doesn't seem right for varchar
5925:                     */
5926:                    if (fieldValue != null) {
5927:                        /* Check for returns or newlines in key fields - remove if found */
5928:                        if (oneField.getTypeString()
5929:                                .equalsIgnoreCase("varchar")) {
5930:                            fieldValue = noNewLine(fieldValue);
5931:                        } /* if not a text field */
5932:
5933:                        if (oneField.getTypeString().equalsIgnoreCase("char")) {
5934:                            fieldValue = noNewLine(fieldValue);
5935:                        } /* if not a text field */
5936:
5937:                        //Used to help mesh boolean values with checkboxes in html
5938:                        //fields
5939:                        if (oneField.isBooleanType()) {
5940:                            if (fieldValue.length() == 0) {
5941:                                if (log.isDebugEnabled()) {
5942:                                    log
5943:                                            .debug("Zero length boolean.  Skipping modification");
5944:                                }
5945:                            } else if (fieldValue.equalsIgnoreCase("Y")
5946:                                    || fieldValue.equalsIgnoreCase("t")
5947:                                    || fieldValue.equalsIgnoreCase("true")) {
5948:                                fieldValue = getBooleanFieldValue(true);
5949:                            } else {
5950:                                fieldValue = getBooleanFieldValue(false);
5951:                            }
5952:                        }
5953:                    } /* if field was null */
5954:
5955:                } /* field was not null and allowed to be */
5956:
5957:                logChange((DBField) oneField, fieldValue);
5958:
5959:                setFieldData(oneField.getName(), fieldValue);
5960:            } /* setField(String, String) */
5961:
5962:            /**
5963:             * Date object Typesafe version of <code>setField</code>.
5964:             * Convenience method to set the field values.
5965:             *
5966:             * @param fieldName  The name to set
5967:             * @param fieldValue the Java date value to set
5968:             * @throws DBException upon error.
5969:             */
5970:            public void setField(String fieldName, java.util.Date fieldValue)
5971:                    throws DBException {
5972:                DataFieldMetaData fieldMetadata = getFieldMetaData(fieldName);
5973:                if (fieldMetadata.isDateOnlyType()) {
5974:                    setField(fieldName, DateTime.getDateForDB(fieldValue,
5975:                            getDataContext()));
5976:                } else if (fieldMetadata.isTimeType()) {
5977:                    setField(fieldName, DateTime.getTimeForDB(fieldValue,
5978:                            getDataContext()));
5979:                } else {
5980:                    //Format as datetime
5981:                    setField(fieldName, DateTime.getDateTimeForDB(fieldValue,
5982:                            getDataContext()));
5983:                }
5984:            }
5985:
5986:            /**
5987:             * Helper Function:
5988:             * If Change Logging is enabled, then this logs what has changed
5989:             *
5990:             * @param oneField   The field to set the data to.
5991:             * @param fieldValue The value to set the field to.
5992:             */
5993:            protected void logChange(DBField oneField, String fieldValue) {
5994:                if (getDef().isLoggingEnabled()) {
5995:
5996:                    /* Record this setField */
5997:                    FieldUpdate myUpdate = new FieldUpdate();
5998:                    myUpdate.fieldName = oneField.getName();
5999:                    myUpdate.oldValue = getFieldData(myUpdate.fieldName);
6000:                    myUpdate.newValue = fieldValue;
6001:
6002:                    /* Put this update in the list of updates since the last retrieve */
6003:                    if (myUpdates == null) {
6004:                        myUpdates = new ArrayList(5);
6005:                    }
6006:
6007:                    myUpdates.add(myUpdate);
6008:                } /* if change logging is enabled */
6009:            }
6010:
6011:            /**
6012:             * Helper function that doesn't fire all the processing... just set's the
6013:             * field data in raw form and forgets it.
6014:             *
6015:             * @param fieldName  The name of the field to set
6016:             * @param fieldValue the value to set it to.
6017:             */
6018:            protected synchronized void setFieldData(String fieldName,
6019:                    String fieldValue) {
6020:                if (fieldData == null) {
6021:                    fieldData = new HashMap();
6022:                }
6023:
6024:                DataField df = (DataField) fieldData.get(fieldName);
6025:                if (df == null) {
6026:                    df = DefaultDataField.getInstance(
6027:                            getFieldMetaData(fieldName), this );
6028:                }
6029:
6030:                df.setValue(fieldValue);
6031:                fieldData.put(fieldName, df);
6032:            }
6033:
6034:            /**
6035:             * Helper function that doesn't fire all the processing... just set's the
6036:             * field data in raw form and forgets it.
6037:             *
6038:             * @param fieldName  The name of the field to set
6039:             * @param fieldValue the value to set it to.
6040:             */
6041:            protected synchronized void setFieldData(String fieldName,
6042:                    byte[] fieldValue) {
6043:                if (fieldData == null) {
6044:                    fieldData = new HashMap();
6045:                }
6046:
6047:                DataField df = (DataField) fieldData.get(fieldName);
6048:                if (df == null) {
6049:                    df = DefaultDataField.getInstance(this 
6050:                            .getFieldMetaData(fieldName), this );
6051:                }
6052:
6053:                df.setValue(fieldValue);
6054:                fieldData.put(fieldName, df);
6055:            }
6056:
6057:            /**
6058:             * Convenience method to set a field to be distinct or not
6059:             * within this database object.
6060:             *
6061:             * @param fieldName the name of the field
6062:             * @param flag      boolean value true if the field is set distinct.
6063:             * @throws DBException If the operation could not be completed
6064:             *                     <p/>
6065:             *                     author Peter Pilgrim <peter.pilgrim@db.com>
6066:             * @see #isFieldDistinct
6067:             * @see #isDistinct()
6068:             */
6069:            public void setFieldDistinct(String fieldName, boolean flag)
6070:                    throws DBException {
6071:
6072:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
6073:
6074:                if (oneField.isVirtual()) {
6075:                    throw new DBException(
6076:                            "("
6077:                                    + myClassName
6078:                                    + ") Field "
6079:                                    + fieldName
6080:                                    + " is a virtual field. Database object should extend "
6081:                                    + "getField method to handle requests for this field");
6082:                }
6083:
6084:                if (oneField.isBinaryObjectType() && flag) {
6085:                    throw new DBException(
6086:                            "("
6087:                                    + myClassName
6088:                                    + ") Field "
6089:                                    + fieldName
6090:                                    + " is a BLOB field.  It should not be retrieved under normal queries");
6091:                }
6092:
6093:                if (oneField.isCharacterLongObjectType()) {
6094:                    log
6095:                            .warn("You are selecting a DISTINCT row based upon a Long Character Object."
6096:                                    + "  Although the database may work with it, it will be a highly "
6097:                                    + "inefficient query.");
6098:                }
6099:
6100:                if (distinctFields == null) {
6101:                    distinctFields = new HashMap();
6102:                }
6103:
6104:                distinctFields.put(oneField.getName(), oneField.getName());
6105:                anyFieldsDistinct = true;
6106:            } /* setFieldDistinct(String, boolean) */
6107:
6108:            /**
6109:             * Convenience method to set the fields to be retrieved
6110:             * within this database object.
6111:             * <b>NOTE AS OF EXPRESSO 5.0</b> setFieldsToRetrieve() could cause you
6112:             * some problems if you're attempting to retrieve long objects since
6113:             * everything will be converted to a STRING.  Please use caution :).
6114:             *
6115:             * @param fieldNames contain the name of the fields separate by "|"
6116:             * @throws DBException If the operation could not be completed
6117:             *                     <p/>
6118:             *                     author Yves Henri Amaizo <amy_amaizo@compuserve.com>
6119:             * @see #isFieldsToRetrieve()
6120:             */
6121:            public void setFieldsToRetrieve(String fieldNames)
6122:                    throws DBException {
6123:
6124:                if (fieldNames == null) {
6125:                    // Allows DBObject to be used within a MultiDBObject for join purposes
6126:                    // when no fields are required from this DBObject
6127:                    anyFieldsToRetrieve = true;
6128:                    anyFieldsToRetrieveMulti = true;
6129:                    return;
6130:                }
6131:
6132:                if (retrieveFields == null) {
6133:                    retrieveFields = new HashMap();
6134:                }
6135:
6136:                String tempString = null;
6137:                StringTokenizer stk = new StringTokenizer(fieldNames, "|");
6138:
6139:                while (stk.hasMoreTokens()) {
6140:                    tempString = stk.nextToken();
6141:
6142:                    //
6143:                    //Will throw a DBException if the field doesn't exist.
6144:                    //
6145:                    DataFieldMetaData oneField = getFieldMetaData(tempString);
6146:
6147:                    if (!oneField.isCharacterLongObjectType()
6148:                            && !oneField.isLongBinaryType()
6149:                            && oneField.isLongObjectType()) {
6150:                        log
6151:                                .warn("Ignoring field to retrieve for field name: "
6152:                                        + tempString
6153:                                        + " the field type is considered a binary large object and"
6154:                                        + " should be retrieved separately");
6155:
6156:                        continue;
6157:                    }
6158:
6159:                    retrieveFields.put(oneField.getName(), oneField.getName());
6160:                    anyFieldsToRetrieve = true;
6161:                    anyFieldsToRetrieveMulti = true; // ADDED HERE BY ABHI
6162:                }
6163:            } /* setFieldsToRetrieve(String) */
6164:
6165:            /**
6166:             * Set the values for each of the key fields of this object from
6167:             * a /-delimited string
6168:             *
6169:             * @param keyValues The string containing one value for each key
6170:             *                  field in the object
6171:             * @throws DBException If the key fields cannot be set
6172:             */
6173:            public synchronized void setKeys(String keyValues)
6174:                    throws DBException {
6175:                StringTokenizer stk = new StringTokenizer(keyValues, "/");
6176:                ArrayList keysToSet = new ArrayList();
6177:
6178:                while (stk.hasMoreTokens()) {
6179:                    keysToSet.add(stk.nextToken());
6180:                }
6181:
6182:                ArrayList keys = getJDBCMetaData().getKeyFieldListArray();
6183:
6184:                if (keys.size() != keysToSet.size()) {
6185:                    throw new DBException("(" + myClassName + ") There are "
6186:                            + keys.size() + " key fields in this table, but "
6187:                            + keyValues + " only contains " + keysToSet.size()
6188:                            + " values");
6189:                }
6190:
6191:                Iterator l = keysToSet.iterator();
6192:
6193:                for (Iterator i = keys.iterator(); i.hasNext();) {
6194:                    setField((String) i.next(), (String) l.next());
6195:                }
6196:            } /* setKeys(String) */
6197:
6198:            /**
6199:             * Set a field's lookup object - this is the name of another database
6200:             * object that can be used to look up valid values for this object. This value
6201:             * is used by the DBMaint servlet to provide automatic lookup ability for
6202:             * related fields.  Side-effect: adds a listener for this object to cache manager
6203:             * in order to maintain caches for valid values
6204:             *
6205:             * @param fieldName  The field name that this lookup is associated with
6206:             * @param objectName The db object used to look up valid values for this field.
6207:             * @throws DBException upon error.
6208:             */
6209:            public synchronized void setLookupObject(String fieldName,
6210:                    String objectName) throws DBException {
6211:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
6212:                oneField.setLookupObject(objectName);
6213:            } /* setLookupObject(String, String) */
6214:
6215:            /**
6216:             * Sets the lookup field name.  This field allows automatic lookups to
6217:             * be performed by matching the field name given with the primary key of the
6218:             * lookup object.  Note to use this feature, the lookup object can only
6219:             * have one and only one primary key field at this time.
6220:             *
6221:             * @param fieldName       the name to set the lookup field value for.
6222:             * @param lookupFieldName the field name in the lookup object to map.
6223:             */
6224:            public synchronized void setLookupField(String fieldName,
6225:                    String lookupFieldName) {
6226:                getDef().getDBField(fieldName).setLookupField(lookupFieldName);
6227:            }
6228:
6229:            /**
6230:             * Set a "mask", or regular expresso to be matched, for the named field.
6231:             * This regular expression is then checked whenever the field is validated
6232:             * <p/>
6233:             * Note: Masks are used to validate a field's data type to prevent sql injections. So, when
6234:             * setting your own mask, make sure it excludes thwarts sql injections.
6235:             *
6236:             * @param fieldName The field name to set
6237:             * @param newMask   the regular expression to apply to this field
6238:             * @throws DBException upon error.
6239:             */
6240:            protected synchronized void setMask(String fieldName, String newMask)
6241:                    throws DBException {
6242:                getDef().setMask(fieldName, newMask);
6243:            } /* setMask(String, String) */
6244:
6245:            /**
6246:             * Specify a maximum number of records to be retrieved in any subsequent
6247:             * searchAndRetrieve() call. Records will be retrieved (in the specified
6248:             * sort order) until the specified maximum is reached, then the remainder
6249:             * of the result set is discarded. Specifying zero indicates that all
6250:             * records are to be retrieved.
6251:             *
6252:             * @param newMax The maximum number of records to retrieve.
6253:             * @throws DBException If the max number is less than 0
6254:             */
6255:            public synchronized void setMaxRecords(int newMax)
6256:                    throws DBException {
6257:                if (maxRecords < 0) {
6258:                    throw new DBException("Max records can't be less than 0");
6259:                }
6260:
6261:                maxRecords = newMax;
6262:            } /* setMaxRecords(int) */
6263:
6264:            /**
6265:             * Set this field to be multi-valued - e.g. there is a specific list of
6266:             * values that are valid for this field. There should be code in the
6267:             * getValidValues method that returns the valid values for the specified
6268:             * multi-valued field.
6269:             *
6270:             * @param fieldName The name of the field to specify as multi-valued
6271:             * @throws DBException If the field name given is not valid, or is a virtual field
6272:             */
6273:            protected synchronized void setMultiValued(String fieldName)
6274:                    throws DBException {
6275:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
6276:
6277:                if (oneField.isVirtual()) {
6278:                    throw new DBException("(" + myClassName + ") Field '"
6279:                            + fieldName
6280:                            + "' is a virtual field - not suitable for "
6281:                            + "setting as multi-valued");
6282:                }
6283:
6284:                oneField.setMultiValued(true);
6285:            } /* setMultiValued(String) */
6286:
6287:            /**
6288:             * Set the name of this object - this name is used to identify the db object
6289:             * with a more human-readable description
6290:             *
6291:             * @param theName New name for this object
6292:             * @throws DBException upon error.
6293:             */
6294:            protected synchronized void setName(String theName)
6295:                    throws DBException {
6296:                getDef().setName(theName);
6297:            } /* setName(String) */
6298:
6299:            /**
6300:             * Specifies the number of records that should be skipped over
6301:             * before any data from the <code>ResultSet</code>
6302:             * is retrieved in any subsequent
6303:             * searchAndRetrieve() call. Records will be skipped over (in the specified
6304:             * sort order) until the record counts is equal to or greater
6305:             * than the offset record. Specifying zero indicates that no
6306:             * records should be skipped over and the
6307:             * <code>ResultSet</code> immediately from the start.
6308:             *
6309:             * @param newOffset The maximum number of records to retrieve.
6310:             * @throws DBException If the max number is less than 0
6311:             *                     <p/>
6312:             *                     author Peter Pilgrim <peterp  at  xenonsoft dot demon dot co dot  uk>
6313:             */
6314:            public synchronized void setOffsetRecord(int newOffset)
6315:                    throws DBException {
6316:                if (newOffset < 0) {
6317:                    throw new DBException("Offset records can't be less than 0");
6318:                }
6319:
6320:                offsetRecord = newOffset;
6321:            } /* setOffsetRecord(int) */
6322:
6323:            /**
6324:             * Set a field as read-only - these fields are not offered for update
6325:             * when a form is produced by the generic database maintenance servlet
6326:             * (DBMaint). It does not otherwise affect the use of the field.
6327:             * <p>Typical uses are for serial-numbered fields and timestamps</p>
6328:             *
6329:             * @param fieldName The name of the field to be reserved as normally read-only.
6330:             * @throws DBException If there is no such field.
6331:             */
6332:            public synchronized void setReadOnly(String fieldName)
6333:                    throws DBException {
6334:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
6335:                oneField.setReadOnly();
6336:            } /* setReadOnly(String) */
6337:
6338:            /**
6339:             * Specify which schema this DB object belongs to. This is
6340:             * necessary when using the local-langauge strings in the
6341:             * DB object for field names, descriptions, etc. The schema
6342:             * will be assumed to be Expresso's schema if not otherwise
6343:             * set.
6344:             * From expresso 5.3, this method is called in ConfigManager at startup. You probably do
6345:             * not need to call it. (Previously, developers had to call it within each DBObject's setup().)
6346:             *
6347:             * @param schema The schema to set
6348:             * @throws DBException upon error.
6349:             */
6350:            public synchronized void setSchema(Schema schema)
6351:                    throws DBException {
6352:                getDef().setSchema(schema.getClass().getName());
6353:            } /* setSchema(String) */
6354:
6355:            /**
6356:             * Set a field as 'secret' - these fields are not shown
6357:             * when a list is produced by the generic database maintenance servlet. In
6358:             * this way, only users with update permission to the object can view the
6359:             * values of "secret" fields.
6360:             *
6361:             * @param fieldName The name of the field to set as 'secret'
6362:             * @throws DBException I fthe field does not exist
6363:             */
6364:            public synchronized void setSecret(String fieldName)
6365:                    throws DBException {
6366:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
6367:                oneField.setSecret();
6368:            } /* setSecret(String) */
6369:
6370:            /**
6371:             * Set a filter for a particular field, permanently for all instances of this class.
6372:             * for a means to set a filter on just a single instance, see setFilterClass
6373:             *
6374:             * @param fieldName    The name of the field to set a particular filter for.
6375:             * @param filterMethod The name of the filter method to use when calling a filter.
6376:             *                     Can be one of three values:<br> See {@link
6377:             *                     com.jcorporate.expresso.core.security.filters.FilterManager#filterString} for
6378:             *                     more information on this parameter.
6379:             * @return old filter just replaced
6380:             * @throws DBException if:  fieldName doesn't exist, or filterType String
6381:             *                     isn't a valid value.
6382:             * @see com.jcorporate.expresso.core.security.filters.FilterManager
6383:             * @see #setFilterClass(com.jcorporate.expresso.core.security.filters.Filter) for a means to set a filter on just an instance
6384:             */
6385:            public synchronized String setStringFilter(String fieldName,
6386:                    String filterMethod) throws DBException {
6387:
6388:                //
6389:                //Parameter Check
6390:                //
6391:                if (!(filterMethod.equalsIgnoreCase("standardFilter")
6392:                        || filterMethod.equalsIgnoreCase("stripFilter") || filterMethod
6393:                        .equalsIgnoreCase("rawFilter"))) {
6394:                    throw new DBException("Undefined Filter Method: "
6395:                            + filterMethod);
6396:                }
6397:
6398:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
6399:                return ((DBField) oneField).setFilterMethod(filterMethod);
6400:            } /* setStringFilter(String, String) */
6401:
6402:            /**
6403:             * get the current filter for a particular field. This is the 'static', permanent filter.
6404:             *
6405:             * @param fieldName The name of the field to set a particular filter for.
6406:             * @return java.lang.String the current filter method.
6407:             * @throws DBException if:  fieldName doesn't exist, or filterType String
6408:             *                     isn't a valid value.
6409:             * @see #getFilterClass() for a means to get a filter on just an instance
6410:             * @see com.jcorporate.expresso.core.security.filters.FilterManager
6411:             */
6412:            public synchronized String getStringFilter(String fieldName)
6413:                    throws DBException {
6414:
6415:                DataFieldMetaData oneField = getFieldMetaData(fieldName);
6416:                return ((DBField) oneField).getFilterMethod();
6417:            } /* getStringFilter(String) */
6418:
6419:            /**
6420:             * Set the target table for this DBObject. Note that an object
6421:             * can span tables by the use of virtual fields, but that this table
6422:             * is the default table for the object.
6423:             *
6424:             * @param theTable Table for this object
6425:             * @throws DBException upon error.
6426:             */
6427:            public synchronized void setTargetTable(String theTable)
6428:                    throws DBException {
6429:                getDef().setTargetTable(theTable);
6430:            } /* setTargetTable(String) */
6431:
6432:            /**
6433:             * Set the target table for this DBObject. Note that an object
6434:             * can span tables by the use of virtual fields, but that this table
6435:             * is the default table for the object.
6436:             *
6437:             * @param theDbSchema Table for this object
6438:             * @throws DBException upon error.
6439:             */
6440:            public synchronized void setTargetDbSchema(String theDbSchema)
6441:                    throws DBException {
6442:                getDef().setTargetDbSchema(theDbSchema);
6443:            } /* setTargetDbSchema(String) */
6444:
6445:            /**
6446:             * Method to set up the fields for this database object. This method should
6447:             * be defined in the implementing object and should make calls to addField,
6448:             * addKey, setMultiValued, etc. as required to define the content of
6449:             * the DBObject. Each setupFields method should call "super.setupFields()"
6450:             * so that field definitions can be "inheritcable"
6451:             *
6452:             * @throws DBException If there is an error setting up the fields
6453:             *                     as requested. For example, if a field allowing null is requested
6454:             *                     as part of the key
6455:             */
6456:            protected void setupFields() throws DBException {
6457:            }
6458:
6459:            /**
6460:             * This allows the invocation of the SQL AVG, MIN, MAX and SUM
6461:             * aggregate functions on one of the DB's columns (the DBObject
6462:             * fieldname is supplied).
6463:             * This function is not meant to be used directly, but is here
6464:             * as the common/shared code used by the following methods in DBObject:
6465:             * avg(String), min(String), max(String), sum(String).
6466:             *
6467:             * @param func      String The SQL aggregate function - must be one of AVG, MIN, MAX, SUM
6468:             * @param fieldName String The DBObject fieldName to use for the aggregate function
6469:             * @return double The result of the SQL function invocation
6470:             * @throws DBException If the function could not be completed
6471:             *                     Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
6472:             */
6473:            protected synchronized double sqlAggrFunction(String func,
6474:                    String fieldName) throws DBException {
6475:                FastStringBuffer myStatement = FastStringBuffer.getInstance();
6476:                String statementSQL = null;
6477:                try {
6478:                    myStatement.append("SELECT " + func);
6479:                    myStatement.append("(" + fieldName + ") FROM ");
6480:                    myStatement.append(this .getJDBCMetaData()
6481:                            .getTargetSQLTable(this .getDataContext()));
6482:                    FastStringBuffer fsb = FastStringBuffer.getInstance();
6483:                    try {
6484:                        if (customWhereClause != null) {
6485:                            if (appendCustomWhere) {
6486:                                myStatement.append(buildWhereClauseBuffer(true,
6487:                                        fsb));
6488:                                myStatement.append(customWhereClause);
6489:                                appendCustomWhere = false;
6490:                            } else {
6491:                                myStatement.append(customWhereClause);
6492:                            }
6493:                        } else {
6494:                            myStatement
6495:                                    .append(buildWhereClauseBuffer(true, fsb));
6496:                        }
6497:                    } finally {
6498:                        fsb.release();
6499:                    }
6500:                    statementSQL = myStatement.toString();
6501:                } finally {
6502:                    myStatement.release();
6503:                    myStatement = null;
6504:                }
6505:                DBConnection myConnection = null;
6506:                try {
6507:                    if (localConnection != null) {
6508:                        myConnection = localConnection;
6509:                    } else {
6510:                        myConnection = getConnectionPool().getConnection(
6511:                                myClassName);
6512:                    }
6513:
6514:                    myConnection.execute(statementSQL);
6515:
6516:                    if (myConnection.next()) {
6517:                        return myConnection.getDouble(1);
6518:                    }
6519:                } catch (DBException de) {
6520:                    throw de;
6521:                } finally {
6522:                    if (localConnection == null) {
6523:                        myConnection.release();
6524:                    }
6525:                }
6526:
6527:                return 0.0;
6528:            } /* sqlAggrFunction() */
6529:
6530:            /**
6531:             * Find the sum of the values in the specified field of records
6532:             * selected by the DBObject selection criteria.
6533:             *
6534:             * @param fieldName String DBObject fieldName to average
6535:             * @return double Sum of the records matching the criteria
6536:             * @throws DBException If the search could not be completed
6537:             */
6538:            public double sum(String fieldName) throws DBException {
6539:                return sqlAggrFunction("SUM", fieldName);
6540:            } /* sum() */
6541:
6542:            /**
6543:             * Update the database with the new info. Update affects only one record,
6544:             * and uses the current field values to write to the DBMS.
6545:             * <p/>
6546:             * by default, updates all fields each time.
6547:             * <p/>
6548:             * for efficiency, developers who are confident that their code does not have any
6549:             * 'blind updates' can set the Setup value UPDATE_CHANGED_ONLY to true
6550:             * (a blind update is where the object is not retieved before values in it are reset)
6551:             *
6552:             * @throws DBException if the update to the database fails due to
6553:             *                     a database error
6554:             * @see #update(boolean)
6555:             */
6556:            public void update() throws DBException {
6557:
6558:                /*
6559:                 * for efficiency, developers who are confident that their code does not have any
6560:                 * 'blind updates' can set the Setup value UPDATE_CHANGED_ONLY to true
6561:                 * (a blind update is where the object is not retieved before values in it are reset)
6562:                 */
6563:                String onlychanged = Setup.getValueUnrequired(getDataContext(),
6564:                        UPDATE_CHANGED_ONLY);
6565:                if (StringUtil.toBoolean(onlychanged)) {
6566:                    update(true);
6567:                } else {
6568:                    update(false);
6569:                }
6570:
6571:            }
6572:
6573:            /**
6574:             * Update the database with the new info. Update affects only one record,
6575:             * and uses the current field values to write to the DBMS.
6576:             * <p/>
6577:             * if param updateChangedFieldsOnly is true, only changed fields are updated.
6578:             * this assumes that the entire object has been retrieved from DB prior
6579:             * to resetting any field.
6580:             *
6581:             * @param updateChangedFieldsOnly if true only modified fields (isChanged = true)
6582:             *                                will be included in the update
6583:             * @throws DBException if the update to the database fails due to
6584:             *                     a database error
6585:             */
6586:            public void update(boolean updateChangedFieldsOnly)
6587:                    throws DBException {
6588:                getExecutor().update(this , updateChangedFieldsOnly);
6589:
6590:                /* Now log the change if we are logging */
6591:                if (getDef().isLoggingEnabled()) {
6592:                    ChangeLog myChangeLog = new ChangeLog();
6593:                    myChangeLog.setDataContext(getDataContext());
6594:                    myChangeLog.setField("ObjectChanged", myClassName);
6595:                    myChangeLog.setField("RecordKey", getMyKeys());
6596:                    myChangeLog.setField("Operation", EVENT_UPDATE);
6597:
6598:                    if (myUpdates != null) {
6599:                        for (Iterator i = myUpdates.iterator(); i.hasNext();) {
6600:
6601:                            FieldUpdate myUpdate = (FieldUpdate) i.next();
6602:
6603:                            if (!myUpdate.oldValue.equals(myUpdate.newValue)) {
6604:                                myChangeLog.setField("ChangedField",
6605:                                        myUpdate.fieldName);
6606:                                myChangeLog.setField("OldValue",
6607:                                        myUpdate.oldValue);
6608:                                myChangeLog.setField("NewValue",
6609:                                        myUpdate.newValue);
6610:                                myChangeLog.add();
6611:                            }
6612:                        } /* for */
6613:
6614:                    }
6615:
6616:                    /* We're done tracking changes */
6617:                    myUpdates = null;
6618:                } /* if */
6619:
6620:                // after update, we know that 'current' values are now the baseline for comparison
6621:                cacheIsChangedComparison();
6622:                setStatus(BaseDataObject.STATUS_CURRENT);
6623:                notifyListeners(EVENT_UPDATE);
6624:            } /*  update() */
6625:
6626:            /**
6627:             * Generate a debuggable string for suitable printing out in an
6628:             * interactive Java IDE. Write the string in PERL hash structure
6629:             * style.  Primary key field are denoted with (*) asterisks.
6630:             * <p/>
6631:             * <pre>
6632:             * com.bar.foo.AcmePayroll@1234BABE{
6633:             *   id* =&gt; 7, fullname =&gt; Peter Pilgrim,
6634:             *   city =&gt; country =&gt; England }
6635:             * </pre>
6636:             * <p/>
6637:             * <p> This is a method is not the standard
6638:             * <code>toString()</code> because some times a data object used
6639:             * professionally can have dozens of data fields. We also dont
6640:             * want to overwhelm you with useless information!
6641:             *
6642:             * @return java.lang.String
6643:             */
6644:            public String toDebugString() {
6645:                FastStringBuffer fsb = FastStringBuffer.getInstance();
6646:                String returnValue = null;
6647:
6648:                try {
6649:                    // Retrieve all the field names
6650:                    DataObjectMetaData metaData = getMetaData();
6651:                    String fieldNames[] = metaData.getFields();
6652:
6653:                    fsb.append(getClass().getName());
6654:                    fsb.append("@");
6655:                    fsb.append(Integer.toHexString(hashCode()));
6656:                    fsb.append("{ ");
6657:
6658:                    for (int k = 0; k < fieldNames.length; ++k) {
6659:                        if (k > 0) {
6660:                            fsb.append(',');
6661:                        }
6662:                        fsb.append(fieldNames[k]);
6663:                        if (metaData.getFieldMetadata(fieldNames[k]).isKey()) {
6664:                            fsb.append('*');
6665:                        }
6666:                        fsb.append(" = ");
6667:                        fsb.append(getField(fieldNames[k]));
6668:                        fsb.append(" ");
6669:                    }
6670:                    fsb.append("}");
6671:
6672:                    returnValue = fsb.toString(); /*!*/
6673:                } catch (Exception ignored) {
6674:                } finally {
6675:                    fsb.release();
6676:                    fsb = null;
6677:                }
6678:
6679:                return returnValue;
6680:            }
6681:
6682:            /**
6683:             * Update all of the records specified by the current search criteria.  If
6684:             * you use a blank DBObject, then all the records in the table will be
6685:             * deleted
6686:             *
6687:             * @throws DBException upon error
6688:             */
6689:            public synchronized void updateAll() throws DBException {
6690:
6691:                //Build an object that we're going to query with.
6692:                DBObject testObject = newInstance();
6693:                DataObjectMetaData metadata = this .getMetaData();
6694:                for (Iterator i = this .getMetaData().getFieldListArray()
6695:                        .iterator(); i.hasNext();) {
6696:                    String fieldName = (String) i.next();
6697:                    DataFieldMetaData fieldMetadata = metadata
6698:                            .getFieldMetadata(fieldName);
6699:                    if (fieldMetadata.isLongObjectType()
6700:                            || fieldMetadata.isVirtual()) {
6701:                        if (log.isDebugEnabled()) {
6702:                            ChangeLog myChangeLog = new ChangeLog();
6703:                            myChangeLog.setDataContext(this .getDataContext());
6704:                            myChangeLog.setField("ObjectChanged",
6705:                                    this .myClassName);
6706:                            myChangeLog.setField("RecordKey", this .getMyKeys());
6707:                            myChangeLog.setField("Operation", "U");
6708:
6709:                            if (myUpdates != null) {
6710:                                for (Iterator j = myUpdates.iterator(); j
6711:                                        .hasNext();) {
6712:
6713:                                    FieldUpdate myUpdate = (FieldUpdate) j
6714:                                            .next();
6715:                                    if (!myUpdate.oldValue
6716:                                            .equals(myUpdate.newValue)) {
6717:                                        myChangeLog.setField("ChangedField",
6718:                                                myUpdate.fieldName);
6719:                                        myChangeLog.setField("OldValue",
6720:                                                myUpdate.oldValue);
6721:                                        myChangeLog.setField("NewValue",
6722:                                                myUpdate.newValue);
6723:                                        myChangeLog.add();
6724:                                    }
6725:                                } /* for */
6726:                            }
6727:                        }
6728:                    } else {
6729:                        testObject
6730:                                .setField(fieldName, this .getField(fieldName));
6731:                    }
6732:                }
6733:
6734:                if (customWhereClause != null) {
6735:                    testObject.setCustomWhereClause(getCustomWhereClause(),
6736:                            appendCustomWhere);
6737:                }
6738:
6739:                testObject.setMaxRecords(100);
6740:
6741:                //
6742:                //Iterate for each 100 items.  So we don't load more records at once
6743:                //than memory can handle
6744:                //
6745:                int num = 0;
6746:                ArrayList al = new ArrayList(100);
6747:                do {
6748:                    al = testObject.searchAndRetrieveList();
6749:                    num = al.size();
6750:                    for (Iterator i = al.iterator(); i.hasNext();) {
6751:                        DBObject oneObject = (DBObject) i.next();
6752:                        oneObject.update(true);
6753:                    }
6754:
6755:                    al.clear();
6756:                } while (num >= 100);
6757:            } /* updateteAll() */
6758:
6759:            /**
6760:             * Update the database with the new info. Update affects all records,
6761:             * and uses the current field values to write to the DBMS.
6762:             *
6763:             * @param oneByOne if true the records will be modifed one by one and
6764:             *                 will be included in the cache
6765:             * @throws DBException if the update to the database fails due to
6766:             *                     a database error
6767:             */
6768:            public void updateAll(boolean oneByOne) throws DBException {
6769:                if (oneByOne) {
6770:                    updateAll();
6771:                } else {
6772:                    doUpdateAll(true);
6773:                }
6774:            } /*  updateAll(boolean) */
6775:
6776:            /**
6777:             * Takes a <code>DataObject</code> and updates all to the underlying data source
6778:             *
6779:             * @param updateChangedCache if true only modified fields (isChanged = true)
6780:             *                           will be included in the update
6781:             * @throws DataException upon error updating the object to the data source
6782:             *                       Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
6783:             * @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.
6784:             */
6785:            private void doUpdateAll(boolean updateChangedCache)
6786:                    throws DataException {
6787:
6788:                boolean needComma = false;
6789:
6790:                try {
6791:                    //
6792:                    //If myPool isn't set yet, then we need to use getDBName to
6793:                    //reset.  FIXME:  Have the system throw exceptions if DBName isn't
6794:                    //set instead.
6795:                    //
6796:                    DBConnectionPool myPool = DBConnectionPool
6797:                            .getInstance(getMappedDataContext());
6798:                    DBConnection localConnection = this .getLocalConnection();
6799:
6800:                    List lobFieldOrder = null;
6801:                    List lobUpdates = null;
6802:                    DataFieldMetaData oneField = null;
6803:                    FastStringBuffer sqlCommand = FastStringBuffer
6804:                            .getInstance();
6805:                    String theSqlString = null;
6806:                    boolean fieldsUpdated = false;
6807:                    try {
6808:                        sqlCommand.append("UPDATE ");
6809:                        sqlCommand.append(this .getJDBCMetaData()
6810:                                .getTargetSQLTable(this .getDataContext()));
6811:                        sqlCommand.append(" SET ");
6812:
6813:                        for (Iterator i = this .getMetaData()
6814:                                .getFieldListArray().iterator(); i.hasNext();) {
6815:                            String oneFieldName = (String) i.next();
6816:                            oneField = this .getFieldMetaData(oneFieldName);
6817:
6818:                            /* We skip any key fields in the update part (not allowed to */
6819:                            /* update them). Also skip any virtual fields */
6820:                            if ((!oneField.isKey()) && (!oneField.isVirtual())
6821:                                    && (!oneField.isAutoIncremented())
6822:                                    && (!oneField.isBinaryObjectType())) {
6823:                                // Only include changed fields in the update if required
6824:                                if (this .getDataField(oneFieldName)
6825:                                        .isValueSet()
6826:                                        && this .getStatus().equals(
6827:                                                DataObject.STATUS_NEW)) {
6828:                                    ;
6829:                                } else {
6830:                                    if (!this .getDataField(oneFieldName)
6831:                                            .isChanged()) {
6832:                                        continue;
6833:                                    }
6834:                                }
6835:
6836:                                this .checkField(oneFieldName, this 
6837:                                        .getField(oneFieldName));
6838:
6839:                                if (needComma) {
6840:                                    sqlCommand.append(", ");
6841:                                }
6842:                                if (!oneField.isKey()) {
6843:                                    fieldsUpdated = true;
6844:                                    sqlCommand.append(oneField.getName());
6845:                                    sqlCommand.append(" = ");
6846:
6847:                                    if (oneField.isDateType()) {
6848:                                        Object tmpData = this .get(oneField
6849:                                                .getName());
6850:                                        String data;
6851:                                        //
6852:                                        //FIXME allow for appropriate support of other data types.
6853:                                        //
6854:                                        if (tmpData == null) {
6855:                                            data = null;
6856:                                        } else if (tmpData instanceof  String) {
6857:                                            data = (String) tmpData;
6858:                                        } else {
6859:                                            data = tmpData.toString();
6860:                                        }
6861:                                        if (data == null || data.length() == 0) {
6862:                                            sqlCommand.append("null");
6863:                                        } else {
6864:                                            sqlCommand
6865:                                                    .append(JDBCUtil
6866:                                                            .getInstance()
6867:                                                            .formatDateTime(
6868:                                                                    this ,
6869:                                                                    oneField
6870:                                                                            .getName()));
6871:                                        }
6872:                                    } else if (oneField
6873:                                            .isCharacterLongObjectType()) {
6874:                                        //
6875:                                        //If we have a CLOB object, then we will go ahead and process
6876:                                        //it, but we execute it in a separate update
6877:                                        //
6878:                                        if (lobUpdates == null) {
6879:                                            lobUpdates = new ArrayList();
6880:                                            lobFieldOrder = new ArrayList();
6881:                                        }
6882:                                        Object tmpData = this .get(oneField
6883:                                                .getName());
6884:                                        String data;
6885:                                        //
6886:                                        //FIXME allow for appropriate support of other data types.
6887:                                        //
6888:                                        if (tmpData == null) {
6889:                                            data = null;
6890:                                        } else if (tmpData instanceof  String) {
6891:                                            data = (String) tmpData;
6892:                                        } else {
6893:                                            data = tmpData.toString();
6894:                                        }
6895:
6896:                                        lobUpdates.add(data);
6897:                                        lobFieldOrder.add(oneFieldName);
6898:
6899:                                        sqlCommand.append("?");
6900:
6901:                                    } else {
6902:                                        sqlCommand.append(this .quoteIfNeeded(
6903:                                                oneField.getName(), null));
6904:                                    }
6905:
6906:                                    sqlCommand.append(" ");
6907:                                    needComma = true;
6908:                                }
6909:                            } /* if it's not a key field */
6910:
6911:                        }
6912:
6913:                        if (!fieldsUpdated) {
6914:                            return;
6915:                        }
6916:
6917:                        String whereClause;
6918:
6919:                        if (customWhereClause != null) {
6920:                            if (appendCustomWhere) {
6921:                                whereClause = buildWhereClause(true)
6922:                                        + customWhereClause;
6923:                                appendCustomWhere = false;
6924:                            } else {
6925:                                whereClause = customWhereClause;
6926:                            }
6927:                            sqlCommand.append(whereClause);
6928:                            customWhereClause = null;
6929:                        } else {
6930:                            whereClause = buildWhereClause(true);
6931:                            sqlCommand.append(whereClause);
6932:                        }
6933:
6934:                        theSqlString = sqlCommand.toString();
6935:
6936:                    } finally {
6937:                        sqlCommand.release();
6938:                        sqlCommand = null;
6939:                    }
6940:
6941:                    DBConnection myConnection = null;
6942:
6943:                    try {
6944:                        if (localConnection != null) {
6945:                            myConnection = localConnection;
6946:                        } else {
6947:                            myConnection = myPool.getConnection(getClass()
6948:                                    .getName());
6949:                        }
6950:
6951:                        if (lobUpdates != null) {
6952:
6953:                            java.sql.PreparedStatement prepStatement = null;
6954:                            try {
6955:                                prepStatement = myConnection
6956:                                        .createPreparedStatement(theSqlString);
6957:
6958:                                int size = lobFieldOrder.size();
6959:                                for (int i = 0; i < size; i++) {
6960:                                    String value = (String) lobUpdates.get(i);
6961:                                    try {
6962:                                        prepStatement.setString(i + 1, value);
6963:                                    } catch (SQLException ex) {
6964:                                        String key = (String) lobFieldOrder
6965:                                                .get(i);
6966:                                        log
6967:                                                .error(
6968:                                                        "Error executing LOB set Character Stream",
6969:                                                        ex);
6970:                                        throw new DataException(
6971:                                                "Error setting character stream for field: "
6972:                                                        + key + " with value: "
6973:                                                        + value
6974:                                                        + " for DBObject: "
6975:                                                        + getClass().getName(),
6976:                                                ex);
6977:                                    }
6978:                                }
6979:                                myConnection.executeUpdate(null);
6980:                            } finally {
6981:                                myConnection.clearPreparedStatement();
6982:                            }
6983:                        } else {
6984:                            myConnection.executeUpdate(theSqlString);
6985:                        }
6986:
6987:                        if ((myConnection.getUpdateCount() == 0)
6988:                                && (this .checkZeroUpdate())) {
6989:                            log.debug("No record updated for SQL '"
6990:                                    + theSqlString + "'");
6991:                        }
6992:                    } catch (NullPointerException npe) {
6993:                        npe.printStackTrace();
6994:                        throw new DataException(npe);
6995:                    } finally {
6996:                        if (localConnection == null) {
6997:                            if (myPool != null && myConnection != null) {
6998:                                myPool.release(myConnection);
6999:                            }
7000:                        }
7001:                    }
7002:                } catch (DBException ex) {
7003:                    String msg = "doUpdateAll(DataObject) error";
7004:                    log.error(msg, ex);
7005:                    throw new DataException(msg, ex);
7006:                }
7007:
7008:            } /* doUpdateAll */
7009:
7010:            /**
7011:             * Verify that this object is working correctly
7012:             * by selecting all records, running a validation on each of them. The
7013:             * validation consists of verifying the referential integrity against all
7014:             * objects defined as being referred to by this object in the checkAllRefs method
7015:             *
7016:             * @throws DBException If the validation fails
7017:             */
7018:            public void verify() throws DBException {
7019:                DBObject list = newInstance();
7020:                list.setDataContext(getDataContext());
7021:
7022:                DBObject oneObject = null;
7023:
7024:                for (Iterator i = list.searchAndRetrieveList().iterator(); i
7025:                        .hasNext();) {
7026:                    oneObject = (DBObject) i.next();
7027:                    oneObject.checkAllRefs();
7028:                } /* each object */
7029:
7030:            }
7031:
7032:            /**
7033:             * Equals implementation.  this == parm1 If and only if: <br/>
7034:             * 1 - The two classes are of the same classname <br />
7035:             * 2 - All the data fields in the two classes are the same <br />
7036:             * 3 - The data contexts are the same<br />
7037:             *
7038:             * @param parm1 The object to compare to
7039:             * @return true if the two objects being compared are equal
7040:             */
7041:            public boolean equals(Object parm1) {
7042:                if (parm1 == null) {
7043:                    return false;
7044:                }
7045:
7046:                //False if other object isn't at least a DBObject.  [Keeps us from
7047:                //throwing ClassCastException later on.
7048:                if (!(parm1 instanceof  DBObject)) {
7049:                    return false;
7050:                }
7051:
7052:                DBObject otherObj = (DBObject) parm1;
7053:
7054:                //
7055:                //True if both objects have no field data.
7056:                //
7057:                if (fieldData == null && otherObj.fieldData == null) {
7058:                    return true;
7059:                    //
7060:                    //False if one or the other have data but not both.
7061:                    //
7062:                } else if (!(fieldData != null && otherObj.fieldData != null)) {
7063:                    return false;
7064:                }
7065:
7066:                //
7067:                //At this point we are guaranteed that both fielddata have values
7068:                //Now return false if the data sizes do not agree.
7069:                //
7070:                if (fieldData.size() != otherObj.fieldData.size()) {
7071:                    return false;
7072:                }
7073:
7074:                //--------------------------------
7075:                //Although the comparisons above this line might seem more suited until
7076:                //after we have validated the classnames.  The truth of the matter is
7077:                //that we have to do string comparisons for the classes below, which
7078:                //is computationally more expensive than integer comparisons.  Thus
7079:                //we do those first to see if we can eliminate something right off the
7080:                //bat.
7081:                //
7082:
7083:                //
7084:                //False if the classnames aren't identical since derived classes must
7085:                //point to separate tables.
7086:                //
7087:                if (!myClassName.equals(parm1.getClass().getName())) {
7088:                    return false;
7089:                }
7090:
7091:                //
7092:                //False if the DBObjects are pointing to different data contexts.
7093:                //
7094:                if (!getDataContext().equals(otherObj.getDataContext())) {
7095:                    return false;
7096:                }
7097:
7098:                //
7099:                //Ok, we've done all the non-computationally extensive comparisons,
7100:                //lets try the field by field comparison
7101:                //
7102:
7103:                try {
7104:                    //
7105:                    //Ok, we've done all the non-computationally extensive comparisons,
7106:                    //lets try the field by field comparison
7107:                    //
7108:                    for (Iterator i = fieldData.keySet().iterator(); i
7109:                            .hasNext();) {
7110:                        String key = (String) i.next();
7111:                        DataField value2 = null;
7112:                        try {
7113:                            value2 = (DataField) otherObj.fieldData.get(key);
7114:                        } catch (ClassCastException ex) {
7115:                            return false;
7116:                        }
7117:
7118:                        DataField this Field = (DataField) fieldData.get(key);
7119:
7120:                        //
7121:                        //Check for one or the other being null.
7122:                        //
7123:                        if ((value2 == null && this Field != null)
7124:                                || (this Field == null && value2 != null)) {
7125:                            return false;
7126:                        }
7127:
7128:                        if (!this Field.getFieldMetaData().getTypeString()
7129:                                .equals(
7130:                                        value2.getFieldMetaData()
7131:                                                .getTypeString())) {
7132:                            //Different data types... definitely won't match :)
7133:                            return false;
7134:                        }
7135:
7136:                        //
7137:                        //If one field value is null and not the other, then they're different
7138:                        //
7139:                        if ((this Field.getValue() == null ^ value2.getValue() == null)) {
7140:                            return false;
7141:                        }
7142:
7143:                        if (this Field.getValue() == null
7144:                                && value2.getValue() == null) {
7145:                            continue;
7146:                        }
7147:
7148:                        if (value2.getValue().equals(this Field.getValue())) {
7149:                            //We're ok... they're equal
7150:                            continue;
7151:                        }
7152:
7153:                        if (this Field.getFieldMetaData().isNumericType()) {
7154:                            //We could have differences due to string formatting, but the
7155:                            //actual value could be the same.  Examples are: 72 vs 72.00
7156:                            BigDecimal bd1 = this Field.asBigDecimal();
7157:                            BigDecimal bd2 = value2.asBigDecimal();
7158:
7159:                            if (bd1.equals(bd2)) {
7160:                                continue;
7161:                            }
7162:                        }
7163:
7164:                        return false;
7165:
7166:                    }
7167:                } catch (Throwable ex) {
7168:                    log.error("Error comparing DBOBjects", ex);
7169:                    return false;
7170:                }
7171:
7172:                //Ok, by now we've checked all fields individually, and we're still at
7173:                //a match.  We think they're equal at this point.
7174:
7175:                return true;
7176:            }
7177:
7178:            /**
7179:             * Same as getField, but works with the DataObject interface for now.  Will
7180:             * eventually return something other than Strings.  For example, dates, times,
7181:             * etc
7182:             *
7183:             * @param fieldName name of the field to get the object for.
7184:             * @return DataField or null if the field doesn't exist yet.
7185:             * @throws DBException upon error.
7186:             */
7187:            public DataField getDataField(String fieldName) throws DBException {
7188:                if (fieldData == null) {
7189:                    fieldData = new HashMap();
7190:                }
7191:                DataField oneField = (DataField) fieldData.get(fieldName);
7192:                if (oneField == null) {
7193:                    oneField = DefaultDataField.getInstance(
7194:                            getFieldMetaData(fieldName), this );
7195:                    fieldData.put(fieldName, oneField);
7196:                }
7197:
7198:                return oneField;
7199:
7200:            }
7201:
7202:            /**
7203:             * Directly gets the DataField Data without having to deal with the DataField
7204:             * itself
7205:             *
7206:             * @param fieldName the name of the field to get
7207:             * @return Object or null.
7208:             * @throws DataException upon error.
7209:             */
7210:            public Object get(String fieldName) throws DataException {
7211:                if (fieldData == null) {
7212:                    return null;
7213:                } else {
7214:                    DataField df = (DataField) fieldData.get(fieldName);
7215:                    if (df == null) {
7216:                        DataField nullField = DefaultDataField.getInstance(
7217:                                getFieldMetaData(fieldName), this );
7218:                        fieldData.put(fieldName, nullField);
7219:                        return nullField.getValue();
7220:                    } else {
7221:                        return df.getValue();
7222:                    }
7223:                }
7224:            }
7225:
7226:            /**
7227:             * sets the field value by using 'instanceof' operator; convenient for template methods, but less efficient
7228:             * than calling setField()
7229:             *
7230:             * @param fieldName the name of the field to set
7231:             * @param o         the object value to set it to.
7232:             * @throws DataException upon error.
7233:             * @see #setField
7234:             */
7235:            public void set(String fieldName, Object o) throws DataException {
7236:                try {
7237:                    //
7238:                    //For now this is a backwards compatability hack to get the interface
7239:                    //out the door.  It will eventually be the bulk of it's own
7240:                    //work
7241:                    //
7242:                    if (o == null) {
7243:                        setField(fieldName, (String) null);
7244:                    } else if (o instanceof  java.lang.String) {
7245:                        setField(fieldName, (String) o);
7246:                    } else if (o instanceof  java.lang.Integer) {
7247:                        setField(fieldName, ((Integer) o).intValue());
7248:                    } else if (o instanceof  java.lang.Boolean) {
7249:                        setField(fieldName, ((Boolean) o).booleanValue());
7250:                    } else {
7251:                        throw new IllegalArgumentException(
7252:                                "DBObject.setFieldValue(String,"
7253:                                        + "Object) must currently have o only of "
7254:                                        + "type String, Integer, or Boolean");
7255:
7256:                    }
7257:                } catch (DBException ex) {
7258:                    throw new DataException(ex);
7259:                }
7260:            }
7261:
7262:            /**
7263:             * Same as setField, but works with the DataObject Interface
7264:             *
7265:             * @param fieldName the name of the field to set.
7266:             * @param o         the object to set the field to... currently should be
7267:             *                  java.lang.Strings only.
7268:             * @throws DataException upon error.
7269:             * @since Expresso 5.0
7270:             */
7271:            public void setDataField(String fieldName, DataField o)
7272:                    throws DataException {
7273:                if (fieldData == null) {
7274:                    fieldData = new HashMap();
7275:                }
7276:
7277:                fieldData.put(fieldName, o);
7278:            }
7279:
7280:            /**
7281:             * Sets the data context that this object is residing in.  This is identical
7282:             * to the pre-Expresso 5.0 <code>setDBName()</code>
7283:             *
7284:             * @param newContext the new name of the data context.
7285:             * @throws IllegalArgumentException if the DataContext does not exist
7286:             */
7287:            public void setDataContext(String newContext) {
7288:                try {
7289:                    setDBName(newContext);
7290:                } catch (DBException ex) {
7291:                    String errorMessage = "Invalid newContext name: "
7292:                            + newContext + ": " + ex.getMessage();
7293:                    log.error(errorMessage, ex);
7294:                    throw new IllegalArgumentException(errorMessage);
7295:                }
7296:            }
7297:
7298:            /**
7299:             * Returns the name of the currently set DataContext
7300:             *
7301:             * @return java.lang.String
7302:             */
7303:            public String getDataContext() {
7304:
7305:                // set from RequestRegistry if this is first access
7306:                if (dbKey == null || dbKey.length() == 0) {
7307:                    try {
7308:                        this .setDataContext(RequestRegistry.getDataContext());
7309:                    } catch (Exception ex) {
7310:                        getLogger().warn(
7311:                                "Problem getting data context from Registry: "
7312:                                        + ex.getClass().getName() + ", msg: "
7313:                                        + ex.getMessage());
7314:                        try {
7315:                            setDBName(DBConnection.DEFAULT_DB_CONTEXT_NAME);
7316:                        } catch (DBException de) {
7317:                            getLogger().error(
7318:                                    "Unable to set database to 'default'", de);
7319:                        }
7320:                    }
7321:
7322:                }
7323:
7324:                return dbKey;
7325:            }
7326:
7327:            /**
7328:             * Returns a <b>Read Only</b> Map containing all the current attributes set
7329:             * for this particular data object instance.
7330:             *
7331:             * @return Read Only <code>java.util.Map</code>
7332:             */
7333:            public Map getAllAttributes() {
7334:                if (attributes == null) {
7335:                    return new HashMap();
7336:                } else {
7337:                    return Collections.unmodifiableMap(attributes);
7338:                }
7339:            }
7340:
7341:            /**
7342:             * Private class that defines errors for fields.
7343:             */
7344:            public class FieldError {
7345:                private String fieldName = null;
7346:                private String errorMessage = null;
7347:
7348:                /**
7349:                 * Default Constructor
7350:                 */
7351:                public FieldError() {
7352:
7353:                }
7354:
7355:                /**
7356:                 * Constructor that sets all fields.
7357:                 *
7358:                 * @param fldName The fieldName to associate with
7359:                 * @param message The appropriate message
7360:                 */
7361:                public FieldError(String fldName, String message) {
7362:                    fieldName = fldName;
7363:                    errorMessage = message;
7364:                }
7365:
7366:                /**
7367:                 * Returns the fieldName that this error message belongs to.
7368:                 *
7369:                 * @return java.lang.String the field name
7370:                 */
7371:                public String getFieldName() {
7372:                    return fieldName;
7373:                }
7374:
7375:                /**
7376:                 * Returns the error message associated with this FieldError class.
7377:                 *
7378:                 * @return the error message
7379:                 */
7380:                public String getErrorMessage() {
7381:                    return errorMessage;
7382:                }
7383:
7384:                /**
7385:                 * Sets the field name for this error class
7386:                 *
7387:                 * @param newValue The new field name value
7388:                 */
7389:                public void setFieldName(String newValue) {
7390:                    fieldName = newValue;
7391:                }
7392:
7393:                /**
7394:                 * Sets the Error Message for this class.
7395:                 *
7396:                 * @param newValue the new Error message for this object
7397:                 */
7398:                public void setErrorMessage(String newValue) {
7399:                    errorMessage = newValue;
7400:                }
7401:
7402:                /**
7403:                 * Overrides default toString() behavior. to return a friendly message
7404:                 *
7405:                 * @return java.lang.String
7406:                 */
7407:                public String toString() {
7408:
7409:                    FastStringBuffer fsb = FastStringBuffer.getInstance();
7410:                    String returnValue = null;
7411:                    try {
7412:                        if (fieldName != null) {
7413:                            fsb.append("Error in field: ");
7414:                            fsb.append(fieldName);
7415:                        }
7416:
7417:                        if (errorMessage != null) {
7418:                            fsb.append("Message: ");
7419:                            fsb.append(errorMessage);
7420:                        }
7421:                        returnValue = fsb.toString();
7422:                    } finally {
7423:                        fsb.release();
7424:                        fsb = null;
7425:                    }
7426:
7427:                    return returnValue;
7428:                }
7429:            }
7430:
7431:            ///////////////////////////////////////////////////////////////////////////
7432:            // embedded object
7433:            /////////////////////////////////////////////////////////////////////////
7434:
7435:            /**
7436:             * Private class used to track field updates
7437:             */
7438:            public class FieldUpdate {
7439:                public String fieldName;
7440:                public String oldValue;
7441:                public String newValue;
7442:
7443:                /**
7444:                 * Constructor
7445:                 */
7446:                public FieldUpdate() {
7447:                    fieldName = ("");
7448:                    oldValue = ("");
7449:                    newValue = ("");
7450:                } /* FieldUpdate() */
7451:
7452:            } /* FieldUpdate */
7453:
7454:            /**
7455:             * set string filters to the given filter on ALL fields that are quoted text fields
7456:             *
7457:             * @param filter filter to set
7458:             * @throws DBException upon error
7459:             */
7460:            public void setStringFiltersOnAll(String filter) throws DBException {
7461:                Iterator iter = getMetaData().getAllFieldsMap().values()
7462:                        .iterator();
7463:                while (iter.hasNext()) {
7464:                    DBField field = (DBField) iter.next();
7465:                    if (field.isQuotedTextType()) {
7466:                        setStringFilter(field.getName(), filter);
7467:                    }
7468:                }
7469:            }
7470:
7471:            /**
7472:             * the INSTANCE value of filter class that will be used for all string filtering for THIS INSTANCE.
7473:             * defaults to null, which means that there is no instance filter, so static filter will be used.
7474:             * If this instance returns null, static value in
7475:             * DBField.getFilterClass() filter class will be used. This method only
7476:             * returns the local data member, not the static value from DBField
7477:             *
7478:             * @return the current filter (a subclass of Filter), or null to indicate default (HTML) filter
7479:             * @see DBField#getFilterClass
7480:             * @see com.jcorporate.expresso.core.security.filters.HtmlFilter
7481:             */
7482:            public Class getFilterClass() {
7483:                return mFilter;
7484:            }
7485:
7486:            /**
7487:             * set the filter class that will be used for all string filtering
7488:             * for this object instance ONLY.
7489:             *
7490:             * @param filter class which is subclass of Filter
7491:             * @return old Class that was just replaced
7492:             * @see com.jcorporate.expresso.core.security.filters.Filter
7493:             * @see DBField#setFilterClass
7494:             * @deprecated use setFilterClass(Filter)
7495:             */
7496:            public Class setFilterClass(Class filter) {
7497:                Class old = mFilter;
7498:                if (filter == null) {
7499:                    mFilter = null;
7500:                } else {
7501:                    mFilter = filter;
7502:                }
7503:                return old;
7504:            }
7505:
7506:            /**
7507:             * set the filter class that will be used for all string filtering
7508:             * for this object instance ONLY.
7509:             *
7510:             * @param filter which is subclass of Filter; we will persist only the class name, NOT the instance passed in
7511:             * @return old filter that was just replaced; can be null
7512:             * @see #setStringFilter(java.lang.String, java.lang.String) for a means to set "permanenent" filters in metadata for all objects of this class
7513:             * @see DBField#setFilterClass
7514:             */
7515:            public Filter setFilterClass(Filter filter) {
7516:                Class old = mFilter;
7517:                if (filter == null) {
7518:                    mFilter = null;
7519:                } else {
7520:                    mFilter = filter.getClass();
7521:                }
7522:
7523:                Filter result = null;
7524:                if (old != null) {
7525:                    try {
7526:                        result = (Filter) old.newInstance();
7527:                    } catch (Exception e) {
7528:                        // should not happen since we were passed in a good filter
7529:                        log.error(e);
7530:                    }
7531:                }
7532:
7533:                return result;
7534:            }
7535:
7536:            /**
7537:             * convenience for getting logger for current (sub) class
7538:             *
7539:             * @return Category for logging
7540:             */
7541:            public Logger getLogger() {
7542:                if (slog == null) {
7543:                    slog = Logger.getLogger(getClass().getName());
7544:                }
7545:
7546:                return slog;
7547:            }
7548:
7549:            /**
7550:             * summarize DataField.isChanged() for all fields.
7551:             * isChanged() per field means that setField() has been called twice for that field,
7552:             * once presumably by retrieval from DB, once to change a value (with
7553:             * new value != former value)
7554:             *
7555:             * @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
7556:             * @throws DBException upon error.
7557:             * @see DataField#isChanged()
7558:             */
7559:            public boolean isChanged() throws DBException {
7560:                boolean result = false;
7561:                for (Iterator iterator = getMetaData().getAllFieldsMap()
7562:                        .values().iterator(); iterator.hasNext();) {
7563:                    DBField field = (DBField) iterator.next();
7564:                    DataField df = getDataField(field.getName());
7565:                    if (df.isChanged()) {
7566:                        result = true;
7567:                        break;
7568:                    }
7569:                }
7570:                return result;
7571:            }
7572:
7573:        } // dbobject
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.