001: /**********************************************************************
002: Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2004 Erik Bengtson - original version in ClassTable
018: 2005 Andy Jefferson - changed to work from ColumnMetaData and create missing entries
019: ...
020: **********************************************************************/package org.jpox.store.mapping;
021:
022: import java.util.HashMap;
023: import java.util.Map;
024:
025: import org.jpox.exceptions.JPOXUserException;
026: import org.jpox.metadata.ColumnMetaData;
027: import org.jpox.metadata.ColumnMetaDataContainer;
028: import org.jpox.metadata.MetaData;
029: import org.jpox.store.DatastoreIdentifier;
030: import org.jpox.util.Localiser;
031:
032: /**
033: * Class to make correspondence between columns in one side of an
034: * association to the mapping at the other side. The 2 sides of the association are
035: * referred to as "sideA" and "sideB". The JDO 2 metadata allows definition
036: * of the correspondence using the
037: * <column name="{column-name}" target="{target-name}"/> syntax.
038: * <P>
039: * This means that the column specified on sideA will be mapped to the specified "target"
040: * column on sideB. If no target is provided then the first available sideB column
041: * is used for the mapping. Where no columns are defined on sideA, then they will be
042: * created to match those on sideB. Checks are made for consistency of the sideA data.
043: * When there is insufficient ColumnMetaData on sideA then a new ColumnMetaData is added
044: * to the column container.
045: *
046: * @version $Revision: 1.1 $
047: */
048: public class CorrespondentColumnsMapper {
049: /** Localiser for messages. */
050: protected static final Localiser LOCALISER = Localiser
051: .getInstance("org.jpox.store.Localisation");
052:
053: /** Map of the ColumnMetaData for each column keyed by the sideB identifier name. */
054: private final Map columnMetaDataBySideBIdentifier = new HashMap();
055:
056: private final String columnsName;
057:
058: /**
059: * Constructor.
060: * Takes the sideB mapping and the side A definition of column metadata and matches them up as
061: * defined by the user, and if not defined by the user matches them as best it can. This constructor
062: * allows specification of the column metadata array directly, rather than taking what the container has
063: * - is used by ColumnCreator where the user has specified multiple columns but only some of them are for
064: * this field being mapped.
065: * @param columnContainer Container of the columns for side A
066: * @param colmds MetaData for the columns to be used
067: * @param mappingSideB the mapping in the side B
068: * @param updateContainer Whether to add any missing ColumnMetaData objects to the container
069: */
070: public CorrespondentColumnsMapper(
071: ColumnMetaDataContainer columnContainer,
072: ColumnMetaData[] colmds, JavaTypeMapping mappingSideB,
073: boolean updateContainer) {
074: // Go through the user-defined columns and allocate them as required
075: if (columnContainer != null && colmds != null) {
076: int noOfUserColumns = colmds.length;
077:
078: // Generate string of user-specified columns for use in diagnostics
079: StringBuffer str = new StringBuffer("Columns [");
080: for (int i = 0; i < noOfUserColumns; i++) {
081: str.append(colmds[i].getName());
082: if (i < noOfUserColumns - 1) {
083: str.append(", ");
084: }
085: }
086: str.append("]");
087: columnsName = str.toString();
088:
089: // Check if too many columns have been defined
090: if (noOfUserColumns > mappingSideB
091: .getNumberOfDatastoreFields()) {
092: throw new JPOXUserException(LOCALISER.msg("020003",
093: columnsName, "" + noOfUserColumns, ""
094: + mappingSideB
095: .getNumberOfDatastoreFields()))
096: .setFatal();
097: }
098:
099: // Retrieve sideB column names
100: DatastoreIdentifier[] sideBidentifiers = new DatastoreIdentifier[mappingSideB
101: .getNumberOfDatastoreFields()];
102: boolean[] sideButilised = new boolean[mappingSideB
103: .getNumberOfDatastoreFields()];
104: for (int i = 0; i < mappingSideB
105: .getNumberOfDatastoreFields(); i++) {
106: sideBidentifiers[i] = mappingSideB.getDataStoreMapping(
107: i).getDatastoreField().getIdentifier();
108: sideButilised[i] = false;
109: }
110: JavaTypeMapping[] sideBidMappings = null;
111: if (mappingSideB instanceof PersistenceCapableMapping) {
112: sideBidMappings = ((PersistenceCapableMapping) mappingSideB)
113: .getJavaTypeMapping();
114: } else {
115: sideBidMappings = ((MultiMapping) mappingSideB)
116: .getJavaTypeMapping();
117: }
118:
119: // Allocate the user-defined columns using the sideB list where target column has been defined
120: for (int i = 0; i < noOfUserColumns; i++) {
121: String targetColumnName = colmds[i].getTarget();
122: if (targetColumnName == null) {
123: // No target column, so try the field
124: String targetFieldName = colmds[i]
125: .getTargetMember();
126: if (targetFieldName != null) {
127: for (int j = 0; j < sideBidMappings.length; j++) {
128: if (sideBidMappings[j].getFieldMetaData()
129: .getName().equals(targetFieldName)) {
130: targetColumnName = sideBidMappings[j]
131: .getDataStoreMapping(0)
132: .getDatastoreField()
133: .getIdentifier()
134: .getIdentifier();
135: break;
136: }
137: }
138: }
139: }
140:
141: // Find the target on sideB
142: if (targetColumnName != null) {
143: boolean targetExists = false;
144: for (int j = 0; j < sideBidentifiers.length; j++) {
145: // This allows for case incorrectness in the specified name
146: if (sideBidentifiers[j].getIdentifier()
147: .equalsIgnoreCase(targetColumnName)
148: && !sideButilised[j]) {
149: putColumn(sideBidentifiers[j], colmds[i]);
150: sideButilised[j] = true;
151: targetExists = true;
152:
153: break;
154: }
155: }
156:
157: // Check for invalid sideB column
158: if (!targetExists) {
159: throw new JPOXUserException(LOCALISER.msg(
160: "020004", columnsName, colmds[i]
161: .getName(), targetColumnName))
162: .setFatal();
163: }
164: }
165: }
166:
167: // Allocate the user defined columns using the sideB list where target column has not been defined
168: for (int i = 0; i < colmds.length; i++) {
169: if (colmds[i].getTarget() == null) {
170: // Find the next unutilised column on sideB
171: for (int j = 0; j < sideBidentifiers.length; j++) {
172: if (!sideButilised[j]) {
173: putColumn(sideBidentifiers[j], colmds[i]);
174: sideButilised[j] = true;
175: break;
176: }
177: }
178: }
179: }
180:
181: // Allocate any missing columns
182: for (int i = colmds.length; i < mappingSideB
183: .getNumberOfDatastoreFields(); i++) {
184: // Find next unallocated sideB column
185: DatastoreIdentifier sideBidentifier = null;
186: for (int j = 0; j < sideBidentifiers.length; j++) {
187: if (!sideButilised[j]) {
188: sideBidentifier = sideBidentifiers[j];
189: sideButilised[j] = true;
190: break;
191: }
192: }
193: if (sideBidentifier == null) {
194: throw new JPOXUserException(LOCALISER.msg("020005",
195: columnsName, "" + i)).setFatal();
196: }
197:
198: // Create a new ColumnMetaData since user hasn't provided enough
199: ColumnMetaData colmd = new ColumnMetaData(
200: (MetaData) columnContainer, (String) null);
201: if (updateContainer) {
202: columnContainer.addColumn(colmd);
203: }
204: putColumn(sideBidentifier, colmd);
205: }
206: } else {
207: columnsName = null;
208: for (int i = 0; i < mappingSideB
209: .getNumberOfDatastoreFields(); i++) {
210: final DatastoreIdentifier sideBidentifier;
211: sideBidentifier = mappingSideB.getDataStoreMapping(i)
212: .getDatastoreField().getIdentifier();
213:
214: // Create a new ColumnMetaData since user hasn't provided enough
215: ColumnMetaData colmd = new ColumnMetaData(
216: (MetaData) columnContainer, (String) null);
217: putColumn(sideBidentifier, colmd);
218: }
219: }
220: }
221:
222: /**
223: * Constructor.
224: * Takes the sideB mapping and the side A definition of column metadata and matches them up as
225: * defined by the user, and if not defined by the user matches them as best it can.
226: * @param columnContainer Container of the columns for side A
227: * @param mappingSideB the mapping in the side B
228: * @param updateContainer Whether to add any missing ColumnMetaData objects to the container
229: */
230: public CorrespondentColumnsMapper(
231: ColumnMetaDataContainer columnContainer,
232: JavaTypeMapping mappingSideB, boolean updateContainer) {
233: // Go through the user-defined columns and allocate them as required
234: if (columnContainer != null) {
235: int noOfUserColumns = columnContainer.getColumnMetaData().length;
236: ColumnMetaData[] colmds = columnContainer
237: .getColumnMetaData();
238:
239: // Generate string of user-specified columns for use in diagnostics
240: StringBuffer str = new StringBuffer("Columns [");
241: for (int i = 0; i < noOfUserColumns; i++) {
242: str.append(colmds[i].getName());
243: if (i < noOfUserColumns - 1) {
244: str.append(", ");
245: }
246: }
247: str.append("]");
248: columnsName = str.toString();
249:
250: // Check if too many columns have been defined
251: if (noOfUserColumns > mappingSideB
252: .getNumberOfDatastoreFields()) {
253: throw new JPOXUserException(LOCALISER.msg("020003",
254: columnsName, "" + noOfUserColumns, ""
255: + mappingSideB
256: .getNumberOfDatastoreFields()))
257: .setFatal();
258: }
259:
260: // Retrieve sideB column names
261: DatastoreIdentifier[] sideBidentifiers = new DatastoreIdentifier[mappingSideB
262: .getNumberOfDatastoreFields()];
263: boolean[] sideButilised = new boolean[mappingSideB
264: .getNumberOfDatastoreFields()];
265: for (int i = 0; i < mappingSideB
266: .getNumberOfDatastoreFields(); i++) {
267: sideBidentifiers[i] = mappingSideB.getDataStoreMapping(
268: i).getDatastoreField().getIdentifier();
269: sideButilised[i] = false;
270: }
271: JavaTypeMapping[] sideBidMappings = null;
272: if (mappingSideB instanceof PersistenceCapableMapping) {
273: sideBidMappings = ((PersistenceCapableMapping) mappingSideB)
274: .getJavaTypeMapping();
275: } else {
276: sideBidMappings = ((MultiMapping) mappingSideB)
277: .getJavaTypeMapping();
278: }
279:
280: // Allocate the user-defined columns using the sideB list where target column has been defined
281: for (int i = 0; i < noOfUserColumns; i++) {
282: String targetColumnName = colmds[i].getTarget();
283: if (targetColumnName == null) {
284: // No target column, so try the field
285: String targetFieldName = colmds[i]
286: .getTargetMember();
287: if (targetFieldName != null) {
288: for (int j = 0; j < sideBidMappings.length; j++) {
289: if (sideBidMappings[j].getFieldMetaData()
290: .getName().equals(targetFieldName)) {
291: targetColumnName = sideBidMappings[j]
292: .getDataStoreMapping(0)
293: .getDatastoreField()
294: .getIdentifier()
295: .getIdentifier();
296: break;
297: }
298: }
299: }
300: }
301:
302: // Find the target on sideB
303: if (targetColumnName != null) {
304: boolean targetExists = false;
305: for (int j = 0; j < sideBidentifiers.length; j++) {
306: // This allows for case incorrectness in the specified name
307: if (sideBidentifiers[j].getIdentifier()
308: .equalsIgnoreCase(targetColumnName)
309: && !sideButilised[j]) {
310: putColumn(sideBidentifiers[j], colmds[i]);
311: sideButilised[j] = true;
312: targetExists = true;
313:
314: break;
315: }
316: }
317:
318: // Check for invalid sideB column
319: if (!targetExists) {
320: throw new JPOXUserException(LOCALISER.msg(
321: "020004", columnsName, colmds[i]
322: .getName(), targetColumnName))
323: .setFatal();
324: }
325: }
326: }
327:
328: // Allocate the user defined columns using the sideB list where target column has not been defined
329: for (int i = 0; i < colmds.length; i++) {
330: if (colmds[i].getTarget() == null) {
331: // Find the next unutilised column on sideB
332: for (int j = 0; j < sideBidentifiers.length; j++) {
333: if (!sideButilised[j]) {
334: putColumn(sideBidentifiers[j], colmds[i]);
335: sideButilised[j] = true;
336: break;
337: }
338: }
339: }
340: }
341:
342: // Allocate any missing columns
343: for (int i = colmds.length; i < mappingSideB
344: .getNumberOfDatastoreFields(); i++) {
345: // Find next unallocated sideB column
346: DatastoreIdentifier sideBidentifier = null;
347: for (int j = 0; j < sideBidentifiers.length; j++) {
348: if (!sideButilised[j]) {
349: sideBidentifier = sideBidentifiers[j];
350: sideButilised[j] = true;
351: break;
352: }
353: }
354: if (sideBidentifier == null) {
355: throw new JPOXUserException(LOCALISER.msg("020005",
356: columnsName, "" + i)).setFatal();
357: }
358:
359: // Create a new ColumnMetaData since user hasn't provided enough
360: ColumnMetaData colmd = new ColumnMetaData(
361: (MetaData) columnContainer, (String) null);
362: if (updateContainer) {
363: columnContainer.addColumn(colmd);
364: }
365: putColumn(sideBidentifier, colmd);
366: }
367: } else {
368: columnsName = null;
369: for (int i = 0; i < mappingSideB
370: .getNumberOfDatastoreFields(); i++) {
371: final DatastoreIdentifier sideBidentifier;
372: sideBidentifier = mappingSideB.getDataStoreMapping(i)
373: .getDatastoreField().getIdentifier();
374:
375: // Create a new ColumnMetaData since user hasn't provided enough
376: ColumnMetaData colmd = new ColumnMetaData(
377: (MetaData) columnContainer, (String) null);
378: putColumn(sideBidentifier, colmd);
379: }
380: }
381: }
382:
383: /**
384: * Accessor for the column MetaData in side A that maps to the side B identifier.
385: * @param name The side B identifier
386: * @return ColumnMetaData in side A that equates to the side B column
387: */
388: public ColumnMetaData getColumnMetaDataByIdentifier(
389: DatastoreIdentifier name) {
390: return (ColumnMetaData) columnMetaDataBySideBIdentifier
391: .get(name);
392: }
393:
394: /**
395: * Method to associate a sideB identifier with a sideA ColumnMetaData
396: * @param identifier side B identifier
397: * @param colmd side A ColumnMetaData
398: */
399: private void putColumn(DatastoreIdentifier identifier,
400: ColumnMetaData colmd) {
401: if (columnMetaDataBySideBIdentifier.put(identifier, colmd) != null) {
402: throw new JPOXUserException(LOCALISER.msg("020006",
403: identifier, columnsName)).setFatal();
404: }
405: }
406: }
|