KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > JdbcMetaDataBuilder


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc;
13
14 import com.versant.core.common.Debug;
15 import com.versant.core.metadata.*;
16 import com.versant.core.metadata.parser.*;
17 import com.versant.core.jdbc.metadata.*;
18 import com.versant.core.jdbc.sql.AutoIncJdbcKeyGenerator;
19 import com.versant.core.jdbc.sql.HighLowJdbcKeyGenerator;
20 import com.versant.core.jdbc.sql.JdbcNameGenerator;
21 import com.versant.core.jdbc.sql.SqlDriver;
22 import com.versant.core.util.BeanUtils;
23 import com.versant.core.util.StringListParser;
24
25 import java.sql.Types JavaDoc;
26 import java.util.*;
27
28 import com.versant.core.common.BindingSupportImpl;
29 import com.versant.core.common.config.ConfigInfo;
30
31 /**
32  * This builds the meta data for the JdbcStorageManager.
33  */

34 public class JdbcMetaDataBuilder extends MetaDataBuilder
35         implements JdoExtensionKeys {
36
37     private final JdbcConfig jdbcConfig;
38     private final SqlDriver sqlDriver;
39
40     private JdbcNameGenerator nameGenerator;
41     private JdbcMappingResolver mappingResolver;
42
43     public final MetaDataEnums MDE = new MetaDataEnums();
44     public final JdbcMetaDataEnums jdbcMDE = new JdbcMetaDataEnums();
45
46     private JdbcKeyGeneratorFactoryRegistry keyGenRegistry;
47     private JdbcConverterFactoryRegistry converterRegistry;
48     private JdbcClassReferenceGraph jdbcClassReferenceGraph;
49
50     /**
51      * Maps ClassMetaData to ClassInfo.
52      */

53     private Map classInfoMap = new HashMap();
54
55     // these are the "fieldnames" used when resolving mappings for these cols
56
public static final String JavaDoc DATASTORE_PK_FIELDNAME = "<pk>";
57     public static final String JavaDoc CLASS_ID_FIELDNAME = "<class-id>";
58     public static final String JavaDoc OPT_LOCK_FIELDNAME = "<opt-lock>";
59     public static final String JavaDoc OWNER_REF_FIELDNAME = "<owner>";
60     public static final String JavaDoc SEQUENCE_FIELDNAME = "<sequence>";
61     public static final String JavaDoc VALUE_FIELDNAME = "<value>";
62     public static final String JavaDoc KEY_FIELDNAME = "<key>";
63
64     // these are the names of the built in key generators
65
public static final String JavaDoc KEYGEN_HIGHLOW = "HIGHLOW";
66     public static final String JavaDoc KEYGEN_AUTOINC = "AUTOINC";
67
68     /**
69      * These hold tempory info we need to track for a class during meta data
70      * generation. Each class for our store is associated with one of these
71      * through the classInfoMap.
72      */

73     public static class ClassInfo {
74
75         public ClassMetaData cmd;
76
77         public ArrayList elements;
78         public JdbcKeyGeneratorFactory keyGenFactory;
79         public Object JavaDoc keyGenFactoryArgs;
80         public String JavaDoc pkConstraintName;
81         public JdoExtension optimisticLockingExt;
82         public String JavaDoc pkFkConstraintName;
83         public JdbcConstraint pkFkConstraint;
84         public ArrayList indexExts = new ArrayList();
85         public ArrayList autoIndexes = new ArrayList(); // of JdbcIndex
86
public JdoExtension inheritance;
87         public JdoExtension classIdExt;
88         public boolean noClassIdCol;
89         private Set createdAfterClient;
90
91         public Set getCreatedAfterClient() {
92             if (createdAfterClient == null) {
93                 createdAfterClient = new HashSet();
94             }
95             return createdAfterClient;
96         }
97     }
98
99     /**
100      * The jdbcDriver parameter may be null if this is not available.
101      */

102     public JdbcMetaDataBuilder(ConfigInfo config, JdbcConfig jdbcConfig,
103             ClassLoader JavaDoc loader, SqlDriver sqlDriver, boolean quiet) {
104         super(config, loader, quiet);
105         this.jdbcConfig = jdbcConfig;
106         this.sqlDriver = sqlDriver;
107     }
108
109     protected FetchGroupBuilder createFetchGroupBuilder() {
110         return new JdbcFetchGroupBuilder(jmd);
111     }
112
113     public boolean isCdRefsInDefaultFetchGroup() {
114         return jdbcConfig.oidsInDefaultFetchGroup;
115     }
116
117     public int getCdCacheStrategy() {
118         return jdbcConfig.cacheStrategy;
119     }
120
121     public ModelMetaData buildMetaData(JdoRoot[] roots) {
122         keyGenRegistry = new JdbcKeyGeneratorFactoryRegistry(loader);
123         keyGenRegistry.add(KEYGEN_HIGHLOW,
124                 keyGenRegistry.getFactory(
125                         HighLowJdbcKeyGenerator.Factory.class.getName()));
126         keyGenRegistry.add(KEYGEN_AUTOINC,
127                 keyGenRegistry.getFactory(
128                         AutoIncJdbcKeyGenerator.Factory.class.getName()));
129         converterRegistry = new JdbcConverterFactoryRegistry(loader);
130
131         mappingResolver = new JdbcMappingResolver();
132         mappingResolver.init(sqlDriver, parseTypeMappings(),
133                 parseJavaTypeMappings());
134         mappingResolver.registerStoreTypes(mdutils);
135
136         if (jdbcConfig.jdbcKeyGenerator == null) {
137             jdbcConfig.jdbcKeyGenerator = JdbcMetaDataBuilder.KEYGEN_HIGHLOW;
138         }
139         if (jdbcConfig.jdbcKeyGeneratorProps == null) {
140
141             jdbcConfig.jdbcKeyGeneratorProps = Collections.EMPTY_MAP;
142
143
144         }
145
146         if (jdbcConfig.jdbcNameGenerator != null) {
147             nameGenerator = (JdbcNameGenerator)BeanUtils.newInstance(
148                     jdbcConfig.jdbcNameGenerator, loader, JdbcNameGenerator.class);
149         } else {
150             nameGenerator = sqlDriver.createJdbcNameGenerator();
151         }
152         BeanUtils.setProperties(nameGenerator, jdbcConfig.jdbcNameGeneratorProps);
153         jmd.jdbcMetaData = new JdbcMetaData(jmd, jdbcConfig);
154
155         return super.buildMetaData(roots);
156     }
157
158     protected void doHorizontal(ClassMetaData[] ca) {
159         //create all the fake field for horizontal instances
160
for (int j = 0; j < ca.length; j++) {
161             ClassMetaData cmd = ca[j];
162             try {
163                 createHorizontalFieldMetaData(cmd, quiet);
164             } catch (RuntimeException JavaDoc e) {
165                 cmd.addError(e, quiet);
166             }
167         }
168     }
169
170     protected void doEmbedded(ClassMetaData[] ca) {
171         //create all the fake field for embedded instances
172
for (int j = 0; j < ca.length; j++) {
173             ClassMetaData cmd = ca[j];
174             try {
175                 createEmbeddeFieldMetaData(cmd, quiet);
176             } catch (RuntimeException JavaDoc e) {
177                 cmd.addError(e, quiet);
178             }
179         }
180     }
181
182     protected void checkForHorizontal(JdoClass jdoCls, ClassMetaData cmd) {
183         if (jdoCls.getInheritance(jdbcMDE.INHERITANCE_ENUM) == JdbcClass.INHERITANCE_HORIZONTAL) {
184             cmd.horizontal = true;
185         }
186     }
187
188     public JdbcConfig getJdbcConfig() {
189         return jdbcConfig;
190     }
191
192     public JdbcNameGenerator getNameGenerator() {
193         return nameGenerator;
194     }
195
196     /**
197      * Get the ClassInfo for cmd.
198      */

199     public ClassInfo getClassInfo(ClassMetaData cmd) {
200         return (ClassInfo)classInfoMap.get(cmd);
201     }
202
203     /**
204      * Get the elements field of the ClassInfo for cmd.
205      */

206     private ArrayList getClassElements(ClassMetaData cmd) {
207         return getClassInfo(cmd).elements;
208     }
209
210     protected void preBuildFetchGroupsHook() {
211         ClassMetaData[] classes = jmd.classes;
212         int clen = classes.length;
213
214         // find all classes that belong to us and do whatever we can without
215
// having all the JdbcClass objects and tables yet
216
if (Debug.DEBUG) {
217             Debug.OUT.println("MDB-JDBC: Creating JdbcClass objects ... ");
218         }
219         for (int i = 0; i < clen; i++) {
220             ClassMetaData cmd = classes[i];
221             if (cmd.pcSuperMetaData != null) {
222                 continue;
223             }
224             try {
225                 createJdbcClass(cmd, quiet);
226             } catch (RuntimeException JavaDoc e) {
227                 cmd.addError(e, quiet);
228             }
229         }
230
231         // Make sure that all classes have tables and table names. This is
232
// done starting at the base classes and recursively processing
233
// subclasses.
234
if (Debug.DEBUG) {
235             Debug.OUT.println("MDB-JDBC: Creating JdbcTable objects ... ");
236         }
237         for (int i = 0; i < clen; i++) {
238             ClassMetaData cmd = classes[i];
239             try {
240                 processBaseClassTable(cmd);
241             } catch (RuntimeException JavaDoc e) {
242                 cmd.addError(e, quiet);
243             }
244
245         }
246
247         // find the primary keys (datastore or application) for all classes
248
// and make sure the pk columns have names
249
if (Debug.DEBUG) {
250             Debug.OUT.println("MDB-JDBC: Finding and naming primary keys ... ");
251         }
252         for (int i = 0; i < clen; i++) {
253             ClassMetaData cmd = classes[i];
254             if (cmd.pcSuperMetaData != null) continue;
255             try {
256                 processPrimaryKey(cmd, quiet);
257             } catch (RuntimeException JavaDoc e) {
258                 cmd.addError(e, quiet);
259             }
260         }
261
262         // create descriminator columns and figure out values
263
if (Debug.DEBUG) {
264             Debug.OUT.println("MDB-JDBC: Creating descriminator columns ... ");
265         }
266         for (int i = 0; i < clen; i++) {
267             ClassMetaData cmd = classes[i];
268             if (cmd.pcSuperMetaData != null) {
269                 continue;
270             }
271             try {
272                 processClassIdCol(cmd, quiet, new HashMap());
273             } catch (RuntimeException JavaDoc e) {
274                 cmd.addError(e, quiet);
275             }
276         }
277
278         // processing the persist after extensions
279
for (int j = 0; j < clen; j++) {
280             ClassMetaData cmd = classes[j];
281             ClassInfo cInfo = getClassInfo(cmd.top);
282             cInfo.getCreatedAfterClient().clear();
283             JdoElement[] elements = cmd.jdoClass.elements;
284             int n = elements.length;
285             for (int i = 0; i < n; i++) {
286                 JdoElement o = elements[i];
287                 if (o instanceof JdoExtension) {
288                     JdoExtension e = (JdoExtension)o;
289                     if (e.key == PERSIST_AFTER) {
290                         if (e.nested == null) continue;
291                         for (int k = 0; k < e.nested.length; k++) {
292                             JdoExtension jdoExtension = e.nested[k];
293                             if (jdoExtension != null) {
294                                 cInfo.getCreatedAfterClient().add(jmd.getClassMetaData(
295                                         jdoExtension.value).top);
296                             }
297                         }
298                     }
299                 }
300             }
301         }
302
303         // calc maxPkSimpleColumns and set datastoreIdentityType
304
if (Debug.DEBUG) {
305             Debug.OUT.println("MDB-JDBC: Calculating maxPkSimpleColumns ... ");
306         }
307         int maxPkSimpleColumns = 0;
308         for (int i = 0; i < clen; i++) {
309             ClassMetaData cmd = classes[i];
310             try {
311                 if (cmd.horizontal) continue;
312                 JdbcClass jdbcClass = ((JdbcClass)cmd.storeClass);
313                 int n = jdbcClass.table.pkSimpleColumnCount;
314                 if (n > maxPkSimpleColumns) {
315                     maxPkSimpleColumns = n;
316                 }
317                 cmd.datastoreIdentityTypeCode = jdbcClass.table.pk[0].javaTypeCode;
318                 cmd.datastoreIdentityType = jdbcClass.table.pk[0].javaType;
319             } catch (RuntimeException JavaDoc e) {
320                 cmd.addError(e, quiet);
321             }
322         }
323         ((JdbcMetaData)jmd.jdbcMetaData).maxPkSimpleColumns = maxPkSimpleColumns;
324
325         // complete optimistic locking - this is done now so that use visible
326
// fields can be used for rowversion and timestamp locking
327
if (Debug.DEBUG) {
328             Debug.OUT.println("MDB-JDBC: Completing optimistic locking ... ");
329         }
330         for (int i = 0; i < clen; i++) {
331             ClassMetaData cmd = classes[i];
332             if (cmd.pcSuperMetaData != null) continue;
333             try {
334                 completeOptimisticLocking(cmd, quiet);
335             } catch (RuntimeException JavaDoc e) {
336                 cmd.addError(e, quiet);
337             }
338         }
339
340         // complete REF fields now that we have all the classes
341
// and primary keys
342
if (Debug.DEBUG) {
343             Debug.OUT.println("MDB-JDBC: Creating REF fields ... ");
344         }
345         for (int i = 0; i < clen; i++) {
346             ClassMetaData cmd = classes[i];
347             try {
348                 processRefAndPolyRefFields(cmd, quiet);
349             } catch (RuntimeException JavaDoc e) {
350                 cmd.addError(e, quiet);
351             }
352         }
353
354         // complete COLLECTION fields now that we have all the classes
355
// and primary keys and refs
356
if (Debug.DEBUG) {
357             Debug.OUT.println("MDB-JDBC: Creating COLLECTION fields ... ");
358         }
359         for (int i = 0; i < clen; i++) {
360             ClassMetaData cmd = classes[i];
361             try {
362                 if (cmd.horizontal) continue;
363                 processCollectionFields(cmd, quiet);
364             } catch (RuntimeException JavaDoc e) {
365                 cmd.addError(e, quiet);
366             }
367         }
368
369         // create the cols arrays on all class tables, name all field columns
370
// without names and sanity check columns and add any extra fake
371
// fields to the fields array on ClassMetaData
372
if (Debug.DEBUG) {
373             Debug.OUT.println(
374                     "MDB-JDBC: Finalizing table column arrays and fake fields ... ");
375         }
376         for (int i = 0; i < clen; i++) {
377             ClassMetaData cmd = classes[i];
378 // if (cmd.horizontal) continue;
379
if (cmd.pcSuperMetaData != null) continue;
380             try {
381                 finalizeFakesAndTableColumns(cmd);
382             } catch (RuntimeException JavaDoc e) {
383                 cmd.addError(e, quiet);
384             }
385         }
386
387         jdbcClassReferenceGraph = new JdbcClassReferenceGraph(jmd.classes);
388         jdbcClassReferenceGraph.sort();
389
390         if (Debug.DEBUG) {
391             Debug.OUT.println("MDB-JDBC: Creating constraints ... ");
392         }
393         for (int i = 0; i < clen; i++) {
394             ClassMetaData cmd = classes[i];
395             try {
396                 doConstraints(cmd);
397             } catch (RuntimeException JavaDoc e) {
398                 cmd.addError(e, quiet);
399             }
400         }
401
402         //release resources that was used for sorting
403
jdbcClassReferenceGraph.releaseMem();
404
405         // build the fields array on JdbcClass for each class
406
if (Debug.DEBUG) {
407             Debug.OUT.println("MDB-JDBC: Finalizing fields arrays ... ");
408         }
409         for (int i = 0; i < clen; i++) {
410             ClassMetaData cmd = classes[i];
411             FieldMetaData[] fa = cmd.fields;
412             if (fa == null) continue;
413             JdbcField[] a = ((JdbcClass)cmd.storeClass).fields = new JdbcField[fa.length];
414             for (int j = fa.length - 1; j >= 0; j--) {
415                 FieldMetaData fmd = fa[j];
416                 fmd.fieldNo = j; // need to do this in case of fake fields
417
JdbcField jdbcField = (JdbcField)fmd.storeField;
418                 a[j] = jdbcField;
419                 if (jdbcField != null) jdbcField.initMainTableCols();
420             }
421         }
422
423         // figure out which columns should be marked as shared
424
if (Debug.DEBUG) {
425             Debug.OUT.println("MDB-JDBC: Choosing shared columns ... ");
426         }
427         SharedColumnChooser sharedColumnChooser = new SharedColumnChooser();
428         for (int i = 0; i < clen; i++) {
429             ClassMetaData cmd = classes[i];
430             if (cmd.pcSuperMetaData != null) continue;
431             if (cmd.horizontal) continue;
432             try {
433                 sharedColumnChooser.chooseSharedColumns(cmd);
434             } catch (RuntimeException JavaDoc e) {
435                 cmd.addError(e, quiet);
436             }
437         }
438
439         // build the fields array on JdbcClass for each class
440
if (Debug.DEBUG) {
441             Debug.OUT.println(
442                     "MDB-JDBC: Finalizing for update columns for fields ... ");
443         }
444         for (int i = 0; i < clen; i++) {
445             ClassMetaData cmd = classes[i];
446             JdbcField[] fields = ((JdbcClass)cmd.storeClass).fields;
447             if (fields == null) continue;
448             for (int j = fields.length - 1; j >= 0; j--) {
449                 JdbcField f = fields[j];
450                 if (f != null) f.initMainTableColsForUpdate();
451             }
452         }
453
454         // copy optimistic locking settings down each heirachy
455
if (Debug.DEBUG) {
456             Debug.OUT.println(
457                     "MDB-JDBC: Copying optimistic locking down heirachies ... ");
458         }
459         for (int i = 0; i < clen; i++) {
460             ClassMetaData cmd = classes[i];
461             ((JdbcClass)cmd.storeClass).copyOptimisticLockingToSubs();
462         }
463
464         // finalize constraints
465
if (Debug.DEBUG) {
466             Debug.OUT.println("MDB-JDBC: Finalizing table constraints ... ");
467         }
468         for (int i = 0; i < clen; i++) {
469             ClassMetaData cmd = classes[i];
470             try {
471                 finalizeConstraints(cmd);
472             } catch (RuntimeException JavaDoc e) {
473                 cmd.addError(e, quiet);
474             }
475         }
476         ArrayList tables = ((JdbcMetaData)jmd.jdbcMetaData).getTables(true);
477         int size = tables.size();
478         for (int i = 0; i < size; i++) {
479             JdbcTable jdbcTable = (JdbcTable)tables.get(i);
480             jdbcTable.nameConstraints(nameGenerator);
481         }
482         for (int i = 0; i < clen; i++) {
483             ClassMetaData cmd = classes[i];
484             try {
485                 finalizeConstraints(cmd);
486             } catch (RuntimeException JavaDoc e) {
487                 cmd.addError(e, quiet);
488             }
489         }
490
491         // find index extensions and create them
492
if (Debug.DEBUG) {
493             Debug.OUT.println("MDB-JDBC: Processing index extensions ... ");
494         }
495         for (int i = 0; i < clen; i++) {
496             ClassMetaData cmd = classes[i];
497             if (cmd.pcSuperMetaData != null) continue;
498             try {
499                 createMainTableIndexes(cmd, quiet);
500             } catch (RuntimeException JavaDoc e) {
501                 cmd.addError(e, quiet);
502             }
503         }
504
505         // make sure link table indexes are named
506
if (Debug.DEBUG) {
507             Debug.OUT.println("MDB-JDBC: Naming link table indexes ...");
508         }
509         for (int i = 0; i < clen; i++) {
510             ClassMetaData cmd = classes[i];
511             FieldMetaData[] fields = cmd.fields;
512             if (fields == null) continue;
513             for (int j = 0; j < fields.length; j++) {
514                 JdbcField jf = (JdbcField)fields[j].storeField;
515                 if (jf != null) {
516                     try {
517                         jf.nameLinkTableIndexes(nameGenerator);
518                     } catch (RuntimeException JavaDoc e) {
519                         cmd.addError(e, quiet);
520                     }
521                 }
522             }
523         }
524
525         // create key generator instances
526
if (Debug.DEBUG) {
527             Debug.OUT.println("MDB-JDBC: Creating key generators ... ");
528         }
529         createKeyGenerators(quiet);
530     }
531
532     /**
533      * Create main table indexes for cmd and recursively all of its subclasses.
534      * This is a NOP if cmd is not a base class. This also make sure that all
535      * of the indexes have names and gets rid of duplicate indexes (only
536      * the first one is kept).
537      */

538     private void createMainTableIndexes(ClassMetaData cmd, boolean quiet) {
539         if (cmd.pcSuperMetaData != null) return;
540
541         ArrayList all = new ArrayList();
542         collectIndexes(cmd, all, quiet);
543         ArrayList auto = new ArrayList();
544         collectAutoIndexes(cmd, auto);
545
546         // get rid of auto indexes that have same columns as manual indexes
547
// or other auto indexes
548
HashSet s = new HashSet();
549         s.addAll(all);
550         for (Iterator i = auto.iterator(); i.hasNext();) {
551             Object JavaDoc o = i.next();
552             if (s.contains(o)) {
553                 i.remove();
554             } else {
555                 s.add(o);
556             }
557         }
558         all.addAll(auto);
559
560         Map tablesToIndexs = new HashMap();
561         for (int i = 0; i < all.size(); i++) {
562             JdbcIndex index = (JdbcIndex)all.get(i);
563             JdbcTable table = null;
564             boolean ignore = false;
565             if (index.cols.length > 0) {
566                 table = index.cols[0].table;
567                 ignore = false;
568                 for (int j = 1; j < index.cols.length; j++) {
569                     JdbcColumn col = index.cols[j];
570                     if (col.table != table) {
571                         System.out.println("\n\n WARNING: This composite index contains colums from " +
572                                 "2 different tables. Ignoring it");
573                         ignore = true;
574                     }
575                 }
576             }
577             if (ignore) continue;
578
579             Set indexs = (Set)tablesToIndexs.get(table);
580             if (indexs == null) {
581                 indexs = new HashSet();
582                 tablesToIndexs.put(table, indexs);
583             }
584             indexs.add(index);
585         }
586
587         for (Iterator iterator = tablesToIndexs.entrySet().iterator();
588              iterator.hasNext();) {
589             Map.Entry mapEntry = (Map.Entry)iterator.next();
590             JdbcTable table = (JdbcTable)mapEntry.getKey();
591             if (table != null) {
592                 Set indexs = (Set)mapEntry.getValue();
593                 JdbcIndex[] a = new JdbcIndex[indexs.size()];
594                 indexs.toArray(a);
595                 table.indexes = a;
596                 for (int i = 0; i < a.length; i++) {
597                     if (a[i] != null && a[i].name == null) {
598                         generateNameForIndex(nameGenerator, table.name,
599                                 a[i]);
600                     }
601                 }
602             }
603         }
604     }
605
606     protected void postAllFieldsCreatedHook() {
607         ClassMetaData[] ca = jmd.classes;
608
609         // fill the stateFieldNo on each JdbcField
610
for (int i = ca.length - 1; i >= 0; i--) {
611             ClassMetaData cmd = ca[i];
612             JdbcClass jc = (JdbcClass)cmd.storeClass;
613             if (jc == null || jc.fields == null) continue;
614             for (int j = jc.fields.length - 1; j >= 0; j--) {
615                 JdbcField f = jc.fields[j];
616                 if (f == null) {
617                     continue;
618                 }
619                 f.stateFieldNo = f.fmd.fieldNo + cmd.superFieldCount;
620             }
621         }
622
623         // fill the stateFields array on each JdbcClass
624
for (int i = ca.length - 1; i >= 0; i--) {
625             ClassMetaData cmd = ca[i];
626             JdbcClass jc = (JdbcClass)cmd.storeClass;
627             if (jc == null) continue;
628             jc.buildStateFields();
629         }
630         fillFGMetaData();
631     }
632
633     private void collectIndexes(ClassMetaData cmd, ArrayList indexes,
634             boolean quiet) {
635         ArrayList indexExts = getClassInfo(cmd).indexExts;
636         for (int j = 0; j < indexExts.size(); j++) {
637             try {
638                 indexes.add(processIndexExtension(cmd,
639                         (JdoExtension)indexExts.get(j), quiet));
640             } catch (RuntimeException JavaDoc e) {
641                 cmd.addError(e, quiet);
642             }
643         }
644         ClassMetaData[] subs = cmd.pcSubclasses;
645         if (subs != null) {
646             for (int i = 0; i < subs.length; i++) {
647                 try {
648                     collectIndexes(subs[i], indexes, quiet);
649                 } catch (RuntimeException JavaDoc e) {
650                     cmd.addError(e, quiet);
651                 }
652             }
653         }
654     }
655
656     private void collectAutoIndexes(ClassMetaData cmd, ArrayList indexes) {
657         indexes.addAll(getClassInfo(cmd).autoIndexes);
658         ClassMetaData[] subs = cmd.pcSubclasses;
659         if (subs != null) {
660             for (int i = 0; i < subs.length; i++) {
661                 collectAutoIndexes(subs[i], indexes);
662             }
663         }
664     }
665
666     /**
667      * Generate the name for an index. The columns in the index must have
668      * names.
669      */

670     public static void generateNameForIndex(JdbcNameGenerator namegen, String JavaDoc tableName,
671             JdbcIndex idx) {
672         int n = idx.cols.length;
673         String JavaDoc[] cn = new String JavaDoc[n];
674         for (int i = 0; i < n; i++) cn[i] = idx.cols[i].name;
675         idx.name = namegen.generateIndexName(tableName, cn);
676     }
677
678     /**
679      * Convert the type mapping strings for s into JdbcTypeMapping instances.
680      */

681     private ArrayList parseTypeMappings() {
682         String JavaDoc sdb = sqlDriver.getName();
683         ArrayList in = jdbcConfig.typeMappings;
684         int n = in.size();
685         ArrayList a = new ArrayList(n);
686         StringListParser p = new StringListParser();
687         for (int i = 0; i < n; i++) {
688             String JavaDoc s = (String JavaDoc)in.get(i);
689             p.setString(s);
690             JdbcTypeMapping m = new JdbcTypeMapping();
691             m.parse(p, converterRegistry);
692             String JavaDoc mdb = m.getDatabase();
693             if (mdb == null || mdb.equals(sdb)) a.add(m);
694         }
695         return a;
696     }
697
698     /**
699      * Convert the java type mapping strings for s into JdbcJavaTypeMapping
700      * instances.
701      */

702     private ArrayList parseJavaTypeMappings() {
703         String JavaDoc sdb = sqlDriver.getName();
704         ArrayList in = jdbcConfig.javaTypeMappings;
705         int n = in.size();
706         ArrayList a = new ArrayList(n);
707         StringListParser p = new StringListParser();
708         for (int i = 0; i < n; i++) {
709             String JavaDoc s = (String JavaDoc)in.get(i);
710             p.setString(s);
711             JdbcJavaTypeMapping m = new JdbcJavaTypeMapping();
712             m.parse(p, converterRegistry);
713             String JavaDoc mdb = m.getDatabase();
714             if (mdb == null || mdb.equals(sdb)) a.add(m);
715         }
716         return a;
717     }
718
719     /**
720      * Process an index extension for cmd. This will resolve the names of
721      * all fields in the index to their column names and register the name
722      * of the index if one has been specified. The index is returned.
723      */

724     private JdbcIndex processIndexExtension(ClassMetaData cmd, JdoExtension e,
725             boolean quiet) {
726         JdbcIndex idx = new JdbcIndex();
727         idx.name = e.value;
728         ArrayList cols = new ArrayList();
729         JdoExtension[] a = e.nested;
730         int n = a == null ? 0 : a.length;
731         for (int i = 0; i < n; i++) {
732             JdoExtension ne = a[i];
733             switch (ne.key) {
734                 case JDBC_CLUSTERED:
735                     try {
736                         idx.clustered = ne.getBoolean();
737                     } catch (RuntimeException JavaDoc x) {
738                         cmd.addError(x, quiet);
739                     }
740                     break;
741                 case JDBC_UNIQUE:
742                     try {
743                         idx.unique = ne.getBoolean();
744                     } catch (RuntimeException JavaDoc x) {
745                         cmd.addError(x, quiet);
746                     }
747                     break;
748                 case FIELD_NAME:
749                     JdbcColumn[] mtc;
750                     String JavaDoc fname = ne.getString();
751                     if (fname.equals(DATASTORE_PK_FIELDNAME)) {
752                         mtc = ((JdbcClass)cmd.storeClass).table.pk;
753                     } else {
754                         FieldMetaData fmd = cmd.getFieldMetaData(fname);
755                         if (fmd == null) {
756                             RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("Field '" + ne.value +
757                                     "' not found\n" + ne.getContext());
758                             cmd.addError(x, quiet);
759                         }
760                         JdbcField jf = (JdbcField)fmd.storeField;
761                         if (jf == null || jf.mainTableCols == null) {
762                             RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime(
763                                     "Field '" + ne.value +
764                                     "' is not stored in the main table\n" + ne.getContext());
765                             fmd.addError(x, quiet);
766                         }
767                         mtc = jf.mainTableCols;
768                     }
769                     for (int j = 0; j < mtc.length; j++) cols.add(mtc[j]);
770                     break;
771                 default:
772                     MetaDataBuilder.throwUnexpectedExtension(ne);
773             }
774         }
775         n = cols.size();
776         if (n == 0) {
777             RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime(
778                     "Index does not include any fields\n" + e.getContext());
779             cmd.addError(x, quiet);
780             return null;
781         }
782         JdbcColumn[] idxCols = new JdbcColumn[n];
783         cols.toArray(idxCols);
784         idx.setCols(idxCols);
785         if (idx.name != null) {
786             JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
787             try {
788                 nameGenerator.addIndexName(
789                         jdbcClass.table.name,
790                         idx.name);
791             } catch (IllegalArgumentException JavaDoc x) {
792                 RuntimeException JavaDoc ex = BindingSupportImpl.getInstance().runtime(
793                         x.getMessage(), x);
794                 cmd.addError(ex, quiet);
795             }
796         }
797         return idx;
798     }
799
800     private void fillFGMetaData() {
801         ClassMetaData[] classes = jmd.classes;
802         int clen = classes.length;
803         if (Debug.DEBUG) {
804             Debug.OUT.println("MDB-JDBC: Filling fetch group meta data ... ");
805         }
806         for (int i = 0; i < clen; i++) {
807             ClassMetaData cmd = classes[i];
808             processFetchGroups(cmd);
809         }
810     }
811
812     /**
813      * Finish up optimistic locking for cmd.
814      */

815     private void completeOptimisticLocking(ClassMetaData cmd, boolean quiet) {
816         JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
817         ClassInfo info = getClassInfo(cmd);
818         switch (jdbcClass.optimisticLocking) {
819             case JdbcClass.OPTIMISTIC_LOCKING_VERSION:
820             case JdbcClass.OPTIMISTIC_LOCKING_TIMESTAMP:
821                 processTimestampOrRowVersionLocking(jdbcClass, cmd,
822                         info.optimisticLockingExt, info.elements, quiet);
823                 break;
824         }
825     }
826
827     private void processTimestampOrRowVersionLocking(JdbcClass jdbcClass,
828             ClassMetaData cmd, JdoExtension e, ArrayList jdbcElements,
829             boolean quiet) {
830         boolean timestamp =
831                 jdbcClass.optimisticLocking == JdbcClass.OPTIMISTIC_LOCKING_TIMESTAMP;
832         JdoExtension[] nested = e == null ? null : e.nested;
833         if (nested != null && nested[0].key == JdoExtensionKeys.FIELD_NAME) {
834             if (nested.length > 1) {
835                 RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("Unexpected extension: " +
836                         nested[1].getContext());
837                 cmd.addError(x, quiet);
838             }
839             String JavaDoc fieldName = nested[0].value;
840             FieldMetaData fmd = cmd.getFieldMetaData(fieldName);
841             if (fmd == null) {
842                 RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("Field '" + fieldName +
843                         " not found\n" + nested[0].getContext());
844                 cmd.addError(x, quiet);
845             }
846             int tc = fmd.typeCode;
847             if (timestamp) {
848                 if (tc != MDStatics.DATE) {
849                     RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime(
850                             "Field '" + fieldName +
851                             " is not a java.util.Date\n" + nested[0].getContext());
852                     fmd.addError(x, quiet);
853                 }
854             } else {
855                 if (tc != MDStatics.INT && tc != MDStatics.SHORT
856                         && tc != MDStatics.BYTE) {
857                     RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime(
858                             "Field '" + fieldName +
859                             " is not an int, short or byte\n" + nested[0].getContext());
860                     fmd.addError(x, quiet);
861                 }
862             }
863             cmd.optimisticLockingField = fmd;
864             jdbcClass.optimisticLockingField = (JdbcSimpleField)fmd.storeField;
865             fmd.setAutoSet(MDStatics.AUTOSET_BOTH);
866         } else {
867             JdbcColumn tc = createColumn(nested,
868                     OPT_LOCK_FIELDNAME, timestamp ? (Class JavaDoc)Date.class : (Class JavaDoc)Short.TYPE);
869             // generate a fake field for the column
870
JdbcSimpleField f = jdbcClass.optimisticLockingField = new JdbcSimpleField();
871             f.fake = true;
872             f.col = tc;
873             FieldMetaData fmd = f.fmd = new FieldMetaData();
874             fmd.fake = true;
875             fmd.category = MDStatics.CATEGORY_SIMPLE;
876             fmd.primaryField = true;
877             fmd.classMetaData = cmd;
878             fmd.defaultFetchGroup = true;
879             fmd.storeField = f;
880
881             fmd.name = timestamp ? "jdoTimestamp" : "jdoVersion";
882
883
884             fmd.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_PERSISTENT;
885             fmd.setType(tc.javaType);
886             fmd.setAutoSet(MDStatics.AUTOSET_BOTH);
887             jdbcElements.add(f);
888             cmd.optimisticLockingField = fmd;
889         }
890     }
891
892     /**
893      * Fill in extra meta data for FetchGroup's.
894      */

895     private void processFetchGroups(ClassMetaData cmd) {
896         FetchGroup[] groups = cmd.fetchGroups;
897         int groupsLen = groups == null ? 0 : groups.length;
898         for (int i = 0; i < groupsLen; i++) {
899             FetchGroup g = groups[i];
900             int totCols = 0;
901             FetchGroupField[] fields = g.fields;
902             if (fields == null) {
903                 continue;
904             }
905             int fieldsLen = fields.length;
906             for (int j = 0; j < fieldsLen; j++) {
907                 FetchGroupField field = fields[j];
908                 FieldMetaData fmd = field.fmd;
909                 JdbcField jdbcField = (JdbcField)fmd.storeField;
910                 JdoExtension ext = field.extension;
911                 if (ext != null && ext.nested != null) {
912                     JdoExtension[] nested = ext.nested;
913                     int nestedLen = nested.length;
914                     for (int k = 0; k < nestedLen; k++) {
915                         JdoExtension e = nested[k];
916                         switch (e.key) {
917                             case JDBC_USE_JOIN:
918                                 try {
919                                     field.jdbcUseJoin = e.getEnum(
920                                             jdbcMDE.USE_JOIN_ENUM);
921                                 } catch (RuntimeException JavaDoc x) {
922                                     cmd.addError(x, quiet);
923                                 }
924                                 break;
925                             case JDBC_USE_KEY_JOIN:
926                                 if (fmd.category != MDStatics.CATEGORY_MAP) {
927                                     RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("The jdbc-use-key-join option is only " +
928                                             "valid for Map fields\n" +
929                                             e.getContext());
930                                     cmd.addError(x, quiet);
931                                 }
932                                 try {
933                                     field.jdbcUseKeyJoin = e.getEnum(
934                                             jdbcMDE.USE_JOIN_ENUM);
935                                 } catch (RuntimeException JavaDoc x) {
936                                     cmd.addError(x, quiet);
937                                 }
938                                 break;
939                             default:
940                                 if (e.isJdbc()) {
941                                     MetaDataBuilder.throwUnexpectedExtension(e);
942                                 }
943                         }
944                     }
945                 }
946                 if (jdbcField == null) continue;
947                 if (field.jdbcUseJoin == 0) {
948                     if (field.nextFetchGroup != null) {
949                         field.jdbcUseJoin = jdbcField.useJoin;
950                     } else {
951                         field.jdbcUseJoin = JdbcField.USE_JOIN_NO;
952                     }
953                 }
954                 if (field.jdbcUseKeyJoin == 0) {
955                     if (field.nextKeyFetchGroup != null) {
956                         field.jdbcUseKeyJoin = jdbcField.getUseKeyJoin();
957                     } else {
958                         field.jdbcUseKeyJoin = JdbcField.USE_JOIN_NO;
959                     }
960                 }
961                 if (jdbcField.mainTableCols != null) {
962                     totCols += jdbcField.mainTableCols.length;
963                 }
964             }
965             g.jdbcTotalCols = totCols;
966         }
967     }
968
969     /**
970      * Process all ref and polyref fields.
971      */

972     private void processRefAndPolyRefFields(ClassMetaData cmd, boolean quiet) {
973         ArrayList elements = getClassElements(cmd);
974         int nelements = elements.size();
975         for (int i = 0; i < nelements; i++) {
976             Object JavaDoc o = elements.get(i);
977             if (o instanceof JdbcRefField) {
978                 processRefField((JdbcRefField)o, quiet);
979             } else if (o instanceof JdbcPolyRefField) {
980                 JdbcPolyRefField f = (JdbcPolyRefField)o;
981                 f.processMetaData(f.fmd.jdoField, this);
982             }
983         }
984     }
985
986     private void doConstraints(ClassMetaData cmd) {
987         for (int i = 0; i < cmd.fields.length; i++) {
988             FieldMetaData fmd = cmd.fields[i];
989             if (fmd.storeField instanceof JdbcRefField) {
990                 createConstraint((JdbcRefField) fmd.storeField);
991             }
992         }
993     }
994
995     /**
996      * Process all ref and collection fields.
997      */

998     private void processCollectionFields(ClassMetaData cmd, boolean quiet) {
999         if (cmd.horizontal) return;
1000        ArrayList elements = getClassElements(cmd);
1001        int nelements = elements.size();
1002        for (int i = 0; i < nelements; i++) {
1003            Object JavaDoc o = elements.get(i);
1004            if (o instanceof JdbcCollectionField) {
1005                processCollectionField((JdbcCollectionField)o, quiet);
1006            }
1007        }
1008    }
1009
1010    /**
1011     * Complete a JdbcRefField except for column names.
1012     */

1013    private void processRefField(JdbcRefField f, boolean quiet) {
1014        FieldMetaData fmd = f.fmd;
1015        f.targetClass = fmd.typeMetaData;
1016        JdbcClass target = (JdbcClass)f.targetClass.storeClass;
1017        JdoField jdoField = fmd.jdoField;
1018        JdoElement context = jdoField == null
1019                ? (JdoElement)fmd.typeMetaData.jdoClass
1020                : (JdoElement)jdoField;
1021        processRefFieldImpl(target, f, fmd, context,
1022                jdoField == null ? null : jdoField.extensions, quiet);
1023
1024    }
1025
1026    public void processRefFieldImpl(JdbcClass target, JdbcRefField f,
1027            FieldMetaData fmd, JdoElement context, JdoExtension[] extensions,
1028            boolean quiet) {
1029        if (target != null) {
1030            f.useJoin = target.useJoin;
1031        } else {
1032            f.useJoin = JdbcRefField.USE_JOIN_NO;
1033        }
1034        if (f.useJoin == JdbcRefField.USE_JOIN_NO && fmd.isDefaultFetchGroupTrue()) {
1035            f.useJoin = JdbcRefField.USE_JOIN_OUTER;
1036        }
1037
1038        JdbcRefMetaDataBuilder rdb = new JdbcRefMetaDataBuilder(
1039                fmd.classMetaData, this,
1040                f.targetClass, context, fmd.name,
1041                extensions, quiet);
1042
1043        f.cols = rdb.getCols();
1044        if (rdb.getUseJoin() != 0) f.useJoin = rdb.getUseJoin();
1045
1046        for (int i = 0; i < f.cols.length; i++) {
1047            f.cols[i].comment = fmd.getCommentName();
1048        }
1049
1050        boolean nulls = fmd.nullValue != MDStatics.NULL_VALUE_EXCEPTION;
1051        int nc = f.cols.length;
1052        for (int i = 0; i < nc; i++) f.cols[i].nulls = nulls;
1053
1054        // include this in the where clause for changed locking only if all
1055
// columns support equalityTest
1056
f.includeForChangedLocking = true;
1057        for (int i = 0; i < nc; i++) {
1058            if (!f.cols[i].equalityTest) {
1059                f.includeForChangedLocking = false;
1060                break;
1061            }
1062        }
1063        f.constraintName = rdb.getConstraintName();
1064        f.createConstraint = !rdb.isDoNotCreateConstraint();
1065    }
1066
1067    private boolean isCircularRef(ClassMetaData cmd1, ClassMetaData cmd2) {
1068        return jdbcClassReferenceGraph.isCircularRef(cmd1, cmd2);
1069    }
1070
1071    private void createConstraint(JdbcRefField f) {
1072        final JdbcClass jdbcClass = (JdbcClass)f.fmd.classMetaData.storeClass;
1073        boolean createConstraint = f.createConstraint && f.targetClass != null
1074                && (!(f.fmd.nullValue != MDStatics.NULL_VALUE_EXCEPTION)
1075                || sqlDriver.isNullForeignKeyOk());
1076
1077
1078        /**
1079         * If this class is involved in a cycle with the refering class then
1080         * don't create the constraint unless the user specifically specified a dependency.
1081         */

1082        if (createConstraint && isCircularRef(jdbcClass.cmd, f.fmd.typeMetaData)) {
1083            if (!getClassInfo(jdbcClass.cmd.top).getCreatedAfterClient().contains(
1084                    f.fmd.typeMetaData.top)) {
1085                createConstraint = false;
1086            }
1087        }
1088
1089        if (createConstraint && jdbcClass.cmd.top == f.fmd.typeMetaData.top) {
1090            createConstraint = false;
1091        }
1092
1093        // create constraint if required
1094
if (createConstraint) {
1095            JdbcConstraint c = new JdbcConstraint();
1096            c.name = f.constraintName;
1097            c.src = jdbcClass.table;
1098            c.srcCols = f.cols;
1099            c.dest = ((JdbcClass)f.targetClass.storeClass).table;
1100            f.constraint = c;
1101            if (c.name != null) {
1102                nameGenerator.addRefConstraintName(c.src.name,
1103                        c.name);
1104            }
1105        }
1106    }
1107
1108    /**
1109     * Find the column in cols with the the supplied refField or null if none.
1110     */

1111    public JdbcColumn findColumn(List cols, JdbcSimpleField refField) {
1112        for (int i = cols.size() - 1; i >= 0; i--) {
1113            JdbcColumn col = (JdbcColumn)cols.get(i);
1114            if (col.refField == refField) return col;
1115        }
1116        return null;
1117    }
1118
1119    /**
1120     * Complete a JdbcCollectionField.
1121     */

1122    private void processCollectionField(JdbcCollectionField f, boolean quiet) {
1123        FieldMetaData fmd = f.fmd;
1124        JdoField jdoField = fmd.jdoField;
1125        JdoElement context;
1126        if (jdoField == null) {
1127            if (fmd.typeMetaData == null) {
1128                context = fmd.classMetaData.jdoClass;
1129            } else {
1130                context = fmd.typeMetaData.jdoClass;
1131            }
1132        } else {
1133            context = jdoField;
1134        }
1135        try {
1136            f.processMetaData(context, this, quiet);
1137        } catch (RuntimeException JavaDoc e) {
1138            fmd.addError(e, quiet);
1139        }
1140    }
1141
1142    /**
1143     * Creates key generators for all classes with keygen factories and
1144     * extracts keygen tables from them. Also set the autoinc flag on
1145     * the primary key of classes using a postInsert key generator.
1146     */

1147    private void createKeyGenerators(boolean quite) {
1148        HashSet keygenSet = new HashSet();
1149        HashSet keygenTables = new HashSet(17);
1150        ClassMetaData[] classes = jmd.classes;
1151        int clen = classes.length;
1152        for (int i = 0; i < clen; i++) {
1153            ClassMetaData cmd = classes[i];
1154            if (cmd.horizontal) continue;
1155            if (cmd.pcSuperMetaData != null) continue;
1156            ClassInfo info = getClassInfo(cmd);
1157            JdbcKeyGeneratorFactory f = info.keyGenFactory;
1158            if (f == null) continue;
1159            JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1160            JdbcKeyGenerator keygen;
1161            try {
1162                keygen = f.createJdbcKeyGenerator(cmd.qname, jdbcClass.table,
1163                        info.keyGenFactoryArgs);
1164            } catch (RuntimeException JavaDoc e) {
1165                cmd.addError(BindingSupportImpl.getInstance().runtime(e.getMessage() + "\n" +
1166                        cmd.jdoClass.getContext(), e), quite);
1167                continue;
1168            }
1169
1170            // recursively set keygen on class and subclasses
1171
jdbcClass.setJdbcKeyGenerator(keygen);
1172
1173            if (keygenSet.contains(keygen)) continue;
1174            keygenSet.add(keygen);
1175            keygen.addKeyGenTables(keygenTables, this);
1176
1177            // set the autoinc flag on the primary key if postInsert keygen
1178
if (keygen.isPostInsertGenerator()) {
1179                jdbcClass.table.pk[0].autoinc = true;
1180                //for sybase this column's type must change to numeric
1181
//all subclass column's must also be updated
1182
sqlDriver.updateClassForPostInsertKeyGen(cmd, mappingResolver);
1183            }
1184        }
1185        JdbcMetaData jdbcMetaData = (JdbcMetaData)jmd.jdbcMetaData;
1186        jdbcMetaData.keyGenTables = new JdbcTable[keygenTables.size()];
1187        keygenTables.toArray(jdbcMetaData.keyGenTables);
1188    }
1189
1190    /**
1191     * Name field columns without names and create the cols array on each
1192     * class table from the jdbcElements collected so far. It will
1193     * recursively process subclasses as required. Fake elements are
1194     * collected at the same time and added to ClassMetaData.fields.
1195     */

1196    private void finalizeFakesAndTableColumns(ClassMetaData cmd) {
1197        ArrayList elements = getClassElements(cmd);
1198        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1199        if (elements != null) {
1200            collectSubclassElements(cmd, elements);
1201
1202            JdbcNameGenerator nameGen = nameGenerator;
1203            String JavaDoc tableName = jdbcClass.tableName;
1204
1205            // make sure the classIdCol has a name
1206
JdbcColumn classIdCol = jdbcClass.classIdCol;
1207            if (classIdCol != null) {
1208                if (classIdCol.name == null) {
1209                    classIdCol.name = nameGen.generateClassIdColumnName(
1210                            tableName);
1211                } else {
1212                    nameGen.addColumnName(tableName, classIdCol.name);
1213                }
1214            }
1215
1216            // find all the JdbcColumn's in all elements and find fake
1217
// fields
1218
ArrayList cols = new ArrayList(elements.size());
1219            ArrayList fakes = new ArrayList();
1220            JdbcTable table = jdbcClass.table;
1221            for (int i = 0; i < elements.size(); i++) {
1222                Object JavaDoc o = elements.get(i);
1223                if (o instanceof JdbcField) {
1224                    JdbcField f = (JdbcField)o;
1225                    f.setMainTable(table);
1226                    if (!f.fmd.primaryKey) f.nameColumns(tableName, nameGen);
1227                    int pos = cols.size();
1228                    f.addMainTableCols(cols);
1229                    // make sure cols from subclasses allow nulls
1230
if (f.fmd.classMetaData.pcSuperMetaData!= null) {
1231                        int pos2 = cols.size();
1232                        for (int j = pos; j < pos2; j++) {
1233                            JdbcColumn sc = (JdbcColumn)cols.get(j);
1234                            sc.nulls = true;
1235                        }
1236                    }
1237                    if (f.fake) {
1238                        fakes.add(f);
1239                    }
1240                } else { // must be a column
1241
JdbcColumn c = (JdbcColumn)o;
1242                    c.setTable(table);
1243                    cols.add(c);
1244                }
1245            }
1246
1247            //this done after all the naming has taken place.
1248
doNullIndicatorColumn(cmd, elements);
1249
1250            // add the fakes (if any) to cmd.fields
1251
int numFakes = fakes.size();
1252            if (numFakes > 0 && cmd.fields != null) {
1253                int n = cmd.fields.length;
1254                FieldMetaData[] a = new FieldMetaData[n + numFakes];
1255                System.arraycopy(cmd.fields, 0, a, 0, n);
1256                for (int i = 0; i < numFakes; i++, n++) {
1257                    JdbcField jdbcField = (JdbcField)fakes.get(i);
1258                    a[n] = jdbcField.fmd;
1259                }
1260                cmd.fields = a;
1261            }
1262
1263            // do a final sanity check on each column and set the table reference
1264
int nc = cols.size();
1265            JdbcColumn[] a = new JdbcColumn[nc];
1266            cols.toArray(a);
1267            for (int i = 0; i < nc; i++) {
1268                JdbcColumn c = a[i];
1269                if (c.name == null) {
1270                    throw BindingSupportImpl.getInstance().internal("Column has no name: " + c + "\n" +
1271                            cmd.jdoClass.getContext());
1272                }
1273                if (c.jdbcType == Types.NULL) {
1274                    throw BindingSupportImpl.getInstance().internal("Column has NULL jdbcType: " + c + "\n" +
1275                            cmd.jdoClass.getContext());
1276                }
1277                if (c.table == null) {
1278                    throw BindingSupportImpl.getInstance().internal("Column null table: " + c + "\n" +
1279                            cmd.jdoClass.getContext());
1280                }
1281                if (!sqlDriver.isBatchingSupportedForJdbcType(c.jdbcType)) {
1282                    jdbcClass.noBatching = true;
1283                }
1284            }
1285
1286            // set the cols array
1287
jdbcClass.table.cols = a;
1288        }
1289
1290        // recursively process all of our subclasses
1291
ClassMetaData[] subclasses = cmd.pcSubclasses;
1292        if (subclasses == null) return;
1293        for (int i = 0; i < subclasses.length; i++) {
1294            ((JdbcClass)subclasses[i].storeClass).setClassIdCol(jdbcClass.classIdCol);
1295            finalizeFakesAndTableColumns(subclasses[i]);
1296        }
1297    }
1298
1299    /**
1300     * Go through all the embedded reference fields on the supplied cmd and find
1301     * nullindicator columns.
1302     */

1303    private void doNullIndicatorColumn(ClassMetaData cmd, ArrayList elements) {
1304        //************** ignored for now **********************.
1305
if (true) return;
1306        //************** ignored for now **********************.
1307
FieldMetaData[] fmds = cmd.fields;
1308        for (int i = 0; i < fmds.length; i++) {
1309            FieldMetaData fmd = fmds[i];
1310            if (fmd.isEmbeddedRef()) {
1311                if (fmd.jdoField != null && fmd.jdoField.extensions != null) {
1312                    JdoExtension ext = JdoExtension.find(JdoExtensionKeys.NULL_INDICATOR, fmd.jdoField.extensions);
1313                    if (ext != null) {
1314                        boolean extFound = false;
1315                        //expect a jdbc-column-name
1316
if (ext.nested != null) {
1317                            JdoExtension colNameExt = ext.nested[0];
1318                            if (colNameExt.key == JdoExtensionKeys.JDBC_COLUMN_NAME) {
1319                                extFound = true;
1320                                //must check to see if we have any column in the table with that name
1321
boolean createColumn = true;
1322                                for (int l = 0; l < elements.size(); l++) {
1323                                    Object JavaDoc o1 = elements.get(l);
1324                                    if (o1 instanceof JdbcSimpleField) {
1325                                        JdbcSimpleField sf = (JdbcSimpleField) o1;
1326                                        if (sf.col.name == ext.value) {
1327                                            createColumn = false;
1328                                        }
1329                                    }
1330                                }
1331                                if (createColumn) {
1332                                    JdbcColumn col = createColumn(ext.nested,
1333                                            CLASS_ID_FIELDNAME, Integer.TYPE);
1334                                    JdbcSimpleField f = new JdbcSimpleField();
1335                                    f.fake = true;
1336                                    f.col = col;
1337                                    FieldMetaData nullFmd = f.fmd = new FieldMetaData();
1338                                    nullFmd.fake = true;
1339                                    nullFmd.category = MDStatics.CATEGORY_SIMPLE;
1340                                    nullFmd.classMetaData = cmd;
1341                                    nullFmd.defaultFetchGroup = true;
1342                                    nullFmd.storeField = f;
1343                                    nullFmd.name = fmd.name + "_null_indicator";
1344                                    nullFmd.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_PERSISTENT;
1345                                    nullFmd.setType(col.javaType);
1346                                    fmd.setNullIndicatorFmd(nullFmd);
1347                                    elements.add(f);
1348                                }
1349                            }
1350                        }
1351                        if (!extFound) {
1352                            throw BindingSupportImpl.getInstance().unsupported(
1353                                    "Found a '"
1354                                    + JdoExtension.toKeyString(JdoExtensionKeys.NULL_INDICATOR)
1355                                    + "' extension with no '"
1356                                    + JdoExtension.toKeyString(JdoExtensionKeys.JDBC_COLUMN_NAME)
1357                                    + "' nested extension");
1358                        }
1359                    }
1360                }
1361            }
1362        }
1363    }
1364
1365    /**
1366     * Name all constraints without names and create the constraint arrays
1367     * on class tables.
1368     */

1369    private void finalizeConstraints(ClassMetaData cmd) {
1370        ClassInfo info = getClassInfo(cmd);
1371        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1372        JdbcField[] fields = jdbcClass.fields;
1373        int len = fields.length;
1374        ArrayList cons = new ArrayList();
1375        JdbcConstraint pkFkConstraint = info.pkFkConstraint;
1376        if (pkFkConstraint != null) {
1377            if (pkFkConstraint.name == null) {
1378                pkFkConstraint.generateName(nameGenerator);
1379            }
1380            cons.add(pkFkConstraint);
1381        }
1382        JdbcTable table = jdbcClass.table;
1383        for (int i = 0; i < len; i++) {
1384            JdbcField field = fields[i];
1385            if (field != null && field.mainTable == jdbcClass.table) {
1386                field.addConstraints(cons);
1387            }
1388        }
1389        int n = cons.size();
1390        if (n > 0) {
1391            table.addConstraints(cons);
1392        }
1393    }
1394
1395    /**
1396     * Recursively add all elements from all subclasses of cmd that are
1397     * stored in the same table to elements. Any class so added has its
1398     * elements removed from the map indicating that it has been processed.
1399     */

1400    private void collectSubclassElements(ClassMetaData cmd, ArrayList elements) {
1401        ClassMetaData[] subclasses = cmd.pcSubclasses;
1402        if (subclasses == null) return;
1403        for (int i = 0; i < subclasses.length; i++) {
1404            ClassMetaData sc = subclasses[i];
1405            if (((JdbcClass)sc.storeClass).table == ((JdbcClass)cmd.storeClass).table) {
1406                ClassInfo scInfo = getClassInfo(sc);
1407                elements.addAll(scInfo.elements);
1408                scInfo.elements = null;
1409                collectSubclassElements(sc, elements);
1410            }
1411        }
1412    }
1413
1414    /**
1415     * Find the primary key (datastore or application) for cmd and make sure
1416     * all the columns have names. If the identity-type is datastore and
1417     * there is no primary key column then create one. This will recursively
1418     * process any PC subclasses.
1419     */

1420    private void processPrimaryKey(ClassMetaData cmd, boolean quiet) {
1421// if (cmd.horizontal) return;
1422
if (cmd.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
1423            processPrimaryKeyAppIdentity(cmd, quiet);
1424        } else {
1425            processPrimaryKeyDatastoreIdentity(cmd);
1426        }
1427
1428        // create subclass table fk constraint to base table if needed
1429
ClassInfo info = getClassInfo(cmd);
1430        ClassMetaData pccmd = cmd.pcSuperMetaData;
1431        if (pccmd != null
1432                && ((JdbcClass)cmd.storeClass).table != ((JdbcClass)pccmd.storeClass).table) {
1433            JdbcConstraint c = new JdbcConstraint();
1434            c.name = info.pkFkConstraintName;
1435            c.src = ((JdbcClass)cmd.storeClass).table;
1436            c.srcCols = ((JdbcClass)cmd.storeClass).table.pk;
1437            c.dest = ((JdbcClass)pccmd.storeClass).table;
1438            if (c.name != null) {
1439                nameGenerator.addRefConstraintName(
1440                        c.src.name, c.name);
1441            }
1442            info.pkFkConstraint = c;
1443        }
1444
1445        if (cmd.pcSubclasses == null) return;
1446        for (int j = 0; j < cmd.pcSubclasses.length; j++) {
1447            processPrimaryKey(cmd.pcSubclasses[j], quiet);
1448        }
1449    }
1450
1451    /**
1452     * Find the primary key for datastore identity class for cmd and make sure
1453     * all the columns have names. If there is no primary key column then
1454     * create one.
1455     */

1456    private void processPrimaryKeyDatastoreIdentity(ClassMetaData cmd) {
1457        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1458        JdbcTable table = jdbcClass.table;
1459        JdbcColumn pkcol = null;
1460
1461        ArrayList jdbcElements = getClassElements(cmd);
1462        int elen = jdbcElements.size();
1463        for (int i = 0; i < elen; i++) {
1464            Object JavaDoc o = jdbcElements.get(i);
1465            if (!(o instanceof JdbcColumn)) continue;
1466            JdbcColumn c = (JdbcColumn)o;
1467            if (c.pk) {
1468                if (pkcol != null) {
1469                    throw BindingSupportImpl.getInstance().runtime("Class " + cmd.qname +
1470                            " has multiple jdbc-primary-key extensions\n" +
1471                            cmd.jdoClass.getContext());
1472                }
1473                pkcol = c;
1474            }
1475        }
1476        if (cmd.pcSuperMetaData != null) {
1477            if (pkcol != null) {
1478                throw BindingSupportImpl.getInstance().runtime("Class " + cmd.qname + " has a " +
1479                        "persistence-capable-superclass so use the inheritance " +
1480                        "extension to specify its primary-key\n" +
1481                        cmd.jdoClass.getContext());
1482            }
1483            // if subclass is stored in its own table then use the inheritance
1484
// extension to build a reference to the superclass
1485
if (table != ((JdbcClass)cmd.pcSuperMetaData.storeClass).table) {
1486                createVerticalInheritancePK(cmd);
1487            }
1488        } else {
1489            if (pkcol == null) {
1490                pkcol = createColumn(null,
1491                        DATASTORE_PK_FIELDNAME, Integer.TYPE);
1492                jdbcElements.add(0, pkcol);
1493            }
1494            // make sure the columns has a name
1495
if (pkcol.name != null) {
1496                try {
1497                    nameGenerator.addColumnName(table.name, pkcol.name);
1498                } catch (IllegalArgumentException JavaDoc e) {
1499                    throw BindingSupportImpl.getInstance().runtime("Invalid jdbc-column-name for datastore identity primary key: " +
1500                            e.getMessage() + "\n" + cmd.jdoClass.getContext());
1501                }
1502            } else {
1503                pkcol.name = nameGenerator.generateDatastorePKName(
1504                        table.name);
1505            }
1506            table.setPk(new JdbcColumn[]{pkcol});
1507        }
1508    }
1509
1510    /**
1511     * Fill in the primary key for a subclass mapped using vertical
1512     * inheritance using the inheritance extension to build a reference to the
1513     * superclass.
1514     */

1515    private void createVerticalInheritancePK(ClassMetaData cmd) {
1516        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1517        JdbcTable table = jdbcClass.table;
1518        JdbcTable superTable = ((JdbcClass)cmd.pcSuperMetaData.storeClass).table;
1519        JdoExtension inheritance = getClassInfo(cmd).inheritance;
1520        JdbcRefMetaDataBuilder rdb = new JdbcRefMetaDataBuilder(cmd, this,
1521                cmd.pcSuperMetaData, inheritance, null,
1522                inheritance == null ? null : inheritance.nested, quiet);
1523        JdbcColumn[] a = rdb.getCols();
1524        for (int i = 0; i < a.length; i++) {
1525            if (a[i].name == null) a[i].name = superTable.pk[i].name;
1526            a[i].pk = true;
1527        }
1528        table.setPk(a);
1529        ArrayList jdbcElements = getClassElements(cmd);
1530        for (int i = 0; i < a.length; i++) {
1531            try {
1532                a[i].addColumnNames(table.name, nameGenerator);
1533            } catch (IllegalArgumentException JavaDoc e) {
1534                throw BindingSupportImpl.getInstance().runtime("Invalid jdbc-column-name: " +
1535                        e.getMessage() + "\n" + inheritance.getContext());
1536            }
1537            jdbcElements.add(i, a[i]);
1538        }
1539    }
1540
1541    /**
1542     * Find the primary key for application identity class cmd and make sure
1543     * all the columns have names.
1544     */

1545    private void processPrimaryKeyAppIdentity(ClassMetaData cmd, boolean quiet) {
1546        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1547        JdbcTable table = jdbcClass.table;
1548        JdbcNameGenerator ng = nameGenerator;
1549        FieldMetaData[] fields = cmd.pkFields;
1550        JdbcColumn[] pkcols = null;
1551        int pkFieldCount = fields == null ? 0 : fields.length;
1552        if (fields != null) {
1553            pkcols = new JdbcColumn[pkFieldCount];
1554            for (int i = 0; i < pkFieldCount; i++) {
1555                try {
1556                    JdbcSimpleField jdbcSimpleField = (JdbcSimpleField)fields[i].storeField;
1557                    pkcols[i] = jdbcSimpleField.col;
1558                    pkcols[i].refField = jdbcSimpleField;
1559                } catch (ClassCastException JavaDoc e) {
1560                    RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("Only simple fields may be mapped as Primary Key's.\n" +
1561                            cmd.jdoClass.getContext());
1562                    cmd.addError(x, quiet);
1563                }
1564            }
1565        }
1566        ClassMetaData pccmd = cmd.pcSuperMetaData;
1567        if (pccmd != null && ((JdbcClass)pccmd.storeClass).inheritance != JdbcClass.INHERITANCE_HORIZONTAL) {
1568            if (pkcols != null) {
1569                throw BindingSupportImpl.getInstance().runtime("Class " + cmd.qname + " has a " +
1570                        "persistence-capable-superclass so use the inheritance " +
1571                        "extension to specify its primary-key\n" +
1572                        cmd.jdoClass.getContext());
1573            }
1574            // if subclass is stored in its own table then use the inheritance
1575
// extension to build a reference to the superclass
1576
if (table != ((JdbcClass)pccmd.storeClass).table) {
1577                createVerticalInheritancePK(cmd);
1578            }
1579        } else {
1580            if (pkcols == null) {
1581                RuntimeException JavaDoc e = BindingSupportImpl.getInstance().runtime("Class " +
1582                        cmd.qname + " has application identity " +
1583                        "but no primary-key fields\n" +
1584                        cmd.jdoClass.getContext());
1585                cmd.addError(e, quiet);
1586            } else {
1587                for (int i = 0; i < pkFieldCount; i++) {
1588                    String JavaDoc cn = pkcols[i].name;
1589                    if (cn == null) {
1590                        pkcols[i].name = ng.generateFieldColumnName(table.name,
1591                                fields[i].name, true);
1592                    } else {
1593                        try {
1594                            ng.addColumnName(table.name, cn);
1595                        } catch (IllegalArgumentException JavaDoc x) {
1596                            throw BindingSupportImpl.getInstance().runtime("Invalid jdbc-column-name: " + x.getMessage() + "\n" +
1597                                    cmd.jdoClass.getContext());
1598                        }
1599                    }
1600                }
1601                table.setPk(pkcols);
1602            }
1603        }
1604    }
1605
1606    /**
1607     * Make sure the persistent capable class cmd has a table and
1608     * recursively process all of its subclasses. NOP if cmd is not
1609     * a base class.
1610     */

1611    private void processBaseClassTable(ClassMetaData cmd) {
1612        ClassMetaData pccmd = cmd.pcSuperMetaData;
1613        if (pccmd != null) return;
1614        createClassTable(cmd);
1615        JdbcClass jdbcClass = ((JdbcClass)cmd.storeClass);
1616        if (cmd.horizontal) {
1617            jdbcClass.doNotCreateTable = true;
1618        }
1619        if (cmd.pcSubclasses == null) return;
1620        for (int j = 0; j < cmd.pcSubclasses.length; j++) {
1621            processSubclassTable(cmd.pcSubclasses[j]);
1622        }
1623    }
1624
1625    private void createClassTable(ClassMetaData cmd) {
1626        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1627        JdbcTable table = new JdbcTable();
1628        table.sqlDriver = sqlDriver;
1629        table.name = jdbcClass.tableName;
1630        table.comment = cmd.qname;
1631        if (table.name == null) {
1632            table.name = nameGenerator.generateClassTableName(cmd.qname);
1633        } else {
1634            addTableName(table, cmd);
1635        }
1636        jdbcClass.setTable(table);
1637        fillTablePkConstraintName(cmd, table);
1638    }
1639
1640    /**
1641     * Make sure a persistent capable subclass cmd has a table and
1642     * recursively process all of its subclasses.
1643     */

1644    private void processSubclassTable(ClassMetaData cmd) {
1645        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1646        ClassMetaData pccmd = cmd.pcSuperMetaData;
1647        switch (jdbcClass.inheritance) {
1648            case JdbcClass.INHERITANCE_FLAT:
1649                jdbcClass.setTable(((JdbcClass)pccmd.storeClass).table);
1650                break;
1651            case JdbcClass.INHERITANCE_VERTICAL:
1652                createClassTable(cmd);
1653                break;
1654            default:
1655                throw BindingSupportImpl.getInstance().internal("Unknown inheritance strategy: " + jdbcClass.inheritance +
1656                        " for " + cmd.qname);
1657        }
1658
1659        if (cmd.pcSubclasses == null) return;
1660        for (int j = 0; j < cmd.pcSubclasses.length; j++) {
1661            processSubclassTable(cmd.pcSubclasses[j]);
1662        }
1663    }
1664
1665    private void addTableName(JdbcTable table,
1666            ClassMetaData cmd) {
1667        try {
1668            nameGenerator.addTableName(table.name);
1669        } catch (IllegalArgumentException JavaDoc x) {
1670            throw BindingSupportImpl.getInstance().runtime("Invalid jdbc-table-name: " + x.getMessage() + "\n" +
1671                    cmd.jdoClass.getContext());
1672        }
1673    }
1674
1675    private void fillTablePkConstraintName(ClassMetaData cmd, JdbcTable table) {
1676        table.pkConstraintName = getClassInfo(cmd).pkConstraintName;
1677        if (table.pkConstraintName == null) {
1678            table.pkConstraintName = nameGenerator.generatePkConstraintName(
1679                    table.name);
1680        } else {
1681            try {
1682                nameGenerator.addPkConstraintName(table.name, table.pkConstraintName);
1683            } catch (IllegalArgumentException JavaDoc e) {
1684                throw BindingSupportImpl.getInstance().runtime("Invalid jdbc-pk-constraint-name: " +
1685                        e.getMessage() + "\n" + cmd.jdoClass.getContext(), e);
1686            }
1687        }
1688    }
1689
1690    /**
1691     * Create meta data for cmd and all of its subclasses and figure out
1692     * everything that does not require access to JdbcClass objects other
1693     * than its superclasses as they may not exist yet.
1694     */

1695    private void createJdbcClass(ClassMetaData cmd, boolean quiet) {
1696        ClassInfo info = new ClassInfo();
1697        info.cmd = cmd;
1698        classInfoMap.put(cmd, info);
1699        JdbcClass jdbcClass = new JdbcClass(sqlDriver);
1700        cmd.storeClass = jdbcClass;
1701        jdbcClass.cmd = cmd;
1702        JdoClass jdoClass = cmd.jdoClass;
1703
1704        // fill in defaults that may be overwritten by extensions
1705
jdbcClass.optimisticLocking = jdbcConfig.jdbcOptimisticLocking;
1706        info.optimisticLockingExt = null;
1707
1708        jdbcClass.useJoin = JdbcRefField.USE_JOIN_OUTER;
1709        jdbcClass.doNotCreateTable = jdbcConfig.jdbcDoNotCreateTable;
1710
1711        // default the descriminator value
1712
switch (jdbcConfig.defaultClassId) {
1713            case JdbcConfig.DEFAULT_CLASS_ID_FULLNAME:
1714                jdbcClass.jdbcClassId = cmd.qname;
1715                break;
1716            case JdbcConfig.DEFAULT_CLASS_ID_NAME:
1717                jdbcClass.jdbcClassId = cmd.jdoClass.name;
1718                break;
1719            default:
1720                jdbcClass.jdbcClassId = cmd.classIdString;
1721        }
1722
1723        if (cmd.pcSuperMetaData != null) {
1724            if (getClassInfo(cmd.top).noClassIdCol) {
1725                jdbcClass.inheritance = JdbcClass.INHERITANCE_VERTICAL;
1726            } else {
1727                jdbcClass.inheritance = jdbcConfig.inheritance;
1728            }
1729        } else {
1730            jdbcClass.inheritance = jdbcConfig.inheritance;
1731            info.noClassIdCol = jdbcConfig.defaultClassId == JdbcConfig.DEFAULT_CLASS_ID_NO;
1732        }
1733
1734        if (cmd.identityType == MDStatics.IDENTITY_TYPE_DATASTORE) {
1735            info.keyGenFactory = keyGenRegistry.getFactory(
1736                    jdbcConfig.jdbcKeyGenerator);
1737            info.keyGenFactoryArgs = info.keyGenFactory.createArgsBean();
1738            BeanUtils.setProperties(info.keyGenFactoryArgs,
1739                    jdbcConfig.jdbcKeyGeneratorProps);
1740        }
1741
1742        // fill in whatever we can from extensions, fields etc
1743
JdoElement[] elements = jdoClass.elements;
1744        int n = elements.length;
1745        ArrayList jdbcElements = info.elements = new ArrayList(n);
1746        for (int i = 0; i < n; i++) {
1747            JdoElement o = elements[i];
1748            if (o instanceof JdoExtension) {
1749                try {
1750                    processClassExtensionPass1(cmd, (JdoExtension)o,
1751                            jdbcElements, quiet);
1752                } catch (RuntimeException JavaDoc e) {
1753                    cmd.addError(e, quiet);
1754                }
1755            } else { // must be a JdoField
1756
JdoField jdoField = (JdoField)o;
1757                FieldMetaData fmd = cmd.getFieldMetaData(jdoField.name);
1758                if (fmd == null) continue; // must be PERSISTENT_NONE
1759

1760                if (fmd.isEmbeddedRef()) {
1761                    continue;
1762                }
1763
1764                JdbcField f = null;
1765                f = createJdbcField(fmd, quiet);
1766                if (f != null) {
1767                    f.fmd = fmd;
1768                    fmd.storeField = f;
1769                    fmd.primaryField = true;
1770                    jdbcElements.add(f);
1771                }
1772            }
1773        }
1774
1775        // create JdbcField's for all persistent fields not yet found
1776
FieldMetaData[] fields = cmd.fields;
1777        n = fields.length;
1778        for (int i = 0; i < n; i++) {
1779            FieldMetaData fmd = fields[i];
1780            if (fmd.storeField != null || fmd.isEmbeddedRef()) continue;
1781            JdbcField f = createJdbcField(fmd, quiet);
1782            if (f != null) {
1783                f.fmd = fmd;
1784                fmd.storeField = f;
1785                fmd.primaryField = true;
1786                jdbcElements.add(f);
1787            }
1788        }
1789
1790        cmd.changedOptimisticLocking =
1791            jdbcClass.optimisticLocking == JdbcClass.OPTIMISTIC_LOCKING_CHANGED;
1792
1793        // process subclasses
1794
if (cmd.pcSubclasses != null) {
1795            for (int i = cmd.pcSubclasses.length - 1; i >= 0; i--) {
1796                createJdbcClass(cmd.pcSubclasses[i], quiet);
1797            }
1798        }
1799    }
1800
1801    /**
1802     * Create the classIdCol and initialise the jdbcClassId to the correct type.
1803     * This is called on the least derived class in the hierarchy and then
1804     * recursively called on subs.
1805     */

1806    private void processClassIdCol(ClassMetaData cmd, boolean quiet,
1807            HashMap classIdMap) {
1808        ClassInfo info = getClassInfo(cmd);
1809        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
1810
1811        if (cmd.pcSuperMetaData == null) {
1812            if (!info.noClassIdCol && cmd.pcSubclasses != null) {
1813                // topmost class so we need to create the column
1814
boolean intClassId = jdbcClass.isIntJdbcClassIdHeirachy();
1815                Class JavaDoc javaType = intClassId ? (Class JavaDoc)Integer.TYPE : (Class JavaDoc)String JavaDoc.class;
1816
1817                if (info.classIdExt != null && info.classIdExt.nested != null) {
1818                    // if there is a jdbc-class-id extension with nested extensions
1819
// then use these to create the column
1820
JdbcColumn col = jdbcClass.classIdCol = createColumn(info.classIdExt.nested, CLASS_ID_FIELDNAME,
1821                            javaType);
1822                    if (col.pk) {
1823                        throw BindingSupportImpl.getInstance().runtime("The jdbc-primary-key option is " +
1824                                "not allowed for a jdbc-class-id column\n" +
1825                                info.classIdExt.getContext());
1826                    }
1827                    info.elements.add(col);
1828                } else {
1829                    // no extensions so just create a suitable column
1830
jdbcClass.classIdCol = createColumn(null,
1831                            CLASS_ID_FIELDNAME, javaType);
1832                    info.elements.add(jdbcClass.classIdCol);
1833                }
1834
1835                // convert heirachy jdbcClassId to Integer if needed
1836
if (intClassId) jdbcClass.convertJdbcClassIdToInteger();
1837            } else {
1838                jdbcClass.jdbcClassId = null;
1839            }
1840        } else { // not the topmost class
1841
if (getClassInfo(cmd.top).noClassIdCol) {
1842                if (jdbcClass.inheritance != JdbcClass.INHERITANCE_VERTICAL) {
1843                    // Flat inheritance and no descriminator is allowed if
1844
// there is no fanout in the heirachy (i.e. each class
1845
// has 0 or 1 subclasses). Only instances of the leaf
1846
// class may be persisted and only instances of the leaf
1847
// class will be returned from the database.
1848
if (cmd.pcSuperMetaData.pcSubclasses.length > 1) {
1849                        throw BindingSupportImpl.getInstance().invalidOperation(
1850                                "Class " + cmd.qname +
1851                                " must use vertical inheritance as\n" +
1852                                "it has siblings and the base class " +
1853                                "does not have a descriminator (jdo_class) column\n" +
1854                                cmd.jdoClass.getContext());
1855                    }
1856                    // only leaf class instances allowed
1857
cmd.pcSuperMetaData.instancesNotAllowed = true;
1858                    // read superclass(es) as us instead
1859
for (ClassMetaData i = cmd.pcSuperMetaData; i != null;
1860                            i = i.pcSuperMetaData) {
1861                        ((JdbcClass)i.storeClass).readAsClass = cmd;
1862                    }
1863                }
1864                jdbcClass.jdbcClassId = null;
1865            }
1866
1867            // subclass so fill in the classIdCol from the topmost class
1868
jdbcClass.classIdCol = ((JdbcClass)cmd.top.storeClass).classIdCol;
1869        }
1870
1871        // check for duplicate descriminator values in the heirachy
1872
if (jdbcClass.jdbcClassId != null) {
1873            ClassMetaData other =
1874                    (ClassMetaData)classIdMap.get(jdbcClass.jdbcClassId);
1875            if (other != null) {
1876                throw BindingSupportImpl.getInstance().invalidOperation("Class " + cmd.qname +
1877                        " has same jdbc-class-id as " + other.qname + ": '" +
1878                        jdbcClass.jdbcClassId + "'\n" +
1879                        cmd.jdoClass.getContext());
1880            }
1881            classIdMap.put(jdbcClass.jdbcClassId, cmd);
1882        }
1883
1884        // process subclasses
1885
if (cmd.pcSubclasses != null) {
1886            ClassMetaData[] subs = cmd.pcSubclasses;
1887            for (int i = 0; i < subs.length; i++) {
1888                processClassIdCol(subs[i], quiet, classIdMap);
1889            }
1890        }
1891    }
1892
1893    /**
1894     * Create a new JdbcField from a JdoField and its extensions. Returns
1895     * null if the JdoField is transactional i.e. we dont give a flying
1896     * banana as we do not store it!
1897     */

1898    private JdbcField createJdbcField(FieldMetaData fmd,
1899            boolean quiet) {
1900        try {
1901            switch (fmd.category) {
1902                case MDStatics.CATEGORY_TRANSACTIONAL:
1903                    return null;
1904                case MDStatics.CATEGORY_SIMPLE:
1905                    return createJdbcSimpleField(fmd);
1906                case MDStatics.CATEGORY_REF:
1907                    return new JdbcRefField();
1908                case MDStatics.CATEGORY_POLYREF:
1909                    return new JdbcPolyRefField();
1910                case MDStatics.CATEGORY_COLLECTION:
1911                    return createJdbcCollectionField(fmd);
1912                case MDStatics.CATEGORY_ARRAY:
1913                    return createFieldForArray(fmd);
1914                case MDStatics.CATEGORY_MAP:
1915                    return new JdbcMapField();
1916                case MDStatics.CATEGORY_EXTERNALIZED:
1917                    return createJdbcSerializedField(fmd);
1918            }
1919            throw BindingSupportImpl.getInstance().internal("Field " + fmd.name + " of " +
1920                    fmd.classMetaData.qname +
1921                    " has bad category: " +
1922                    MDStaticUtils.toCategoryString(fmd.category));
1923        } catch (RuntimeException JavaDoc e) {
1924            fmd.addError(e, quiet);
1925        }
1926        return null;
1927    }
1928
1929    private JdbcCollectionField createJdbcCollectionField(FieldMetaData fmd) {
1930        return createJdbcField(fmd,
1931                fmd.jdoCollection == null ? null : fmd.jdoCollection.extensions);
1932    }
1933
1934    private JdbcCollectionField createJdbcField(FieldMetaData fmd,
1935            JdoExtension[] exts) {
1936        if (exts != null) {
1937            // look for an extension indicating that this collection is stored
1938
// using a foreign key in the value class instead of a link table
1939
// or is part of a many-to-many relationship
1940
JdoExtension[] a = exts;
1941            int n = a == null ? 0 : a.length;
1942            boolean gotLink = false;
1943            JdoExtension ie = null;
1944            for (int i = 0; i < n; i++) {
1945                JdoExtension e = a[i];
1946                switch (e.key) {
1947                    case INVERSE:
1948                    case JDBC_LINK_FOREIGN_KEY:
1949                        if (ie != null || gotLink) {
1950                            throw BindingSupportImpl.getInstance().runtime("The " + (ie != null ? "inverse" : "jdbc-link-table") +
1951                                    " extension has already been specified\n" +
1952                                    e.getContext());
1953                        }
1954                        ie = e;
1955                        break;
1956                    case JDBC_LINK_TABLE:
1957                        if (ie != null || gotLink) {
1958                            throw BindingSupportImpl.getInstance().runtime("The " + (ie != null ? "inverse" : "jdbc-link-table") +
1959                                    " extension has already been specified\n" +
1960                                    e.getContext());
1961                        }
1962                        gotLink = true;
1963                        break;
1964                }
1965            }
1966            if (ie != null) {
1967                // see what type of field the inverse is to see if this is
1968
// is a many-to-many or one-to-many
1969
ClassMetaData ecmd = fmd.elementTypeMetaData;
1970                if (ecmd == null) {
1971                    throw BindingSupportImpl.getInstance().runtime("The inverse extension may only be used for " +
1972                            "collections of PC instances\n" + ie.getContext());
1973                }
1974                String JavaDoc fname = ie.getString();
1975                FieldMetaData f = ecmd.getFieldMetaData(fname);
1976                if (f == null) {
1977                    if (fname != null && (fname.equals("") || fname.equals(
1978                            FieldMetaData.NO_FIELD_TEXT))) {
1979                        return new JdbcFKCollectionField();
1980                    } else {
1981                        throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' not found on " +
1982                                fmd.elementType + "\n" + ie.getContext());
1983                    }
1984                }
1985                switch (f.category) {
1986                    case MDStatics.CATEGORY_REF:
1987                        return new JdbcFKCollectionField();
1988                    case MDStatics.CATEGORY_ARRAY:
1989                    case MDStatics.CATEGORY_COLLECTION:
1990                        return new JdbcLinkCollectionField();
1991                }
1992                throw BindingSupportImpl.getInstance().runtime("Field '" + fname + "' is not a reference, collection or array\n" +
1993                        ie.getContext());
1994            }
1995        }
1996        return new JdbcLinkCollectionField();
1997    }
1998
1999    private JdbcSimpleField createJdbcSimpleField(FieldMetaData fmd) {
2000        JdbcSimpleField f = new JdbcSimpleField();
2001        JdoField jdoField = fmd.jdoField;
2002        JdoExtension[] a = jdoField == null ? null : jdoField.extensions;
2003        int n = a == null ? 0 : a.length;
2004        for (int i = 0; i < n; i++) {
2005            JdoExtension e = a[i];
2006            switch (e.key) {
2007                case JDBC_COLUMN:
2008                    // handled when column is created
2009
break;
2010                default:
2011                    if (e.isJdbc()) MetaDataBuilder.throwUnexpectedExtension(e);
2012            }
2013        }
2014        f.col = createColumn(a, fmd.name, fmd.type);
2015        f.col.comment = fmd.getCommentName();
2016        if ((f.col.pk = fmd.primaryKey)
2017                || fmd.nullValue == MDStatics.NULL_VALUE_EXCEPTION) {
2018            f.col.nulls = false;
2019        } else if (fmd.nullValue == MDStatics.NULL_VALUE_NONE) {
2020            f.col.nulls = true;
2021        }
2022        if (fmd.embeddedFakeField) {
2023            f.col.nulls = true;
2024        }
2025        f.includeForChangedLocking = f.col.equalityTest;
2026        return f;
2027    }
2028
2029    private JdbcSimpleField createJdbcSerializedField(FieldMetaData fmd) {
2030        JdbcSimpleField f = new JdbcSimpleField();
2031        JdoField jdoField = fmd.jdoField;
2032        JdoExtension[] a = jdoField == null ? null : jdoField.extensions;
2033        int n = a == null ? 0 : a.length;
2034        for (int i = 0; i < n; i++) {
2035            JdoExtension e = a[i];
2036            switch (e.key) {
2037                case JDBC_COLUMN:
2038                    // handled when column is created
2039
break;
2040                default:
2041                    if (e.isJdbc()) MetaDataBuilder.throwUnexpectedExtension(e);
2042            }
2043        }
2044        f.col = createColumn(a, fmd.name,
2045                fmd.externalizer.getExternalType());
2046        f.col.comment = fmd.getCommentName();
2047        if (fmd.nullValue == MDStatics.NULL_VALUE_EXCEPTION) {
2048            f.col.nulls = false;
2049        } else if (fmd.nullValue == MDStatics.NULL_VALUE_NONE) {
2050            f.col.nulls = true;
2051        }
2052        f.includeForChangedLocking = f.col.equalityTest;
2053        return f;
2054    }
2055
2056    /**
2057     * Create a JdbcField for an array[]. This will create a JdbcSimpleField
2058     * if the array is embedded or a JdbcLinkCollectionField if not.
2059     */

2060    private JdbcField createFieldForArray(FieldMetaData fmd) {
2061        if (fmd.embedded) {
2062            return createJdbcSimpleField(fmd);
2063        } else {
2064            return createJdbcField(fmd,
2065                    fmd.jdoArray == null ? null : fmd.jdoArray.extensions);
2066        }
2067    }
2068
2069    /**
2070     * Process a pass 1 extension for a class. This ignores jdbc-datastore
2071     * extensions as these should have already been processed. Some
2072     * extensions are skipped to be processed later.
2073     *
2074     * @param jdbcElements These are the JdbcColumn's and JdbcField's that
2075     * have been created so far for the class
2076     */

2077    private void processClassExtensionPass1(ClassMetaData cmd, JdoExtension e,
2078            ArrayList jdbcElements, boolean quiet) {
2079        JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
2080        ClassInfo info;
2081        switch (e.key) {
2082            case DATASTORE:
2083                // do nothing as this should have already been processed
2084
break;
2085            case JDBC_INHERITANCE:
2086// if (cmd.pcSuperMetaData == null) {
2087
// RuntimeException x = BindingSupportImpl.getInstance().runtime("Class " +
2088
// cmd.qname + " does not have a " +
2089
// "persistence-capable-superclass\n" +
2090
// e.getContext());
2091
// cmd.addError(x, quiet);
2092
// }
2093
try {
2094                    if (e.value != null) {
2095                        jdbcClass.inheritance = e.getEnum(jdbcMDE.INHERITANCE_ENUM);
2096                    }
2097                    getClassInfo(cmd).inheritance = e;
2098                } catch (RuntimeException JavaDoc x) {
2099                    cmd.addError(x, quiet);
2100                }
2101                break;
2102            case JDBC_TABLE_NAME:
2103                if (jdbcClass.tableName != null) {
2104                    RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("Class " +
2105                            cmd.qname + " already has a jdbc-table-name: " +
2106                            jdbcClass.tableName + "\n" +
2107                            e.getContext());
2108                    cmd.addError(x, quiet);
2109                }
2110                jdbcClass.tableName = e.getString();
2111                break;
2112            case JDBC_KEY_GENERATOR:
2113                if (cmd.pcSuperMetaData != null) {
2114                    RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("The jdbc-key-generator extension is only allowed for " +
2115                            "the least derived class in a heirachy\n" +
2116                            e.getContext());
2117                    cmd.addError(x, quiet);
2118                }
2119                info = getClassInfo(cmd);
2120                if (e.value != null) {
2121                    JdbcKeyGeneratorFactory f = findKeyGenFactory(e);
2122                    if (info.keyGenFactory != f) {
2123                        info.keyGenFactory = f;
2124                        info.keyGenFactoryArgs = f.createArgsBean();
2125                    }
2126                } else if (info.keyGenFactory == null) {
2127                    RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("The jdbc-key-generator extension must specify a factory " +
2128                            "for application identity classes\n" +
2129                            e.getContext());
2130                    cmd.addError(x, quiet);
2131                }
2132                BeanUtils.setProperties(info.keyGenFactoryArgs,
2133                        e.getPropertyMap());
2134                break;
2135            case JDBC_INDEX:
2136                try {
2137                    info = getClassInfo(cmd);
2138                    info.indexExts.add(e);
2139                } catch (RuntimeException JavaDoc x) {
2140                    cmd.addError(x, quiet);
2141                }
2142                break;
2143            case JDBC_OPTIMISTIC_LOCKING:
2144                try {
2145                    processClassOptimisticLocking(e, jdbcClass, cmd, quiet);
2146                } catch (RuntimeException JavaDoc x) {
2147                    cmd.addError(x, quiet);
2148                }
2149                break;
2150            case JDBC_CLASS_ID:
2151                try {
2152                    processJdbcClassIdExtension(cmd, e, jdbcClass);
2153                } catch (RuntimeException JavaDoc x) {
2154                    cmd.addError(x, quiet);
2155                }
2156                break;
2157            case JDBC_COLUMN:
2158                try {
2159                    processClassColumnExtension(e, cmd,
2160                            jdbcElements);
2161                } catch (RuntimeException JavaDoc x) {
2162                    cmd.addError(x, quiet);
2163                }
2164                break;
2165            case JDBC_PRIMARY_KEY:
2166                try {
2167                    processClassPrimaryKeyExtension(e, cmd,
2168                            jdbcElements);
2169                } catch (RuntimeException JavaDoc x) {
2170                    cmd.addError(x, quiet);
2171                }
2172                break;
2173            case JDBC_PK_FK_CONSTRAINT_NAME:
2174                info = getClassInfo(cmd);
2175                if (info.pkFkConstraintName != null) {
2176                    RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("The jdbc-pk-fk-constraint extension may only appear once\n" +
2177                            e.getContext());
2178                    cmd.addError(x, quiet);
2179                }
2180                info.pkFkConstraintName = e.getString();
2181                break;
2182            case JDBC_USE_JOIN:
2183                try {
2184                    jdbcClass.useJoin = e.getEnum(jdbcMDE.USE_JOIN_ENUM);
2185                } catch (RuntimeException JavaDoc x) {
2186                    cmd.addError(x, quiet);
2187                }
2188                break;
2189            case JDBC_DO_NOT_CREATE_TABLE:
2190                try {
2191                    jdbcClass.doNotCreateTable = e.getBoolean();
2192                } catch (RuntimeException JavaDoc x) {
2193                    cmd.addError(x, quiet);
2194                }
2195                break;
2196            default:
2197                if (e.isJdbc()) MetaDataBuilder.throwUnexpectedExtension(e);
2198        }
2199    }
2200
2201    private void processJdbcClassIdExtension(ClassMetaData cmd, JdoExtension e,
2202            JdbcClass jdbcClass) {
2203        ClassInfo info;
2204        info = getClassInfo(cmd);
2205        if (info.classIdExt != null) {
2206            throw BindingSupportImpl.getInstance().invalidOperation("The jdbc-class-id extension may " +
2207                    "only be specified once\n" + e.getContext());
2208        }
2209        info.classIdExt = e;
2210        if (e.value != null) {
2211            info.noClassIdCol = false;
2212            if (e.isNoValue()) {
2213                if (cmd.pcSuperMetaData != null) {
2214                    throw BindingSupportImpl.getInstance().invalidOperation(JdoExtension.NO_VALUE + " is only valid " +
2215                            "for the least derived class in the " +
2216                            "heircachy:\n" + e.getContext());
2217                }
2218                info.noClassIdCol = true;
2219            } else if (JdoExtension.NAME_VALUE.equals(e.value)) {
2220                jdbcClass.jdbcClassId = cmd.jdoClass.name;
2221            } else if (JdoExtension.FULLNAME_VALUE.equals(e.value)) {
2222                jdbcClass.jdbcClassId = cmd.qname;
2223            } else {
2224                jdbcClass.jdbcClassId = e.value;
2225            }
2226        }
2227        if (e.nested != null) {
2228            if (cmd.pcSuperMetaData != null) {
2229                throw BindingSupportImpl.getInstance().runtime("The jdbc-class-id extension may " +
2230                        "only define a column for the least derived class in the " +
2231                        "heirachy\n" + e.getContext());
2232            }
2233        }
2234    }
2235
2236    private void processClassOptimisticLocking(JdoExtension e,
2237            JdbcClass jdbcClass, ClassMetaData cmd, boolean quiet) {
2238        if (cmd.pcSuperMetaData != null) {
2239            RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("The jdbc-optimistic-locking " +
2240                    "option may only be specified for the least derived class " +
2241                    "in a heirachy\n" + e.getContext());
2242            cmd.addError(x, quiet);
2243        }
2244        if (e.value != null) {
2245            try {
2246                jdbcClass.optimisticLocking = e.getEnum(
2247                        jdbcMDE.OPTIMISTIC_LOCKING_ENUM);
2248            } catch (RuntimeException JavaDoc x) {
2249                cmd.addError(x, quiet);
2250            }
2251        }
2252        getClassInfo(cmd).optimisticLockingExt = e;
2253    }
2254
2255    private void processClassColumnExtension(JdoExtension e, ClassMetaData cmd,
2256            ArrayList jdbcElements) {
2257        JdbcColumn col = createColumn(e.nested,
2258                CLASS_ID_FIELDNAME, Integer.TYPE);
2259        JdbcSimpleField f = new JdbcSimpleField();
2260        f.fake = true;
2261        f.col = col;
2262        FieldMetaData fmd = f.fmd = new FieldMetaData();
2263        fmd.fake = true;
2264        fmd.category = MDStatics.CATEGORY_SIMPLE;
2265        fmd.classMetaData = cmd;
2266        fmd.defaultFetchGroup = true;
2267        fmd.storeField = f;
2268        fmd.name = "jdo" + jdbcElements.size();
2269        fmd.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_PERSISTENT;
2270        fmd.setType(col.javaType);
2271        jdbcElements.add(f);
2272    }
2273
2274    private void processClassPrimaryKeyExtension(JdoExtension e,
2275            ClassMetaData cmd, ArrayList jdbcElements) {
2276        if (cmd.identityType != MDStatics.IDENTITY_TYPE_DATASTORE) {
2277            throw BindingSupportImpl.getInstance().runtime("jdbc-primary-key only allowed for classes " +
2278                    "with identity-type 'datastore'\n" +
2279                    e.getContext());
2280        }
2281
2282        ClassInfo info;
2283        JdoExtension[] a = e.nested;
2284        int n = a == null ? 0 : a.length;
2285        for (int i = 0; i < n; i++) {
2286            JdoExtension f = a[i];
2287            switch (f.key) {
2288                case JDBC_COLUMN:
2289                    // handled when column is created
2290
break;
2291                case JDBC_CONSTRAINT:
2292                    info = getClassInfo(cmd);
2293                    if (info.pkConstraintName != null) {
2294                        throw BindingSupportImpl.getInstance().runtime("The jdbc-constraint extension may only appear once\n" +
2295                                e.getContext());
2296                    }
2297                    info.pkConstraintName = e.getString();
2298                    break;
2299                default:
2300                    if (e.isJdbc()) MetaDataBuilder.throwUnexpectedExtension(e);
2301            }
2302        }
2303
2304        JdbcColumn col = createColumn(a,
2305                DATASTORE_PK_FIELDNAME, Integer.TYPE);
2306        col.pk = true;
2307        col.nulls = false;
2308        jdbcElements.add(col);
2309    }
2310
2311    /**
2312     * Find the factory specified in a jdbc-key-generator extension. If the
2313     * factory is new an instance is created and stored for future access.
2314     */

2315    private JdbcKeyGeneratorFactory findKeyGenFactory(JdoExtension e) {
2316        String JavaDoc fname = e.getString();
2317        try {
2318            return keyGenRegistry.getFactory(fname);
2319        } catch (Exception JavaDoc x) {
2320            throw BindingSupportImpl.getInstance().runtime(x.getMessage() +
2321                    "\n" + e.getContext(), x);
2322        }
2323    }
2324
2325    /**
2326     * Create a JdbcConverterFactory instance from an extension.
2327     */

2328    private JdbcConverterFactory createJdbcConverterFactory(JdoExtension e) {
2329        try {
2330            return converterRegistry.getFactory(e.getString());
2331        } catch (Exception JavaDoc x) {
2332            throw BindingSupportImpl.getInstance().runtime(
2333                    "Unable to create JdbcConverterFactory\n" + e.getContext(),
2334                    x);
2335        }
2336    }
2337
2338    /**
2339     * Create a column from an optional array of extensions, an optional
2340     * fieldName and a base column. The properties of the base column are
2341     * the defaults. If nested is not null then it is searched for a
2342     * jdbc-column extension with a value matching the database
2343     * property of the store. If none is found then a jdbc-column extension
2344     * with no value is used. If it contains no jdbc-column extensions then
2345     * it is ignored. If the jdbc-type has been set in the extension then
2346     * it is used to provide the defaults, not the column.
2347     */

2348    public JdbcColumn createColumn(JdoExtension[] nested, JdbcColumn base) {
2349
2350        // look for a jdbc-column extension matching our db or with no db
2351
nested = findMatchingJdbcColumn(nested);
2352
2353        // copy the base column and use the mapping to change properties
2354
JdbcColumn c = base.copy();
2355        if (nested != null) {
2356            JdbcJavaTypeMapping m = createFieldMapping(nested);
2357            mappingResolver.fillMappingForJdbcType(m);
2358            c.updateFrom(m, mappingResolver);
2359            updateColumnName(nested, c);
2360            updateConverter(nested, c);
2361        }
2362        return c;
2363    }
2364
2365    /**
2366     * Create a column from an optional array of extensions, an optional
2367     * fieldName and an optional javaType. If nested is not null then it is
2368     * searched for a jdbc-column extension with a value matching the database
2369     * property of the store. If none is found then a jdbc-column extension
2370     * with no value is used. If it contains no jdbc-column extensions then
2371     * it is ignored.
2372     */

2373    public JdbcColumn createColumn(JdoExtension[] nested,
2374            String JavaDoc fieldName, Class JavaDoc javaType) {
2375
2376        // look for a jdbc-column extension matching our db or with no db
2377
nested = findMatchingJdbcColumn(nested);
2378
2379        // create a fully resolved mapping and use this to make the column
2380
JdbcJavaTypeMapping m = createFieldMapping(nested);
2381        m = mappingResolver.resolveMapping(m, fieldName, javaType);
2382        JdbcColumn c = new JdbcColumn(m, mappingResolver);
2383        updateColumnName(nested, c);
2384        updateConverter(nested, c);
2385        c.comment = fieldName;
2386        return c;
2387    }
2388
2389    /**
2390     * Set the converter (if any).
2391     */

2392    private void updateConverter(JdoExtension[] nested,
2393            JdbcColumn c) {
2394        if (nested != null) {
2395            boolean done = false;
2396            int n = nested.length;
2397            for (int i = 0; i < n; i++) {
2398                JdoExtension e = nested[i];
2399                if (e.key == JDBC_CONVERTER) {
2400                    if (done) {
2401                        throw BindingSupportImpl.getInstance().runtime("jdbc-converter extension has already been used\n" +
2402                                e.getContext());
2403                    }
2404                    JdbcConverterFactory f = createJdbcConverterFactory(e);
2405                    HashMap p = e.getPropertyMap();
2406                    try {
2407                        c.converter = f.createJdbcConverter(c, p,
2408                                mappingResolver);
2409                    } catch (IllegalArgumentException JavaDoc x) {
2410                        throw BindingSupportImpl.getInstance().runtime("Unable to create JdbcConverter\n" +
2411                                e.getContext(), x);
2412                    }
2413                    done = true;
2414                }
2415            }
2416        }
2417    }
2418
2419    /**
2420     * Set the column name (if any).
2421     */

2422    private void updateColumnName(JdoExtension[] nested, JdbcColumn c) {
2423        if (nested != null) {
2424            int n = nested.length;
2425            for (int i = 0; i < n; i++) {
2426                JdoExtension e = nested[i];
2427                if (e.key != JDBC_COLUMN_NAME) continue;
2428                if (c.name != null) {
2429                    throw BindingSupportImpl.getInstance().runtime("Only one jdbc-column-name extension is allowed\n" +
2430                            e.getContext());
2431                }
2432                c.name = e.getString();
2433            }
2434        }
2435    }
2436
2437    /**
2438     * If nested is not null then it is searched for a jdbc-column extension
2439     * with a value matching the database property of the store. If none is
2440     * found then a jdbc-column extension with no value is used. If it
2441     * contains no jdbc-column extensions then it is ignored.
2442     */

2443    private JdoExtension[] findMatchingJdbcColumn(JdoExtension[] nested) {
2444        if (nested != null) {
2445            JdoExtension matchGeneral = null;
2446            JdoExtension matchSpecific = null;
2447            String JavaDoc sdb = sqlDriver.getName();
2448            int n = nested.length;
2449            for (int i = 0; i < n; i++) {
2450                JdoExtension e = nested[i];
2451                if (e.key != JDBC_COLUMN) continue;
2452                String JavaDoc db = e.value;
2453                if (db == null) {
2454                    if (matchGeneral != null) {
2455                        throw BindingSupportImpl.getInstance().runtime("Only one all databases jdbc-column extension is allowed\n" +
2456                                e.getContext());
2457                    }
2458                    matchGeneral = e;
2459                } else if (db.equals(sdb)) {
2460                    if (matchSpecific != null) {
2461                        throw BindingSupportImpl.getInstance().runtime("Only one jdbc-column extension is allowed per database\n" +
2462                                e.getContext());
2463                    }
2464                    matchSpecific = e;
2465                }
2466            }
2467            if (matchSpecific != null) {
2468                nested = matchSpecific.nested;
2469            } else if (matchGeneral != null) {
2470                nested = matchGeneral.nested;
2471            } else {
2472                nested = null;
2473            }
2474        }
2475        return nested;
2476    }
2477
2478    /**
2479     * Create a field mapping from an array of extensions.
2480     *
2481     * @param nested Nested extensions (may be null)
2482     */

2483    private JdbcJavaTypeMapping createFieldMapping(JdoExtension[] nested) {
2484        JdbcJavaTypeMapping m = new JdbcJavaTypeMapping();
2485        if (nested == null) return m;
2486        int n = nested.length;
2487        for (int i = 0; i < n; i++) {
2488            JdoExtension e = nested[i];
2489            switch (e.key) {
2490                case JDBC_COLUMN_NAME:
2491                    if (m.getColumnName() != null) {
2492                        throw BindingSupportImpl.getInstance().runtime("jdbc-column-name has already been set\n" +
2493                                e.getContext());
2494                    }
2495                    m.setColumnName(e.getString());
2496                    break;
2497                case JDBC_TYPE:
2498                    if (m.getJdbcType() != 0) {
2499                        throw BindingSupportImpl.getInstance().runtime("jdbc-type has already been set\n" +
2500                                e.getContext());
2501                    }
2502                    m.setJdbcType(getJdbcType(e));
2503                    break;
2504                case JDBC_SQL_TYPE:
2505                    if (m.getSqlType() != null) {
2506                        throw BindingSupportImpl.getInstance().runtime("jdbc-sql-type has already been set\n" +
2507                                e.getContext());
2508                    }
2509                    m.setSqlType(e.getString());
2510                    break;
2511                case JDBC_LENGTH:
2512                    if (m.getLength() >= 0) {
2513                        throw BindingSupportImpl.getInstance().runtime("jdbc-length has already been set\n" +
2514                                e.getContext());
2515                    }
2516                    m.setLength(e.getInt());
2517                    break;
2518                case JDBC_SCALE:
2519                    if (m.getScale() >= 0) {
2520                        throw BindingSupportImpl.getInstance().runtime("jdbc-scale has already been set\n" +
2521                                e.getContext());
2522                    }
2523                    m.setScale(e.getInt());
2524                    break;
2525                case JDBC_NULLS:
2526                    if (m.getNulls() != JdbcJavaTypeMapping.NOT_SET) {
2527                        throw BindingSupportImpl.getInstance().runtime("jdbc-nulls has already been set\n" +
2528                                e.getContext());
2529                    }
2530                    m.setNulls(e.getBoolean());
2531                    break;
2532                case JDBC_SHARED:
2533                    // ignore - shared columns are now figured out automatically
2534
break;
2535                case JDBC_JAVA_TYPE:
2536                    if (m.getJavaType() != null) {
2537                        throw BindingSupportImpl.getInstance().runtime("jdbc-java-type has already been set\n" +
2538                                e.getContext());
2539                    }
2540                    m.setJavaType(e.getType(loader));
2541                    break;
2542                case JDBC_CONVERTER:
2543                    // handled once the column has been created
2544
break;
2545                default:
2546                    if (e.isJdbc()) MetaDataBuilder.throwUnexpectedExtension(e);
2547                    break;
2548            }
2549        }
2550        return m;
2551    }
2552
2553    /**
2554     * Get the value of an extension that must be a JDBC type name.
2555     *
2556     * @see java.sql.Types
2557     */

2558    public int getJdbcType(JdoExtension e) {
2559        try {
2560            return JdbcTypes.parse(e.getString());
2561        } catch (IllegalArgumentException JavaDoc x) {
2562            throw BindingSupportImpl.getInstance().runtime(x.getMessage() +
2563                    "\n" + e.getContext());
2564        }
2565    }
2566
2567    public ModelMetaData getJmd() {
2568        return jmd;
2569    }
2570
2571    public SqlDriver getSqlDriver() {
2572        return sqlDriver;
2573    }
2574
2575    public JdbcMappingResolver getMappingResolver() {
2576        return mappingResolver;
2577    }
2578
2579    protected void setMasterDetailFlags(ClassMetaData[] classes) {
2580        if (Debug.DEBUG) {
2581            System.out.println(
2582                    "MDB-JDBC: Setting master detail flags");
2583        }
2584        int clen = classes.length;
2585        for (int i = 0; i < clen; i++) {
2586            ClassMetaData cmd = classes[i];
2587            if (cmd.stateFields == null) continue; // possible if prev error
2588
for (int j = 0; j < cmd.stateFields.length; j++) {
2589                FieldMetaData fmd = cmd.stateFields[j];
2590                switch (fmd.category) {
2591                    case MDStatics.CATEGORY_REF:
2592                        JdbcField jdbcField = (JdbcField)fmd.storeField;
2593                        if (jdbcField != null) { // possible if prev error
2594
JdbcFKCollectionField masterField =
2595                                    ((JdbcRefField)jdbcField).masterCollectionField;
2596                            if (masterField != null) {
2597                                fmd.inverseFieldMetaData = masterField.fmd;
2598                                fmd.isDetail = true;
2599                                fmd.managed = masterField.fmd.managed;
2600                            }
2601                        }
2602                        break;
2603                    case MDStatics.CATEGORY_COLLECTION:
2604                        if (fmd.storeField instanceof JdbcFKCollectionField) {
2605                            fmd.inverseFieldMetaData = ((JdbcFKCollectionField)fmd.storeField).fkField.fmd;
2606                            fmd.isMaster = true;
2607                        }
2608                        break;
2609                }
2610            }
2611        }
2612    }
2613
2614    protected void calcSuperCounts(ClassMetaData[] classes) {
2615        super.calcSuperCounts(classes);
2616        // find all the references that are being used to complete
2617
// collections mapped using a foreign key
2618
for (int j = 0; j < classes.length; j++) {
2619            ClassMetaData cmd = classes[j];
2620            if (cmd == null || cmd.stateFields == null) {
2621                continue;
2622            }
2623            int[] a = new int[cmd.stateFields.length];
2624            int c = 0;
2625            for (int i = cmd.stateFields.length - 1; i >= 0; i--) {
2626                FieldMetaData f = cmd.stateFields[i];
2627                if (f.storeField instanceof JdbcRefField) {
2628                    JdbcRefField rf = (JdbcRefField)f.storeField;
2629                    if (rf.masterCollectionField != null) {
2630                        a[c++] = i;
2631                    }
2632                }
2633            }
2634            if (c > 0) {
2635                int[] b = new int[c];
2636                System.arraycopy(a, 0, b, 0, c);
2637                cmd.fkCollectionRefStateFieldNos = b;
2638            } else {
2639                cmd.fkCollectionRefStateFieldNos = null;
2640            }
2641        }
2642    }
2643
2644}
2645
Popular Tags