001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc;
012:
013: import com.versant.core.metadata.parser.JdoExtension;
014: import com.versant.core.metadata.parser.JdoElement;
015: import com.versant.core.metadata.parser.JdoExtensionKeys;
016: import com.versant.core.metadata.ClassMetaData;
017: import com.versant.core.metadata.MetaDataBuilder;
018: import com.versant.core.metadata.MDStatics;
019: import com.versant.core.jdbc.metadata.*;
020:
021: import java.util.ArrayList;
022:
023: import com.versant.core.common.BindingSupportImpl;
024:
025: /**
026: * This will analyze meta data for a reference to a PC class from an
027: * array of extensions. It is used for simple reference fields and for link
028: * tables. Instances of this class may only be used once.
029: */
030: public class JdbcRefMetaDataBuilder implements JdoExtensionKeys {
031:
032: private JdbcMetaDataBuilder mdb;
033: private ClassMetaData targetClass;
034: private JdoElement context;
035: private String fieldName;
036:
037: private ArrayList colsList = new ArrayList();
038:
039: private boolean doNotCreateConstraint;
040: private String constraintName;
041: private int useJoin;
042: private JdbcColumn[] cols;
043:
044: public JdbcRefMetaDataBuilder(ClassMetaData cmd,
045: JdbcMetaDataBuilder mdb, ClassMetaData targetClass,
046: JdoElement context, String fieldName,
047: JdoExtension[] extensions, boolean quiet) {
048: this .mdb = mdb;
049: this .targetClass = targetClass;
050: this .context = context;
051: this .fieldName = fieldName;
052: try {
053: process(extensions);
054: } catch (RuntimeException e) {
055: cmd.addError(e, quiet);
056: }
057: }
058:
059: private void process(JdoExtension[] extensions) {
060: JdbcClass target = (JdbcClass) targetClass.storeClass;
061: int pklen = 1;
062: if (target != null) {
063: JdbcTable table = target.table;
064: if (table != null) {
065: JdbcColumn[] pk = table.pk;
066: if (pk != null) {
067: pklen = pk.length;
068: }
069: }
070: }
071:
072: boolean hadJdbcColumn = false;
073: if (extensions != null) {
074: JdbcColumn col;
075: int ne = extensions.length;
076: for (int i = 0; i < ne; i++) {
077: JdoExtension e = extensions[i];
078: switch (e.key) {
079:
080: case JDBC_COLUMN:
081: if (hadJdbcColumn)
082: break;
083: if (pklen > 1) {
084: throw BindingSupportImpl.getInstance().runtime(
085: "jdbc-column extensions must be nested in jdbc-ref "
086: + "extensions as "
087: + targetClass.qname + " has "
088: + "composite primary key\n"
089: + e.getContext());
090: }
091: if (target != null) {
092: colsList.add(mdb.createColumn(extensions,
093: target.table.pk[0]));
094: } else { // not a jdbc class
095: colsList.add(mdb.createColumn(extensions,
096: fieldName, String.class));
097: }
098: hadJdbcColumn = true;
099: break;
100:
101: case JDBC_REF:
102: if (targetClass.top.identityType != MDStatics.IDENTITY_TYPE_APPLICATION) {
103: throw BindingSupportImpl
104: .getInstance()
105: .runtime(
106: "The jdbc-ref extension may only be used here "
107: + "when referencing an application identity class\n"
108: + e.getContext());
109: }
110: String fname = e.getString();
111: JdbcSimpleField pkf = target.findPkField(fname);
112: if (pkf == null) {
113: throw BindingSupportImpl.getInstance().runtime(
114: "Primary key field '" + fname
115: + "' not found on "
116: + target.cmd.qname + "\n"
117: + e.getContext());
118: }
119: col = mdb.findColumn(colsList, pkf);
120: if (col != null) {
121: throw BindingSupportImpl.getInstance().runtime(
122: "Duplicate jdbc-ref extension\n"
123: + e.getContext());
124: }
125: col = mdb.createColumn(e.nested,
126: findColForPkField(pkf));
127: col.refField = pkf;
128: colsList.add(col);
129: break;
130:
131: case JDBC_USE_JOIN:
132: useJoin = e.getEnum(mdb.jdbcMDE.USE_JOIN_ENUM);
133: break;
134:
135: case JDBC_CONSTRAINT:
136: constraintName = e.value;
137: if (e.isNoValue())
138: doNotCreateConstraint = true;
139: break;
140: case JDBC_INDEX:
141: //Handled
142: break;
143:
144: default:
145: if (e.isJdbc()) {
146: MetaDataBuilder.throwUnexpectedExtension(e);
147: }
148: break;
149: }
150: }
151: }
152:
153: // check the columns (if specified) or generate them if not
154: int nc = colsList.size();
155: if (target == null && nc == 0) {
156: colsList.add(mdb
157: .createColumn(null, fieldName, String.class));
158: nc = 1;
159: }
160: if (nc == 0) {
161: JdbcColumn[] pks = target.table.pk;
162: if (pks != null) {
163: int n = pks.length;
164: for (int i = 0; i < n; i++) {
165: JdbcColumn c = pks[i].copy();
166: c.refField = pks[i].refField;
167: colsList.add(c);
168: }
169: }
170: } else {
171: if (pklen != nc) {
172: throw BindingSupportImpl.getInstance().runtime(
173: "Class " + targetClass.qname + " has " + pklen
174: + " primary key column(s) but only "
175: + nc + " column(s) "
176: + "have been declared\n"
177: + context.getContext());
178: }
179: if (pklen > 1) { // make sure order of colsList matches ref table
180: cols = new JdbcColumn[nc];
181: for (int i = 0; i < nc; i++) {
182: JdbcColumn col = (JdbcColumn) colsList.get(i);
183: cols[((JdbcClass) targetClass.top.storeClass)
184: .findPkFieldIndex(col.refField)] = col;
185: }
186: }
187: }
188:
189: if (cols == null) {
190: nc = colsList.size();
191: cols = new JdbcColumn[nc];
192: colsList.toArray(cols);
193: }
194:
195: // make sure all the cols have pk=false and foreignKey=true
196: for (int i = cols.length - 1; i >= 0; i--) {
197: JdbcColumn c = cols[i];
198: c.pk = false;
199: c.foreignKey = true;
200: }
201: }
202:
203: /**
204: * If pkf belongs to targetClass then return its column. Otherwise
205: * return the corresponding column from the tableClass for targetClass
206: * (i.e. the class that owns the table that targetClass is mapped to
207: * for vertical inheritance).
208: */
209: private JdbcColumn findColForPkField(JdbcSimpleField pkf) {
210: if (pkf.fmd.classMetaData == targetClass)
211: return pkf.col;
212: JdbcColumn[] pk = ((JdbcClass) targetClass.storeClass).table.pk;
213: for (int i = pk.length - 1; i >= 0; i--) {
214: System.out.println("pk[" + i + "] = " + pk[i]);
215: if (pk[i].refField == pkf)
216: return pk[i];
217: }
218: throw BindingSupportImpl.getInstance().internal(
219: "No column found in table " + "for "
220: + targetClass.qname + " to match "
221: + pkf.fmd.name);
222: }
223:
224: public boolean isDoNotCreateConstraint() {
225: return doNotCreateConstraint;
226: }
227:
228: public String getConstraintName() {
229: return constraintName;
230: }
231:
232: public int getUseJoin() {
233: return useJoin;
234: }
235:
236: public JdbcColumn[] getCols() {
237: return cols;
238: }
239:
240: public ArrayList getColsList() {
241: return colsList;
242: }
243:
244: }
|