KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > dataobjects > jdbc > JoinedDataObject


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

64 package com.jcorporate.expresso.core.dataobjects.jdbc;
65
66 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
67 import com.jcorporate.expresso.core.ExpressoSchema;
68 import com.jcorporate.expresso.core.dataobjects.BaseDataObject;
69 import com.jcorporate.expresso.core.dataobjects.DataException;
70 import com.jcorporate.expresso.core.dataobjects.DataExecutorInterface;
71 import com.jcorporate.expresso.core.dataobjects.DataField;
72 import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
73 import com.jcorporate.expresso.core.dataobjects.DataObject;
74 import com.jcorporate.expresso.core.dataobjects.DataObjectFactory;
75 import com.jcorporate.expresso.core.dataobjects.DataObjectMetaData;
76 import com.jcorporate.expresso.core.dataobjects.DataQueryInterface;
77 import com.jcorporate.expresso.core.dataobjects.Defineable;
78 import com.jcorporate.expresso.core.dataobjects.NestableDataObject;
79 import com.jcorporate.expresso.core.dataobjects.Securable;
80 import com.jcorporate.expresso.core.db.DBConnection;
81 import com.jcorporate.expresso.core.db.DBConnectionPool;
82 import com.jcorporate.expresso.core.db.DBException;
83 import com.jcorporate.expresso.core.dbobj.RequestContext;
84 import com.jcorporate.expresso.core.dbobj.SecuredDBObject;
85 import com.jcorporate.expresso.core.misc.StringUtil;
86 import com.jcorporate.expresso.core.registry.RequestRegistry;
87 import com.jcorporate.expresso.core.security.User;
88 import com.jcorporate.expresso.core.security.filters.Filter;
89 import com.jcorporate.expresso.kernel.util.ClassLocator;
90 import com.jcorporate.expresso.kernel.util.FastStringBuffer;
91 import com.jcorporate.expresso.services.dbobj.Setup;
92 import org.apache.log4j.Logger;
93 import org.apache.oro.text.regex.Pattern;
94
95 import java.util.ArrayList JavaDoc;
96 import java.util.HashMap JavaDoc;
97 import java.util.Iterator JavaDoc;
98 import java.util.List JavaDoc;
99 import java.util.Locale JavaDoc;
100 import java.util.Map JavaDoc;
101 import java.util.Set JavaDoc;
102 import java.util.StringTokenizer JavaDoc;
103 import java.util.TreeSet JavaDoc;
104
105
106 /**
107  * DataObject for use with 1:1 joins between database objects.
108  * This class provides a multi-dbobject view of the underlying databases. It
109  * is similar to MultiDBObject in that it joins several JDBC-based dataobjects
110  * together. The biggest differences are:
111  * <ul>
112  * <li>XML-defined joins. Use XML definitions in your classpath to define the
113  * joins. In accordance with jdbc-join_5_1.dtd</li>
114  * <li>DBMaint compatible. By refactoring DBMaint so that it is DataObject aware
115  * rather than DBObject aware, we gain the ability to manipulate/update the joins
116  * in DBMaint directly rather than making readonly database joins.</li>
117  * </ul>
118  * <p>Limitation: Although the definitions can list LEFT RIGHT and INNER joins,
119  * if you are chaining 3 or more tables, you can only LEFT, RIGHT or INNER join the
120  * very last relation or all the relations.</p>
121  *
122  * @author Michael Rimov
123  * <p/>
124  * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
125  */

126 public class JoinedDataObject implements DataObject, Defineable, Securable
127         , NestableDataObject {
128     private static final transient String JavaDoc thisClass = JoinedDataObject.class.
129             getName() +
130             ".";
131
132     private static final Logger LOG = Logger.getLogger(JoinedDataObject.class);
133
134     public static final int UNSPECIFIED_JOIN = -1;
135
136     /**
137      * Static variables for join type
138      */

139     public static final int INNER_JOIN = 0;
140
141     /**
142      * Static field to indicate RIGHT_JOIN type
143      */

144     public static final int RIGHT_JOIN = 1;
145
146     /**
147      * Static field to indicate LEFT_JOIN type
148      */

149     public static final int LEFT_JOIN = 2;
150
151
152     /**
153      * The logger instance.
154      */

155     private static transient final Logger log = Logger.getLogger(JoinedDataObject.class);
156
157     /**
158      * Local connection that we use if it's initialized, but if it's null we
159      * generate our own connection
160      */

161     protected transient DBConnection localConnection = null;
162
163     /**
164      * The number of records we must skip over before we start reading the
165      * <code>ResultSet</code> proper in a searchAndRetrieve. 0 means no limit
166      */

167     protected int offsetRecord = 0;
168
169
170     /**
171      * Max Records to retrieve in a single query. 0 means no limit
172      */

173     protected int maxRecords = 0;
174
175     /**
176      * Attributes of this DB Object
177      */

178     private HashMap attributes = null;
179
180     /**
181      * Hash that contains the DB objects that relate to make up this query,
182      * indexed by the "short" name provided by the user.
183      */

184     private HashMap myDataObjects = new HashMap();
185
186
187     /**
188      * The vector of MultiDB objects retrieved by the last searchAndRetrieve
189      * method
190      */

191     private ArrayList JavaDoc recordSet = null;
192
193
194     /**
195      * My Locale
196      */

197     private Locale JavaDoc myLocale = null;
198
199     /**
200      * If we are using a custom where clause for this query, it's stored here.
201      * If null, then build the where clause
202      */

203     private String JavaDoc customWhereClause = null;
204
205     /**
206      * Flag to indicate if the custom WHERE clause should be appended to the
207      * generated WHERE clause. If false, customWhereClause will be used instead
208      * of generating one
209      */

210     private boolean appendCustomWhere = false;
211
212     /**
213      * This flag tells the buildWhereClause method(s) to either be case
214      * sensitive (normal queries) or to be case insensitive. The case
215      * insensitive query is useful when you want to do a search and retreive
216      * and want case insensitive matching.
217      */

218     private boolean caseSensitiveQuery = true;
219
220
221     /**
222      * Pre-calculation that is performed upon construction to speed access to
223      * field definitions.
224      */

225     private String JavaDoc thisDefinitionName = null;
226
227
228     /**
229      * A list of definitions
230      */

231     transient private static Map definitions = new ConcurrentReaderHashMap();
232
233     /**
234      * Security UID of this data object
235      */

236     private int uid = Securable.SYSTEM_ACCOUNT;
237
238     /**
239      * Utility class that currently provides help for getFieldDate() function.
240      */

241     transient static private final JDBCUtil sJdbcUtil = JDBCUtil.getInstance();
242
243
244     /**
245      * A list of my current fields. It is keyed by localAlias.FieldName. This
246      * one ONLY contains the DataFields that are mapped to both objects. [Ie
247      * part of the join.] If you set one field, you'll that way, set both.
248      */

249     private Map myFields = new HashMap();
250
251
252     /**
253      * Creates a new JoinedDataObject object, setting db context and UID from thread context (set by servlet filter)
254      */

255     public JoinedDataObject() {
256         setDataContext(RequestRegistry.getDataContext());
257         try {
258             setRequestingUid(RequestRegistry.getUser().getUid());
259         } catch (DBException e) {
260             LOG.error(e);
261         }
262     }
263
264     /**
265      * Creates a new JoinedDataObject object.
266      *
267      * @param request based on the controller request object as most likely implemented.
268      */

269     public JoinedDataObject(RequestContext request) {
270         setDataContext(request.getDBName());
271         setRequestingUid(request.getUid());
272     }
273
274     /**
275      * Uses the classloader to load the given xml file url as the definition
276      * of the JoinedDataObject
277      *
278      * @param xmlFileURL the name of the XML file URL
279      * @throws DataException upon error
280      */

281     public JoinedDataObject(String JavaDoc xmlFileURL) throws DataException {
282         thisDefinitionName = xmlFileURL;
283         init();
284     }
285
286     /**
287      * Constructs a new joined data object to have the same definition as the
288      * parameter passed in.
289      *
290      * @param definition the definition to duplicate
291      * @throws DataException upon initialization error
292      */

293     public JoinedDataObject(JoinedDataObject definition) throws DataException {
294         this.setDefinitionName(definition.getDefinitionName());
295         this.setDataContext(definition.getDataContext());
296     }
297
298     /**
299      * Constructor for runtime initialization of custom joined data object. The
300      * definition MUST be unique across the application lifetime, and this constructor
301      * has to be only called once. The definition will exist for the lifetime
302      * of the object.
303      * <p><b>Note:</b>You may get strange errors if the definition already exists,
304      * it will appear that this constructor didn't 'take' and you'll get a
305      * different definition.</p>
306      *
307      * @param definition the definition bean to utilize
308      * @param definitionName the name of the definition
309      * @throws DataException upon error
310      */

311     public JoinedDataObject(JoinedDigesterBean definition,
312                             String JavaDoc definitionName) throws DataException {
313         thisDefinitionName = definitionName;
314         initWithBean(definition, definitionName);
315     }
316
317     /**
318      * Retrieves the definition name in accordance with the
319      *
320      * @return java.lang.String
321      */

322     public String JavaDoc getDefinitionName() {
323         return thisDefinitionName;
324     }
325
326     /**
327      * Initialize the dataobject.
328      *
329      * @throws DataException upon error
330      */

331     protected synchronized void init() throws DataException {
332         if (log.isDebugEnabled()) {
333             log.debug("Initializing new JoinedDataObject");
334         }
335         synchronized (JoinedDataObject.class) {
336             try {
337                 if (this.getMetaData() == null) {
338                     JoinedDataObjectMetaData metadata = constructMetadata();
339                     definitions.put(getDefinitionName(), metadata);
340                     initializeXML(metadata);
341                 }
342
343                 myDataObjects = ((JoinedDataObjectMetaData) this
344                         .getMetaData()).createNestedDataObjects();
345             } catch (Throwable JavaDoc t) {
346                 if (definitions.containsKey(getDefinitionName())) {
347                     definitions.remove(getDefinitionName());
348                 }
349                 log.error("Error initializing data object", t);
350                 throw new DataException("Error initializing joined data object"
351                         , t);
352             }
353         }
354     }
355
356
357     /**
358      * Construction method so that you can create custom derived metadata classes
359      * derived from JoinedDataObjectMetaData for custom fields, etc.
360      *
361      * @return JoinedDataObjectMetaData or derived class.
362      */

363     protected JoinedDataObjectMetaData constructMetadata() {
364         return new JoinedDataObjectMetaData();
365     }
366
367     /**
368      * Initializes the JoinedDataobject with a digester bean and a definition name
369      * This is useful for users creating joins on the fly
370      *
371      * @param digesterBean Filled out metadata Bean
372      * @param definitionName the name of the definition
373      * @throws DataException upon error
374      */

375     protected synchronized void initWithBean(JoinedDigesterBean digesterBean
376                                              , String JavaDoc definitionName) throws
377             DataException {
378         if (log.isDebugEnabled()) {
379             log.debug("Initializing new JoinedDataObject");
380         }
381         synchronized (JoinedDataObject.class) {
382             try {
383                 if (getMetaData() == null) {
384                     JoinedDataObjectMetaData metadata = constructMetadata();
385                     definitions.put(definitionName, metadata);
386                     initializeFromJoinedDigesterBean(digesterBean, metadata);
387                 }
388
389                 this.myDataObjects = ((JoinedDataObjectMetaData) this
390                         .getMetaData()).createNestedDataObjects();
391             } catch (Throwable JavaDoc t) {
392                 if (definitions.containsKey(this.getDefinitionName())) {
393                     definitions.remove(this.getDefinitionName());
394                 }
395                 log.error("Error initializing data object", t);
396                 throw new DataException("Error initializing joined data object"
397                         , t);
398             }
399         }
400
401     }
402
403     /**
404      * Initialization step based upon the digester bean either handed in, or loaded
405      * from XML data.
406      *
407      * @param digesterBean the digester bean with populated data
408      * @param metadata the meatadata to load the digester bean into
409      * @throws DataException upon error
410      */

411     protected void initializeFromJoinedDigesterBean(JoinedDigesterBean
412             digesterBean,
413                                                     JoinedDataObjectMetaData metadata) throws DataException {
414         metadata.setSelectDistinct(digesterBean.isDistinct());
415         metadata.setDescription(digesterBean.getDescription());
416
417         List JavaDoc l = digesterBean.getDataObjects();
418         for (Iterator JavaDoc i = l.iterator(); i.hasNext();) {
419             JoinedDigesterBean.DigesterJoinedDataObject oneObj =
420                     (JoinedDigesterBean.DigesterJoinedDataObject) i.next();
421             try {
422                 Class JavaDoc c = ClassLocator.loadClass(oneObj.getClassName());
423                 metadata.addDataObject(c, oneObj.getDefinitionName()
424                         , oneObj.getAlias(),
425                         oneObj.getFieldExpressionList());
426             } catch (ClassNotFoundException JavaDoc ex) {
427                 throw new DataException("Error parsing definition: unable to locate class: "
428                         + oneObj.getClassName());
429             }
430         }
431
432         metadata.setPermissions(digesterBean.getPermissions());
433
434         List JavaDoc r = digesterBean.getRelations();
435         for (Iterator JavaDoc i = r.iterator(); i.hasNext();) {
436             JoinedDigesterBean.DigesterJoinRelations oneRelation =
437                     (JoinedDigesterBean.DigesterJoinRelations) i.next();
438
439             try {
440                 String JavaDoc joinType = oneRelation.getJoinType();
441                 int joinValue = UNSPECIFIED_JOIN;
442
443                 if ("inner".equalsIgnoreCase(joinType)) {
444                     joinValue = INNER_JOIN;
445
446                 } else if ("left".equalsIgnoreCase(joinType)) {
447                     joinValue = LEFT_JOIN;
448
449                 } else if ("right".equalsIgnoreCase(joinType)) {
450                     joinValue = RIGHT_JOIN;
451                 }
452
453                 metadata.setForeignKey(oneRelation.getLocalAlias(),
454                         oneRelation.getLocalKey(),
455                         oneRelation.getForeignAlias(),
456                         oneRelation.getForeignKey(), joinValue);
457             } catch (DBException ex) {
458                 throw new DataException("Error setting keys for join", ex);
459             }
460         }
461
462     }
463
464     /**
465      * Initialize the metadata based upon XML.
466      *
467      * @param metadata the metadata object to fill out with the XML data
468      */

469     protected void initializeXML(JoinedDataObjectMetaData metadata) throws
470             DataException {
471         if (log.isDebugEnabled()) {
472             log.debug("Initializing XML metadata for: "
473                     + this.getDefinitionName());
474         }
475
476         String JavaDoc fileLocation = this.getDefinitionName();
477         java.net.URL JavaDoc def = this.getClass().getResource(fileLocation);
478         if (def == null) {
479             throw new DataException("Unable to load metadata at: "
480                     + fileLocation);
481         }
482
483         JoinedDigesterBean digesterBean = new JoinedDigesterBean();
484         digesterBean.loadJoinData(def);
485         initializeFromJoinedDigesterBean(digesterBean, metadata);
486     }
487
488     /**
489      * Retrieves all the attributes for this data object
490      *
491      * @return a map of all the attributes for this instance of the data object
492      */

493     public Map getAllAttributes() {
494         return attributes;
495     }
496
497     /**
498      * Set an attribute. Attributes are temporary (e.g. not stored in the DBMS)
499      * values associated with this particular DB object instance.
500      *
501      * @param attributeName The name of the attribute being defined
502      * @param attributeValue The object to store under this attribute name
503      */

504     public void setAttribute(String JavaDoc attributeName, Object JavaDoc attributeValue) {
505         if (attributes == null) {
506             attributes = new HashMap();
507         }
508
509         attributes.put(attributeName, attributeValue);
510     }
511
512     /**
513      * Retrieve the attribute for this particular dataobject.
514      *
515      * @param attributeName the name of the attribute to get
516      * @return the object value of the attribute or null if it doesn't exist.
517      */

518     public Object JavaDoc getAttribute(String JavaDoc attributeName) {
519         if (attributes != null) {
520             return attributes.get(attributeName);
521         } else {
522             return null;
523         }
524     }
525
526     /**
527      * This tells the buildWhereClause to either respect case (true) or ignore
528      * case (false). You can call this method before doing a search and
529      * retreive if you want to match without worrying about case. For example
530      * if you where to call this method with isCaseSensitiveQuery = FALSE then
531      * this comparison would match in the search: vendor_name actual value =
532      * "My Name" query value = "my name" This would match in a search and
533      * retrieve.
534      *
535      * @param isCaseSensitiveQuery boolean
536      */

537     public void setCaseSensitiveQuery(boolean isCaseSensitiveQuery) {
538         caseSensitiveQuery = isCaseSensitiveQuery;
539     }
540
541     /**
542      * Set a custom WHERE clause
543      *
544      * @param customWhereClause the custom where clause to use.
545      * @param append if true the supplied WHERE clause will be appended to the one
546      * generated, else it will replace it
547      */

548     public synchronized void setCustomWhereClause(String JavaDoc customWhereClause
549                                                   , boolean append) {
550         this.customWhereClause = customWhereClause;
551         this.appendCustomWhere = append;
552
553     }
554
555     /**
556      * Sets the data context. Also sets the data context for all the nested
557      * data obejcts
558      *
559      * @param newContext A valid data context name.
560      */

561     public void setDataContext(String JavaDoc newContext) {
562         for (Iterator JavaDoc i = myDataObjects.keySet().iterator(); i.hasNext();) {
563             String JavaDoc key = (String JavaDoc) i.next();
564
565             DataObject oneobj = (DataObject) myDataObjects.get(key);
566             oneobj.setDataContext(newContext);
567         }
568     }
569
570     /**
571      * Returns the name of the currently set DataContext
572      *
573      * @return java.lang.String
574      */

575     public String JavaDoc getDataContext() {
576         Iterator JavaDoc i = myDataObjects.values().iterator();
577         if (i.hasNext()) {
578             DataObject oneobj = (DataObject) i.next();
579             return oneobj.getDataContext();
580         } else {
581             return "default";
582         }
583     }
584
585     /**
586      * Sets the field data for the named field. This particular implementation assumes
587      * that the proper data fields have already been created, and we're only
588      * resetting the appropriate types.
589      *
590      * @param fieldName The data field to set. It is defined by [shortname].[fieldname]
591      * @param o The Object to set it by.
592      */

593     public void setDataField(String JavaDoc fieldName, DataField o) throws
594             DataException {
595
596         DataField field = (DataField) this.myFields.get(fieldName);
597
598         if (field != null && !(o instanceof JoinedDataField)) {
599             throw new IllegalStateException JavaDoc("Can't be setting a joined data field with a non-joined data field");
600         }
601
602         if (field != null) {
603             this.myFields.put(fieldName, o);
604         } else {
605             JoinedDataObjectMetaData metadata = this.getJoinMetaData();
606             String JavaDoc[] location = metadata.getObjectAndField(fieldName);
607             DataObject localObject = (DataObject) this.myDataObjects.get(location[0]);
608             localObject.set(location[1], o);
609         }
610     }
611
612     /**
613      * Returns the object embedded within the field keyed by the fieldName
614      * parameter
615      *
616      * @param fieldName The name of the field to get
617      * @return The object if it isn't null for the data value or null.
618      * @throws DBException upon error
619      */

620     public DataField getDataField(String JavaDoc fieldName) throws DBException {
621
622         //
623
//First check for existence of a join field, at which point, return it.
624
//
625
DataField field = (DataField) this.myFields.get(fieldName);
626         if (field != null) {
627             return field;
628         }
629
630         JoinedDataObjectMetaData metadata = this.getJoinMetaData();
631         String JavaDoc rightKey = (String JavaDoc) metadata.foreignKeyToPrimaryKeyMap.get(fieldName);
632         String JavaDoc leftKey = (String JavaDoc) metadata.primaryToForeignKeyMap.get(fieldName);
633         String JavaDoc location[] = metadata.getObjectAndField(fieldName);
634         DataObject oneObj = (DataObject) myDataObjects.get(location[0]);
635         JoinedDataField joinField = null;
636
637         if ((rightKey == null || rightKey.length() == 0)
638                 && (leftKey == null || leftKey.length() == 0)) {
639             field = oneObj.getDataField(location[1]);
640             if (field.getFieldMetaData().isVirtual()) {
641                 field.setValue(oneObj.getField(location[1]));
642             }
643             return field;
644         } else if ((rightKey != null && rightKey.length() > 0)
645                 && (leftKey == null || leftKey.length() == 0)) {
646             //We have left key... set right key as well.
647
String JavaDoc rightLocation[] = metadata.getObjectAndField(rightKey);
648
649             joinField = (JoinedDataField) this.myFields.get(fieldName);
650             if (joinField == null) {
651                 joinField = JoinedDataField.getInstance(this
652                         , (DataObject) this.myDataObjects.get(location[0])
653                         , location[1],
654                         (DataObject) this.myDataObjects.get(rightLocation[0])
655                         , rightLocation[1]);
656             }
657
658             //
659
//We need to put it in twice since a lookup of either the left or the right
660
//key should return the joined data object
661
//
662
this.myFields.put(fieldName, joinField);
663             this.myFields.put(rightKey, joinField);
664
665         } else {
666             //Right Key is not null, joinKey is null;
667
String JavaDoc leftLocation[] = metadata.getObjectAndField(leftKey);
668             joinField = (JoinedDataField) this.myFields.get(leftKey);
669             if (joinField == null) {
670                 joinField = JoinedDataField.getInstance(this
671                         , (DataObject) this.myDataObjects.get(leftLocation[0])
672                         , leftLocation[1],
673                         (DataObject) this.myDataObjects.get(location[0]), location[1]);
674             }
675
676             //
677
//We need to put it in twice since a lookup of either the left or the right
678
//key should return the joined data object
679
//
680
this.myFields.put(fieldName, joinField);
681             this.myFields.put(leftKey, joinField);
682
683         }
684
685         return joinField;
686     }
687
688
689     /**
690      * Use this function to acquire the Executor interface that is associated
691      * with this data object
692      *
693      * @return DataExecutorInterface or null if no Executor has been associated
694      * with this object
695      */

696     public DataExecutorInterface getExecutor() {
697         Iterator JavaDoc i = myDataObjects.values().iterator();
698         if (i.hasNext()) {
699             DataObject oneobj = (DataObject) i.next();
700             return oneobj.getExecutor();
701         } else {
702             return null;
703         }
704     }
705
706
707     /**
708      * Retrieve the Field MetaData for the specified field name
709      *
710      * @param fieldName The name of the field in a 'alias.fieldname' format.
711      * @return The metadata for the field name.
712      */

713     public DataFieldMetaData getFieldMetaData(String JavaDoc fieldName) {
714         StringTokenizer JavaDoc stok = new StringTokenizer JavaDoc(StringUtil.notNull(fieldName)
715                 , ".");
716         if (!stok.hasMoreTokens()) {
717             throw new IllegalArgumentException JavaDoc("Field name must be proper format");
718         }
719
720         JDBCDataObject dataobject = (JDBCDataObject) this.myDataObjects.get(stok.
721                 nextToken());
722         if (!stok.hasMoreTokens()) {
723             throw new IllegalArgumentException JavaDoc("Field name must be proper format");
724         }
725
726         if (dataobject == null) {
727             throw new IllegalArgumentException JavaDoc("Unable to find specified data object in this Join");
728         }
729
730         return dataobject.getFieldMetaData(stok.nextToken());
731     }
732
733     /**
734      * Sets the name of the field
735      *
736      * @param fieldName the name of the field to set
737      * @param o the object to set.
738      * @throws IllegalArgumentException if filedName is improperly formatted
739      */

740     public void set(String JavaDoc fieldName, Object JavaDoc o) throws DataException {
741
742         try {
743             this.getDataField(fieldName).setValue(o);
744         } catch (DBException ex) {
745             throw new DataException("Unable to set field value for field "
746                     + fieldName + " value " + o.toString());
747         }
748     }
749
750     /**
751      * Get the value object associated with the field specifieed.
752      *
753      * @param fieldName The field name in a [alias].[fieldname] format
754      * @return the object value of the field.
755      * @throws DataException upon error
756      */

757     public Object JavaDoc get(String JavaDoc fieldName) throws DataException {
758         try {
759             return this.getDataField(fieldName).getValue();
760         } catch (DBException ex) {
761             throw new DataException("Error retrieving data field", ex);
762         }
763     }
764
765
766     /**
767      * Retrieve the key used for this join
768      *
769      * @return java.lang.String
770      */

771     public String JavaDoc getKey() {
772         JoinedDataObjectMetaData metadata = (JoinedDataObjectMetaData) this.
773                 getMetaData();
774
775         String JavaDoc alias = metadata.getPrimaryAlias();
776         JDBCDataObject primaryObject = null;
777         try {
778             primaryObject = this.getByShortName(alias);
779             return primaryObject.getKey();
780         } catch (DataException ex) {
781             log.error("Error dataobject by name: " + alias, ex);
782             return "";
783         }
784     }
785
786
787     /**
788      * SEt the locale of the object
789      *
790      * @param newLocale the new locale
791      */

792     public void setLocale(Locale JavaDoc newLocale) {
793         myLocale = newLocale;
794     }
795
796     /**
797      * Retrieve the Locale of the current object
798      *
799      * @return java.util.Locale
800      */

801     public Locale JavaDoc getLocale() {
802         if (myLocale == null) {
803             myLocale = Locale.getDefault();
804         }
805
806         return myLocale;
807     }
808
809     /**
810      * Retrieve the mapped data context for this data object
811      *
812      * @return java.lang.String
813      */

814     public String JavaDoc getMappedDataContext() {
815         JoinedDataObjectMetaData metadata = (JoinedDataObjectMetaData) this.
816                 getMetaData();
817
818         String JavaDoc alias = metadata.getPrimaryAlias();
819         JDBCDataObject primaryObject = null;
820         try {
821             primaryObject = this.getByShortName(alias);
822             return primaryObject.getMappedDataContext();
823         } catch (DataException ex) {
824             log.error("Error dataobject by name: " + alias, ex);
825             return "";
826         }
827     }
828
829     /**
830      * Set the maximum number of records to receive
831      *
832      * @param newMax the new maximum number of records to retrieve
833      */

834     public void setMaxRecords(int newMax) throws DBException {
835         maxRecords = newMax;
836     }
837
838     /**
839      * Retrieve the maximum number of records.
840      *
841      * @return the maximum number of records to retreive
842      */

843     public int getMaxRecords() {
844         return maxRecords;
845     }
846
847     /**
848      * Retrieve the database object's metadata. Metadata is a description of the
849      * database object, so it contains static information such as as description,
850      * field names, field types. Etc.
851      * <p>For implementers of this interface: It is best to store the metadata
852      * somewhere rather than recreating it each and every time. For low-memory
853      * requirements, a WeakHashMap is recommended</p>
854      *
855      * @return a built DataObjectMetaData for this database object
856      */

857     public DataObjectMetaData getMetaData() {
858         return (DataObjectMetaData) definitions.get(this.getDefinitionName());
859     }
860
861     /**
862      * Type safe return to the metadata for internal use.
863      *
864      * @return JoinedDataObjectMetaData
865      */

866     public JoinedDataObjectMetaData getJoinMetaData() {
867         return (JoinedDataObjectMetaData) getMetaData();
868     }
869
870
871     /**
872      * This function is useful for low level work where you want to modify the
873      * underlying data objects in a behavior diffent that the default behavior
874      * of the JoinedDataObject. Because of the nature of the object, if you
875      * modify the underlying dataobjects in any way, it is recommended that you
876      * discard the join that created them since integrity between the dataobjects
877      * might be compromised.
878      *
879      * @return ArrayList in the same order as the DataObjects are specified in
880      * the Join Definition.
881      */

882     public ArrayList JavaDoc getDataObjects() {
883         ArrayList JavaDoc al = new ArrayList JavaDoc(this.myDataObjects.size());
884         JoinedDataObjectMetaData metadata = this.getJoinMetaData();
885         for (Iterator JavaDoc i = metadata.getAliasesInOrder().iterator(); i.hasNext();) {
886             String JavaDoc oneAlias = (String JavaDoc) i.next();
887             al.add(this.myDataObjects.get(oneAlias));
888         }
889
890         return al;
891     }
892
893     /**
894      * Specifies the number of records that should be skipped over before any
895      * data from the <code>ResultSet</code> is retrieved in any subsequent
896      * searchAndRetrieve() call. Records will be skipped over (in the
897      * specified sort order) until the record counts is equal to or greater
898      * than the offset record. Specifying zero indicates that no records
899      * should be skipped over and the <code>ResultSet</code> immediately from
900      * the start.
901      *
902      * @param newOffset The maximum number of records to retrieve.
903      * @throws DBException If the max number is less than 0
904      */

905     public void setOffsetRecord(int newOffset) throws DBException {
906         if (newOffset < 0) {
907             throw new DBException("Offset records can't be less than 0");
908         }
909
910         offsetRecord = newOffset;
911     }
912
913     /* setOffsetRecord(int) */
914
915     /**
916      * Gets the number of records that be skipped. The offset records. A DB
917      * Object can be told to skip a certain number of records, before reading
918      * records from the <code>ResultSet</code>.
919      *
920      * @return The maximum number of records that should be skipped over before
921      * reading the data records.
922      * @see #setOffsetRecord(int)
923      */

924     public int getOffsetRecord() {
925         return offsetRecord;
926     }
927
928     /* getOffsetRecord() */
929
930     /**
931      * Retrieve the QueryInterface... currently not supported
932      *
933      * @return nothing.
934      */

935     public DataQueryInterface getQueryInterface() {
936         throw new java.lang.UnsupportedOperationException JavaDoc("Method getQueryInterface() not yet implemented.");
937     }
938
939     /**
940      * Retrieve the status of the data object
941      *
942      * @return the status of the primary data object. [It is assumed that the
943      * other ones have the same status as the primary]
944      */

945     public String JavaDoc getStatus() {
946         JoinedDataObjectMetaData metadata = (JoinedDataObjectMetaData) this.
947                 getMetaData();
948
949         String JavaDoc alias = metadata.getPrimaryAlias();
950         JDBCDataObject primaryObject = null;
951         try {
952             primaryObject = this.getByShortName(alias);
953             return primaryObject.getStatus();
954         } catch (DataException ex) {
955             log.error("Error dataobject by name: " + alias, ex);
956             return "";
957         }
958     }
959
960     /**
961      * Sets the status of the joined data object
962      *
963      * @param newValue see BaseDataObject for possible values
964      */

965     public void setStatus(String JavaDoc newValue) {
966         JoinedDataObjectMetaData metadata = (JoinedDataObjectMetaData) this.
967                 getMetaData();
968
969         String JavaDoc alias = metadata.getPrimaryAlias();
970         JDBCDataObject primaryObject = null;
971         try {
972             primaryObject = this.getByShortName(alias);
973             primaryObject.setStatus(newValue);
974         } catch (DataException ex) {
975             log.error("Error dataobject by name: " + alias, ex);
976         }
977
978     }
979
980
981     /**
982      * Retrieve a list of valid value object for this particular dbobject
983      *
984      * @param fieldName name of the field to retrieve the list for. Should be
985      * of the format [shortname].[fieldname]
986      * @return java.util.List of valid values
987      * @throws DBException upon error
988      */

989     public List JavaDoc getValidValuesList(String JavaDoc fieldName) throws DBException {
990
991         JoinedDataObjectMetaData metadata = this.getJoinMetaData();
992         String JavaDoc location[] = metadata.getObjectAndField(fieldName);
993
994         DataObject dataobject = (DataObject) this.myDataObjects.get(location[0]);
995         return dataobject.getValidValuesList(location[1]);
996     }
997
998     /**
999      * Adds records to all joined tables based upon the values input. If another
1000     * record exists [due to relations], the existing record, is skipped.
1001     *
1002     * @throws DBException upon database access error
1003     */

1004    public void add() throws DBException {
1005        checkInitialized();
1006        isAllowed(SecuredDBObject.ADD);
1007
1008        DBConnection connection = null;
1009        boolean localConn = false;
1010        if (localConnection != null) {
1011            connection = localConnection;
1012            localConn = true;
1013        } else {
1014            DBConnectionPool myPool = DBConnectionPool.getInstance(this.
1015                    getMappedDataContext());
1016            connection = myPool.getConnection("Joined DataObject Add");
1017            connection.setAutoCommit(false);
1018        }
1019
1020        JoinedDataObjectMetaData joinMetadata = this.getJoinMetaData();
1021        try {
1022            String JavaDoc allAliases[] = (String JavaDoc[]) joinMetadata.getAliasesInOrder()
1023                    .toArray(new String JavaDoc[myDataObjects.size()]);
1024
1025            //Ok, now work backwards so we can get autoincs from right tables
1026
//and apply it to foreign keys in the left tables.
1027
for (int i = (allAliases.length - 1); i >= 0; i--) {
1028                String JavaDoc oneAlias = allAliases[i];
1029                JDBCDataObject oneObject = (JDBCDataObject) this.myDataObjects.
1030                        get(oneAlias);
1031                oneObject.setConnection(connection, this.getDataContext());
1032                if (!oneObject.find()) {
1033                    oneObject.add();
1034                } else {
1035                    //If we only updated then we continue on to the
1036
//next alias because
1037
//autoinc fields will stay the same.
1038
oneObject.update();
1039                    continue;
1040                }
1041
1042                JDBCObjectMetaData metadata = oneObject.getJDBCMetaData();
1043                for (Iterator JavaDoc j = metadata
1044                        .getKeyFieldListArray().iterator(); j.hasNext();) {
1045                    String JavaDoc oneFieldName = (String JavaDoc) j.next();
1046
1047                    //
1048
//We only have to worry about autoinc fields since they're the
1049
//part that haven't been set
1050
//
1051
if (metadata.getFieldMetadata(oneFieldName).
1052                            isAutoIncremented()) {
1053                        String JavaDoc rightFieldName = allAliases[i] + "."
1054                                + oneFieldName;
1055                        String JavaDoc leftFieldName = (String JavaDoc) joinMetadata.
1056                                primaryToForeignKeyMap.get(rightFieldName);
1057                        if (leftFieldName != null && leftFieldName.length() > 0) {
1058                            String JavaDoc location[] = joinMetadata.getObjectAndField(leftFieldName);
1059                            JDBCDataObject leftObject = (JDBCDataObject) this.
1060                                    myDataObjects.get(location[0]);
1061                            leftObject.set(location[1],
1062                                    oneObject.get(oneFieldName));
1063                        }
1064                    }
1065
1066                }
1067
1068            }
1069            if (!localConn) {
1070                connection.commit();
1071                connection.release();
1072            }
1073// if (localConnection == null && connection != null) {
1074
// connection.commit();
1075
// }
1076
} catch (DBException ex) {
1077            log.error("Error adding JoinedDataObject", ex);
1078// if (localConnection == null && connection != null) {
1079
if (!localConn) {
1080                connection.rollback();
1081                connection.release();
1082            } else {
1083                throw ex;
1084            }
1085        } finally {
1086// if (localConnection == null && connection != null) {
1087
// connection.release();
1088
// }
1089
}
1090
1091        setStatus(BaseDataObject.STATUS_CURRENT);
1092    }
1093
1094
1095    /**
1096     * Retrieve a list of valid value object for this particular dbobject
1097     *
1098     * @param fieldName name of the field to retrieve the list for.
1099     * @param fieldValue the String value of the field
1100     * @throws DBException upon error
1101     * @throws IllegalArgumentException if unable to parse the fieldName
1102     */

1103    public void checkField(String JavaDoc fieldName, String JavaDoc fieldValue) throws
1104            DBException {
1105        checkInitialized();
1106
1107        String JavaDoc location[] = this.getJoinMetaData().getObjectAndField(fieldName);
1108
1109        DataObject oneObj = (DataObject) myDataObjects.get(location[0]);
1110
1111        oneObj.checkField(location[1], fieldValue);
1112    }
1113
1114
1115    /**
1116     * See if the current user has permission to perform the permissions
1117     *
1118     * @param requestedFunction (A)dd, (U)pdate, (D)elete, (S)earch
1119     * @return boolean: true if the operation is allowed, or false if it is not
1120     * @see #isAllowed
1121     */

1122    public boolean checkAllowed(String JavaDoc requestedFunction) throws DBException {
1123        try {
1124            isAllowed(requestedFunction);
1125        } catch (SecurityException JavaDoc de) {
1126            log.debug(de);
1127            return false;
1128        }
1129
1130        return true;
1131    }
1132    /* checkAllowed(String) */
1133
1134    /**
1135     * Clears all currently loaded fields
1136     */

1137    public void clear() throws DBException {
1138        checkInitialized();
1139
1140        for (Iterator JavaDoc i = this.myDataObjects.values().iterator(); i.hasNext();) {
1141            JDBCDataObject oneObj = (JDBCDataObject) i.next();
1142            oneObj.clear();
1143        }
1144    }
1145
1146    /**
1147     * Add the DISTINCT keyword to the query if the join is supposed to be
1148     * distinct.
1149     *
1150     * @param contextPool the connection pool for the current context
1151     * @return the string including distinct keywords if necessary OR an empty string
1152     */

1153    protected String JavaDoc getDistinct(DBConnectionPool contextPool) {
1154        if (this.getJoinMetaData().isSelectDistinct()) {
1155            return " " + contextPool.getDistinctRowsetKeyword() + " ";
1156        } else {
1157            return "";
1158        }
1159    }
1160
1161    /**
1162     * Retrieves the count of any particular join based upon the field values
1163     * of the data object.
1164     *
1165     * @return integer for the number of non-null records found.
1166     * @throws DBException upon data access error
1167     * @throws DataException for metadata access error
1168     */

1169    public int count() throws DBException {
1170        checkInitialized();
1171        isAllowed(SecuredDBObject.SEARCH);
1172
1173        DBConnectionPool myPool = DBConnectionPool.getInstance(this.
1174                getMappedDataContext());
1175        ;
1176        DBConnection myConnection = null;
1177
1178        JDBCDataObject primaryObject = (JDBCDataObject) this.getJoinMetaData().
1179                getDataObjectsInOrder().get(0);
1180        ArrayList JavaDoc keyFields = primaryObject.getMetaData().getKeyFieldListArray();
1181
1182        //
1183
//Performance improvement. If there is only one key field, count on it
1184
//only.
1185
//
1186
FastStringBuffer myStatement = FastStringBuffer.getInstance();
1187        try {
1188            if (keyFields.size() > 1) {
1189                myStatement.append("SELECT ");
1190                myStatement.append(getDistinct(myPool));
1191                myStatement.append(" COUNT(*) FROM ");
1192            } else {
1193                DataFieldMetaData keymetadata = primaryObject
1194                        .getFieldMetaData((String JavaDoc)
1195                        keyFields.get(0));
1196                myStatement.append("SELECT ");
1197                myStatement.append(getDistinct(myPool));
1198                myStatement.append("COUNT( "
1199                        + primaryObject.getJDBCMetaData().
1200                        getTargetSQLTable(primaryObject.
1201                        getDataContext()) + "."
1202                        + keymetadata.getName() + ") FROM ");
1203            }
1204
1205            myStatement.append(this.buildFromClause());
1206
1207            if (customWhereClause != null) {
1208                if (appendCustomWhere) {
1209                    buildWhereClauseBuffer(true, myStatement);
1210                    formatCustomWhereClause(myStatement);
1211                } else {
1212                    formatCustomWhereClause(myStatement);
1213                }
1214            } else {
1215                buildWhereClauseBuffer(true, myStatement);
1216            }
1217
1218            if (localConnection != null) {
1219                myConnection = localConnection;
1220            } else {
1221
1222                myConnection = myPool.getConnection(this.getDefinitionName());
1223            }
1224
1225            myConnection.execute(myStatement.toString());
1226
1227            if (myConnection.next()) {
1228                return myConnection.getInt(1);
1229            } else {
1230                log.warn("count() result set didn't return any values");
1231            }
1232        } catch (DBException de) {
1233            log.error("Error performing count.", de);
1234            throw de;
1235        } catch (Exception JavaDoc e) {
1236            log.error("Error performing count.", e);
1237            throw new DataException("Exception performing count", e);
1238        } finally {
1239            myStatement.release();
1240            if (localConnection == null && myConnection != null) {
1241                myConnection.release();
1242            }
1243        }
1244
1245        return 0;
1246    }
1247
1248    /**
1249     * Deletes the join. Use with extreme care since you could wipe out
1250     * referential integrity with other objects.
1251     *
1252     * @throws DBException upon error
1253     */

1254    public void delete() throws DBException {
1255        checkInitialized();
1256        isAllowed(SecuredDBObject.DELETE);
1257        DBConnectionPool myPool = null;
1258        DBConnection myConnection = null;
1259
1260        //
1261
//Performance improvement. If there is only one key field, count on it
1262
//only.
1263
//
1264
FastStringBuffer myStatement = FastStringBuffer.getInstance();
1265        try {
1266            myStatement.append("DELETE FROM ");
1267
1268            myStatement.append(this.buildFromClause());
1269
1270            if (customWhereClause != null) {
1271                if (appendCustomWhere) {
1272                    buildWhereClauseBuffer(true, myStatement);
1273                    formatCustomWhereClause(myStatement);
1274                } else {
1275                    formatCustomWhereClause(myStatement);
1276                }
1277            } else {
1278                buildWhereClauseBuffer(true, myStatement);
1279            }
1280
1281            if (localConnection != null) {
1282                myConnection = localConnection;
1283            } else {
1284                myPool = DBConnectionPool.getInstance(this.getDataContext());
1285                myConnection = myPool.getConnection(this.getDefinitionName());
1286            }
1287
1288            myConnection.executeUpdate(myStatement.toString());
1289
1290        } catch (DBException de) {
1291            log.error("Error performing count.", de);
1292            throw de;
1293        } catch (Exception JavaDoc e) {
1294            log.error("Error performing count.", e);
1295            throw new DataException("Exception performing count", e);
1296        } finally {
1297            myStatement.release();
1298            if (localConnection == null && myConnection != null) {
1299                myConnection.release();
1300            }
1301        }
1302
1303        setStatus(BaseDataObject.STATUS_DELETED);
1304    }
1305
1306    /**
1307     * Tests whether two joined DataObjects are equal
1308     *
1309     * @param otherObject the object to compare against
1310     * @return true if the objects are equal according to our tests
1311     */

1312    public boolean equals(Object JavaDoc otherObject) {
1313        checkInitialized();
1314        if (!(otherObject instanceof JoinedDataObject)) {
1315            return false;
1316        }
1317
1318        JoinedDataObject other = (JoinedDataObject) otherObject;
1319
1320        boolean returnValue = true;
1321
1322        for (Iterator JavaDoc i = myDataObjects.keySet().iterator(); i.hasNext();) {
1323            String JavaDoc oneObjName = (String JavaDoc) i.next();
1324
1325            if (!other.myDataObjects.containsKey(oneObjName)) {
1326                return false;
1327            }
1328
1329            DataObject myObj = (DataObject) myDataObjects.get(oneObjName);
1330            DataObject otherObj = (DataObject) other.myDataObjects.get(oneObjName);
1331
1332            returnValue &= myObj.equals(otherObj);
1333
1334            if (returnValue == false) {
1335                break;
1336            }
1337        }
1338
1339        return returnValue;
1340    }
1341
1342    /**
1343     * Returns a hash code value for the object.
1344     *
1345     * @return a hash code value for this object.
1346     */

1347    public int hashCode() {
1348        int returnValue = 0;
1349        for (Iterator JavaDoc i = myDataObjects.keySet().iterator(); i.hasNext();) {
1350            String JavaDoc oneObjName = (String JavaDoc) i.next();
1351
1352            DataObject myObj = (DataObject) myDataObjects.get(oneObjName);
1353
1354            returnValue ^= myObj.hashCode();
1355        }
1356
1357        return returnValue;
1358    }
1359
1360
1361    /**
1362     * Find the object.
1363     *
1364     * @return true if the object was found.
1365     * @throws DBException upon database access error
1366     * @see com.jcorporate.expresso.core.dataobjects.DataObject#find
1367     */

1368    public boolean find() throws DBException {
1369        checkInitialized();
1370        isAllowed(SecuredDBObject.SEARCH);
1371
1372        JoinedDataObject test = new JoinedDataObject(this);
1373        test.setMaxRecords(1);
1374        test.clear();
1375        test.setRequestingUid(this.getRequestingUid());
1376        test.setDataContext(this.getDataContext());
1377
1378        ArrayList JavaDoc fieldList = this.getMetaData().getFieldListArray();
1379        for (Iterator JavaDoc i = fieldList.iterator(); i.hasNext();) {
1380            String JavaDoc fieldName = (String JavaDoc) i.next();
1381            test.set(fieldName, this.getDataField(fieldName).getValue());
1382        }
1383
1384        //
1385
//Get the search'ed recordset, and if we have a record, load it into
1386
//ourselves
1387
//
1388
ArrayList JavaDoc al = test.searchAndRetrieveList(null);
1389        if (al.size() == 0) {
1390            return false;
1391        } else {
1392            JoinedDataObject oneObj = (JoinedDataObject) al.get(0);
1393            for (Iterator JavaDoc i = fieldList.iterator();
1394                 i.hasNext();) {
1395                String JavaDoc fieldName = (String JavaDoc) i.next();
1396                this.set(fieldName, oneObj.get(fieldName));
1397            }
1398
1399        }
1400        setStatus(BaseDataObject.STATUS_CURRENT);
1401        return true;
1402    }
1403
1404
1405    /**
1406     * Simple query method for querying dataobjects. Retrieve a list of all
1407     * objects matching the given criteria.
1408     *
1409     * @param sortOrder A pipe delimited set of fields to sort the resultset
1410     * with.
1411     * @return ArrayList of JoinedDataObjects
1412     * @throws DBException upon database access error
1413     * <p/>
1414     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1415     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
1416     */

1417    public ArrayList JavaDoc searchAndRetrieveList(String JavaDoc sortOrder) throws DBException {
1418        boolean needComma = false;
1419        isAllowed(SecuredDBObject.SEARCH);
1420
1421        HashMap rtrvListByTable = new HashMap();
1422
1423        DBConnectionPool myPool = DBConnectionPool.getInstance(getDataContext());
1424        DBConnection myConnection = null;
1425
1426        try {
1427            if (localConnection != null) {
1428                myConnection = localConnection;
1429            } else {
1430                myConnection = myPool.getConnection("JoinedDataObject.searchAndRetrieveList()");
1431            }
1432            recordSet = new ArrayList JavaDoc();
1433
1434            String JavaDoc fieldName = null;
1435            FastStringBuffer myStatement = new FastStringBuffer(256);
1436            myStatement.append("SELECT ");
1437
1438            if (myConnection.getLimitationPosition()
1439                    == DBConnection.LIMITATION_AFTER_SELECT &&
1440                    (offsetRecord > 0 || maxRecords > 0)) {
1441
1442                // Insert limitation stub after table nomination
1443
String JavaDoc limitStub = JDBCUtil.getInstance().makeLimitationStub(myConnection, this);
1444
1445                myStatement.append(" ");
1446                myStatement.append(limitStub);
1447                myStatement.append(" ");
1448            }
1449
1450            myStatement.append(this.getDistinct(myPool));
1451
1452            Iterator JavaDoc eachObj = this.getJoinMetaData().getDataObjectsInOrder().
1453                    iterator();
1454            Iterator JavaDoc eachAlias = this.getJoinMetaData().getAliasesInOrder().
1455                    iterator();
1456            JDBCDataObject oneObj = null;
1457            String JavaDoc oneAlias;
1458
1459            while (eachObj.hasNext()) {
1460                oneObj = (JDBCDataObject) eachObj.next();
1461                oneAlias = (String JavaDoc) eachAlias.next();
1462                JDBCObjectMetaData metadata = oneObj.getJDBCMetaData();
1463
1464                ArrayList JavaDoc retrievedFieldList = (ArrayList JavaDoc) rtrvListByTable.get(
1465                        metadata.getTargetSQLTable(oneObj.getDataContext()));
1466                if (retrievedFieldList == null) {
1467                    retrievedFieldList = new ArrayList JavaDoc();
1468                    rtrvListByTable.put(metadata.getTargetSQLTable(oneObj.
1469                            getDataContext()), retrievedFieldList);
1470                }
1471
1472                List JavaDoc fieldsToRetrieve = this.getJoinMetaData().
1473                        getFieldsToRetrieve(oneAlias);
1474                if (fieldsToRetrieve != null) {
1475                    if (fieldsToRetrieve.isEmpty()) {
1476                        // we don't want to include any fields from this table
1477
continue;
1478                    }
1479                    JoinedDataObjectMetaData.FieldList fieldList = null;
1480                    for (Iterator JavaDoc i = fieldsToRetrieve.iterator(); i.hasNext();) {
1481                        fieldList = (JoinedDataObjectMetaData.FieldList) i.next();
1482                        String JavaDoc fieldExpression = fieldList.getFieldExpression();
1483                        fieldName = fieldList.getFieldName();
1484                        boolean isExpression = fieldList.isExpression();
1485                        DataFieldMetaData metaData =
1486                                oneObj.getFieldMetaData(fieldName);
1487
1488                        if (!metaData.isVirtual()
1489                                && !metaData.isBinaryObjectType()) {
1490                            if (needComma) {
1491                                myStatement.append(", ");
1492                            }
1493                            if (isExpression) {
1494                                // add the expression 'as-is' to the select
1495
myStatement.append(fieldExpression);
1496                            } else {
1497                                myStatement.append(selectFieldString(oneObj
1498                                        , fieldName));
1499                            }
1500                            retrievedFieldList.add(fieldName);
1501                            needComma = true;
1502                        }
1503                        /* if field is not virtual & not binary*/
1504                    }
1505                    /* for each field */
1506                } else {
1507                    for (Iterator JavaDoc i = metadata.getFieldListArray().iterator();
1508                         i.hasNext();
1509                            ) {
1510                        fieldName = (String JavaDoc) i.next();
1511                        DataFieldMetaData metaData =
1512                                oneObj.getFieldMetaData(fieldName);
1513
1514                        if (!metaData.isVirtual()
1515                                && !metaData.isBinaryObjectType()) {
1516                            if (needComma) {
1517                                myStatement.append(", ");
1518                            }
1519
1520                            myStatement.append(selectFieldString(oneObj
1521                                    , fieldName));
1522                            retrievedFieldList.add(fieldName);
1523                            needComma = true;
1524                        }
1525
1526                        /* if field is not virtual & not binary*/
1527                    }
1528
1529                    /* for each field */
1530                }
1531            }
1532
1533            myStatement.append(" FROM ");
1534            myStatement.append(this.buildFromClause());
1535
1536            if (myConnection.getLimitationPosition()
1537                    == DBConnection.LIMITATION_AFTER_TABLE &&
1538                    (offsetRecord > 0 || maxRecords > 0)) {
1539
1540                // Insert limitation stub after table nomination
1541
String JavaDoc limitStub = JDBCUtil.getInstance().makeLimitationStub(myConnection, this);
1542                myStatement.append(" ");
1543                myStatement.append(limitStub);
1544                myStatement.append(" ");
1545            }
1546
1547            if (customWhereClause != null) {
1548                if (appendCustomWhere) {
1549                    buildWhereClauseBuffer(true, myStatement);
1550                    formatCustomWhereClause(myStatement);
1551                } else {
1552                    formatCustomWhereClause(myStatement);
1553                }
1554            } else {
1555                buildWhereClauseBuffer(true, myStatement);
1556            }
1557
1558            if (myConnection.getLimitationPosition()
1559                    == DBConnection.LIMITATION_AFTER_WHERE &&
1560                    (offsetRecord > 0 || maxRecords > 0)) {
1561
1562                // Insert limitation stub after table nomination
1563
String JavaDoc limitStub = JDBCUtil.getInstance().makeLimitationStub(myConnection, this);
1564
1565                myStatement.append(" ");
1566                myStatement.append(limitStub);
1567                myStatement.append(" ");
1568            }
1569
1570            myStatement.append(this.buildOrderByString(sortOrder));
1571            myConnection.execute(myStatement.toString());
1572
1573            int recordCount = 0;
1574            int retrieveCount = 0;
1575            while (myConnection.next()) {
1576                recordCount++;
1577                retrieveCount++;
1578
1579                //If there's limitation syntax on, then the first record will be the
1580
//maximum record.
1581
if (retrieveCount < offsetRecord && offsetRecord > 0 &&
1582                        myConnection.getLimitationPosition()
1583                        == DBConnection.LIMITATION_DISABLED) {
1584                    continue;
1585                } else if (retrieveCount == offsetRecord && offsetRecord > 0 &&
1586                        myConnection.getLimitationPosition()
1587                        == DBConnection.LIMITATION_DISABLED) {
1588                    recordCount = 0; //Reset count for counting for max records.
1589
continue; //Skip this record... next one, we will start loading.
1590
}
1591                if ((recordCount > maxRecords) && (maxRecords > 0)) {
1592                    this.setAttribute("More Records", "Y");
1593                    break;
1594                }
1595
1596                JoinedDataObject myObj = new JoinedDataObject(this);
1597                int i = 1;
1598
1599                if (log.isDebugEnabled()) {
1600                    log.debug("Retrieved row " + recordCount);
1601                }
1602
1603                String JavaDoc oneFieldValue = null;
1604
1605// eachObj = this.getJoinMetaData().getDataObjectsInOrder().iterator();
1606
eachAlias = this.getJoinMetaData().getAliasesInOrder().iterator();
1607
1608                while (eachAlias.hasNext()) {
1609                    oneAlias = (String JavaDoc) eachAlias.next();
1610                    oneObj = (JDBCDataObject) myObj.myDataObjects.get(oneAlias);
1611                    JDBCObjectMetaData objectMetadata = oneObj.getJDBCMetaData();
1612
1613                    ArrayList JavaDoc retrievedFieldList = (ArrayList JavaDoc) rtrvListByTable.
1614                            get(objectMetadata.getTargetSQLTable(oneObj.
1615                            getDataContext()));
1616
1617                    for (Iterator JavaDoc it = retrievedFieldList.iterator();
1618                         it.hasNext();) {
1619                        fieldName = (String JavaDoc) it.next();
1620                        DataFieldMetaData metaData = oneObj.getFieldMetaData(fieldName);
1621
1622                        if (!metaData.isVirtual()
1623                                && !metaData.isBinaryObjectType()) {
1624                            try {
1625                                oneFieldValue = myConnection.getString(i);
1626                            } catch (DBException de) {
1627                                String JavaDoc myName = (thisClass
1628                                        + "searchAndRetrieve()");
1629                                throw new DBException(myName +
1630                                        ":Error retrieving field '" +
1631                                        objectMetadata.getTargetSQLTable(oneObj.
1632                                        getDataContext()) +
1633                                        "." + fieldName + "'",
1634                                        de);
1635                            }
1636
1637                            i++;
1638
1639                            if (log.isDebugEnabled()) {
1640                                log.debug("Setting " +
1641                                        objectMetadata.getTargetTable() + "." +
1642                                        fieldName + " to " + oneFieldValue);
1643                            }
1644
1645                            myObj.set(oneAlias + "." + fieldName, oneFieldValue);
1646                        }
1647                    }
1648                }
1649                /* each db object */
1650
1651                myObj.setDataContext(getDataContext());
1652
1653                if (myObj instanceof Securable) {
1654                    myObj.setRequestingUid(this.getRequestingUid());
1655                }
1656
1657                myObj.setStatus(BaseDataObject.STATUS_CURRENT);
1658                recordSet.add(myObj);
1659            }
1660
1661            /* each row retrieved from the db */
1662        } catch (Exception JavaDoc e) {
1663            log.error("Error finding join", e);
1664            throw new DBException("Error finding objects", e);
1665        } catch (Throwable JavaDoc t) {
1666            log.error("Error finding join", t);
1667            throw new DBException("Error finding objects", t);
1668        } finally {
1669            if (localConnection == null) {
1670                myPool.release(myConnection);
1671            }
1672        }
1673
1674        return recordSet;
1675
1676    }
1677
1678    /**
1679     * Retrieve an unsorted array of DataObjects representing the results
1680     * of the join fields
1681     *
1682     * @return java.util.ArrayList of JoinedDataObject
1683     * @throws DBException upon data access error.
1684     */

1685    public ArrayList JavaDoc searchAndRetrieveList() throws DBException {
1686        return searchAndRetrieveList(null);
1687    }
1688
1689    /**
1690     * Build an appropriate String for use in the select part of an SQL
1691     * statement
1692     *
1693     * @param oneObj Database object containing the field
1694     * @param fieldName The name of the field to be handled
1695     * @return The portion of the select clause with the appropriate function
1696     * wrapped around it
1697     * @throws DBException upon data access error
1698     * <p/>
1699     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1700     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
1701     */

1702    protected String JavaDoc selectFieldString(JDBCDataObject oneObj, String JavaDoc fieldName) throws
1703            DBException {
1704        checkInitialized();
1705        return StringUtil.replaceStringOnce(oneObj.selectFieldString(fieldName),
1706                fieldName
1707                , oneObj.getJDBCMetaData().
1708                getTargetSQLTable(oneObj.
1709                getDataContext()) + "." + fieldName);
1710    }
1711
1712    /* selectFieldString(String) */
1713
1714    /**
1715     * Update the current join to the database. All this does is search for exiting
1716     * objects and if it finds it, updates, if not, it
1717     *
1718     * @throws DBException upon database access error
1719     */

1720    public void update() throws DBException {
1721        checkInitialized();
1722        isAllowed(SecuredDBObject.UPDATE);
1723        DBConnection connection = null;
1724
1725        boolean localConn = false;
1726        if (localConnection != null) {
1727            connection = localConnection;
1728            localConn = true;
1729        } else {
1730            DBConnectionPool myPool = DBConnectionPool.getInstance(this.
1731                    getMappedDataContext());
1732
1733            connection = myPool.getConnection("Joined DataObject Update");
1734            connection.setAutoCommit(false);
1735        }
1736
1737        try {
1738            for (Iterator JavaDoc i = this.myDataObjects.values().iterator(); i.hasNext();) {
1739                JDBCDataObject oneObj = (JDBCDataObject) i.next();
1740                oneObj.setConnection(connection, this.getDataContext());
1741                this.addOrUpdate(oneObj);
1742            }
1743// if (localConnection == null && connection != null) {
1744
if (!localConn) {
1745                connection.commit();
1746                connection.release();
1747            }
1748
1749        } catch (DBException ex) {
1750            log.error("Error executing SQL statement", ex);
1751// if (localConnection == null && connection != null) {
1752
if (!localConn) {
1753                connection.rollback();
1754                connection.release();
1755            } else {
1756                throw ex;
1757            }
1758        } catch (Throwable JavaDoc t) {
1759            log.error("Error updating join", t);
1760            if (!localConn) {
1761                connection.rollback();
1762                connection.release();
1763            }
1764            throw new DataException("Error updating join", t);
1765        } finally {
1766// if (localConnection == null && connection != null) {
1767
// if (!localConn) {
1768
// connection.release();
1769
// }
1770
}
1771
1772        //Reset the local connections so they don't behave like they're in
1773
//a transaction after this.
1774
for (Iterator JavaDoc i = this.myDataObjects.values().iterator(); i.hasNext();) {
1775            JDBCDataObject oneObj = (JDBCDataObject) i.next();
1776            oneObj.setConnection(null, this.getDataContext());
1777        }
1778
1779        setStatus(BaseDataObject.STATUS_CURRENT);
1780    }
1781
1782    /**
1783     * Determine if a record with these fields exists already - if so, update. If
1784     * not, add a new record.
1785     *
1786     * @param testObject the JDBCObject to add or update.
1787     */

1788    public synchronized void addOrUpdate(JDBCDataObject testObject) throws
1789            DBException {
1790        JDBCDataObject searchObj = (JDBCDataObject) DataObjectFactory.
1791                createDataObject(testObject
1792                .getClass(), this.getDataContext(), this.getRequestingUid());
1793
1794        searchObj.setDataContext(getDataContext());
1795        String JavaDoc oneFieldName = null;
1796
1797        for (Iterator JavaDoc i = searchObj.getMetaData().getKeyFieldListArray().
1798                iterator(); i.hasNext();) {
1799            oneFieldName = (String JavaDoc) i.next();
1800            searchObj.set(oneFieldName, testObject.getField(oneFieldName));
1801        }
1802
1803        if (searchObj.find()) {
1804
1805            // special case: if there are no fields besides key
1806
// fields, then do nothing since key fields never update this way
1807
if (testObject.getMetaData().getKeyFieldListArray().size()
1808                    == testObject.getMetaData().getFieldListArray().size()) {
1809                // do nothing
1810
// @todo add logic for virtual fields
1811
} else {
1812                testObject.getExecutor().update(testObject, true);
1813            }
1814        } else {
1815            testObject.add();
1816        }
1817    }
1818    /* addOrUpdate() */
1819
1820    /**
1821     * Retrieve the JDBCDataObject as defined by the 'short name'
1822     *
1823     * @param shortName the name to retrieve
1824     * @return JDBCDataObject
1825     * @throws DataException upon error
1826     */

1827    private JDBCDataObject getByShortName(String JavaDoc shortName) throws
1828            DataException {
1829        StringUtil.assertNotBlank(shortName, "Short name may not be blank");
1830
1831        JDBCDataObject oneObj = (JDBCDataObject) myDataObjects.get(shortName);
1832
1833        if (oneObj == null) {
1834            throw new DataException("No such object as '" + shortName + "'");
1835        }
1836
1837        return oneObj;
1838    }
1839
1840
1841    /**
1842     * Builds a 'FROM' clause using ANSI 'JOIN' syntax.
1843     *
1844     * @param leftShortName short name for the left hand table.
1845     * @param leftColumn name of the column from the left hand table for the
1846     * JOIN condition
1847     * @param rightShortName short name for the right hand table.
1848     * @param rightColumn name of the column from the right hand table for the
1849     * JOIN condition
1850     * @param joinType the type of join to be performed, i.e. LEFT, RIGHT or
1851     * INNER.
1852     * @return java.lang.String
1853     * @throws DBException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
1854     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
1855     */

1856    private String JavaDoc buildJoin(String JavaDoc leftShortName, String JavaDoc leftColumn,
1857                             String JavaDoc rightShortName, String JavaDoc rightColumn
1858                             , int joinType) throws DBException {
1859        checkInitialized();
1860        JDBCDataObject leftDBObj = getByShortName(leftShortName);
1861        JDBCDataObject rightDBObj = getByShortName(rightShortName);
1862
1863        ArrayList JavaDoc leftCols = new ArrayList JavaDoc(3);
1864        ArrayList JavaDoc rightCols = new ArrayList JavaDoc(3);
1865        StringTokenizer JavaDoc stok = new StringTokenizer JavaDoc(leftColumn, "|");
1866        while (stok.hasMoreTokens()) {
1867            leftCols.add(stok.nextToken());
1868        }
1869        stok = new StringTokenizer JavaDoc(rightColumn, "|");
1870        while (stok.hasMoreTokens()) {
1871            rightCols.add(stok.nextToken());
1872        }
1873        FastStringBuffer buffer = FastStringBuffer.getInstance();
1874
1875        try {
1876
1877            switch (joinType) {
1878                case INNER_JOIN:
1879                    buffer.append(" INNER JOIN ");
1880                    break;
1881
1882                case RIGHT_JOIN:
1883                    buffer.append(" RIGHT JOIN ");
1884                    break;
1885
1886                case LEFT_JOIN:
1887                    buffer.append(" LEFT JOIN ");
1888                    break;
1889
1890                case UNSPECIFIED_JOIN:
1891                    throw new IllegalArgumentException JavaDoc("BuildJoin does not support Unspecified joins");
1892
1893            }
1894
1895            buffer.append(rightDBObj.getJDBCMetaData().getTargetSQLTable(rightDBObj.getDataContext()));
1896            buffer.append(" ON ");
1897            buffer.append(leftDBObj.getJDBCMetaData().getTargetSQLTable(leftDBObj.getDataContext()));
1898            buffer.append(".");
1899            buffer.append(leftColumn);
1900            buffer.append(" = ");
1901            buffer.append(rightDBObj.getJDBCMetaData().getTargetSQLTable(rightDBObj.getDataContext()));
1902            buffer.append(".");
1903            buffer.append(rightColumn);
1904            buffer.append(" ");
1905            boolean addAnd = false;
1906
1907            for (int i = 0; i < leftCols.size(); i++) {
1908                if (addAnd) {
1909                    buffer.append(" AND ");
1910                }
1911                buffer.append(leftDBObj.getJDBCMetaData().getTargetTable());
1912                buffer.append(".");
1913                buffer.append((String JavaDoc) leftCols.get(i));
1914                buffer.append(" = ");
1915                buffer.append(rightDBObj.getJDBCMetaData().getTargetTable());
1916                buffer.append(".");
1917                buffer.append((String JavaDoc) rightCols.get(i));
1918                addAnd = true;
1919            }
1920            buffer.append(") ");
1921
1922            return buffer.toString();
1923        } catch (DBException ex) {
1924            throw new DBException("Error building join string", ex);
1925        } finally {
1926            buffer.release();
1927        }
1928    }
1929
1930
1931    /**
1932     * Use this method to set the key to the definition name for the Defineable
1933     * database object. The actual meaning of the definitionName may be different.
1934     * For example, AutoDBObject's definition name is the database table name.
1935     * JoinedDataObject's definition name is the classpath URL location to the
1936     * definition XML file.
1937     *
1938     * @param definitionName java.lang.String, the actual definition of the
1939     * dataobject.
1940     * @throws DataException if the DataObject is unable to initialize itself
1941     * with the given definition name.
1942     */

1943    public void setDefinitionName(String JavaDoc definitionName) throws DataException {
1944        thisDefinitionName = definitionName;
1945        init();
1946    }
1947
1948    /**
1949     * Retrieve the current UID definition name.
1950     *
1951     * @return integer represeting the user ID
1952     */

1953    public int getRequestingUid() {
1954        return this.uid;
1955    }
1956
1957    /**
1958     * Sets the UID for the operations.... also sets the UID for all contained
1959     * elements.
1960     *
1961     * @param newUid the user id for the operation.
1962     */

1963    public void setRequestingUid(int newUid) {
1964
1965        this.uid = newUid;
1966        for (Iterator JavaDoc i = this.myDataObjects.keySet().iterator(); i.hasNext();) {
1967
1968            String JavaDoc key = (String JavaDoc) i.next();
1969
1970            DataObject value = (DataObject) myDataObjects.get(key);
1971
1972            if (value instanceof Securable) {
1973                ((Securable) value).setRequestingUid(newUid);
1974            }
1975        }
1976    }
1977
1978    /**
1979     * Check if the function is allowed. It does this by checking all the nested
1980     * data objects ot make sure that each one is allowed.
1981     *
1982     * @param requestedFunction the function name to check
1983     * @throws SecurityException if the method is not allowed.
1984     */

1985    public void isAllowed(String JavaDoc requestedFunction) throws SecurityException JavaDoc,
1986            com.jcorporate.expresso.core.db.DBException {
1987        checkInitialized();
1988
1989        if (getRequestingUid() == SecuredDBObject.SYSTEM_ACCOUNT
1990                || User.getUserFromId(getRequestingUid(), getDataContext()).isAdmin()) {
1991            // all access ok
1992
return;
1993        }
1994
1995        //First check for any values from the permissions map
1996
//If such operations are flat not allowed for this join, then
1997
//we return immediately
1998
Boolean JavaDoc override = (Boolean JavaDoc) getJoinMetaData().getPermissions().get(requestedFunction);
1999        if (override != null && override.booleanValue() == false) {
2000            throw new SecurityException JavaDoc("Method: " + requestedFunction
2001                    + " not allowed");
2002        }
2003
2004        for (Iterator JavaDoc i = this.myDataObjects.keySet().iterator(); i.hasNext();) {
2005            String JavaDoc key = (String JavaDoc) i.next();
2006
2007            DataObject value = (DataObject) myDataObjects.get(key);
2008            if (value instanceof Securable) {
2009                ((Securable) value).isAllowed(requestedFunction);
2010            } else {
2011                String JavaDoc insecureAllowed = Setup.getValue(getDataContext(), ExpressoSchema.class.getName(),
2012                        "insecureDBMaint");
2013
2014                if (!StringUtil.toBoolean(insecureAllowed)) {
2015                    throw new SecurityException JavaDoc("Insecured Database Object Access Not Allowed");
2016                }
2017            }
2018        }
2019    }
2020
2021    /**
2022     * One liner function that allows you to quickly enforce proper initialization
2023     * or the system throws an exception.
2024     *
2025     * @throws IllegalStateException if the object is not initialized as of yet.
2026     */

2027    private void checkInitialized() {
2028        if (!isInitialized()) {
2029            throw new IllegalStateException JavaDoc("Defineable DataObject must " +
2030                    "be initialized with setDefinition() prior to use");
2031        }
2032
2033    }
2034
2035    /**
2036     * Function that checks if initialized.
2037     *
2038     * @return true if the object has been initialized.
2039     */

2040    protected boolean isInitialized() {
2041        return (getMetaData() != null);
2042    }
2043
2044    /**
2045     * Builds the 'FROM' clause without the 'from' part. [That way Update works too]
2046     *
2047     * @return java.lang.String
2048     * @throws DataException upon construction error
2049     * <p/>
2050     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
2051     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
2052     */

2053    protected String JavaDoc buildFromClause() throws DataException {
2054        FastStringBuffer fsb = FastStringBuffer.getInstance();
2055        try {
2056
2057            JoinedDataObjectMetaData metadata = this.getJoinMetaData();
2058
2059            ArrayList JavaDoc explicitRelations = new ArrayList JavaDoc();
2060            ArrayList JavaDoc implicitRelations = new ArrayList JavaDoc();
2061            Set JavaDoc definedSet = new TreeSet JavaDoc();
2062
2063            boolean needComma = false;
2064
2065            List JavaDoc aliases = metadata.getAliasesInOrder();
2066            Iterator JavaDoc eachAlias = aliases.iterator();
2067            String JavaDoc lastAlias = (String JavaDoc) eachAlias.next();
2068            if (aliases.size() < 2) {
2069                throw new IllegalStateException JavaDoc("Must have at least two tables to perform a join");
2070            }
2071
2072            //
2073
//We must list tables in implicit joins first. So this
2074
//loop sorts the implicitly defined joins and the unspecified joins.
2075
//
2076
while (eachAlias.hasNext()) {
2077                String JavaDoc oneAlias = (String JavaDoc) eachAlias.next();
2078                JoinedDataObjectMetaData.Relation oneRelation =
2079                        metadata.getRelation(lastAlias, oneAlias);
2080
2081                if (oneRelation == null) {
2082                    //ok, we have an issue because we've found no relation to
2083
//'oneAlias' so we need to iterate through the releations and
2084
//find the unspecified join. Throw an execption otherwise.
2085
Map m = metadata.getAllRelations();
2086
2087                    for (Iterator JavaDoc j = m.values().iterator(); j.hasNext();) {
2088                        JoinedDataObjectMetaData.Relation testRelation =
2089                                (JoinedDataObjectMetaData.Relation) j.next();
2090
2091                        if (testRelation != null) {
2092                            if (oneAlias.equals(testRelation.getForeignAlias())) {
2093                                oneRelation = testRelation;
2094                                break;
2095                            }
2096                        }
2097                    }
2098
2099                    if (oneRelation == null) {
2100                        throw new DataException("Unable to find any way to relate alias: "
2101                                + oneAlias + " to the rest of the join.");
2102                    }
2103
2104                    if (oneRelation.getJoinType()
2105                            != JoinedDataObject.UNSPECIFIED_JOIN) {
2106                        throw new DataException("Alias " + oneAlias
2107                                + " currently has to be " +
2108                                "joined to the rest of the query through an unspecified" +
2109                                " join type rather than an ANSI join");
2110                    }
2111
2112                }
2113
2114                if (oneRelation.getJoinType()
2115                        == JoinedDataObject.UNSPECIFIED_JOIN) {
2116                    implicitRelations.add(oneRelation);
2117                } else {
2118                    definedSet.add(oneRelation.getLocalAlias());
2119                    definedSet.add(oneRelation.getForeignAlias());
2120                    explicitRelations.add(oneRelation);
2121                }
2122
2123                lastAlias = oneAlias;
2124            }
2125
2126            //
2127
//Now add all undefined table relations
2128
//
2129
//
2130
//We must list tables in implicit joins first. So we add all tables
2131
//listed without a specified join type first.
2132
//
2133
for (Iterator JavaDoc i = implicitRelations.iterator(); i.hasNext();) {
2134                if (needComma) {
2135                    fsb.append(", ");
2136                }
2137
2138                JoinedDataObjectMetaData.Relation oneRelation = (
2139                        JoinedDataObjectMetaData.Relation) i.next();
2140                String JavaDoc leftAlias = oneRelation.getLocalAlias();
2141                String JavaDoc rightAlias = oneRelation.getForeignAlias();
2142
2143                if (!definedSet.contains(leftAlias)) {
2144                    definedSet.add(leftAlias);
2145                    JDBCDataObject oneObj = (JDBCDataObject) this.myDataObjects.
2146                            get(leftAlias);
2147                    fsb.append(oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext()));
2148                    needComma = true;
2149
2150                    if (!definedSet.contains(rightAlias)) {
2151                        fsb.append(", ");
2152                    }
2153                }
2154
2155                if (!definedSet.contains(rightAlias)) {
2156                    definedSet.add(rightAlias);
2157                    JDBCDataObject oneObj = (JDBCDataObject) this.myDataObjects.
2158                            get(rightAlias);
2159                    fsb.append(oneObj.getJDBCMetaData().getTargetTable());
2160                    needComma = true;
2161                }
2162
2163            }
2164
2165            //
2166
//Now we add all explicitly defined joins to the select list.
2167
//
2168
boolean firstJoin = true;
2169            for (Iterator JavaDoc i = explicitRelations.iterator(); i.hasNext();) {
2170                if (needComma) {
2171                    fsb.append(", ");
2172                }
2173                needComma = false;
2174
2175                JoinedDataObjectMetaData.Relation relation = (
2176                        JoinedDataObjectMetaData.Relation) i.next();
2177
2178                if (firstJoin) {
2179                    JDBCDataObject oneObj = (JDBCDataObject) this.myDataObjects.
2180                            get(relation.getLocalAlias());
2181                    fsb.append(oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext()));
2182                    firstJoin = false;
2183                }
2184
2185                fsb.append(this.buildJoin(relation.getLocalAlias()
2186                        , relation.getLocalField(),
2187                        relation.getForeignAlias()
2188                        , relation.getForeignField()
2189                        , relation.getJoinType()));
2190            }
2191
2192            return fsb.toString();
2193        } catch (DBException dbe) {
2194            log.error("Error building 'FROM' clause", dbe);
2195            throw new DataException("Error building 'FROM' clause", dbe);
2196        } catch (Exception JavaDoc ex) {
2197            log.error("Error building 'FROM' clause", ex);
2198            throw new DataException("Error building 'FROM' clause", ex);
2199        } finally {
2200            fsb.release();
2201        }
2202    }
2203
2204    /**
2205     * Builds the part of the query that is for describing the fields/names to
2206     * be selected
2207     *
2208     * @return java.lang.String
2209     * @throws DBException if there's an error creating the string.
2210     * <p/>
2211     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
2212     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
2213     */

2214    protected String JavaDoc buildSelectFieldsString() throws DBException {
2215
2216        boolean needComma = false;
2217        FastStringBuffer fsb = FastStringBuffer.getInstance();
2218        try {
2219            for (Iterator JavaDoc i = this.getMetaData().getFieldListArray().iterator();
2220                 i.hasNext();) {
2221                if (needComma) {
2222                    fsb.append(", ");
2223                }
2224
2225                String JavaDoc fullFieldName = (String JavaDoc) i.next();
2226                String JavaDoc location[] = this.getJoinMetaData().getObjectAndField(fullFieldName);
2227                JDBCDataObject obj = (JDBCDataObject) this.myDataObjects.get(location[0]);
2228                DataFieldMetaData metadata = obj.getFieldMetaData(location[1]);
2229                if (metadata.isLongObjectType() || metadata.isVirtual()) {
2230                    continue;
2231                }
2232
2233                fsb.append(obj.getJDBCMetaData().getTargetSQLTable(obj.
2234                        getDataContext()));
2235                fsb.append(".");
2236                fsb.append(location[1]);
2237                needComma = true;
2238            }
2239
2240            return fsb.toString();
2241        } finally {
2242            fsb.release();
2243        }
2244    }
2245
2246
2247    /**
2248     * Builds the update field string portion setting question marks for
2249     * all the field values to be formatted and parsed later in a Prepared
2250     * statement
2251     *
2252     * @return java.lang.String
2253     * @throws DataException upon error
2254     * <p/>
2255     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
2256     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
2257     */

2258    protected String JavaDoc buildUpdateFieldString() throws DataException {
2259        boolean needComma = false;
2260        FastStringBuffer sqlCommand = FastStringBuffer.getInstance();
2261        try {
2262            DataFieldMetaData oneField = null;
2263            JoinedDataObjectMetaData metadata = this.getJoinMetaData();
2264            for (Iterator JavaDoc i = this.getMetaData().getFieldListArray().iterator();
2265                 i.hasNext();) {
2266                String JavaDoc oneFieldName = (String JavaDoc) i.next();
2267                oneField = this.getFieldMetaData(oneFieldName);
2268
2269                /* We skip any key fields in the update part (not allowed to */
2270                /* update them). Also skip any virtual fields */
2271                if ((!oneField.isKey()) && (!oneField.isVirtual()) &&
2272                        (!oneField.isAutoIncremented())
2273                        && (!oneField.isBinaryObjectType())) {
2274
2275                    try {
2276                        this.checkField(oneFieldName
2277                                , this.getDataField(oneFieldName).
2278                                asString());
2279                    } catch (DBException ex) {
2280                        log.error("Error validating field " + oneFieldName, ex);
2281                        throw new DataException("Error validating field "
2282                                + oneFieldName, ex);
2283                    }
2284
2285                    if (needComma) {
2286                        sqlCommand.append(", ");
2287                    }
2288                    String JavaDoc location[] = metadata.getObjectAndField(oneFieldName);
2289                    sqlCommand.append(((JDBCDataObject) this.myDataObjects.get(location[0])).getJDBCMetaData().getTargetSQLTable(((
2290                            JDBCDataObject) this.myDataObjects.get(location[0])).
2291                            getDataContext()));
2292                    sqlCommand.append(".");
2293                    sqlCommand.append(oneField.getName());
2294                    sqlCommand.append(" = ? ");
2295                    needComma = true;
2296                }
2297            }
2298            return sqlCommand.toString();
2299        } catch (Throwable JavaDoc t) {
2300            log.error("Error building update field string", t);
2301            throw new DataException("Error building update field string", t);
2302        } finally {
2303            sqlCommand.release();
2304        }
2305    }
2306
2307    /**
2308     * Create the 'ORDER BY' clause in the select statement.
2309     *
2310     * @param sortKeys pipe delimited field names in the format [alias].[fieldName]
2311     * @return the order by string to append to the select statement.
2312     * @throws DBException Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
2313     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
2314     */

2315    protected String JavaDoc buildOrderByString(String JavaDoc sortKeys) throws DBException {
2316
2317        if (sortKeys == null || sortKeys.length() == 0) {
2318            return "";
2319        }
2320
2321        StringTokenizer JavaDoc stok = new StringTokenizer JavaDoc(sortKeys, "|");
2322        if (!stok.hasMoreTokens()) {
2323            return "";
2324        }
2325
2326        FastStringBuffer fsb = FastStringBuffer.getInstance();
2327        try {
2328            fsb.append(" ORDER BY ");
2329            boolean needComma = false;
2330            while (stok.hasMoreTokens()) {
2331                String JavaDoc fieldName = stok.nextToken();
2332                String JavaDoc location[] = this.getJoinMetaData().getObjectAndField(fieldName);
2333                JDBCDataObject obj = (JDBCDataObject) this.myDataObjects.get(location[0]);
2334
2335                if (needComma) {
2336                    fsb.append(", ");
2337                }
2338
2339                fsb.append(obj.getJDBCMetaData().getTargetSQLTable(obj.
2340                        getDataContext()));
2341                fsb.append(".");
2342                fsb.append(location[1]);
2343                needComma = true;
2344            }
2345
2346            return fsb.toString();
2347        } finally {
2348            fsb.release();
2349        }
2350    }
2351
2352
2353    /**
2354     * Build and return a string consisting of an SQL 'where' clause
2355     * using the current field values as criteria for the search. See
2356     * setCustomWhereClause for information on specifying a more complex where clause.
2357     *
2358     * @param useAllFields True if all fields are to be used, false for only key fields
2359     * @param myStatement the preallocated buffer to append to
2360     * @return java.lang.String.
2361     * <p/>
2362     * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
2363     * @throws DBException upon error
2364     * @since $DatabaseSchema $Date: 2004/12/01 07:56:43 $
2365     */

2366    protected String JavaDoc buildWhereClauseBuffer(boolean useAllFields
2367                                            , FastStringBuffer myStatement) throws
2368            DBException {
2369
2370        /* list of field names (with table names prefixed) */
2371        List JavaDoc fields = new ArrayList JavaDoc();
2372        JDBCDataObject oneObj = null;
2373        String JavaDoc fieldName = null;
2374        Map byTableName = new HashMap();
2375        JoinedDataObjectMetaData metadata = this.getJoinMetaData();
2376
2377        if (useAllFields) {
2378
2379// oneObj = (JDBCDataObject) eachObj.next();
2380
for (Iterator JavaDoc j = this.getJoinMetaData().getAliasesInOrder().
2381                    iterator(); j.hasNext();) {
2382                String JavaDoc oneAlias = (String JavaDoc) j.next();
2383                oneObj = (JDBCDataObject) this.myDataObjects.get(oneAlias);
2384
2385                byTableName.put(oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext()), oneObj);
2386
2387                for (Iterator JavaDoc i = oneObj.getMetaData().getFieldListArray().
2388                        iterator(); i.hasNext();) {
2389                    fieldName = (String JavaDoc) i.next();
2390
2391                    DataFieldMetaData fieldMetaData = oneObj.getFieldMetaData(fieldName);
2392                    if (!fieldMetaData.isVirtual()
2393                            && !fieldMetaData.isLongObjectType()) {
2394                        fields.add(oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext()) + "." +
2395                                fieldName);
2396                    }
2397                }
2398                /* for each field */
2399            }
2400        } else { /* for each db object */
2401            for (Iterator JavaDoc eachAlias = metadata.getAliasesInOrder().iterator();
2402                 eachAlias.hasNext();) {
2403                String JavaDoc oneAlias = (String JavaDoc) eachAlias.next();
2404                oneObj = (JDBCDataObject) this.myDataObjects.get(oneAlias);
2405                byTableName.put(oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext()), oneObj);
2406
2407                for (Iterator JavaDoc i = oneObj.getMetaData().getKeyFieldListArray().
2408                        iterator();
2409                     i.hasNext();) {
2410                    fieldName = (String JavaDoc) i.next();
2411
2412                    DataFieldMetaData fieldMetaData = oneObj.getFieldMetaData(fieldName);
2413                    if (!fieldMetaData.isVirtual()
2414                            && !fieldMetaData.isBinaryObjectType()) {
2415                        fields.add(oneObj.getJDBCMetaData().getTargetSQLTable(oneObj.getDataContext()) + "." +
2416                                fieldName);
2417                    }
2418                }
2419                /* for each field */
2420
2421            }
2422            /* for each db object */
2423
2424        }
2425
2426        //
2427
// Now go thru each field - if it is non-empty, add it's criteria
2428
// to the where clause. If it is empty, just skip to the next one
2429
// If the relationship is part of a predefined join, then we skip this
2430
// portion since the relationship is defined in the join syntax
2431
// already
2432
//
2433

2434        boolean addWhere = true;
2435        boolean addAnd = false;
2436        String JavaDoc oneFieldName = null;
2437        String JavaDoc oneTableName = null;
2438        String JavaDoc oneFullName = null;
2439        String JavaDoc oneFieldValue = null;
2440
2441        boolean skipField = false;
2442
2443        FieldRangeParser parser = FieldRangeParser.getInstance();
2444        for (Iterator JavaDoc fieldsToUse = fields.iterator();
2445             fieldsToUse.hasNext();) {
2446            oneFullName = (String JavaDoc) fieldsToUse.next();
2447
2448            StringTokenizer JavaDoc stk = new StringTokenizer JavaDoc(oneFullName, ".");
2449            oneTableName = stk.nextToken();
2450            oneFieldName = stk.nextToken();
2451
2452            oneObj = (JDBCDataObject) byTableName.get(oneTableName);
2453            skipField = false;
2454            DataField oneDataField = oneObj.getDataField(oneFieldName);
2455            oneFieldValue = StringUtil.notNull(oneDataField.asString());
2456            String JavaDoc rangeString = null;
2457            JDBCObjectMetaData objMetadata = oneObj.getJDBCMetaData();
2458
2459            if (oneFieldValue == null || oneFieldValue.length() == 0) {
2460                skipField = true;
2461            } else if (oneFieldValue.length() > 0) {
2462
2463                rangeString = parser.denotesRange(oneFieldValue);
2464                if (rangeString != null && rangeString.length() > 0) {
2465                    if (!parser.isValidRange(oneDataField.getFieldMetaData()
2466                            , oneFieldValue)) {
2467                        throw new DataException("Invalid range: " + rangeString);
2468                    }
2469                }
2470                if (oneFieldValue.trim().equalsIgnoreCase("is null") ||
2471                        oneFieldValue.trim().equalsIgnoreCase("is not null")) {
2472                    ;
2473                } else {
2474                    oneFieldValue = oneObj.quoteIfNeeded(oneFieldName
2475                            , rangeString);
2476                }
2477            }
2478
2479            if (oneFieldValue.equals("\'\'")) {
2480                skipField = true;
2481            }
2482
2483            if (!skipField) {
2484                // check to see if the field value is valid (protects agains sql injection)
2485
try {
2486                    String JavaDoc unalteredFieldValue = oneObj.getDataField(oneFieldName).asString();
2487                    if (rangeString != null) {
2488                        boolean valid = parser.isValidRange(oneObj.
2489                                getFieldMetaData(oneFieldName), unalteredFieldValue);
2490                        if (!valid) {
2491                            throw new DataException("Invalid field range value: "
2492                                    + unalteredFieldValue);
2493                        }
2494                    } else if (sJdbcUtil.containsWildCards(oneObj
2495                            , oneFieldValue)) {
2496                        Object JavaDoc origValue = oneObj.getDataField(oneFieldName).
2497                                getValue();
2498
2499                        String JavaDoc[] wildcards = null;
2500                        wildcards = (String JavaDoc[]) oneObj.getConnectionPool().
2501                                getWildCardsList().toArray(new String JavaDoc[0]);
2502                        Filter filter = new Filter(wildcards, wildcards);
2503                        String JavaDoc valueWithoutWildCards = filter.stripFilter(unalteredFieldValue);
2504                        // if the value without wildcards is empty, then we know the field is valid
2505
if (!valueWithoutWildCards.equals("")) {
2506                            oneObj.getDataField(oneFieldName).setValue(valueWithoutWildCards);
2507                            oneObj.getDataField(oneFieldName).checkValue();
2508                            oneObj.getDataField(oneFieldName).setValue(origValue);
2509                        }
2510                    } else {
2511                        oneObj.getDataField(oneFieldName).checkValue();
2512                    }
2513                } catch (DBException ex) {
2514                    if (ex instanceof DataException) {
2515                        throw ((DataException) ex);
2516                    } else {
2517                        throw new DataException("Error getting field value", ex);
2518                    }
2519                }
2520
2521                if (addWhere) {
2522                    myStatement.append(" WHERE ");
2523                    addWhere = false;
2524                }
2525                if (addAnd) {
2526                    myStatement.append(" AND ");
2527                }
2528                if (sJdbcUtil.containsWildCards(oneObj, oneFieldValue)) {
2529                    if (caseSensitiveQuery) {
2530                        myStatement.append(objMetadata.getTargetSQLTable(oneObj.
2531                                getDataContext()) + "." +
2532                                oneFieldName);
2533                        myStatement.append(" LIKE ");
2534                        myStatement.append(oneFieldValue);
2535                    } else {
2536                        myStatement.append("UPPER(");
2537                        myStatement.append(objMetadata.getTargetSQLTable(oneObj.
2538                                getDataContext()) + "." +
2539                                oneFieldName);
2540                        myStatement.append(") LIKE ");
2541                        myStatement.append(oneFieldValue.toUpperCase());
2542                    }
2543                } else if (rangeString != null) {
2544                    myStatement.append(objMetadata.getTargetSQLTable(oneObj.
2545                            getDataContext()) + "." +
2546                            oneFieldName);
2547                    myStatement.append(" " + rangeString + " ");
2548                    myStatement.append(oneFieldValue);
2549                } else {
2550                    if ((oneFieldValue.trim().equalsIgnoreCase("is null")) ||
2551                            (oneFieldValue.trim().equalsIgnoreCase("is not null"))) {
2552                        myStatement.append(objMetadata.getTargetSQLTable(oneObj.
2553                                getDataContext()) + "." +
2554                                oneFieldName);
2555                        myStatement.append(" = ");
2556                        myStatement.append(oneFieldValue);
2557                    } else {
2558                        if (caseSensitiveQuery) {
2559                            myStatement.append(objMetadata.getTargetSQLTable(oneObj.getDataContext()) + "." +
2560                                    oneFieldName);
2561                            myStatement.append(" = ");
2562                            myStatement.append(oneFieldValue);
2563                        } else {
2564                            DataFieldMetaData dfmd = oneDataField.
2565                                    getFieldMetaData();
2566                            if (dfmd.isQuotedTextType()) {
2567                                myStatement.append("UPPER(");
2568                                myStatement.append(objMetadata.
2569                                        getTargetSQLTable(oneObj.getDataContext())
2570                                        + "." +
2571                                        oneFieldName);
2572                                myStatement.append(") = ");
2573                                myStatement.append(oneFieldValue.toUpperCase());
2574                            } else {
2575                                myStatement.append(objMetadata.
2576                                        getTargetSQLTable(oneObj.getDataContext())
2577                                        + "." +
2578                                        oneFieldName);
2579                                myStatement.append(" = ");
2580                                myStatement.append(oneFieldValue);
2581                            }
2582                        }
2583                    }
2584                }
2585
2586                addAnd = true;
2587            }
2588
2589            /* if field is not skipped for some reason */
2590        }
2591
2592        /* for each field */
2593        boolean needAnd = false;
2594
2595        if (!addWhere) {
2596            needAnd = true;
2597        }
2598
2599        String JavaDoc oneRelation = null;
2600
2601        //
2602
//What we do here is add all the 'equality join' relationships to the
2603
//Where CLAUSE. such as [table].[field] = [tableright].[fieldright]
2604
//
2605
FastStringBuffer fieldDefinitions = FastStringBuffer.getInstance();
2606        try {
2607            for (Iterator JavaDoc e = metadata.getSQLRelationList().iterator();
2608                 e.hasNext();) {
2609                oneRelation = (String JavaDoc) e.next();
2610
2611                if (needAnd) {
2612                    fieldDefinitions.append(" AND ");
2613                }
2614
2615                fieldDefinitions.append(oneRelation);
2616                needAnd = true;
2617            }
2618            if (log.isDebugEnabled()) {
2619                log.debug(fieldDefinitions.toString());
2620            }
2621
2622            //
2623
//It's possible to get an empty where clause if all joins are
2624
//specified and no other fields specified. So check for possible
2625
//empty where clause and only add it if necessary.
2626
//
2627
String JavaDoc result = fieldDefinitions.toString();
2628            if (result.length() > 0) {
2629                if (addWhere) {
2630                    myStatement.append(" WHERE ");
2631                }
2632                myStatement.append(result);
2633            }
2634
2635        } finally {
2636            fieldDefinitions.release();
2637        }
2638
2639        return myStatement.toString();
2640    }
2641
2642
2643    /**
2644     * Replaces alias names in the customWhereClause with their correct
2645     * table names
2646     *
2647     * @param myStatement the preallocated buffer to append to
2648     */

2649    private void formatCustomWhereClause(FastStringBuffer myStatement) throws
2650            DBException {
2651
2652        String JavaDoc customWhere = " " + this.customWhereClause;
2653
2654        if (myStatement.toString().indexOf(" WHERE ") != -1) {
2655            myStatement.append(" AND ");
2656        } else {
2657            myStatement.append(" WHERE ");
2658        }
2659        for (Iterator JavaDoc eachAlias = this.getJoinMetaData().getAliasesInOrder().
2660                iterator();
2661             eachAlias.hasNext();) {
2662            String JavaDoc oneAlias = (String JavaDoc) eachAlias.next();
2663            JDBCDataObject oneObj = (JDBCDataObject) this.myDataObjects.get(oneAlias);
2664            // replace the alias names in the custom where clause with the
2665
// table names. Using the following method ensures that if any
2666
// alias is a substring of another, we don't hit problems
2667
customWhere =
2668                    StringUtil.replaceAll(customWhere, " " + oneAlias + ".",
2669                            " "
2670                    + oneObj.getJDBCMetaData().getTargetTable()
2671                    + ".");
2672            if (customWhere.indexOf("(" + oneAlias + ".") != -1) {
2673                customWhere =
2674                        StringUtil.replaceAll(customWhere, "(" + oneAlias + ".",
2675                                "("
2676                        + oneObj.getJDBCMetaData().getTargetTable()
2677                        + ".");
2678            }
2679        }
2680        this.setCustomWhereClause(null, false);
2681        myStatement.append(customWhere.trim());
2682    }
2683
2684    /**
2685     * Retrieves a nested dataobject based upon the public field name that
2686     * the DataObject publishes. For example, in JoinedDataObjects, the field
2687     * name &quot;"abcd.efgh" represents DataObject 'abcd', and field name
2688     * 'efgh'.
2689     * <p>The actual usage of the naming convention will differ from the, thus
2690     * you'll need to use getMetaData().getFieldList() to get the names of the
2691     * fields of a <code>Nestable</code> object to get a valid field Name</p>
2692     *
2693     * @param fieldName the full field name to get the nested data object.
2694     * @return DataObject (Actually JDBCDataObjects) or possibly null.
2695     * @throws IllegalArgumentException if the field name does not map to any
2696     * DataObject
2697     */

2698    public DataObject getNestedFromFieldName(String JavaDoc fieldName) {
2699        String JavaDoc location[] = this.getJoinMetaData().getObjectAndField(fieldName);
2700        return (DataObject) this.myDataObjects.get(location[0]);
2701    }
2702
2703    /**
2704     * Retrieve an array of all nested data objects. May be empty if there
2705     * are no nested data objects. Ordering is undefined by this function.
2706     *
2707     * @return Array of DataObjects. Should never return null.
2708     */

2709    public DataObject[] getAllNested() {
2710        DataObject[] returnValue = new DataObject[this.myDataObjects.size()];
2711        return (DataObject[]) this.myDataObjects.values().toArray(returnValue);
2712    }
2713    /* buildWhereClause(boolean) */
2714
2715
2716    /**
2717     * Often times, field names for the external interface will be different
2718     * from the of a nested field name in a data object. Use this function
2719     * to get the internal field name for the DataObject returned by
2720     * <code>getNestedFromFieldName()</code>
2721     *
2722     * @param fieldName The external name of the field
2723     * @return java.lang.String, the field corresponding to the field name
2724     * of the data object
2725     * @throws IllegalArgumentException if the given field name cannot map
2726     * to a field.
2727     */

2728    public String JavaDoc getFieldFromNestedName(String JavaDoc fieldName) {
2729        String JavaDoc location[] = this.getJoinMetaData().getObjectAndField(fieldName);
2730        return location[1];
2731    }
2732
2733
2734    /**
2735     * Retrieve the field value as a String
2736     *
2737     * @param fieldName the name of the field to retrieve
2738     * @return Object or null if the field was null
2739     * @throws DBException upon error
2740     * @throws IllegalArgumentException if fieldname is invalid
2741     */

2742    public String JavaDoc getField(String JavaDoc fieldName) throws DBException {
2743        return this.getDataField(fieldName).asString();
2744    }
2745
2746    /**
2747     * Set a specific DB connection for use with this JoinedDataObject. If you do not set
2748     * a connection, the db object will request it's own connection from the
2749     * appropriate connection pool & release it again after every operation (e.g.
2750     * add, update, etc). It is important to use your own explicit connection when
2751     * dealing with a database transactional environment (e.g. commit(), rollback()).
2752     *
2753     * @param newConnection The new DBConnection object to be used by this DB Object
2754     */

2755    public synchronized void setConnection(DBConnection newConnection) throws
2756            DBException {
2757        setConnection(newConnection, newConnection.getDataContext());
2758    }
2759    /* setConnection(DBConnection) */
2760
2761    /**
2762     * <p/>
2763     * Set a specific DB connection for use with this JoinedDataObject. If you do not set
2764     * a connection, the object will request it's own connection from the
2765     * appropriate connection pool & release it again after every operation (e.g.
2766     * add, update, etc). It is important to use your own explicit connection when
2767     * dealing with a database transactional environment (e.g. commit(), rollback()).
2768     * </p>
2769     * <p>The difference between this and setConnection(DBConnection) is that this
2770     * is used for using otherDB capabilities within a transaction. So you use
2771     * a dbconnection from your other pool, but the setup tables are in a different
2772     * context</p>
2773     *
2774     * @param newConnection The new DBConnection object to be used by this DB Object
2775     * @param setupTablesContext the data context that is used for the expresso setup tables.
2776     * @see #setConnection(DBConnection)
2777     */

2778    public synchronized void setConnection(DBConnection newConnection,
2779                                           String JavaDoc setupTablesContext) throws
2780            DBException {
2781        localConnection = newConnection;
2782        this.setDataContext(setupTablesContext);
2783    }
2784
2785    /**
2786     * Set a regular expression "mask" for this base data object that specifies it's
2787     * valid values. The mask should already be compiled by the regular
2788     * expression compiler
2789     *
2790     * @param newMask The compiled regular expression mask
2791     */

2792    public void setGlobalMask(Pattern newMask) {
2793        this.setGlobalMask(newMask);
2794    }
2795
2796    /**
2797     * Get the compiled regular expression for this base data object.
2798     *
2799     * @return the precompiled regular expression mask
2800     */

2801    public Pattern getGlobalMask() {
2802        return this.getGlobalMask();
2803    }
2804
2805
2806    /**
2807     * Return boolean if the data object has a mask set
2808     *
2809     * @return True if the data object mask is set, else false if it is not
2810     */

2811    public boolean isGlobalMasked() {
2812        return this.isGlobalMasked();
2813    }
2814    /* isGlobalMasked() */
2815
2816    /**
2817     * {@inheritDoc}
2818     *
2819     * @throws DataException upon setField error.
2820     */

2821    public void setFieldsWithDefaults() throws DataException {
2822        for (Iterator JavaDoc j = this.getJoinMetaData().getAliasesInOrder().
2823                iterator(); j.hasNext();) {
2824            String JavaDoc oneAlias = (String JavaDoc) j.next();
2825            JDBCDataObject oneObj = (JDBCDataObject) this.myDataObjects.get(oneAlias);
2826            oneObj.setFieldsWithDefaults();
2827        }
2828    }
2829
2830
2831}
2832
Popular Tags