0001: /*
0002: * Copyright (c) 1998 - 2005 Versant Corporation
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * Versant Corporation - initial API and implementation
0010: */
0011: package com.versant.core.jdbc;
0012:
0013: import com.versant.core.common.Debug;
0014: import com.versant.core.metadata.*;
0015: import com.versant.core.metadata.parser.*;
0016: import com.versant.core.jdbc.metadata.*;
0017: import com.versant.core.jdbc.sql.AutoIncJdbcKeyGenerator;
0018: import com.versant.core.jdbc.sql.HighLowJdbcKeyGenerator;
0019: import com.versant.core.jdbc.sql.JdbcNameGenerator;
0020: import com.versant.core.jdbc.sql.SqlDriver;
0021: import com.versant.core.util.BeanUtils;
0022: import com.versant.core.util.StringListParser;
0023:
0024: import java.sql.Types;
0025: import java.util.*;
0026:
0027: import com.versant.core.common.BindingSupportImpl;
0028: import com.versant.core.common.config.ConfigInfo;
0029:
0030: /**
0031: * This builds the meta data for the JdbcStorageManager.
0032: */
0033: public class JdbcMetaDataBuilder extends MetaDataBuilder implements
0034: JdoExtensionKeys {
0035:
0036: private final JdbcConfig jdbcConfig;
0037: private final SqlDriver sqlDriver;
0038:
0039: private JdbcNameGenerator nameGenerator;
0040: private JdbcMappingResolver mappingResolver;
0041:
0042: public final MetaDataEnums MDE = new MetaDataEnums();
0043: public final JdbcMetaDataEnums jdbcMDE = new JdbcMetaDataEnums();
0044:
0045: private JdbcKeyGeneratorFactoryRegistry keyGenRegistry;
0046: private JdbcConverterFactoryRegistry converterRegistry;
0047: private JdbcClassReferenceGraph jdbcClassReferenceGraph;
0048:
0049: /**
0050: * Maps ClassMetaData to ClassInfo.
0051: */
0052: private Map classInfoMap = new HashMap();
0053:
0054: // these are the "fieldnames" used when resolving mappings for these cols
0055: public static final String DATASTORE_PK_FIELDNAME = "<pk>";
0056: public static final String CLASS_ID_FIELDNAME = "<class-id>";
0057: public static final String OPT_LOCK_FIELDNAME = "<opt-lock>";
0058: public static final String OWNER_REF_FIELDNAME = "<owner>";
0059: public static final String SEQUENCE_FIELDNAME = "<sequence>";
0060: public static final String VALUE_FIELDNAME = "<value>";
0061: public static final String KEY_FIELDNAME = "<key>";
0062:
0063: // these are the names of the built in key generators
0064: public static final String KEYGEN_HIGHLOW = "HIGHLOW";
0065: public static final String KEYGEN_AUTOINC = "AUTOINC";
0066:
0067: /**
0068: * These hold tempory info we need to track for a class during meta data
0069: * generation. Each class for our store is associated with one of these
0070: * through the classInfoMap.
0071: */
0072: public static class ClassInfo {
0073:
0074: public ClassMetaData cmd;
0075:
0076: public ArrayList elements;
0077: public JdbcKeyGeneratorFactory keyGenFactory;
0078: public Object keyGenFactoryArgs;
0079: public String pkConstraintName;
0080: public JdoExtension optimisticLockingExt;
0081: public String pkFkConstraintName;
0082: public JdbcConstraint pkFkConstraint;
0083: public ArrayList indexExts = new ArrayList();
0084: public ArrayList autoIndexes = new ArrayList(); // of JdbcIndex
0085: public JdoExtension inheritance;
0086: public JdoExtension classIdExt;
0087: public boolean noClassIdCol;
0088: private Set createdAfterClient;
0089:
0090: public Set getCreatedAfterClient() {
0091: if (createdAfterClient == null) {
0092: createdAfterClient = new HashSet();
0093: }
0094: return createdAfterClient;
0095: }
0096: }
0097:
0098: /**
0099: * The jdbcDriver parameter may be null if this is not available.
0100: */
0101: public JdbcMetaDataBuilder(ConfigInfo config,
0102: JdbcConfig jdbcConfig, ClassLoader loader,
0103: SqlDriver sqlDriver, boolean quiet) {
0104: super (config, loader, quiet);
0105: this .jdbcConfig = jdbcConfig;
0106: this .sqlDriver = sqlDriver;
0107: }
0108:
0109: protected FetchGroupBuilder createFetchGroupBuilder() {
0110: return new JdbcFetchGroupBuilder(jmd);
0111: }
0112:
0113: public boolean isCdRefsInDefaultFetchGroup() {
0114: return jdbcConfig.oidsInDefaultFetchGroup;
0115: }
0116:
0117: public int getCdCacheStrategy() {
0118: return jdbcConfig.cacheStrategy;
0119: }
0120:
0121: public ModelMetaData buildMetaData(JdoRoot[] roots) {
0122: keyGenRegistry = new JdbcKeyGeneratorFactoryRegistry(loader);
0123: keyGenRegistry.add(KEYGEN_HIGHLOW, keyGenRegistry
0124: .getFactory(HighLowJdbcKeyGenerator.Factory.class
0125: .getName()));
0126: keyGenRegistry.add(KEYGEN_AUTOINC, keyGenRegistry
0127: .getFactory(AutoIncJdbcKeyGenerator.Factory.class
0128: .getName()));
0129: converterRegistry = new JdbcConverterFactoryRegistry(loader);
0130:
0131: mappingResolver = new JdbcMappingResolver();
0132: mappingResolver.init(sqlDriver, parseTypeMappings(),
0133: parseJavaTypeMappings());
0134: mappingResolver.registerStoreTypes(mdutils);
0135:
0136: if (jdbcConfig.jdbcKeyGenerator == null) {
0137: jdbcConfig.jdbcKeyGenerator = JdbcMetaDataBuilder.KEYGEN_HIGHLOW;
0138: }
0139: if (jdbcConfig.jdbcKeyGeneratorProps == null) {
0140:
0141: jdbcConfig.jdbcKeyGeneratorProps = Collections.EMPTY_MAP;
0142:
0143: }
0144:
0145: if (jdbcConfig.jdbcNameGenerator != null) {
0146: nameGenerator = (JdbcNameGenerator) BeanUtils.newInstance(
0147: jdbcConfig.jdbcNameGenerator, loader,
0148: JdbcNameGenerator.class);
0149: } else {
0150: nameGenerator = sqlDriver.createJdbcNameGenerator();
0151: }
0152: BeanUtils.setProperties(nameGenerator,
0153: jdbcConfig.jdbcNameGeneratorProps);
0154: jmd.jdbcMetaData = new JdbcMetaData(jmd, jdbcConfig);
0155:
0156: return super .buildMetaData(roots);
0157: }
0158:
0159: protected void doHorizontal(ClassMetaData[] ca) {
0160: //create all the fake field for horizontal instances
0161: for (int j = 0; j < ca.length; j++) {
0162: ClassMetaData cmd = ca[j];
0163: try {
0164: createHorizontalFieldMetaData(cmd, quiet);
0165: } catch (RuntimeException e) {
0166: cmd.addError(e, quiet);
0167: }
0168: }
0169: }
0170:
0171: protected void doEmbedded(ClassMetaData[] ca) {
0172: //create all the fake field for embedded instances
0173: for (int j = 0; j < ca.length; j++) {
0174: ClassMetaData cmd = ca[j];
0175: try {
0176: createEmbeddeFieldMetaData(cmd, quiet);
0177: } catch (RuntimeException e) {
0178: cmd.addError(e, quiet);
0179: }
0180: }
0181: }
0182:
0183: protected void checkForHorizontal(JdoClass jdoCls, ClassMetaData cmd) {
0184: if (jdoCls.getInheritance(jdbcMDE.INHERITANCE_ENUM) == JdbcClass.INHERITANCE_HORIZONTAL) {
0185: cmd.horizontal = true;
0186: }
0187: }
0188:
0189: public JdbcConfig getJdbcConfig() {
0190: return jdbcConfig;
0191: }
0192:
0193: public JdbcNameGenerator getNameGenerator() {
0194: return nameGenerator;
0195: }
0196:
0197: /**
0198: * Get the ClassInfo for cmd.
0199: */
0200: public ClassInfo getClassInfo(ClassMetaData cmd) {
0201: return (ClassInfo) classInfoMap.get(cmd);
0202: }
0203:
0204: /**
0205: * Get the elements field of the ClassInfo for cmd.
0206: */
0207: private ArrayList getClassElements(ClassMetaData cmd) {
0208: return getClassInfo(cmd).elements;
0209: }
0210:
0211: protected void preBuildFetchGroupsHook() {
0212: ClassMetaData[] classes = jmd.classes;
0213: int clen = classes.length;
0214:
0215: // find all classes that belong to us and do whatever we can without
0216: // having all the JdbcClass objects and tables yet
0217: if (Debug.DEBUG) {
0218: Debug.OUT
0219: .println("MDB-JDBC: Creating JdbcClass objects ... ");
0220: }
0221: for (int i = 0; i < clen; i++) {
0222: ClassMetaData cmd = classes[i];
0223: if (cmd.pcSuperMetaData != null) {
0224: continue;
0225: }
0226: try {
0227: createJdbcClass(cmd, quiet);
0228: } catch (RuntimeException e) {
0229: cmd.addError(e, quiet);
0230: }
0231: }
0232:
0233: // Make sure that all classes have tables and table names. This is
0234: // done starting at the base classes and recursively processing
0235: // subclasses.
0236: if (Debug.DEBUG) {
0237: Debug.OUT
0238: .println("MDB-JDBC: Creating JdbcTable objects ... ");
0239: }
0240: for (int i = 0; i < clen; i++) {
0241: ClassMetaData cmd = classes[i];
0242: try {
0243: processBaseClassTable(cmd);
0244: } catch (RuntimeException e) {
0245: cmd.addError(e, quiet);
0246: }
0247:
0248: }
0249:
0250: // find the primary keys (datastore or application) for all classes
0251: // and make sure the pk columns have names
0252: if (Debug.DEBUG) {
0253: Debug.OUT
0254: .println("MDB-JDBC: Finding and naming primary keys ... ");
0255: }
0256: for (int i = 0; i < clen; i++) {
0257: ClassMetaData cmd = classes[i];
0258: if (cmd.pcSuperMetaData != null)
0259: continue;
0260: try {
0261: processPrimaryKey(cmd, quiet);
0262: } catch (RuntimeException e) {
0263: cmd.addError(e, quiet);
0264: }
0265: }
0266:
0267: // create descriminator columns and figure out values
0268: if (Debug.DEBUG) {
0269: Debug.OUT
0270: .println("MDB-JDBC: Creating descriminator columns ... ");
0271: }
0272: for (int i = 0; i < clen; i++) {
0273: ClassMetaData cmd = classes[i];
0274: if (cmd.pcSuperMetaData != null) {
0275: continue;
0276: }
0277: try {
0278: processClassIdCol(cmd, quiet, new HashMap());
0279: } catch (RuntimeException e) {
0280: cmd.addError(e, quiet);
0281: }
0282: }
0283:
0284: // processing the persist after extensions
0285: for (int j = 0; j < clen; j++) {
0286: ClassMetaData cmd = classes[j];
0287: ClassInfo cInfo = getClassInfo(cmd.top);
0288: cInfo.getCreatedAfterClient().clear();
0289: JdoElement[] elements = cmd.jdoClass.elements;
0290: int n = elements.length;
0291: for (int i = 0; i < n; i++) {
0292: JdoElement o = elements[i];
0293: if (o instanceof JdoExtension) {
0294: JdoExtension e = (JdoExtension) o;
0295: if (e.key == PERSIST_AFTER) {
0296: if (e.nested == null)
0297: continue;
0298: for (int k = 0; k < e.nested.length; k++) {
0299: JdoExtension jdoExtension = e.nested[k];
0300: if (jdoExtension != null) {
0301: cInfo
0302: .getCreatedAfterClient()
0303: .add(
0304: jmd
0305: .getClassMetaData(jdoExtension.value).top);
0306: }
0307: }
0308: }
0309: }
0310: }
0311: }
0312:
0313: // calc maxPkSimpleColumns and set datastoreIdentityType
0314: if (Debug.DEBUG) {
0315: Debug.OUT
0316: .println("MDB-JDBC: Calculating maxPkSimpleColumns ... ");
0317: }
0318: int maxPkSimpleColumns = 0;
0319: for (int i = 0; i < clen; i++) {
0320: ClassMetaData cmd = classes[i];
0321: try {
0322: if (cmd.horizontal)
0323: continue;
0324: JdbcClass jdbcClass = ((JdbcClass) cmd.storeClass);
0325: int n = jdbcClass.table.pkSimpleColumnCount;
0326: if (n > maxPkSimpleColumns) {
0327: maxPkSimpleColumns = n;
0328: }
0329: cmd.datastoreIdentityTypeCode = jdbcClass.table.pk[0].javaTypeCode;
0330: cmd.datastoreIdentityType = jdbcClass.table.pk[0].javaType;
0331: } catch (RuntimeException e) {
0332: cmd.addError(e, quiet);
0333: }
0334: }
0335: ((JdbcMetaData) jmd.jdbcMetaData).maxPkSimpleColumns = maxPkSimpleColumns;
0336:
0337: // complete optimistic locking - this is done now so that use visible
0338: // fields can be used for rowversion and timestamp locking
0339: if (Debug.DEBUG) {
0340: Debug.OUT
0341: .println("MDB-JDBC: Completing optimistic locking ... ");
0342: }
0343: for (int i = 0; i < clen; i++) {
0344: ClassMetaData cmd = classes[i];
0345: if (cmd.pcSuperMetaData != null)
0346: continue;
0347: try {
0348: completeOptimisticLocking(cmd, quiet);
0349: } catch (RuntimeException e) {
0350: cmd.addError(e, quiet);
0351: }
0352: }
0353:
0354: // complete REF fields now that we have all the classes
0355: // and primary keys
0356: if (Debug.DEBUG) {
0357: Debug.OUT.println("MDB-JDBC: Creating REF fields ... ");
0358: }
0359: for (int i = 0; i < clen; i++) {
0360: ClassMetaData cmd = classes[i];
0361: try {
0362: processRefAndPolyRefFields(cmd, quiet);
0363: } catch (RuntimeException e) {
0364: cmd.addError(e, quiet);
0365: }
0366: }
0367:
0368: // complete COLLECTION fields now that we have all the classes
0369: // and primary keys and refs
0370: if (Debug.DEBUG) {
0371: Debug.OUT
0372: .println("MDB-JDBC: Creating COLLECTION fields ... ");
0373: }
0374: for (int i = 0; i < clen; i++) {
0375: ClassMetaData cmd = classes[i];
0376: try {
0377: if (cmd.horizontal)
0378: continue;
0379: processCollectionFields(cmd, quiet);
0380: } catch (RuntimeException e) {
0381: cmd.addError(e, quiet);
0382: }
0383: }
0384:
0385: // create the cols arrays on all class tables, name all field columns
0386: // without names and sanity check columns and add any extra fake
0387: // fields to the fields array on ClassMetaData
0388: if (Debug.DEBUG) {
0389: Debug.OUT
0390: .println("MDB-JDBC: Finalizing table column arrays and fake fields ... ");
0391: }
0392: for (int i = 0; i < clen; i++) {
0393: ClassMetaData cmd = classes[i];
0394: // if (cmd.horizontal) continue;
0395: if (cmd.pcSuperMetaData != null)
0396: continue;
0397: try {
0398: finalizeFakesAndTableColumns(cmd);
0399: } catch (RuntimeException e) {
0400: cmd.addError(e, quiet);
0401: }
0402: }
0403:
0404: jdbcClassReferenceGraph = new JdbcClassReferenceGraph(
0405: jmd.classes);
0406: jdbcClassReferenceGraph.sort();
0407:
0408: if (Debug.DEBUG) {
0409: Debug.OUT.println("MDB-JDBC: Creating constraints ... ");
0410: }
0411: for (int i = 0; i < clen; i++) {
0412: ClassMetaData cmd = classes[i];
0413: try {
0414: doConstraints(cmd);
0415: } catch (RuntimeException e) {
0416: cmd.addError(e, quiet);
0417: }
0418: }
0419:
0420: //release resources that was used for sorting
0421: jdbcClassReferenceGraph.releaseMem();
0422:
0423: // build the fields array on JdbcClass for each class
0424: if (Debug.DEBUG) {
0425: Debug.OUT
0426: .println("MDB-JDBC: Finalizing fields arrays ... ");
0427: }
0428: for (int i = 0; i < clen; i++) {
0429: ClassMetaData cmd = classes[i];
0430: FieldMetaData[] fa = cmd.fields;
0431: if (fa == null)
0432: continue;
0433: JdbcField[] a = ((JdbcClass) cmd.storeClass).fields = new JdbcField[fa.length];
0434: for (int j = fa.length - 1; j >= 0; j--) {
0435: FieldMetaData fmd = fa[j];
0436: fmd.fieldNo = j; // need to do this in case of fake fields
0437: JdbcField jdbcField = (JdbcField) fmd.storeField;
0438: a[j] = jdbcField;
0439: if (jdbcField != null)
0440: jdbcField.initMainTableCols();
0441: }
0442: }
0443:
0444: // figure out which columns should be marked as shared
0445: if (Debug.DEBUG) {
0446: Debug.OUT.println("MDB-JDBC: Choosing shared columns ... ");
0447: }
0448: SharedColumnChooser sharedColumnChooser = new SharedColumnChooser();
0449: for (int i = 0; i < clen; i++) {
0450: ClassMetaData cmd = classes[i];
0451: if (cmd.pcSuperMetaData != null)
0452: continue;
0453: if (cmd.horizontal)
0454: continue;
0455: try {
0456: sharedColumnChooser.chooseSharedColumns(cmd);
0457: } catch (RuntimeException e) {
0458: cmd.addError(e, quiet);
0459: }
0460: }
0461:
0462: // build the fields array on JdbcClass for each class
0463: if (Debug.DEBUG) {
0464: Debug.OUT
0465: .println("MDB-JDBC: Finalizing for update columns for fields ... ");
0466: }
0467: for (int i = 0; i < clen; i++) {
0468: ClassMetaData cmd = classes[i];
0469: JdbcField[] fields = ((JdbcClass) cmd.storeClass).fields;
0470: if (fields == null)
0471: continue;
0472: for (int j = fields.length - 1; j >= 0; j--) {
0473: JdbcField f = fields[j];
0474: if (f != null)
0475: f.initMainTableColsForUpdate();
0476: }
0477: }
0478:
0479: // copy optimistic locking settings down each heirachy
0480: if (Debug.DEBUG) {
0481: Debug.OUT
0482: .println("MDB-JDBC: Copying optimistic locking down heirachies ... ");
0483: }
0484: for (int i = 0; i < clen; i++) {
0485: ClassMetaData cmd = classes[i];
0486: ((JdbcClass) cmd.storeClass).copyOptimisticLockingToSubs();
0487: }
0488:
0489: // finalize constraints
0490: if (Debug.DEBUG) {
0491: Debug.OUT
0492: .println("MDB-JDBC: Finalizing table constraints ... ");
0493: }
0494: for (int i = 0; i < clen; i++) {
0495: ClassMetaData cmd = classes[i];
0496: try {
0497: finalizeConstraints(cmd);
0498: } catch (RuntimeException e) {
0499: cmd.addError(e, quiet);
0500: }
0501: }
0502: ArrayList tables = ((JdbcMetaData) jmd.jdbcMetaData)
0503: .getTables(true);
0504: int size = tables.size();
0505: for (int i = 0; i < size; i++) {
0506: JdbcTable jdbcTable = (JdbcTable) tables.get(i);
0507: jdbcTable.nameConstraints(nameGenerator);
0508: }
0509: for (int i = 0; i < clen; i++) {
0510: ClassMetaData cmd = classes[i];
0511: try {
0512: finalizeConstraints(cmd);
0513: } catch (RuntimeException e) {
0514: cmd.addError(e, quiet);
0515: }
0516: }
0517:
0518: // find index extensions and create them
0519: if (Debug.DEBUG) {
0520: Debug.OUT
0521: .println("MDB-JDBC: Processing index extensions ... ");
0522: }
0523: for (int i = 0; i < clen; i++) {
0524: ClassMetaData cmd = classes[i];
0525: if (cmd.pcSuperMetaData != null)
0526: continue;
0527: try {
0528: createMainTableIndexes(cmd, quiet);
0529: } catch (RuntimeException e) {
0530: cmd.addError(e, quiet);
0531: }
0532: }
0533:
0534: // make sure link table indexes are named
0535: if (Debug.DEBUG) {
0536: Debug.OUT
0537: .println("MDB-JDBC: Naming link table indexes ...");
0538: }
0539: for (int i = 0; i < clen; i++) {
0540: ClassMetaData cmd = classes[i];
0541: FieldMetaData[] fields = cmd.fields;
0542: if (fields == null)
0543: continue;
0544: for (int j = 0; j < fields.length; j++) {
0545: JdbcField jf = (JdbcField) fields[j].storeField;
0546: if (jf != null) {
0547: try {
0548: jf.nameLinkTableIndexes(nameGenerator);
0549: } catch (RuntimeException e) {
0550: cmd.addError(e, quiet);
0551: }
0552: }
0553: }
0554: }
0555:
0556: // create key generator instances
0557: if (Debug.DEBUG) {
0558: Debug.OUT.println("MDB-JDBC: Creating key generators ... ");
0559: }
0560: createKeyGenerators(quiet);
0561: }
0562:
0563: /**
0564: * Create main table indexes for cmd and recursively all of its subclasses.
0565: * This is a NOP if cmd is not a base class. This also make sure that all
0566: * of the indexes have names and gets rid of duplicate indexes (only
0567: * the first one is kept).
0568: */
0569: private void createMainTableIndexes(ClassMetaData cmd, boolean quiet) {
0570: if (cmd.pcSuperMetaData != null)
0571: return;
0572:
0573: ArrayList all = new ArrayList();
0574: collectIndexes(cmd, all, quiet);
0575: ArrayList auto = new ArrayList();
0576: collectAutoIndexes(cmd, auto);
0577:
0578: // get rid of auto indexes that have same columns as manual indexes
0579: // or other auto indexes
0580: HashSet s = new HashSet();
0581: s.addAll(all);
0582: for (Iterator i = auto.iterator(); i.hasNext();) {
0583: Object o = i.next();
0584: if (s.contains(o)) {
0585: i.remove();
0586: } else {
0587: s.add(o);
0588: }
0589: }
0590: all.addAll(auto);
0591:
0592: Map tablesToIndexs = new HashMap();
0593: for (int i = 0; i < all.size(); i++) {
0594: JdbcIndex index = (JdbcIndex) all.get(i);
0595: JdbcTable table = null;
0596: boolean ignore = false;
0597: if (index.cols.length > 0) {
0598: table = index.cols[0].table;
0599: ignore = false;
0600: for (int j = 1; j < index.cols.length; j++) {
0601: JdbcColumn col = index.cols[j];
0602: if (col.table != table) {
0603: System.out
0604: .println("\n\n WARNING: This composite index contains colums from "
0605: + "2 different tables. Ignoring it");
0606: ignore = true;
0607: }
0608: }
0609: }
0610: if (ignore)
0611: continue;
0612:
0613: Set indexs = (Set) tablesToIndexs.get(table);
0614: if (indexs == null) {
0615: indexs = new HashSet();
0616: tablesToIndexs.put(table, indexs);
0617: }
0618: indexs.add(index);
0619: }
0620:
0621: for (Iterator iterator = tablesToIndexs.entrySet().iterator(); iterator
0622: .hasNext();) {
0623: Map.Entry mapEntry = (Map.Entry) iterator.next();
0624: JdbcTable table = (JdbcTable) mapEntry.getKey();
0625: if (table != null) {
0626: Set indexs = (Set) mapEntry.getValue();
0627: JdbcIndex[] a = new JdbcIndex[indexs.size()];
0628: indexs.toArray(a);
0629: table.indexes = a;
0630: for (int i = 0; i < a.length; i++) {
0631: if (a[i] != null && a[i].name == null) {
0632: generateNameForIndex(nameGenerator, table.name,
0633: a[i]);
0634: }
0635: }
0636: }
0637: }
0638: }
0639:
0640: protected void postAllFieldsCreatedHook() {
0641: ClassMetaData[] ca = jmd.classes;
0642:
0643: // fill the stateFieldNo on each JdbcField
0644: for (int i = ca.length - 1; i >= 0; i--) {
0645: ClassMetaData cmd = ca[i];
0646: JdbcClass jc = (JdbcClass) cmd.storeClass;
0647: if (jc == null || jc.fields == null)
0648: continue;
0649: for (int j = jc.fields.length - 1; j >= 0; j--) {
0650: JdbcField f = jc.fields[j];
0651: if (f == null) {
0652: continue;
0653: }
0654: f.stateFieldNo = f.fmd.fieldNo + cmd.super FieldCount;
0655: }
0656: }
0657:
0658: // fill the stateFields array on each JdbcClass
0659: for (int i = ca.length - 1; i >= 0; i--) {
0660: ClassMetaData cmd = ca[i];
0661: JdbcClass jc = (JdbcClass) cmd.storeClass;
0662: if (jc == null)
0663: continue;
0664: jc.buildStateFields();
0665: }
0666: fillFGMetaData();
0667: }
0668:
0669: private void collectIndexes(ClassMetaData cmd, ArrayList indexes,
0670: boolean quiet) {
0671: ArrayList indexExts = getClassInfo(cmd).indexExts;
0672: for (int j = 0; j < indexExts.size(); j++) {
0673: try {
0674: indexes.add(processIndexExtension(cmd,
0675: (JdoExtension) indexExts.get(j), quiet));
0676: } catch (RuntimeException e) {
0677: cmd.addError(e, quiet);
0678: }
0679: }
0680: ClassMetaData[] subs = cmd.pcSubclasses;
0681: if (subs != null) {
0682: for (int i = 0; i < subs.length; i++) {
0683: try {
0684: collectIndexes(subs[i], indexes, quiet);
0685: } catch (RuntimeException e) {
0686: cmd.addError(e, quiet);
0687: }
0688: }
0689: }
0690: }
0691:
0692: private void collectAutoIndexes(ClassMetaData cmd, ArrayList indexes) {
0693: indexes.addAll(getClassInfo(cmd).autoIndexes);
0694: ClassMetaData[] subs = cmd.pcSubclasses;
0695: if (subs != null) {
0696: for (int i = 0; i < subs.length; i++) {
0697: collectAutoIndexes(subs[i], indexes);
0698: }
0699: }
0700: }
0701:
0702: /**
0703: * Generate the name for an index. The columns in the index must have
0704: * names.
0705: */
0706: public static void generateNameForIndex(JdbcNameGenerator namegen,
0707: String tableName, JdbcIndex idx) {
0708: int n = idx.cols.length;
0709: String[] cn = new String[n];
0710: for (int i = 0; i < n; i++)
0711: cn[i] = idx.cols[i].name;
0712: idx.name = namegen.generateIndexName(tableName, cn);
0713: }
0714:
0715: /**
0716: * Convert the type mapping strings for s into JdbcTypeMapping instances.
0717: */
0718: private ArrayList parseTypeMappings() {
0719: String sdb = sqlDriver.getName();
0720: ArrayList in = jdbcConfig.typeMappings;
0721: int n = in.size();
0722: ArrayList a = new ArrayList(n);
0723: StringListParser p = new StringListParser();
0724: for (int i = 0; i < n; i++) {
0725: String s = (String) in.get(i);
0726: p.setString(s);
0727: JdbcTypeMapping m = new JdbcTypeMapping();
0728: m.parse(p, converterRegistry);
0729: String mdb = m.getDatabase();
0730: if (mdb == null || mdb.equals(sdb))
0731: a.add(m);
0732: }
0733: return a;
0734: }
0735:
0736: /**
0737: * Convert the java type mapping strings for s into JdbcJavaTypeMapping
0738: * instances.
0739: */
0740: private ArrayList parseJavaTypeMappings() {
0741: String sdb = sqlDriver.getName();
0742: ArrayList in = jdbcConfig.javaTypeMappings;
0743: int n = in.size();
0744: ArrayList a = new ArrayList(n);
0745: StringListParser p = new StringListParser();
0746: for (int i = 0; i < n; i++) {
0747: String s = (String) in.get(i);
0748: p.setString(s);
0749: JdbcJavaTypeMapping m = new JdbcJavaTypeMapping();
0750: m.parse(p, converterRegistry);
0751: String mdb = m.getDatabase();
0752: if (mdb == null || mdb.equals(sdb))
0753: a.add(m);
0754: }
0755: return a;
0756: }
0757:
0758: /**
0759: * Process an index extension for cmd. This will resolve the names of
0760: * all fields in the index to their column names and register the name
0761: * of the index if one has been specified. The index is returned.
0762: */
0763: private JdbcIndex processIndexExtension(ClassMetaData cmd,
0764: JdoExtension e, boolean quiet) {
0765: JdbcIndex idx = new JdbcIndex();
0766: idx.name = e.value;
0767: ArrayList cols = new ArrayList();
0768: JdoExtension[] a = e.nested;
0769: int n = a == null ? 0 : a.length;
0770: for (int i = 0; i < n; i++) {
0771: JdoExtension ne = a[i];
0772: switch (ne.key) {
0773: case JDBC_CLUSTERED:
0774: try {
0775: idx.clustered = ne.getBoolean();
0776: } catch (RuntimeException x) {
0777: cmd.addError(x, quiet);
0778: }
0779: break;
0780: case JDBC_UNIQUE:
0781: try {
0782: idx.unique = ne.getBoolean();
0783: } catch (RuntimeException x) {
0784: cmd.addError(x, quiet);
0785: }
0786: break;
0787: case FIELD_NAME:
0788: JdbcColumn[] mtc;
0789: String fname = ne.getString();
0790: if (fname.equals(DATASTORE_PK_FIELDNAME)) {
0791: mtc = ((JdbcClass) cmd.storeClass).table.pk;
0792: } else {
0793: FieldMetaData fmd = cmd.getFieldMetaData(fname);
0794: if (fmd == null) {
0795: RuntimeException x = BindingSupportImpl
0796: .getInstance().runtime(
0797: "Field '" + ne.value
0798: + "' not found\n"
0799: + ne.getContext());
0800: cmd.addError(x, quiet);
0801: }
0802: JdbcField jf = (JdbcField) fmd.storeField;
0803: if (jf == null || jf.mainTableCols == null) {
0804: RuntimeException x = BindingSupportImpl
0805: .getInstance()
0806: .runtime(
0807: "Field '"
0808: + ne.value
0809: + "' is not stored in the main table\n"
0810: + ne.getContext());
0811: fmd.addError(x, quiet);
0812: }
0813: mtc = jf.mainTableCols;
0814: }
0815: for (int j = 0; j < mtc.length; j++)
0816: cols.add(mtc[j]);
0817: break;
0818: default:
0819: MetaDataBuilder.throwUnexpectedExtension(ne);
0820: }
0821: }
0822: n = cols.size();
0823: if (n == 0) {
0824: RuntimeException x = BindingSupportImpl.getInstance()
0825: .runtime(
0826: "Index does not include any fields\n"
0827: + e.getContext());
0828: cmd.addError(x, quiet);
0829: return null;
0830: }
0831: JdbcColumn[] idxCols = new JdbcColumn[n];
0832: cols.toArray(idxCols);
0833: idx.setCols(idxCols);
0834: if (idx.name != null) {
0835: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
0836: try {
0837: nameGenerator.addIndexName(jdbcClass.table.name,
0838: idx.name);
0839: } catch (IllegalArgumentException x) {
0840: RuntimeException ex = BindingSupportImpl.getInstance()
0841: .runtime(x.getMessage(), x);
0842: cmd.addError(ex, quiet);
0843: }
0844: }
0845: return idx;
0846: }
0847:
0848: private void fillFGMetaData() {
0849: ClassMetaData[] classes = jmd.classes;
0850: int clen = classes.length;
0851: if (Debug.DEBUG) {
0852: Debug.OUT
0853: .println("MDB-JDBC: Filling fetch group meta data ... ");
0854: }
0855: for (int i = 0; i < clen; i++) {
0856: ClassMetaData cmd = classes[i];
0857: processFetchGroups(cmd);
0858: }
0859: }
0860:
0861: /**
0862: * Finish up optimistic locking for cmd.
0863: */
0864: private void completeOptimisticLocking(ClassMetaData cmd,
0865: boolean quiet) {
0866: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
0867: ClassInfo info = getClassInfo(cmd);
0868: switch (jdbcClass.optimisticLocking) {
0869: case JdbcClass.OPTIMISTIC_LOCKING_VERSION:
0870: case JdbcClass.OPTIMISTIC_LOCKING_TIMESTAMP:
0871: processTimestampOrRowVersionLocking(jdbcClass, cmd,
0872: info.optimisticLockingExt, info.elements, quiet);
0873: break;
0874: }
0875: }
0876:
0877: private void processTimestampOrRowVersionLocking(
0878: JdbcClass jdbcClass, ClassMetaData cmd, JdoExtension e,
0879: ArrayList jdbcElements, boolean quiet) {
0880: boolean timestamp = jdbcClass.optimisticLocking == JdbcClass.OPTIMISTIC_LOCKING_TIMESTAMP;
0881: JdoExtension[] nested = e == null ? null : e.nested;
0882: if (nested != null
0883: && nested[0].key == JdoExtensionKeys.FIELD_NAME) {
0884: if (nested.length > 1) {
0885: RuntimeException x = BindingSupportImpl.getInstance()
0886: .runtime(
0887: "Unexpected extension: "
0888: + nested[1].getContext());
0889: cmd.addError(x, quiet);
0890: }
0891: String fieldName = nested[0].value;
0892: FieldMetaData fmd = cmd.getFieldMetaData(fieldName);
0893: if (fmd == null) {
0894: RuntimeException x = BindingSupportImpl.getInstance()
0895: .runtime(
0896: "Field '" + fieldName + " not found\n"
0897: + nested[0].getContext());
0898: cmd.addError(x, quiet);
0899: }
0900: int tc = fmd.typeCode;
0901: if (timestamp) {
0902: if (tc != MDStatics.DATE) {
0903: RuntimeException x = BindingSupportImpl
0904: .getInstance()
0905: .runtime(
0906: "Field '"
0907: + fieldName
0908: + " is not a java.util.Date\n"
0909: + nested[0].getContext());
0910: fmd.addError(x, quiet);
0911: }
0912: } else {
0913: if (tc != MDStatics.INT && tc != MDStatics.SHORT
0914: && tc != MDStatics.BYTE) {
0915: RuntimeException x = BindingSupportImpl
0916: .getInstance()
0917: .runtime(
0918: "Field '"
0919: + fieldName
0920: + " is not an int, short or byte\n"
0921: + nested[0].getContext());
0922: fmd.addError(x, quiet);
0923: }
0924: }
0925: cmd.optimisticLockingField = fmd;
0926: jdbcClass.optimisticLockingField = (JdbcSimpleField) fmd.storeField;
0927: fmd.setAutoSet(MDStatics.AUTOSET_BOTH);
0928: } else {
0929: JdbcColumn tc = createColumn(nested, OPT_LOCK_FIELDNAME,
0930: timestamp ? (Class) Date.class : (Class) Short.TYPE);
0931: // generate a fake field for the column
0932: JdbcSimpleField f = jdbcClass.optimisticLockingField = new JdbcSimpleField();
0933: f.fake = true;
0934: f.col = tc;
0935: FieldMetaData fmd = f.fmd = new FieldMetaData();
0936: fmd.fake = true;
0937: fmd.category = MDStatics.CATEGORY_SIMPLE;
0938: fmd.primaryField = true;
0939: fmd.classMetaData = cmd;
0940: fmd.defaultFetchGroup = true;
0941: fmd.storeField = f;
0942:
0943: fmd.name = timestamp ? "jdoTimestamp" : "jdoVersion";
0944:
0945: fmd.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_PERSISTENT;
0946: fmd.setType(tc.javaType);
0947: fmd.setAutoSet(MDStatics.AUTOSET_BOTH);
0948: jdbcElements.add(f);
0949: cmd.optimisticLockingField = fmd;
0950: }
0951: }
0952:
0953: /**
0954: * Fill in extra meta data for FetchGroup's.
0955: */
0956: private void processFetchGroups(ClassMetaData cmd) {
0957: FetchGroup[] groups = cmd.fetchGroups;
0958: int groupsLen = groups == null ? 0 : groups.length;
0959: for (int i = 0; i < groupsLen; i++) {
0960: FetchGroup g = groups[i];
0961: int totCols = 0;
0962: FetchGroupField[] fields = g.fields;
0963: if (fields == null) {
0964: continue;
0965: }
0966: int fieldsLen = fields.length;
0967: for (int j = 0; j < fieldsLen; j++) {
0968: FetchGroupField field = fields[j];
0969: FieldMetaData fmd = field.fmd;
0970: JdbcField jdbcField = (JdbcField) fmd.storeField;
0971: JdoExtension ext = field.extension;
0972: if (ext != null && ext.nested != null) {
0973: JdoExtension[] nested = ext.nested;
0974: int nestedLen = nested.length;
0975: for (int k = 0; k < nestedLen; k++) {
0976: JdoExtension e = nested[k];
0977: switch (e.key) {
0978: case JDBC_USE_JOIN:
0979: try {
0980: field.jdbcUseJoin = e
0981: .getEnum(jdbcMDE.USE_JOIN_ENUM);
0982: } catch (RuntimeException x) {
0983: cmd.addError(x, quiet);
0984: }
0985: break;
0986: case JDBC_USE_KEY_JOIN:
0987: if (fmd.category != MDStatics.CATEGORY_MAP) {
0988: RuntimeException x = BindingSupportImpl
0989: .getInstance()
0990: .runtime(
0991: "The jdbc-use-key-join option is only "
0992: + "valid for Map fields\n"
0993: + e
0994: .getContext());
0995: cmd.addError(x, quiet);
0996: }
0997: try {
0998: field.jdbcUseKeyJoin = e
0999: .getEnum(jdbcMDE.USE_JOIN_ENUM);
1000: } catch (RuntimeException x) {
1001: cmd.addError(x, quiet);
1002: }
1003: break;
1004: default:
1005: if (e.isJdbc()) {
1006: MetaDataBuilder
1007: .throwUnexpectedExtension(e);
1008: }
1009: }
1010: }
1011: }
1012: if (jdbcField == null)
1013: continue;
1014: if (field.jdbcUseJoin == 0) {
1015: if (field.nextFetchGroup != null) {
1016: field.jdbcUseJoin = jdbcField.useJoin;
1017: } else {
1018: field.jdbcUseJoin = JdbcField.USE_JOIN_NO;
1019: }
1020: }
1021: if (field.jdbcUseKeyJoin == 0) {
1022: if (field.nextKeyFetchGroup != null) {
1023: field.jdbcUseKeyJoin = jdbcField
1024: .getUseKeyJoin();
1025: } else {
1026: field.jdbcUseKeyJoin = JdbcField.USE_JOIN_NO;
1027: }
1028: }
1029: if (jdbcField.mainTableCols != null) {
1030: totCols += jdbcField.mainTableCols.length;
1031: }
1032: }
1033: g.jdbcTotalCols = totCols;
1034: }
1035: }
1036:
1037: /**
1038: * Process all ref and polyref fields.
1039: */
1040: private void processRefAndPolyRefFields(ClassMetaData cmd,
1041: boolean quiet) {
1042: ArrayList elements = getClassElements(cmd);
1043: int nelements = elements.size();
1044: for (int i = 0; i < nelements; i++) {
1045: Object o = elements.get(i);
1046: if (o instanceof JdbcRefField) {
1047: processRefField((JdbcRefField) o, quiet);
1048: } else if (o instanceof JdbcPolyRefField) {
1049: JdbcPolyRefField f = (JdbcPolyRefField) o;
1050: f.processMetaData(f.fmd.jdoField, this );
1051: }
1052: }
1053: }
1054:
1055: private void doConstraints(ClassMetaData cmd) {
1056: for (int i = 0; i < cmd.fields.length; i++) {
1057: FieldMetaData fmd = cmd.fields[i];
1058: if (fmd.storeField instanceof JdbcRefField) {
1059: createConstraint((JdbcRefField) fmd.storeField);
1060: }
1061: }
1062: }
1063:
1064: /**
1065: * Process all ref and collection fields.
1066: */
1067: private void processCollectionFields(ClassMetaData cmd,
1068: boolean quiet) {
1069: if (cmd.horizontal)
1070: return;
1071: ArrayList elements = getClassElements(cmd);
1072: int nelements = elements.size();
1073: for (int i = 0; i < nelements; i++) {
1074: Object o = elements.get(i);
1075: if (o instanceof JdbcCollectionField) {
1076: processCollectionField((JdbcCollectionField) o, quiet);
1077: }
1078: }
1079: }
1080:
1081: /**
1082: * Complete a JdbcRefField except for column names.
1083: */
1084: private void processRefField(JdbcRefField f, boolean quiet) {
1085: FieldMetaData fmd = f.fmd;
1086: f.targetClass = fmd.typeMetaData;
1087: JdbcClass target = (JdbcClass) f.targetClass.storeClass;
1088: JdoField jdoField = fmd.jdoField;
1089: JdoElement context = jdoField == null ? (JdoElement) fmd.typeMetaData.jdoClass
1090: : (JdoElement) jdoField;
1091: processRefFieldImpl(target, f, fmd, context,
1092: jdoField == null ? null : jdoField.extensions, quiet);
1093:
1094: }
1095:
1096: public void processRefFieldImpl(JdbcClass target, JdbcRefField f,
1097: FieldMetaData fmd, JdoElement context,
1098: JdoExtension[] extensions, boolean quiet) {
1099: if (target != null) {
1100: f.useJoin = target.useJoin;
1101: } else {
1102: f.useJoin = JdbcRefField.USE_JOIN_NO;
1103: }
1104: if (f.useJoin == JdbcRefField.USE_JOIN_NO
1105: && fmd.isDefaultFetchGroupTrue()) {
1106: f.useJoin = JdbcRefField.USE_JOIN_OUTER;
1107: }
1108:
1109: JdbcRefMetaDataBuilder rdb = new JdbcRefMetaDataBuilder(
1110: fmd.classMetaData, this , f.targetClass, context,
1111: fmd.name, extensions, quiet);
1112:
1113: f.cols = rdb.getCols();
1114: if (rdb.getUseJoin() != 0)
1115: f.useJoin = rdb.getUseJoin();
1116:
1117: for (int i = 0; i < f.cols.length; i++) {
1118: f.cols[i].comment = fmd.getCommentName();
1119: }
1120:
1121: boolean nulls = fmd.nullValue != MDStatics.NULL_VALUE_EXCEPTION;
1122: int nc = f.cols.length;
1123: for (int i = 0; i < nc; i++)
1124: f.cols[i].nulls = nulls;
1125:
1126: // include this in the where clause for changed locking only if all
1127: // columns support equalityTest
1128: f.includeForChangedLocking = true;
1129: for (int i = 0; i < nc; i++) {
1130: if (!f.cols[i].equalityTest) {
1131: f.includeForChangedLocking = false;
1132: break;
1133: }
1134: }
1135: f.constraintName = rdb.getConstraintName();
1136: f.createConstraint = !rdb.isDoNotCreateConstraint();
1137: }
1138:
1139: private boolean isCircularRef(ClassMetaData cmd1, ClassMetaData cmd2) {
1140: return jdbcClassReferenceGraph.isCircularRef(cmd1, cmd2);
1141: }
1142:
1143: private void createConstraint(JdbcRefField f) {
1144: final JdbcClass jdbcClass = (JdbcClass) f.fmd.classMetaData.storeClass;
1145: boolean createConstraint = f.createConstraint
1146: && f.targetClass != null
1147: && (!(f.fmd.nullValue != MDStatics.NULL_VALUE_EXCEPTION) || sqlDriver
1148: .isNullForeignKeyOk());
1149:
1150: /**
1151: * If this class is involved in a cycle with the refering class then
1152: * don't create the constraint unless the user specifically specified a dependency.
1153: */
1154: if (createConstraint
1155: && isCircularRef(jdbcClass.cmd, f.fmd.typeMetaData)) {
1156: if (!getClassInfo(jdbcClass.cmd.top)
1157: .getCreatedAfterClient().contains(
1158: f.fmd.typeMetaData.top)) {
1159: createConstraint = false;
1160: }
1161: }
1162:
1163: if (createConstraint
1164: && jdbcClass.cmd.top == f.fmd.typeMetaData.top) {
1165: createConstraint = false;
1166: }
1167:
1168: // create constraint if required
1169: if (createConstraint) {
1170: JdbcConstraint c = new JdbcConstraint();
1171: c.name = f.constraintName;
1172: c.src = jdbcClass.table;
1173: c.srcCols = f.cols;
1174: c.dest = ((JdbcClass) f.targetClass.storeClass).table;
1175: f.constraint = c;
1176: if (c.name != null) {
1177: nameGenerator.addRefConstraintName(c.src.name, c.name);
1178: }
1179: }
1180: }
1181:
1182: /**
1183: * Find the column in cols with the the supplied refField or null if none.
1184: */
1185: public JdbcColumn findColumn(List cols, JdbcSimpleField refField) {
1186: for (int i = cols.size() - 1; i >= 0; i--) {
1187: JdbcColumn col = (JdbcColumn) cols.get(i);
1188: if (col.refField == refField)
1189: return col;
1190: }
1191: return null;
1192: }
1193:
1194: /**
1195: * Complete a JdbcCollectionField.
1196: */
1197: private void processCollectionField(JdbcCollectionField f,
1198: boolean quiet) {
1199: FieldMetaData fmd = f.fmd;
1200: JdoField jdoField = fmd.jdoField;
1201: JdoElement context;
1202: if (jdoField == null) {
1203: if (fmd.typeMetaData == null) {
1204: context = fmd.classMetaData.jdoClass;
1205: } else {
1206: context = fmd.typeMetaData.jdoClass;
1207: }
1208: } else {
1209: context = jdoField;
1210: }
1211: try {
1212: f.processMetaData(context, this , quiet);
1213: } catch (RuntimeException e) {
1214: fmd.addError(e, quiet);
1215: }
1216: }
1217:
1218: /**
1219: * Creates key generators for all classes with keygen factories and
1220: * extracts keygen tables from them. Also set the autoinc flag on
1221: * the primary key of classes using a postInsert key generator.
1222: */
1223: private void createKeyGenerators(boolean quite) {
1224: HashSet keygenSet = new HashSet();
1225: HashSet keygenTables = new HashSet(17);
1226: ClassMetaData[] classes = jmd.classes;
1227: int clen = classes.length;
1228: for (int i = 0; i < clen; i++) {
1229: ClassMetaData cmd = classes[i];
1230: if (cmd.horizontal)
1231: continue;
1232: if (cmd.pcSuperMetaData != null)
1233: continue;
1234: ClassInfo info = getClassInfo(cmd);
1235: JdbcKeyGeneratorFactory f = info.keyGenFactory;
1236: if (f == null)
1237: continue;
1238: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1239: JdbcKeyGenerator keygen;
1240: try {
1241: keygen = f.createJdbcKeyGenerator(cmd.qname,
1242: jdbcClass.table, info.keyGenFactoryArgs);
1243: } catch (RuntimeException e) {
1244: cmd.addError(BindingSupportImpl.getInstance().runtime(
1245: e.getMessage() + "\n"
1246: + cmd.jdoClass.getContext(), e), quite);
1247: continue;
1248: }
1249:
1250: // recursively set keygen on class and subclasses
1251: jdbcClass.setJdbcKeyGenerator(keygen);
1252:
1253: if (keygenSet.contains(keygen))
1254: continue;
1255: keygenSet.add(keygen);
1256: keygen.addKeyGenTables(keygenTables, this );
1257:
1258: // set the autoinc flag on the primary key if postInsert keygen
1259: if (keygen.isPostInsertGenerator()) {
1260: jdbcClass.table.pk[0].autoinc = true;
1261: //for sybase this column's type must change to numeric
1262: //all subclass column's must also be updated
1263: sqlDriver.updateClassForPostInsertKeyGen(cmd,
1264: mappingResolver);
1265: }
1266: }
1267: JdbcMetaData jdbcMetaData = (JdbcMetaData) jmd.jdbcMetaData;
1268: jdbcMetaData.keyGenTables = new JdbcTable[keygenTables.size()];
1269: keygenTables.toArray(jdbcMetaData.keyGenTables);
1270: }
1271:
1272: /**
1273: * Name field columns without names and create the cols array on each
1274: * class table from the jdbcElements collected so far. It will
1275: * recursively process subclasses as required. Fake elements are
1276: * collected at the same time and added to ClassMetaData.fields.
1277: */
1278: private void finalizeFakesAndTableColumns(ClassMetaData cmd) {
1279: ArrayList elements = getClassElements(cmd);
1280: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1281: if (elements != null) {
1282: collectSubclassElements(cmd, elements);
1283:
1284: JdbcNameGenerator nameGen = nameGenerator;
1285: String tableName = jdbcClass.tableName;
1286:
1287: // make sure the classIdCol has a name
1288: JdbcColumn classIdCol = jdbcClass.classIdCol;
1289: if (classIdCol != null) {
1290: if (classIdCol.name == null) {
1291: classIdCol.name = nameGen
1292: .generateClassIdColumnName(tableName);
1293: } else {
1294: nameGen.addColumnName(tableName, classIdCol.name);
1295: }
1296: }
1297:
1298: // find all the JdbcColumn's in all elements and find fake
1299: // fields
1300: ArrayList cols = new ArrayList(elements.size());
1301: ArrayList fakes = new ArrayList();
1302: JdbcTable table = jdbcClass.table;
1303: for (int i = 0; i < elements.size(); i++) {
1304: Object o = elements.get(i);
1305: if (o instanceof JdbcField) {
1306: JdbcField f = (JdbcField) o;
1307: f.setMainTable(table);
1308: if (!f.fmd.primaryKey)
1309: f.nameColumns(tableName, nameGen);
1310: int pos = cols.size();
1311: f.addMainTableCols(cols);
1312: // make sure cols from subclasses allow nulls
1313: if (f.fmd.classMetaData.pcSuperMetaData != null) {
1314: int pos2 = cols.size();
1315: for (int j = pos; j < pos2; j++) {
1316: JdbcColumn sc = (JdbcColumn) cols.get(j);
1317: sc.nulls = true;
1318: }
1319: }
1320: if (f.fake) {
1321: fakes.add(f);
1322: }
1323: } else { // must be a column
1324: JdbcColumn c = (JdbcColumn) o;
1325: c.setTable(table);
1326: cols.add(c);
1327: }
1328: }
1329:
1330: //this done after all the naming has taken place.
1331: doNullIndicatorColumn(cmd, elements);
1332:
1333: // add the fakes (if any) to cmd.fields
1334: int numFakes = fakes.size();
1335: if (numFakes > 0 && cmd.fields != null) {
1336: int n = cmd.fields.length;
1337: FieldMetaData[] a = new FieldMetaData[n + numFakes];
1338: System.arraycopy(cmd.fields, 0, a, 0, n);
1339: for (int i = 0; i < numFakes; i++, n++) {
1340: JdbcField jdbcField = (JdbcField) fakes.get(i);
1341: a[n] = jdbcField.fmd;
1342: }
1343: cmd.fields = a;
1344: }
1345:
1346: // do a final sanity check on each column and set the table reference
1347: int nc = cols.size();
1348: JdbcColumn[] a = new JdbcColumn[nc];
1349: cols.toArray(a);
1350: for (int i = 0; i < nc; i++) {
1351: JdbcColumn c = a[i];
1352: if (c.name == null) {
1353: throw BindingSupportImpl.getInstance().internal(
1354: "Column has no name: " + c + "\n"
1355: + cmd.jdoClass.getContext());
1356: }
1357: if (c.jdbcType == Types.NULL) {
1358: throw BindingSupportImpl.getInstance().internal(
1359: "Column has NULL jdbcType: " + c + "\n"
1360: + cmd.jdoClass.getContext());
1361: }
1362: if (c.table == null) {
1363: throw BindingSupportImpl.getInstance().internal(
1364: "Column null table: " + c + "\n"
1365: + cmd.jdoClass.getContext());
1366: }
1367: if (!sqlDriver
1368: .isBatchingSupportedForJdbcType(c.jdbcType)) {
1369: jdbcClass.noBatching = true;
1370: }
1371: }
1372:
1373: // set the cols array
1374: jdbcClass.table.cols = a;
1375: }
1376:
1377: // recursively process all of our subclasses
1378: ClassMetaData[] subclasses = cmd.pcSubclasses;
1379: if (subclasses == null)
1380: return;
1381: for (int i = 0; i < subclasses.length; i++) {
1382: ((JdbcClass) subclasses[i].storeClass)
1383: .setClassIdCol(jdbcClass.classIdCol);
1384: finalizeFakesAndTableColumns(subclasses[i]);
1385: }
1386: }
1387:
1388: /**
1389: * Go through all the embedded reference fields on the supplied cmd and find
1390: * nullindicator columns.
1391: */
1392: private void doNullIndicatorColumn(ClassMetaData cmd,
1393: ArrayList elements) {
1394: //************** ignored for now **********************.
1395: if (true)
1396: return;
1397: //************** ignored for now **********************.
1398: FieldMetaData[] fmds = cmd.fields;
1399: for (int i = 0; i < fmds.length; i++) {
1400: FieldMetaData fmd = fmds[i];
1401: if (fmd.isEmbeddedRef()) {
1402: if (fmd.jdoField != null
1403: && fmd.jdoField.extensions != null) {
1404: JdoExtension ext = JdoExtension.find(
1405: JdoExtensionKeys.NULL_INDICATOR,
1406: fmd.jdoField.extensions);
1407: if (ext != null) {
1408: boolean extFound = false;
1409: //expect a jdbc-column-name
1410: if (ext.nested != null) {
1411: JdoExtension colNameExt = ext.nested[0];
1412: if (colNameExt.key == JdoExtensionKeys.JDBC_COLUMN_NAME) {
1413: extFound = true;
1414: //must check to see if we have any column in the table with that name
1415: boolean createColumn = true;
1416: for (int l = 0; l < elements.size(); l++) {
1417: Object o1 = elements.get(l);
1418: if (o1 instanceof JdbcSimpleField) {
1419: JdbcSimpleField sf = (JdbcSimpleField) o1;
1420: if (sf.col.name == ext.value) {
1421: createColumn = false;
1422: }
1423: }
1424: }
1425: if (createColumn) {
1426: JdbcColumn col = createColumn(
1427: ext.nested,
1428: CLASS_ID_FIELDNAME,
1429: Integer.TYPE);
1430: JdbcSimpleField f = new JdbcSimpleField();
1431: f.fake = true;
1432: f.col = col;
1433: FieldMetaData nullFmd = f.fmd = new FieldMetaData();
1434: nullFmd.fake = true;
1435: nullFmd.category = MDStatics.CATEGORY_SIMPLE;
1436: nullFmd.classMetaData = cmd;
1437: nullFmd.defaultFetchGroup = true;
1438: nullFmd.storeField = f;
1439: nullFmd.name = fmd.name
1440: + "_null_indicator";
1441: nullFmd.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_PERSISTENT;
1442: nullFmd.setType(col.javaType);
1443: fmd.setNullIndicatorFmd(nullFmd);
1444: elements.add(f);
1445: }
1446: }
1447: }
1448: if (!extFound) {
1449: throw BindingSupportImpl
1450: .getInstance()
1451: .unsupported(
1452: "Found a '"
1453: + JdoExtension
1454: .toKeyString(JdoExtensionKeys.NULL_INDICATOR)
1455: + "' extension with no '"
1456: + JdoExtension
1457: .toKeyString(JdoExtensionKeys.JDBC_COLUMN_NAME)
1458: + "' nested extension");
1459: }
1460: }
1461: }
1462: }
1463: }
1464: }
1465:
1466: /**
1467: * Name all constraints without names and create the constraint arrays
1468: * on class tables.
1469: */
1470: private void finalizeConstraints(ClassMetaData cmd) {
1471: ClassInfo info = getClassInfo(cmd);
1472: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1473: JdbcField[] fields = jdbcClass.fields;
1474: int len = fields.length;
1475: ArrayList cons = new ArrayList();
1476: JdbcConstraint pkFkConstraint = info.pkFkConstraint;
1477: if (pkFkConstraint != null) {
1478: if (pkFkConstraint.name == null) {
1479: pkFkConstraint.generateName(nameGenerator);
1480: }
1481: cons.add(pkFkConstraint);
1482: }
1483: JdbcTable table = jdbcClass.table;
1484: for (int i = 0; i < len; i++) {
1485: JdbcField field = fields[i];
1486: if (field != null && field.mainTable == jdbcClass.table) {
1487: field.addConstraints(cons);
1488: }
1489: }
1490: int n = cons.size();
1491: if (n > 0) {
1492: table.addConstraints(cons);
1493: }
1494: }
1495:
1496: /**
1497: * Recursively add all elements from all subclasses of cmd that are
1498: * stored in the same table to elements. Any class so added has its
1499: * elements removed from the map indicating that it has been processed.
1500: */
1501: private void collectSubclassElements(ClassMetaData cmd,
1502: ArrayList elements) {
1503: ClassMetaData[] subclasses = cmd.pcSubclasses;
1504: if (subclasses == null)
1505: return;
1506: for (int i = 0; i < subclasses.length; i++) {
1507: ClassMetaData sc = subclasses[i];
1508: if (((JdbcClass) sc.storeClass).table == ((JdbcClass) cmd.storeClass).table) {
1509: ClassInfo scInfo = getClassInfo(sc);
1510: elements.addAll(scInfo.elements);
1511: scInfo.elements = null;
1512: collectSubclassElements(sc, elements);
1513: }
1514: }
1515: }
1516:
1517: /**
1518: * Find the primary key (datastore or application) for cmd and make sure
1519: * all the columns have names. If the identity-type is datastore and
1520: * there is no primary key column then create one. This will recursively
1521: * process any PC subclasses.
1522: */
1523: private void processPrimaryKey(ClassMetaData cmd, boolean quiet) {
1524: // if (cmd.horizontal) return;
1525: if (cmd.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
1526: processPrimaryKeyAppIdentity(cmd, quiet);
1527: } else {
1528: processPrimaryKeyDatastoreIdentity(cmd);
1529: }
1530:
1531: // create subclass table fk constraint to base table if needed
1532: ClassInfo info = getClassInfo(cmd);
1533: ClassMetaData pccmd = cmd.pcSuperMetaData;
1534: if (pccmd != null
1535: && ((JdbcClass) cmd.storeClass).table != ((JdbcClass) pccmd.storeClass).table) {
1536: JdbcConstraint c = new JdbcConstraint();
1537: c.name = info.pkFkConstraintName;
1538: c.src = ((JdbcClass) cmd.storeClass).table;
1539: c.srcCols = ((JdbcClass) cmd.storeClass).table.pk;
1540: c.dest = ((JdbcClass) pccmd.storeClass).table;
1541: if (c.name != null) {
1542: nameGenerator.addRefConstraintName(c.src.name, c.name);
1543: }
1544: info.pkFkConstraint = c;
1545: }
1546:
1547: if (cmd.pcSubclasses == null)
1548: return;
1549: for (int j = 0; j < cmd.pcSubclasses.length; j++) {
1550: processPrimaryKey(cmd.pcSubclasses[j], quiet);
1551: }
1552: }
1553:
1554: /**
1555: * Find the primary key for datastore identity class for cmd and make sure
1556: * all the columns have names. If there is no primary key column then
1557: * create one.
1558: */
1559: private void processPrimaryKeyDatastoreIdentity(ClassMetaData cmd) {
1560: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1561: JdbcTable table = jdbcClass.table;
1562: JdbcColumn pkcol = null;
1563:
1564: ArrayList jdbcElements = getClassElements(cmd);
1565: int elen = jdbcElements.size();
1566: for (int i = 0; i < elen; i++) {
1567: Object o = jdbcElements.get(i);
1568: if (!(o instanceof JdbcColumn))
1569: continue;
1570: JdbcColumn c = (JdbcColumn) o;
1571: if (c.pk) {
1572: if (pkcol != null) {
1573: throw BindingSupportImpl
1574: .getInstance()
1575: .runtime(
1576: "Class "
1577: + cmd.qname
1578: + " has multiple jdbc-primary-key extensions\n"
1579: + cmd.jdoClass.getContext());
1580: }
1581: pkcol = c;
1582: }
1583: }
1584: if (cmd.pcSuperMetaData != null) {
1585: if (pkcol != null) {
1586: throw BindingSupportImpl
1587: .getInstance()
1588: .runtime(
1589: "Class "
1590: + cmd.qname
1591: + " has a "
1592: + "persistence-capable-superclass so use the inheritance "
1593: + "extension to specify its primary-key\n"
1594: + cmd.jdoClass.getContext());
1595: }
1596: // if subclass is stored in its own table then use the inheritance
1597: // extension to build a reference to the superclass
1598: if (table != ((JdbcClass) cmd.pcSuperMetaData.storeClass).table) {
1599: createVerticalInheritancePK(cmd);
1600: }
1601: } else {
1602: if (pkcol == null) {
1603: pkcol = createColumn(null, DATASTORE_PK_FIELDNAME,
1604: Integer.TYPE);
1605: jdbcElements.add(0, pkcol);
1606: }
1607: // make sure the columns has a name
1608: if (pkcol.name != null) {
1609: try {
1610: nameGenerator.addColumnName(table.name, pkcol.name);
1611: } catch (IllegalArgumentException e) {
1612: throw BindingSupportImpl.getInstance().runtime(
1613: "Invalid jdbc-column-name for datastore identity primary key: "
1614: + e.getMessage() + "\n"
1615: + cmd.jdoClass.getContext());
1616: }
1617: } else {
1618: pkcol.name = nameGenerator
1619: .generateDatastorePKName(table.name);
1620: }
1621: table.setPk(new JdbcColumn[] { pkcol });
1622: }
1623: }
1624:
1625: /**
1626: * Fill in the primary key for a subclass mapped using vertical
1627: * inheritance using the inheritance extension to build a reference to the
1628: * superclass.
1629: */
1630: private void createVerticalInheritancePK(ClassMetaData cmd) {
1631: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1632: JdbcTable table = jdbcClass.table;
1633: JdbcTable super Table = ((JdbcClass) cmd.pcSuperMetaData.storeClass).table;
1634: JdoExtension inheritance = getClassInfo(cmd).inheritance;
1635: JdbcRefMetaDataBuilder rdb = new JdbcRefMetaDataBuilder(cmd,
1636: this , cmd.pcSuperMetaData, inheritance, null,
1637: inheritance == null ? null : inheritance.nested, quiet);
1638: JdbcColumn[] a = rdb.getCols();
1639: for (int i = 0; i < a.length; i++) {
1640: if (a[i].name == null)
1641: a[i].name = super Table.pk[i].name;
1642: a[i].pk = true;
1643: }
1644: table.setPk(a);
1645: ArrayList jdbcElements = getClassElements(cmd);
1646: for (int i = 0; i < a.length; i++) {
1647: try {
1648: a[i].addColumnNames(table.name, nameGenerator);
1649: } catch (IllegalArgumentException e) {
1650: throw BindingSupportImpl.getInstance().runtime(
1651: "Invalid jdbc-column-name: " + e.getMessage()
1652: + "\n" + inheritance.getContext());
1653: }
1654: jdbcElements.add(i, a[i]);
1655: }
1656: }
1657:
1658: /**
1659: * Find the primary key for application identity class cmd and make sure
1660: * all the columns have names.
1661: */
1662: private void processPrimaryKeyAppIdentity(ClassMetaData cmd,
1663: boolean quiet) {
1664: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1665: JdbcTable table = jdbcClass.table;
1666: JdbcNameGenerator ng = nameGenerator;
1667: FieldMetaData[] fields = cmd.pkFields;
1668: JdbcColumn[] pkcols = null;
1669: int pkFieldCount = fields == null ? 0 : fields.length;
1670: if (fields != null) {
1671: pkcols = new JdbcColumn[pkFieldCount];
1672: for (int i = 0; i < pkFieldCount; i++) {
1673: try {
1674: JdbcSimpleField jdbcSimpleField = (JdbcSimpleField) fields[i].storeField;
1675: pkcols[i] = jdbcSimpleField.col;
1676: pkcols[i].refField = jdbcSimpleField;
1677: } catch (ClassCastException e) {
1678: RuntimeException x = BindingSupportImpl
1679: .getInstance()
1680: .runtime(
1681: "Only simple fields may be mapped as Primary Key's.\n"
1682: + cmd.jdoClass.getContext());
1683: cmd.addError(x, quiet);
1684: }
1685: }
1686: }
1687: ClassMetaData pccmd = cmd.pcSuperMetaData;
1688: if (pccmd != null
1689: && ((JdbcClass) pccmd.storeClass).inheritance != JdbcClass.INHERITANCE_HORIZONTAL) {
1690: if (pkcols != null) {
1691: throw BindingSupportImpl
1692: .getInstance()
1693: .runtime(
1694: "Class "
1695: + cmd.qname
1696: + " has a "
1697: + "persistence-capable-superclass so use the inheritance "
1698: + "extension to specify its primary-key\n"
1699: + cmd.jdoClass.getContext());
1700: }
1701: // if subclass is stored in its own table then use the inheritance
1702: // extension to build a reference to the superclass
1703: if (table != ((JdbcClass) pccmd.storeClass).table) {
1704: createVerticalInheritancePK(cmd);
1705: }
1706: } else {
1707: if (pkcols == null) {
1708: RuntimeException e = BindingSupportImpl.getInstance()
1709: .runtime(
1710: "Class " + cmd.qname
1711: + " has application identity "
1712: + "but no primary-key fields\n"
1713: + cmd.jdoClass.getContext());
1714: cmd.addError(e, quiet);
1715: } else {
1716: for (int i = 0; i < pkFieldCount; i++) {
1717: String cn = pkcols[i].name;
1718: if (cn == null) {
1719: pkcols[i].name = ng.generateFieldColumnName(
1720: table.name, fields[i].name, true);
1721: } else {
1722: try {
1723: ng.addColumnName(table.name, cn);
1724: } catch (IllegalArgumentException x) {
1725: throw BindingSupportImpl
1726: .getInstance()
1727: .runtime(
1728: "Invalid jdbc-column-name: "
1729: + x.getMessage()
1730: + "\n"
1731: + cmd.jdoClass
1732: .getContext());
1733: }
1734: }
1735: }
1736: table.setPk(pkcols);
1737: }
1738: }
1739: }
1740:
1741: /**
1742: * Make sure the persistent capable class cmd has a table and
1743: * recursively process all of its subclasses. NOP if cmd is not
1744: * a base class.
1745: */
1746: private void processBaseClassTable(ClassMetaData cmd) {
1747: ClassMetaData pccmd = cmd.pcSuperMetaData;
1748: if (pccmd != null)
1749: return;
1750: createClassTable(cmd);
1751: JdbcClass jdbcClass = ((JdbcClass) cmd.storeClass);
1752: if (cmd.horizontal) {
1753: jdbcClass.doNotCreateTable = true;
1754: }
1755: if (cmd.pcSubclasses == null)
1756: return;
1757: for (int j = 0; j < cmd.pcSubclasses.length; j++) {
1758: processSubclassTable(cmd.pcSubclasses[j]);
1759: }
1760: }
1761:
1762: private void createClassTable(ClassMetaData cmd) {
1763: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1764: JdbcTable table = new JdbcTable();
1765: table.sqlDriver = sqlDriver;
1766: table.name = jdbcClass.tableName;
1767: table.comment = cmd.qname;
1768: if (table.name == null) {
1769: table.name = nameGenerator
1770: .generateClassTableName(cmd.qname);
1771: } else {
1772: addTableName(table, cmd);
1773: }
1774: jdbcClass.setTable(table);
1775: fillTablePkConstraintName(cmd, table);
1776: }
1777:
1778: /**
1779: * Make sure a persistent capable subclass cmd has a table and
1780: * recursively process all of its subclasses.
1781: */
1782: private void processSubclassTable(ClassMetaData cmd) {
1783: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1784: ClassMetaData pccmd = cmd.pcSuperMetaData;
1785: switch (jdbcClass.inheritance) {
1786: case JdbcClass.INHERITANCE_FLAT:
1787: jdbcClass.setTable(((JdbcClass) pccmd.storeClass).table);
1788: break;
1789: case JdbcClass.INHERITANCE_VERTICAL:
1790: createClassTable(cmd);
1791: break;
1792: default:
1793: throw BindingSupportImpl.getInstance().internal(
1794: "Unknown inheritance strategy: "
1795: + jdbcClass.inheritance + " for "
1796: + cmd.qname);
1797: }
1798:
1799: if (cmd.pcSubclasses == null)
1800: return;
1801: for (int j = 0; j < cmd.pcSubclasses.length; j++) {
1802: processSubclassTable(cmd.pcSubclasses[j]);
1803: }
1804: }
1805:
1806: private void addTableName(JdbcTable table, ClassMetaData cmd) {
1807: try {
1808: nameGenerator.addTableName(table.name);
1809: } catch (IllegalArgumentException x) {
1810: throw BindingSupportImpl.getInstance().runtime(
1811: "Invalid jdbc-table-name: " + x.getMessage() + "\n"
1812: + cmd.jdoClass.getContext());
1813: }
1814: }
1815:
1816: private void fillTablePkConstraintName(ClassMetaData cmd,
1817: JdbcTable table) {
1818: table.pkConstraintName = getClassInfo(cmd).pkConstraintName;
1819: if (table.pkConstraintName == null) {
1820: table.pkConstraintName = nameGenerator
1821: .generatePkConstraintName(table.name);
1822: } else {
1823: try {
1824: nameGenerator.addPkConstraintName(table.name,
1825: table.pkConstraintName);
1826: } catch (IllegalArgumentException e) {
1827: throw BindingSupportImpl.getInstance().runtime(
1828: "Invalid jdbc-pk-constraint-name: "
1829: + e.getMessage() + "\n"
1830: + cmd.jdoClass.getContext(), e);
1831: }
1832: }
1833: }
1834:
1835: /**
1836: * Create meta data for cmd and all of its subclasses and figure out
1837: * everything that does not require access to JdbcClass objects other
1838: * than its superclasses as they may not exist yet.
1839: */
1840: private void createJdbcClass(ClassMetaData cmd, boolean quiet) {
1841: ClassInfo info = new ClassInfo();
1842: info.cmd = cmd;
1843: classInfoMap.put(cmd, info);
1844: JdbcClass jdbcClass = new JdbcClass(sqlDriver);
1845: cmd.storeClass = jdbcClass;
1846: jdbcClass.cmd = cmd;
1847: JdoClass jdoClass = cmd.jdoClass;
1848:
1849: // fill in defaults that may be overwritten by extensions
1850: jdbcClass.optimisticLocking = jdbcConfig.jdbcOptimisticLocking;
1851: info.optimisticLockingExt = null;
1852:
1853: jdbcClass.useJoin = JdbcRefField.USE_JOIN_OUTER;
1854: jdbcClass.doNotCreateTable = jdbcConfig.jdbcDoNotCreateTable;
1855:
1856: // default the descriminator value
1857: switch (jdbcConfig.defaultClassId) {
1858: case JdbcConfig.DEFAULT_CLASS_ID_FULLNAME:
1859: jdbcClass.jdbcClassId = cmd.qname;
1860: break;
1861: case JdbcConfig.DEFAULT_CLASS_ID_NAME:
1862: jdbcClass.jdbcClassId = cmd.jdoClass.name;
1863: break;
1864: default:
1865: jdbcClass.jdbcClassId = cmd.classIdString;
1866: }
1867:
1868: if (cmd.pcSuperMetaData != null) {
1869: if (getClassInfo(cmd.top).noClassIdCol) {
1870: jdbcClass.inheritance = JdbcClass.INHERITANCE_VERTICAL;
1871: } else {
1872: jdbcClass.inheritance = jdbcConfig.inheritance;
1873: }
1874: } else {
1875: jdbcClass.inheritance = jdbcConfig.inheritance;
1876: info.noClassIdCol = jdbcConfig.defaultClassId == JdbcConfig.DEFAULT_CLASS_ID_NO;
1877: }
1878:
1879: if (cmd.identityType == MDStatics.IDENTITY_TYPE_DATASTORE) {
1880: info.keyGenFactory = keyGenRegistry
1881: .getFactory(jdbcConfig.jdbcKeyGenerator);
1882: info.keyGenFactoryArgs = info.keyGenFactory
1883: .createArgsBean();
1884: BeanUtils.setProperties(info.keyGenFactoryArgs,
1885: jdbcConfig.jdbcKeyGeneratorProps);
1886: }
1887:
1888: // fill in whatever we can from extensions, fields etc
1889: JdoElement[] elements = jdoClass.elements;
1890: int n = elements.length;
1891: ArrayList jdbcElements = info.elements = new ArrayList(n);
1892: for (int i = 0; i < n; i++) {
1893: JdoElement o = elements[i];
1894: if (o instanceof JdoExtension) {
1895: try {
1896: processClassExtensionPass1(cmd, (JdoExtension) o,
1897: jdbcElements, quiet);
1898: } catch (RuntimeException e) {
1899: cmd.addError(e, quiet);
1900: }
1901: } else { // must be a JdoField
1902: JdoField jdoField = (JdoField) o;
1903: FieldMetaData fmd = cmd.getFieldMetaData(jdoField.name);
1904: if (fmd == null)
1905: continue; // must be PERSISTENT_NONE
1906:
1907: if (fmd.isEmbeddedRef()) {
1908: continue;
1909: }
1910:
1911: JdbcField f = null;
1912: f = createJdbcField(fmd, quiet);
1913: if (f != null) {
1914: f.fmd = fmd;
1915: fmd.storeField = f;
1916: fmd.primaryField = true;
1917: jdbcElements.add(f);
1918: }
1919: }
1920: }
1921:
1922: // create JdbcField's for all persistent fields not yet found
1923: FieldMetaData[] fields = cmd.fields;
1924: n = fields.length;
1925: for (int i = 0; i < n; i++) {
1926: FieldMetaData fmd = fields[i];
1927: if (fmd.storeField != null || fmd.isEmbeddedRef())
1928: continue;
1929: JdbcField f = createJdbcField(fmd, quiet);
1930: if (f != null) {
1931: f.fmd = fmd;
1932: fmd.storeField = f;
1933: fmd.primaryField = true;
1934: jdbcElements.add(f);
1935: }
1936: }
1937:
1938: cmd.changedOptimisticLocking = jdbcClass.optimisticLocking == JdbcClass.OPTIMISTIC_LOCKING_CHANGED;
1939:
1940: // process subclasses
1941: if (cmd.pcSubclasses != null) {
1942: for (int i = cmd.pcSubclasses.length - 1; i >= 0; i--) {
1943: createJdbcClass(cmd.pcSubclasses[i], quiet);
1944: }
1945: }
1946: }
1947:
1948: /**
1949: * Create the classIdCol and initialise the jdbcClassId to the correct type.
1950: * This is called on the least derived class in the hierarchy and then
1951: * recursively called on subs.
1952: */
1953: private void processClassIdCol(ClassMetaData cmd, boolean quiet,
1954: HashMap classIdMap) {
1955: ClassInfo info = getClassInfo(cmd);
1956: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
1957:
1958: if (cmd.pcSuperMetaData == null) {
1959: if (!info.noClassIdCol && cmd.pcSubclasses != null) {
1960: // topmost class so we need to create the column
1961: boolean intClassId = jdbcClass
1962: .isIntJdbcClassIdHeirachy();
1963: Class javaType = intClassId ? (Class) Integer.TYPE
1964: : (Class) String.class;
1965:
1966: if (info.classIdExt != null
1967: && info.classIdExt.nested != null) {
1968: // if there is a jdbc-class-id extension with nested extensions
1969: // then use these to create the column
1970: JdbcColumn col = jdbcClass.classIdCol = createColumn(
1971: info.classIdExt.nested, CLASS_ID_FIELDNAME,
1972: javaType);
1973: if (col.pk) {
1974: throw BindingSupportImpl
1975: .getInstance()
1976: .runtime(
1977: "The jdbc-primary-key option is "
1978: + "not allowed for a jdbc-class-id column\n"
1979: + info.classIdExt
1980: .getContext());
1981: }
1982: info.elements.add(col);
1983: } else {
1984: // no extensions so just create a suitable column
1985: jdbcClass.classIdCol = createColumn(null,
1986: CLASS_ID_FIELDNAME, javaType);
1987: info.elements.add(jdbcClass.classIdCol);
1988: }
1989:
1990: // convert heirachy jdbcClassId to Integer if needed
1991: if (intClassId)
1992: jdbcClass.convertJdbcClassIdToInteger();
1993: } else {
1994: jdbcClass.jdbcClassId = null;
1995: }
1996: } else { // not the topmost class
1997: if (getClassInfo(cmd.top).noClassIdCol) {
1998: if (jdbcClass.inheritance != JdbcClass.INHERITANCE_VERTICAL) {
1999: // Flat inheritance and no descriminator is allowed if
2000: // there is no fanout in the heirachy (i.e. each class
2001: // has 0 or 1 subclasses). Only instances of the leaf
2002: // class may be persisted and only instances of the leaf
2003: // class will be returned from the database.
2004: if (cmd.pcSuperMetaData.pcSubclasses.length > 1) {
2005: throw BindingSupportImpl
2006: .getInstance()
2007: .invalidOperation(
2008: "Class "
2009: + cmd.qname
2010: + " must use vertical inheritance as\n"
2011: + "it has siblings and the base class "
2012: + "does not have a descriminator (jdo_class) column\n"
2013: + cmd.jdoClass
2014: .getContext());
2015: }
2016: // only leaf class instances allowed
2017: cmd.pcSuperMetaData.instancesNotAllowed = true;
2018: // read superclass(es) as us instead
2019: for (ClassMetaData i = cmd.pcSuperMetaData; i != null; i = i.pcSuperMetaData) {
2020: ((JdbcClass) i.storeClass).readAsClass = cmd;
2021: }
2022: }
2023: jdbcClass.jdbcClassId = null;
2024: }
2025:
2026: // subclass so fill in the classIdCol from the topmost class
2027: jdbcClass.classIdCol = ((JdbcClass) cmd.top.storeClass).classIdCol;
2028: }
2029:
2030: // check for duplicate descriminator values in the heirachy
2031: if (jdbcClass.jdbcClassId != null) {
2032: ClassMetaData other = (ClassMetaData) classIdMap
2033: .get(jdbcClass.jdbcClassId);
2034: if (other != null) {
2035: throw BindingSupportImpl.getInstance()
2036: .invalidOperation(
2037: "Class " + cmd.qname
2038: + " has same jdbc-class-id as "
2039: + other.qname + ": '"
2040: + jdbcClass.jdbcClassId + "'\n"
2041: + cmd.jdoClass.getContext());
2042: }
2043: classIdMap.put(jdbcClass.jdbcClassId, cmd);
2044: }
2045:
2046: // process subclasses
2047: if (cmd.pcSubclasses != null) {
2048: ClassMetaData[] subs = cmd.pcSubclasses;
2049: for (int i = 0; i < subs.length; i++) {
2050: processClassIdCol(subs[i], quiet, classIdMap);
2051: }
2052: }
2053: }
2054:
2055: /**
2056: * Create a new JdbcField from a JdoField and its extensions. Returns
2057: * null if the JdoField is transactional i.e. we dont give a flying
2058: * banana as we do not store it!
2059: */
2060: private JdbcField createJdbcField(FieldMetaData fmd, boolean quiet) {
2061: try {
2062: switch (fmd.category) {
2063: case MDStatics.CATEGORY_TRANSACTIONAL:
2064: return null;
2065: case MDStatics.CATEGORY_SIMPLE:
2066: return createJdbcSimpleField(fmd);
2067: case MDStatics.CATEGORY_REF:
2068: return new JdbcRefField();
2069: case MDStatics.CATEGORY_POLYREF:
2070: return new JdbcPolyRefField();
2071: case MDStatics.CATEGORY_COLLECTION:
2072: return createJdbcCollectionField(fmd);
2073: case MDStatics.CATEGORY_ARRAY:
2074: return createFieldForArray(fmd);
2075: case MDStatics.CATEGORY_MAP:
2076: return new JdbcMapField();
2077: case MDStatics.CATEGORY_EXTERNALIZED:
2078: return createJdbcSerializedField(fmd);
2079: }
2080: throw BindingSupportImpl.getInstance().internal(
2081: "Field "
2082: + fmd.name
2083: + " of "
2084: + fmd.classMetaData.qname
2085: + " has bad category: "
2086: + MDStaticUtils
2087: .toCategoryString(fmd.category));
2088: } catch (RuntimeException e) {
2089: fmd.addError(e, quiet);
2090: }
2091: return null;
2092: }
2093:
2094: private JdbcCollectionField createJdbcCollectionField(
2095: FieldMetaData fmd) {
2096: return createJdbcField(fmd, fmd.jdoCollection == null ? null
2097: : fmd.jdoCollection.extensions);
2098: }
2099:
2100: private JdbcCollectionField createJdbcField(FieldMetaData fmd,
2101: JdoExtension[] exts) {
2102: if (exts != null) {
2103: // look for an extension indicating that this collection is stored
2104: // using a foreign key in the value class instead of a link table
2105: // or is part of a many-to-many relationship
2106: JdoExtension[] a = exts;
2107: int n = a == null ? 0 : a.length;
2108: boolean gotLink = false;
2109: JdoExtension ie = null;
2110: for (int i = 0; i < n; i++) {
2111: JdoExtension e = a[i];
2112: switch (e.key) {
2113: case INVERSE:
2114: case JDBC_LINK_FOREIGN_KEY:
2115: if (ie != null || gotLink) {
2116: throw BindingSupportImpl
2117: .getInstance()
2118: .runtime(
2119: "The "
2120: + (ie != null ? "inverse"
2121: : "jdbc-link-table")
2122: + " extension has already been specified\n"
2123: + e.getContext());
2124: }
2125: ie = e;
2126: break;
2127: case JDBC_LINK_TABLE:
2128: if (ie != null || gotLink) {
2129: throw BindingSupportImpl
2130: .getInstance()
2131: .runtime(
2132: "The "
2133: + (ie != null ? "inverse"
2134: : "jdbc-link-table")
2135: + " extension has already been specified\n"
2136: + e.getContext());
2137: }
2138: gotLink = true;
2139: break;
2140: }
2141: }
2142: if (ie != null) {
2143: // see what type of field the inverse is to see if this is
2144: // is a many-to-many or one-to-many
2145: ClassMetaData ecmd = fmd.elementTypeMetaData;
2146: if (ecmd == null) {
2147: throw BindingSupportImpl.getInstance().runtime(
2148: "The inverse extension may only be used for "
2149: + "collections of PC instances\n"
2150: + ie.getContext());
2151: }
2152: String fname = ie.getString();
2153: FieldMetaData f = ecmd.getFieldMetaData(fname);
2154: if (f == null) {
2155: if (fname != null
2156: && (fname.equals("") || fname
2157: .equals(FieldMetaData.NO_FIELD_TEXT))) {
2158: return new JdbcFKCollectionField();
2159: } else {
2160: throw BindingSupportImpl.getInstance().runtime(
2161: "Field '" + fname + "' not found on "
2162: + fmd.elementType + "\n"
2163: + ie.getContext());
2164: }
2165: }
2166: switch (f.category) {
2167: case MDStatics.CATEGORY_REF:
2168: return new JdbcFKCollectionField();
2169: case MDStatics.CATEGORY_ARRAY:
2170: case MDStatics.CATEGORY_COLLECTION:
2171: return new JdbcLinkCollectionField();
2172: }
2173: throw BindingSupportImpl
2174: .getInstance()
2175: .runtime(
2176: "Field '"
2177: + fname
2178: + "' is not a reference, collection or array\n"
2179: + ie.getContext());
2180: }
2181: }
2182: return new JdbcLinkCollectionField();
2183: }
2184:
2185: private JdbcSimpleField createJdbcSimpleField(FieldMetaData fmd) {
2186: JdbcSimpleField f = new JdbcSimpleField();
2187: JdoField jdoField = fmd.jdoField;
2188: JdoExtension[] a = jdoField == null ? null
2189: : jdoField.extensions;
2190: int n = a == null ? 0 : a.length;
2191: for (int i = 0; i < n; i++) {
2192: JdoExtension e = a[i];
2193: switch (e.key) {
2194: case JDBC_COLUMN:
2195: // handled when column is created
2196: break;
2197: default:
2198: if (e.isJdbc())
2199: MetaDataBuilder.throwUnexpectedExtension(e);
2200: }
2201: }
2202: f.col = createColumn(a, fmd.name, fmd.type);
2203: f.col.comment = fmd.getCommentName();
2204: if ((f.col.pk = fmd.primaryKey)
2205: || fmd.nullValue == MDStatics.NULL_VALUE_EXCEPTION) {
2206: f.col.nulls = false;
2207: } else if (fmd.nullValue == MDStatics.NULL_VALUE_NONE) {
2208: f.col.nulls = true;
2209: }
2210: if (fmd.embeddedFakeField) {
2211: f.col.nulls = true;
2212: }
2213: f.includeForChangedLocking = f.col.equalityTest;
2214: return f;
2215: }
2216:
2217: private JdbcSimpleField createJdbcSerializedField(FieldMetaData fmd) {
2218: JdbcSimpleField f = new JdbcSimpleField();
2219: JdoField jdoField = fmd.jdoField;
2220: JdoExtension[] a = jdoField == null ? null
2221: : jdoField.extensions;
2222: int n = a == null ? 0 : a.length;
2223: for (int i = 0; i < n; i++) {
2224: JdoExtension e = a[i];
2225: switch (e.key) {
2226: case JDBC_COLUMN:
2227: // handled when column is created
2228: break;
2229: default:
2230: if (e.isJdbc())
2231: MetaDataBuilder.throwUnexpectedExtension(e);
2232: }
2233: }
2234: f.col = createColumn(a, fmd.name, fmd.externalizer
2235: .getExternalType());
2236: f.col.comment = fmd.getCommentName();
2237: if (fmd.nullValue == MDStatics.NULL_VALUE_EXCEPTION) {
2238: f.col.nulls = false;
2239: } else if (fmd.nullValue == MDStatics.NULL_VALUE_NONE) {
2240: f.col.nulls = true;
2241: }
2242: f.includeForChangedLocking = f.col.equalityTest;
2243: return f;
2244: }
2245:
2246: /**
2247: * Create a JdbcField for an array[]. This will create a JdbcSimpleField
2248: * if the array is embedded or a JdbcLinkCollectionField if not.
2249: */
2250: private JdbcField createFieldForArray(FieldMetaData fmd) {
2251: if (fmd.embedded) {
2252: return createJdbcSimpleField(fmd);
2253: } else {
2254: return createJdbcField(fmd, fmd.jdoArray == null ? null
2255: : fmd.jdoArray.extensions);
2256: }
2257: }
2258:
2259: /**
2260: * Process a pass 1 extension for a class. This ignores jdbc-datastore
2261: * extensions as these should have already been processed. Some
2262: * extensions are skipped to be processed later.
2263: *
2264: * @param jdbcElements These are the JdbcColumn's and JdbcField's that
2265: * have been created so far for the class
2266: */
2267: private void processClassExtensionPass1(ClassMetaData cmd,
2268: JdoExtension e, ArrayList jdbcElements, boolean quiet) {
2269: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
2270: ClassInfo info;
2271: switch (e.key) {
2272: case DATASTORE:
2273: // do nothing as this should have already been processed
2274: break;
2275: case JDBC_INHERITANCE:
2276: // if (cmd.pcSuperMetaData == null) {
2277: // RuntimeException x = BindingSupportImpl.getInstance().runtime("Class " +
2278: // cmd.qname + " does not have a " +
2279: // "persistence-capable-superclass\n" +
2280: // e.getContext());
2281: // cmd.addError(x, quiet);
2282: // }
2283: try {
2284: if (e.value != null) {
2285: jdbcClass.inheritance = e
2286: .getEnum(jdbcMDE.INHERITANCE_ENUM);
2287: }
2288: getClassInfo(cmd).inheritance = e;
2289: } catch (RuntimeException x) {
2290: cmd.addError(x, quiet);
2291: }
2292: break;
2293: case JDBC_TABLE_NAME:
2294: if (jdbcClass.tableName != null) {
2295: RuntimeException x = BindingSupportImpl
2296: .getInstance()
2297: .runtime(
2298: "Class "
2299: + cmd.qname
2300: + " already has a jdbc-table-name: "
2301: + jdbcClass.tableName + "\n"
2302: + e.getContext());
2303: cmd.addError(x, quiet);
2304: }
2305: jdbcClass.tableName = e.getString();
2306: break;
2307: case JDBC_KEY_GENERATOR:
2308: if (cmd.pcSuperMetaData != null) {
2309: RuntimeException x = BindingSupportImpl
2310: .getInstance()
2311: .runtime(
2312: "The jdbc-key-generator extension is only allowed for "
2313: + "the least derived class in a heirachy\n"
2314: + e.getContext());
2315: cmd.addError(x, quiet);
2316: }
2317: info = getClassInfo(cmd);
2318: if (e.value != null) {
2319: JdbcKeyGeneratorFactory f = findKeyGenFactory(e);
2320: if (info.keyGenFactory != f) {
2321: info.keyGenFactory = f;
2322: info.keyGenFactoryArgs = f.createArgsBean();
2323: }
2324: } else if (info.keyGenFactory == null) {
2325: RuntimeException x = BindingSupportImpl
2326: .getInstance()
2327: .runtime(
2328: "The jdbc-key-generator extension must specify a factory "
2329: + "for application identity classes\n"
2330: + e.getContext());
2331: cmd.addError(x, quiet);
2332: }
2333: BeanUtils.setProperties(info.keyGenFactoryArgs, e
2334: .getPropertyMap());
2335: break;
2336: case JDBC_INDEX:
2337: try {
2338: info = getClassInfo(cmd);
2339: info.indexExts.add(e);
2340: } catch (RuntimeException x) {
2341: cmd.addError(x, quiet);
2342: }
2343: break;
2344: case JDBC_OPTIMISTIC_LOCKING:
2345: try {
2346: processClassOptimisticLocking(e, jdbcClass, cmd, quiet);
2347: } catch (RuntimeException x) {
2348: cmd.addError(x, quiet);
2349: }
2350: break;
2351: case JDBC_CLASS_ID:
2352: try {
2353: processJdbcClassIdExtension(cmd, e, jdbcClass);
2354: } catch (RuntimeException x) {
2355: cmd.addError(x, quiet);
2356: }
2357: break;
2358: case JDBC_COLUMN:
2359: try {
2360: processClassColumnExtension(e, cmd, jdbcElements);
2361: } catch (RuntimeException x) {
2362: cmd.addError(x, quiet);
2363: }
2364: break;
2365: case JDBC_PRIMARY_KEY:
2366: try {
2367: processClassPrimaryKeyExtension(e, cmd, jdbcElements);
2368: } catch (RuntimeException x) {
2369: cmd.addError(x, quiet);
2370: }
2371: break;
2372: case JDBC_PK_FK_CONSTRAINT_NAME:
2373: info = getClassInfo(cmd);
2374: if (info.pkFkConstraintName != null) {
2375: RuntimeException x = BindingSupportImpl.getInstance()
2376: .runtime(
2377: "The jdbc-pk-fk-constraint extension may only appear once\n"
2378: + e.getContext());
2379: cmd.addError(x, quiet);
2380: }
2381: info.pkFkConstraintName = e.getString();
2382: break;
2383: case JDBC_USE_JOIN:
2384: try {
2385: jdbcClass.useJoin = e.getEnum(jdbcMDE.USE_JOIN_ENUM);
2386: } catch (RuntimeException x) {
2387: cmd.addError(x, quiet);
2388: }
2389: break;
2390: case JDBC_DO_NOT_CREATE_TABLE:
2391: try {
2392: jdbcClass.doNotCreateTable = e.getBoolean();
2393: } catch (RuntimeException x) {
2394: cmd.addError(x, quiet);
2395: }
2396: break;
2397: default:
2398: if (e.isJdbc())
2399: MetaDataBuilder.throwUnexpectedExtension(e);
2400: }
2401: }
2402:
2403: private void processJdbcClassIdExtension(ClassMetaData cmd,
2404: JdoExtension e, JdbcClass jdbcClass) {
2405: ClassInfo info;
2406: info = getClassInfo(cmd);
2407: if (info.classIdExt != null) {
2408: throw BindingSupportImpl.getInstance().invalidOperation(
2409: "The jdbc-class-id extension may "
2410: + "only be specified once\n"
2411: + e.getContext());
2412: }
2413: info.classIdExt = e;
2414: if (e.value != null) {
2415: info.noClassIdCol = false;
2416: if (e.isNoValue()) {
2417: if (cmd.pcSuperMetaData != null) {
2418: throw BindingSupportImpl
2419: .getInstance()
2420: .invalidOperation(
2421: JdoExtension.NO_VALUE
2422: + " is only valid "
2423: + "for the least derived class in the "
2424: + "heircachy:\n"
2425: + e.getContext());
2426: }
2427: info.noClassIdCol = true;
2428: } else if (JdoExtension.NAME_VALUE.equals(e.value)) {
2429: jdbcClass.jdbcClassId = cmd.jdoClass.name;
2430: } else if (JdoExtension.FULLNAME_VALUE.equals(e.value)) {
2431: jdbcClass.jdbcClassId = cmd.qname;
2432: } else {
2433: jdbcClass.jdbcClassId = e.value;
2434: }
2435: }
2436: if (e.nested != null) {
2437: if (cmd.pcSuperMetaData != null) {
2438: throw BindingSupportImpl
2439: .getInstance()
2440: .runtime(
2441: "The jdbc-class-id extension may "
2442: + "only define a column for the least derived class in the "
2443: + "heirachy\n" + e.getContext());
2444: }
2445: }
2446: }
2447:
2448: private void processClassOptimisticLocking(JdoExtension e,
2449: JdbcClass jdbcClass, ClassMetaData cmd, boolean quiet) {
2450: if (cmd.pcSuperMetaData != null) {
2451: RuntimeException x = BindingSupportImpl
2452: .getInstance()
2453: .runtime(
2454: "The jdbc-optimistic-locking "
2455: + "option may only be specified for the least derived class "
2456: + "in a heirachy\n"
2457: + e.getContext());
2458: cmd.addError(x, quiet);
2459: }
2460: if (e.value != null) {
2461: try {
2462: jdbcClass.optimisticLocking = e
2463: .getEnum(jdbcMDE.OPTIMISTIC_LOCKING_ENUM);
2464: } catch (RuntimeException x) {
2465: cmd.addError(x, quiet);
2466: }
2467: }
2468: getClassInfo(cmd).optimisticLockingExt = e;
2469: }
2470:
2471: private void processClassColumnExtension(JdoExtension e,
2472: ClassMetaData cmd, ArrayList jdbcElements) {
2473: JdbcColumn col = createColumn(e.nested, CLASS_ID_FIELDNAME,
2474: Integer.TYPE);
2475: JdbcSimpleField f = new JdbcSimpleField();
2476: f.fake = true;
2477: f.col = col;
2478: FieldMetaData fmd = f.fmd = new FieldMetaData();
2479: fmd.fake = true;
2480: fmd.category = MDStatics.CATEGORY_SIMPLE;
2481: fmd.classMetaData = cmd;
2482: fmd.defaultFetchGroup = true;
2483: fmd.storeField = f;
2484: fmd.name = "jdo" + jdbcElements.size();
2485: fmd.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_PERSISTENT;
2486: fmd.setType(col.javaType);
2487: jdbcElements.add(f);
2488: }
2489:
2490: private void processClassPrimaryKeyExtension(JdoExtension e,
2491: ClassMetaData cmd, ArrayList jdbcElements) {
2492: if (cmd.identityType != MDStatics.IDENTITY_TYPE_DATASTORE) {
2493: throw BindingSupportImpl.getInstance().runtime(
2494: "jdbc-primary-key only allowed for classes "
2495: + "with identity-type 'datastore'\n"
2496: + e.getContext());
2497: }
2498:
2499: ClassInfo info;
2500: JdoExtension[] a = e.nested;
2501: int n = a == null ? 0 : a.length;
2502: for (int i = 0; i < n; i++) {
2503: JdoExtension f = a[i];
2504: switch (f.key) {
2505: case JDBC_COLUMN:
2506: // handled when column is created
2507: break;
2508: case JDBC_CONSTRAINT:
2509: info = getClassInfo(cmd);
2510: if (info.pkConstraintName != null) {
2511: throw BindingSupportImpl.getInstance().runtime(
2512: "The jdbc-constraint extension may only appear once\n"
2513: + e.getContext());
2514: }
2515: info.pkConstraintName = e.getString();
2516: break;
2517: default:
2518: if (e.isJdbc())
2519: MetaDataBuilder.throwUnexpectedExtension(e);
2520: }
2521: }
2522:
2523: JdbcColumn col = createColumn(a, DATASTORE_PK_FIELDNAME,
2524: Integer.TYPE);
2525: col.pk = true;
2526: col.nulls = false;
2527: jdbcElements.add(col);
2528: }
2529:
2530: /**
2531: * Find the factory specified in a jdbc-key-generator extension. If the
2532: * factory is new an instance is created and stored for future access.
2533: */
2534: private JdbcKeyGeneratorFactory findKeyGenFactory(JdoExtension e) {
2535: String fname = e.getString();
2536: try {
2537: return keyGenRegistry.getFactory(fname);
2538: } catch (Exception x) {
2539: throw BindingSupportImpl.getInstance().runtime(
2540: x.getMessage() + "\n" + e.getContext(), x);
2541: }
2542: }
2543:
2544: /**
2545: * Create a JdbcConverterFactory instance from an extension.
2546: */
2547: private JdbcConverterFactory createJdbcConverterFactory(
2548: JdoExtension e) {
2549: try {
2550: return converterRegistry.getFactory(e.getString());
2551: } catch (Exception x) {
2552: throw BindingSupportImpl.getInstance().runtime(
2553: "Unable to create JdbcConverterFactory\n"
2554: + e.getContext(), x);
2555: }
2556: }
2557:
2558: /**
2559: * Create a column from an optional array of extensions, an optional
2560: * fieldName and a base column. The properties of the base column are
2561: * the defaults. If nested is not null then it is searched for a
2562: * jdbc-column extension with a value matching the database
2563: * property of the store. If none is found then a jdbc-column extension
2564: * with no value is used. If it contains no jdbc-column extensions then
2565: * it is ignored. If the jdbc-type has been set in the extension then
2566: * it is used to provide the defaults, not the column.
2567: */
2568: public JdbcColumn createColumn(JdoExtension[] nested,
2569: JdbcColumn base) {
2570:
2571: // look for a jdbc-column extension matching our db or with no db
2572: nested = findMatchingJdbcColumn(nested);
2573:
2574: // copy the base column and use the mapping to change properties
2575: JdbcColumn c = base.copy();
2576: if (nested != null) {
2577: JdbcJavaTypeMapping m = createFieldMapping(nested);
2578: mappingResolver.fillMappingForJdbcType(m);
2579: c.updateFrom(m, mappingResolver);
2580: updateColumnName(nested, c);
2581: updateConverter(nested, c);
2582: }
2583: return c;
2584: }
2585:
2586: /**
2587: * Create a column from an optional array of extensions, an optional
2588: * fieldName and an optional javaType. If nested is not null then it is
2589: * searched for a jdbc-column extension with a value matching the database
2590: * property of the store. If none is found then a jdbc-column extension
2591: * with no value is used. If it contains no jdbc-column extensions then
2592: * it is ignored.
2593: */
2594: public JdbcColumn createColumn(JdoExtension[] nested,
2595: String fieldName, Class javaType) {
2596:
2597: // look for a jdbc-column extension matching our db or with no db
2598: nested = findMatchingJdbcColumn(nested);
2599:
2600: // create a fully resolved mapping and use this to make the column
2601: JdbcJavaTypeMapping m = createFieldMapping(nested);
2602: m = mappingResolver.resolveMapping(m, fieldName, javaType);
2603: JdbcColumn c = new JdbcColumn(m, mappingResolver);
2604: updateColumnName(nested, c);
2605: updateConverter(nested, c);
2606: c.comment = fieldName;
2607: return c;
2608: }
2609:
2610: /**
2611: * Set the converter (if any).
2612: */
2613: private void updateConverter(JdoExtension[] nested, JdbcColumn c) {
2614: if (nested != null) {
2615: boolean done = false;
2616: int n = nested.length;
2617: for (int i = 0; i < n; i++) {
2618: JdoExtension e = nested[i];
2619: if (e.key == JDBC_CONVERTER) {
2620: if (done) {
2621: throw BindingSupportImpl.getInstance().runtime(
2622: "jdbc-converter extension has already been used\n"
2623: + e.getContext());
2624: }
2625: JdbcConverterFactory f = createJdbcConverterFactory(e);
2626: HashMap p = e.getPropertyMap();
2627: try {
2628: c.converter = f.createJdbcConverter(c, p,
2629: mappingResolver);
2630: } catch (IllegalArgumentException x) {
2631: throw BindingSupportImpl.getInstance().runtime(
2632: "Unable to create JdbcConverter\n"
2633: + e.getContext(), x);
2634: }
2635: done = true;
2636: }
2637: }
2638: }
2639: }
2640:
2641: /**
2642: * Set the column name (if any).
2643: */
2644: private void updateColumnName(JdoExtension[] nested, JdbcColumn c) {
2645: if (nested != null) {
2646: int n = nested.length;
2647: for (int i = 0; i < n; i++) {
2648: JdoExtension e = nested[i];
2649: if (e.key != JDBC_COLUMN_NAME)
2650: continue;
2651: if (c.name != null) {
2652: throw BindingSupportImpl.getInstance().runtime(
2653: "Only one jdbc-column-name extension is allowed\n"
2654: + e.getContext());
2655: }
2656: c.name = e.getString();
2657: }
2658: }
2659: }
2660:
2661: /**
2662: * If nested is not null then it is searched for a jdbc-column extension
2663: * with a value matching the database property of the store. If none is
2664: * found then a jdbc-column extension with no value is used. If it
2665: * contains no jdbc-column extensions then it is ignored.
2666: */
2667: private JdoExtension[] findMatchingJdbcColumn(JdoExtension[] nested) {
2668: if (nested != null) {
2669: JdoExtension matchGeneral = null;
2670: JdoExtension matchSpecific = null;
2671: String sdb = sqlDriver.getName();
2672: int n = nested.length;
2673: for (int i = 0; i < n; i++) {
2674: JdoExtension e = nested[i];
2675: if (e.key != JDBC_COLUMN)
2676: continue;
2677: String db = e.value;
2678: if (db == null) {
2679: if (matchGeneral != null) {
2680: throw BindingSupportImpl.getInstance().runtime(
2681: "Only one all databases jdbc-column extension is allowed\n"
2682: + e.getContext());
2683: }
2684: matchGeneral = e;
2685: } else if (db.equals(sdb)) {
2686: if (matchSpecific != null) {
2687: throw BindingSupportImpl.getInstance().runtime(
2688: "Only one jdbc-column extension is allowed per database\n"
2689: + e.getContext());
2690: }
2691: matchSpecific = e;
2692: }
2693: }
2694: if (matchSpecific != null) {
2695: nested = matchSpecific.nested;
2696: } else if (matchGeneral != null) {
2697: nested = matchGeneral.nested;
2698: } else {
2699: nested = null;
2700: }
2701: }
2702: return nested;
2703: }
2704:
2705: /**
2706: * Create a field mapping from an array of extensions.
2707: *
2708: * @param nested Nested extensions (may be null)
2709: */
2710: private JdbcJavaTypeMapping createFieldMapping(JdoExtension[] nested) {
2711: JdbcJavaTypeMapping m = new JdbcJavaTypeMapping();
2712: if (nested == null)
2713: return m;
2714: int n = nested.length;
2715: for (int i = 0; i < n; i++) {
2716: JdoExtension e = nested[i];
2717: switch (e.key) {
2718: case JDBC_COLUMN_NAME:
2719: if (m.getColumnName() != null) {
2720: throw BindingSupportImpl.getInstance().runtime(
2721: "jdbc-column-name has already been set\n"
2722: + e.getContext());
2723: }
2724: m.setColumnName(e.getString());
2725: break;
2726: case JDBC_TYPE:
2727: if (m.getJdbcType() != 0) {
2728: throw BindingSupportImpl.getInstance().runtime(
2729: "jdbc-type has already been set\n"
2730: + e.getContext());
2731: }
2732: m.setJdbcType(getJdbcType(e));
2733: break;
2734: case JDBC_SQL_TYPE:
2735: if (m.getSqlType() != null) {
2736: throw BindingSupportImpl.getInstance().runtime(
2737: "jdbc-sql-type has already been set\n"
2738: + e.getContext());
2739: }
2740: m.setSqlType(e.getString());
2741: break;
2742: case JDBC_LENGTH:
2743: if (m.getLength() >= 0) {
2744: throw BindingSupportImpl.getInstance().runtime(
2745: "jdbc-length has already been set\n"
2746: + e.getContext());
2747: }
2748: m.setLength(e.getInt());
2749: break;
2750: case JDBC_SCALE:
2751: if (m.getScale() >= 0) {
2752: throw BindingSupportImpl.getInstance().runtime(
2753: "jdbc-scale has already been set\n"
2754: + e.getContext());
2755: }
2756: m.setScale(e.getInt());
2757: break;
2758: case JDBC_NULLS:
2759: if (m.getNulls() != JdbcJavaTypeMapping.NOT_SET) {
2760: throw BindingSupportImpl.getInstance().runtime(
2761: "jdbc-nulls has already been set\n"
2762: + e.getContext());
2763: }
2764: m.setNulls(e.getBoolean());
2765: break;
2766: case JDBC_SHARED:
2767: // ignore - shared columns are now figured out automatically
2768: break;
2769: case JDBC_JAVA_TYPE:
2770: if (m.getJavaType() != null) {
2771: throw BindingSupportImpl.getInstance().runtime(
2772: "jdbc-java-type has already been set\n"
2773: + e.getContext());
2774: }
2775: m.setJavaType(e.getType(loader));
2776: break;
2777: case JDBC_CONVERTER:
2778: // handled once the column has been created
2779: break;
2780: default:
2781: if (e.isJdbc())
2782: MetaDataBuilder.throwUnexpectedExtension(e);
2783: break;
2784: }
2785: }
2786: return m;
2787: }
2788:
2789: /**
2790: * Get the value of an extension that must be a JDBC type name.
2791: *
2792: * @see java.sql.Types
2793: */
2794: public int getJdbcType(JdoExtension e) {
2795: try {
2796: return JdbcTypes.parse(e.getString());
2797: } catch (IllegalArgumentException x) {
2798: throw BindingSupportImpl.getInstance().runtime(
2799: x.getMessage() + "\n" + e.getContext());
2800: }
2801: }
2802:
2803: public ModelMetaData getJmd() {
2804: return jmd;
2805: }
2806:
2807: public SqlDriver getSqlDriver() {
2808: return sqlDriver;
2809: }
2810:
2811: public JdbcMappingResolver getMappingResolver() {
2812: return mappingResolver;
2813: }
2814:
2815: protected void setMasterDetailFlags(ClassMetaData[] classes) {
2816: if (Debug.DEBUG) {
2817: System.out.println("MDB-JDBC: Setting master detail flags");
2818: }
2819: int clen = classes.length;
2820: for (int i = 0; i < clen; i++) {
2821: ClassMetaData cmd = classes[i];
2822: if (cmd.stateFields == null)
2823: continue; // possible if prev error
2824: for (int j = 0; j < cmd.stateFields.length; j++) {
2825: FieldMetaData fmd = cmd.stateFields[j];
2826: switch (fmd.category) {
2827: case MDStatics.CATEGORY_REF:
2828: JdbcField jdbcField = (JdbcField) fmd.storeField;
2829: if (jdbcField != null) { // possible if prev error
2830: JdbcFKCollectionField masterField = ((JdbcRefField) jdbcField).masterCollectionField;
2831: if (masterField != null) {
2832: fmd.inverseFieldMetaData = masterField.fmd;
2833: fmd.isDetail = true;
2834: fmd.managed = masterField.fmd.managed;
2835: }
2836: }
2837: break;
2838: case MDStatics.CATEGORY_COLLECTION:
2839: if (fmd.storeField instanceof JdbcFKCollectionField) {
2840: fmd.inverseFieldMetaData = ((JdbcFKCollectionField) fmd.storeField).fkField.fmd;
2841: fmd.isMaster = true;
2842: }
2843: break;
2844: }
2845: }
2846: }
2847: }
2848:
2849: protected void calcSuperCounts(ClassMetaData[] classes) {
2850: super .calcSuperCounts(classes);
2851: // find all the references that are being used to complete
2852: // collections mapped using a foreign key
2853: for (int j = 0; j < classes.length; j++) {
2854: ClassMetaData cmd = classes[j];
2855: if (cmd == null || cmd.stateFields == null) {
2856: continue;
2857: }
2858: int[] a = new int[cmd.stateFields.length];
2859: int c = 0;
2860: for (int i = cmd.stateFields.length - 1; i >= 0; i--) {
2861: FieldMetaData f = cmd.stateFields[i];
2862: if (f.storeField instanceof JdbcRefField) {
2863: JdbcRefField rf = (JdbcRefField) f.storeField;
2864: if (rf.masterCollectionField != null) {
2865: a[c++] = i;
2866: }
2867: }
2868: }
2869: if (c > 0) {
2870: int[] b = new int[c];
2871: System.arraycopy(a, 0, b, 0, c);
2872: cmd.fkCollectionRefStateFieldNos = b;
2873: } else {
2874: cmd.fkCollectionRefStateFieldNos = null;
2875: }
2876: }
2877: }
2878:
2879: }
|