001: /**********************************************************************
002: Copyright (c) 2005 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: ...
018: **********************************************************************/package org.jpox.store.mapping;
019:
020: import java.util.ArrayList;
021: import java.util.Iterator;
022: import java.util.List;
023:
024: import org.jpox.ClassLoaderResolver;
025: import org.jpox.ObjectManager;
026: import org.jpox.ObjectManagerHelper;
027: import org.jpox.StateManager;
028: import org.jpox.api.ApiAdapter;
029: import org.jpox.exceptions.JPOXException;
030: import org.jpox.exceptions.JPOXUserException;
031: import org.jpox.metadata.AbstractClassMetaData;
032: import org.jpox.metadata.AbstractMemberMetaData;
033: import org.jpox.metadata.EmbeddedMetaData;
034: import org.jpox.metadata.FieldPersistenceModifier;
035: import org.jpox.metadata.MetaDataManager;
036: import org.jpox.state.StateManagerFactory;
037: import org.jpox.store.DatastoreAdapter;
038: import org.jpox.store.DatastoreContainerObject;
039: import org.jpox.store.DatastoreField;
040: import org.jpox.store.expression.LogicSetExpression;
041: import org.jpox.store.expression.ObjectExpression;
042: import org.jpox.store.expression.ObjectLiteral;
043: import org.jpox.store.expression.QueryExpression;
044: import org.jpox.store.expression.ScalarExpression;
045:
046: /**
047: * Mapping for an embedded PC object.
048: * The PC object can be embedded directly (1-1 relation) or be the element of
049: * a collection, or be the key or value of a map.
050: *
051: * @version $Revision: 1.35 $
052: **/
053: public abstract class EmbeddedMapping extends SingleFieldMapping
054: implements SimpleDatastoreRepresentation {
055: /** Mappings of the fields of the embedded PC. */
056: protected List javaTypeMappings;
057:
058: /** ClassLoader resolver */
059: protected ClassLoaderResolver clr;
060:
061: /** EmbeddedMetaData for the object being embedded. */
062: protected EmbeddedMetaData emd;
063:
064: /** Type name for the object being embedded. */
065: protected String typeName;
066:
067: /** Type of PC object. Corresponds to the values in StateManagerImpl. */
068: protected int objectType = -1;
069:
070: /** MetaData for the embedded class. */
071: protected AbstractClassMetaData embCmd = null;
072:
073: /**
074: * Initialize this JavaTypeMapping with the given DatastoreAdapter for the given FieldMetaData.
075: *
076: * @param dba The Datastore Adapter that this Mapping should use.
077: * @param fmd FieldMetaData for the field to be mapped (if any)
078: * @param container The datastore container storing this mapping (if any)
079: * @param clr the ClassLoaderResolver
080: * @throws JPOXException
081: */
082: public void initialize(DatastoreAdapter dba,
083: AbstractMemberMetaData fmd,
084: DatastoreContainerObject container, ClassLoaderResolver clr) {
085: throw new JPOXException("subclass must override this method")
086: .setFatal();
087: }
088:
089: /**
090: * Initialize this JavaTypeMapping with the given DatastoreAdapter for
091: * the given FieldMetaData.
092: *
093: * @param dba Datastore Adapter
094: * @param fmd metadata for the field
095: * @param datastoreContainer Table for persisting this field
096: * @param clr The ClassLoaderResolver
097: * @param emd Embedded MetaData for the object being embedded
098: * @param typeName type of the embedded PC object
099: * @param objectType Type of the PC object being embedded (see StateManagerImpl object types)
100: */
101: public void initialize(DatastoreAdapter dba,
102: AbstractMemberMetaData fmd,
103: DatastoreContainerObject datastoreContainer,
104: ClassLoaderResolver clr, EmbeddedMetaData emd,
105: String typeName, int objectType) {
106: super .initialize(dba, fmd, datastoreContainer, clr);
107: this .clr = clr;
108: this .emd = emd;
109: this .typeName = typeName;
110: this .objectType = objectType;
111:
112: // Find the MetaData for the embedded PC class
113: MetaDataManager mmgr = fmd.getAbstractClassMetaData()
114: .getMetaDataManager();
115: AbstractClassMetaData pcCmd = mmgr.getMetaDataForClass(
116: typeName, clr);
117: if (pcCmd == null) {
118: // Not found so must be an interface
119: if (fmd != null) {
120: // Try using the fieldTypes on the field/property - we support it if only 1 implementation
121: String[] fieldTypes = fmd.getFieldTypes();
122: if (fieldTypes != null && fieldTypes.length == 1) {
123: pcCmd = mmgr
124: .getMetaDataForClass(fieldTypes[0], clr);
125: } else if (fieldTypes != null && fieldTypes.length > 1) {
126: // TODO Cater for multiple implementations
127: throw new JPOXUserException(
128: "Field "
129: + fmd.getFullFieldName()
130: + " is a reference field that is embedded. "
131: + "JPOX doesnt support embedded reference fields that have more than 1 implementation");
132: }
133: }
134:
135: if (pcCmd == null) {
136: // Try a persistent interface
137: pcCmd = mmgr.getMetaDataForInterface(clr
138: .classForName(typeName), clr);
139: if (pcCmd == null && fmd.getFieldTypes() != null
140: && fmd.getFieldTypes().length == 1) {
141: // No MetaData for the type so try "fieldType" specified on the field
142: pcCmd = mmgr.getMetaDataForInterface(clr
143: .classForName(fmd.getFieldTypes()[0]), clr);
144: }
145: }
146: }
147:
148: embCmd = pcCmd;
149:
150: AbstractMemberMetaData[] embFmds;
151: if (emd == null && pcCmd.isEmbeddedOnly()) {
152: // No <embedded> block yet the class is defined as embedded-only so just use its own definition of fields
153: embFmds = pcCmd.getManagedMembers();
154: } else {
155: // <embedded> block so use those field definitions
156: embFmds = emd.getFieldMetaData();
157: }
158:
159: // Add all fields of the embedded class (that are persistent)
160: int[] pcFieldNumbers = pcCmd.getAllMemberPositions();
161: for (int i = 0; i < pcFieldNumbers.length; i++) {
162: AbstractMemberMetaData pcFmd = pcCmd
163: .getMetaDataForManagedMemberAtAbsolutePosition(pcFieldNumbers[i]);
164: if (pcFmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
165: if (emd != null && emd.getOwnerMember() != null
166: && emd.getOwnerMember().equals(pcFmd.getName())) {
167: // Do nothing since we dont map owner fields (since the owner is the containing object)
168: } else {
169: AbstractMemberMetaData embeddedFmd = null;
170: for (int j = 0; j < embFmds.length; j++) {
171: // Why are these even possible ? Why are they here ? Why dont they use localised messages ?
172: if (embFmds[j] == null) {
173: throw new RuntimeException(
174: "embFmds[j] is null"
175: + pcCmd.toString()
176: + " type " + typeName);
177: }
178: if (pcCmd.getMetaDataForMember(embFmds[j]
179: .getName()) == null) {
180: throw new RuntimeException(
181: "pcCmd.getField(embFmds[j].getName()) is null"
182: + pcCmd.toString()
183: + " type " + typeName
184: + " embFmds[j].getName() "
185: + embFmds[j].getName());
186: }
187:
188: // Check if this field has a mapping in the embedded specification
189: if (pcCmd.getMetaDataForMember(
190: embFmds[j].getName())
191: .getAbsoluteFieldNumber() == pcFieldNumbers[i]) {
192: embeddedFmd = embFmds[j];
193: break;
194: }
195: }
196:
197: JavaTypeMapping embFmdMapping;
198: if (embeddedFmd != null) {
199: // User has provided a field definition so map with that
200: embFmdMapping = dba.getMappingManager()
201: .getMapping(datastoreContainer,
202: embeddedFmd, dba, clr,
203: JavaTypeMapping.MAPPING_FIELD);
204: } else {
205: // User hasn't provided a field definition so map with the classes own definition
206: embFmdMapping = dba.getMappingManager()
207: .getMapping(datastoreContainer, pcFmd,
208: dba, clr,
209: JavaTypeMapping.MAPPING_FIELD);
210: }
211:
212: this .addJavaTypeMapping(embFmdMapping);
213: for (int j = 0; j < embFmdMapping
214: .getNumberOfDatastoreFields(); j++) {
215: DatastoreMapping datastoreMapping = embFmdMapping
216: .getDataStoreMapping(j);
217: this .addDataStoreMapping(datastoreMapping);
218: if (fmd.isPrimaryKey()) {
219: // Overall embedded field should be part of PK, so make all datastore fields part of it
220: DatastoreField datastoreFld = datastoreMapping
221: .getDatastoreField();
222: if (datastoreFld != null) {
223: datastoreFld.setAsPrimaryKey();
224: }
225: }
226: }
227: }
228: }
229: }
230: }
231:
232: /**
233: * Method to prepare a field mapping for use in the datastore.
234: * Overridden so it does nothing
235: */
236: protected void prepareDatastoreMapping() {
237: }
238:
239: /**
240: * Add a new JavaTypeMapping to manage.
241: * @param mapping the JavaTypeMapping
242: */
243: public void addJavaTypeMapping(JavaTypeMapping mapping) {
244: if (javaTypeMappings == null) {
245: javaTypeMappings = new ArrayList();
246: }
247: if (mapping == null) {
248: throw new JPOXException(
249: "mapping argument in EmbeddedMapping.addJavaTypeMapping is null")
250: .setFatal();
251: }
252: javaTypeMappings.add(mapping);
253: }
254:
255: /**
256: * Accessor for the number of java type mappings
257: * @return Number of java type mappings of the fields of the embedded PC element
258: */
259: public int getNumberOfJavaTypeMappings() {
260: return javaTypeMappings != null ? javaTypeMappings.size() : 0;
261: }
262:
263: /**
264: * Accessor for the java type mappings
265: * @param i the index position of the java type mapping
266: * @return the java type mapping
267: */
268: public JavaTypeMapping getJavaTypeMapping(int i) {
269: if (javaTypeMappings == null) {
270: return null;
271: }
272: return (JavaTypeMapping) javaTypeMappings.get(i);
273: }
274:
275: /**
276: * Accessor for the sub type mapping for a particular field name
277: * @param fieldName The field name
278: * @return The type mapping for that field in the embedded object
279: */
280: public JavaTypeMapping getJavaTypeMapping(String fieldName) {
281: if (javaTypeMappings == null) {
282: return null;
283: }
284: Iterator iter = javaTypeMappings.iterator();
285: while (iter.hasNext()) {
286: JavaTypeMapping m = (JavaTypeMapping) iter.next();
287: if (m.getFieldMetaData().getName().equals(fieldName)) {
288: return m;
289: }
290: }
291: return null;
292: }
293:
294: /**
295: * Mutator for the embedded object in the datastore.
296: * @param om The Object Manager managing this object
297: * @param ps The Prepared Statement
298: * @param param Param numbers in the PreparedStatement for the fields of this object
299: * @param value The embedded object to use
300: */
301: public void setObject(ObjectManager om, Object ps, int[] param,
302: Object value) {
303: setObject(om, ps, param, value, null, -1);
304: }
305:
306: /**
307: * Mutator for the embedded object in the datastore.
308: * @param om The Object Manager managing this object
309: * @param ps The Prepared Statement
310: * @param param Param numbers in the PreparedStatement for the fields of this object
311: * @param value The embedded object to use
312: * @param ownerSM StateManager of the owning object containing this embedded object
313: * @param ownerFieldNumber Field number in the owning object where this is stored
314: */
315: public void setObject(ObjectManager om, Object ps, int[] param,
316: Object value, StateManager ownerSM, int ownerFieldNumber) {
317: if (value == null) {
318: int n = 0;
319: String nullColumn = null;
320: String nullValue = null;
321: if (emd != null) {
322: nullColumn = emd.getNullIndicatorColumn();
323: nullValue = emd.getNullIndicatorValue();
324: }
325: for (int i = 0; i < javaTypeMappings.size(); i++) {
326: JavaTypeMapping mapping = ((JavaTypeMapping) javaTypeMappings
327: .get(i));
328: int[] posMapping = new int[mapping
329: .getNumberOfDatastoreFields()];
330: for (int j = 0; j < posMapping.length; j++) {
331: posMapping[j] = param[n++];
332: }
333:
334: // Null out this field unless it is the null-indicator column and has a value
335: // in which case apply the required value
336: if (nullColumn != null
337: && nullValue != null
338: && mapping.getFieldMetaData()
339: .getColumnMetaData().length > 0
340: && mapping.getFieldMetaData()
341: .getColumnMetaData()[0].getName()
342: .equals(nullColumn)) {
343: // Try to cater for user having an integer based column and value
344: if (mapping instanceof IntegerMapping
345: || mapping instanceof BigIntegerMapping
346: || mapping instanceof LongMapping
347: || mapping instanceof ShortMapping) {
348: Object convertedValue = null;
349: try {
350: if (mapping instanceof IntegerMapping
351: || mapping instanceof ShortMapping) {
352: convertedValue = new Integer(nullValue);
353: } else if (mapping instanceof LongMapping
354: || mapping instanceof BigIntegerMapping) {
355: convertedValue = new Long(nullValue);
356: }
357: } catch (Exception e) {
358: }
359: mapping.setObject(om, ps, posMapping,
360: convertedValue);
361: } else {
362: mapping
363: .setObject(om, ps, posMapping,
364: nullValue);
365: }
366: } else {
367: if (mapping.getNumberOfDatastoreFields() > 0) {
368: mapping.setObject(om, ps, posMapping, null);
369: }
370: }
371: }
372: } else {
373: ApiAdapter api = om.getApiAdapter();
374: if (!api.isPersistable(value)) {
375: throw new JPOXException(LOCALISER.msg("041016", value
376: .getClass(), value)).setFatal();
377: }
378:
379: AbstractClassMetaData embCmd = om.getMetaDataManager()
380: .getMetaDataForClass(value.getClass().getName(),
381: om.getClassLoaderResolver());
382: StateManager embSM = om.findStateManager(value);
383: if (embSM == null
384: || ObjectManagerHelper.getObjectManager(value) == null) {
385: // Assign a StateManager to manage our embedded object
386: embSM = StateManagerFactory.newStateManagerForEmbedded(
387: om, value, false);
388: embSM.addEmbeddedOwner(ownerSM, ownerFieldNumber);
389: embSM.setPcObjectType(objectType);
390: }
391:
392: int n = 0;
393: for (int i = 0; i < javaTypeMappings.size(); i++) {
394: JavaTypeMapping mapping = ((JavaTypeMapping) javaTypeMappings
395: .get(i));
396: int[] posMapping = new int[mapping
397: .getNumberOfDatastoreFields()];
398: for (int j = 0; j < posMapping.length; j++) {
399: posMapping[j] = param[n++];
400: }
401:
402: // Retrieve value of field from Embedded StateManager
403: int embAbsFieldNum = embCmd
404: .getAbsolutePositionOfMember(mapping
405: .getFieldMetaData().getName());
406: Object fieldValue = embSM.provideField(embAbsFieldNum);
407:
408: if (mapping instanceof EmbeddedPCMapping) {
409: mapping.setObject(om, ps, posMapping, fieldValue,
410: embSM, embAbsFieldNum);
411: } else {
412: if (mapping.getNumberOfDatastoreFields() > 0) {
413: mapping.setObject(om, ps, posMapping,
414: fieldValue);
415: }
416: }
417: }
418: }
419: }
420:
421: /**
422: * Accessor for the embedded object from the result set
423: * @param om ObjectManager managing this object
424: * @param rs The ResultSet
425: * @param param Array of param numbers in the ResultSet for the fields of this object
426: * @return The embedded object
427: */
428: public Object getObject(ObjectManager om, Object rs, int[] param) {
429: return getObject(om, rs, param, null, -1);
430: }
431:
432: /**
433: * Accessor for the embedded object from the result set
434: * @param om Object Manager managing this object
435: * @param rs The ResultSet
436: * @param param Array of param numbers in the ResultSet for the fields of this object
437: * @param ownerSM StateManager of the owning object containing this embedded object
438: * @param ownerFieldNumber Field number in the owning object where this is stored
439: * @return The embedded object
440: */
441: public Object getObject(ObjectManager om, Object rs, int[] param,
442: StateManager ownerSM, int ownerFieldNumber) {
443: Object value = null;
444:
445: // Create a PersistenceCapable to put the values into
446: Class embeddedType = getJavaType();
447: if (fmd.getFieldTypes() != null
448: && fmd.getFieldTypes().length > 0) {
449: // Embedded type has field-type defined so use that as our embedded type
450: embeddedType = om.getClassLoaderResolver().classForName(
451: fmd.getFieldTypes()[0]);
452: }
453: StateManager embSM = StateManagerFactory
454: .newStateManagerForHollow(om, embeddedType,
455: (Object) null);
456: embSM.setPcObjectType(objectType);
457: value = embSM.getObject();
458:
459: String nullColumn = null;
460: String nullValue = null;
461: if (emd != null) {
462: nullColumn = emd.getNullIndicatorColumn();
463: nullValue = emd.getNullIndicatorValue();
464: }
465:
466: int n = 0;
467: for (int i = 0; i < javaTypeMappings.size(); i++) {
468: JavaTypeMapping mapping = ((JavaTypeMapping) javaTypeMappings
469: .get(i));
470: int embAbsFieldNum = embCmd
471: .getAbsolutePositionOfMember(mapping
472: .getFieldMetaData().getName());
473: if (mapping instanceof EmbeddedPCMapping) {
474: // We have a nested embedded
475: int numSubParams = mapping.getNumberOfDatastoreFields();
476: int[] subParam = new int[numSubParams];
477: int k = 0;
478: for (int j = n; j < n + numSubParams; j++) {
479: subParam[k++] = param[j];
480: }
481: n += numSubParams;
482:
483: // Use the sub-object mapping to extract the value for that object
484: Object subValue = mapping.getObject(om, rs, subParam,
485: embSM, embAbsFieldNum);
486: if (subValue != null) {
487: embSM.replaceField(embAbsFieldNum, subValue, true);
488: }
489:
490: // TODO Check the null column and its value in the sub-embedded ?
491: } else {
492: // Extract the value(s) for this field and update the PC if it is not null
493: int[] posMapping = new int[mapping
494: .getNumberOfDatastoreFields()];
495: for (int j = 0; j < posMapping.length; j++) {
496: posMapping[j] = param[n++];
497: }
498: Object fieldValue = mapping.getObject(om, rs,
499: posMapping);
500: if (fieldValue != null) {
501: embSM
502: .replaceField(embAbsFieldNum, fieldValue,
503: true);
504: } else {
505: // If the value is null, but the field is not a primitive update it
506: AbstractMemberMetaData embFmd = embCmd
507: .getMetaDataForManagedMemberAtAbsolutePosition(embAbsFieldNum);
508: if (!embFmd.getType().isPrimitive()) {
509: embSM.replaceField(embAbsFieldNum, fieldValue,
510: true);
511: }
512: }
513:
514: // Check for the null column and its value
515: if (nullColumn != null
516: && mapping.getFieldMetaData()
517: .getColumnMetaData()[0].getName()
518: .equals(nullColumn)) {
519: if ((nullValue == null && fieldValue == null)
520: || (nullValue != null && fieldValue
521: .toString().equals(nullValue))) {
522: value = null;
523: break;
524: }
525: }
526: }
527: }
528:
529: // Update owner field in the element (if present)
530: if (emd != null) {
531: String ownerField = emd.getOwnerMember();
532: if (ownerField != null) {
533: int ownerFieldNumberInElement = embCmd
534: .getAbsolutePositionOfMember(ownerField);
535: if (ownerFieldNumberInElement >= 0) {
536: embSM.replaceField(ownerFieldNumberInElement,
537: ownerSM.getObject(), true);
538: }
539: }
540: }
541:
542: // Register our owner now that we have our values set
543: if (value != null && ownerSM != null) {
544: embSM.addEmbeddedOwner(ownerSM, ownerFieldNumber);
545: }
546:
547: return value;
548: }
549:
550: /**
551: * Accessor for the sample value for this type.
552: * @return Sample value
553: */
554: public Object getSampleValue(ClassLoaderResolver clr) {
555: return null;
556: }
557:
558: /**
559: * Accessor for the Java type being represented here.
560: * @return The Java type
561: */
562: public Class getJavaType() {
563: return clr.classForName(typeName);
564: }
565:
566: // --------------------------------------- JDOQL Query Methods ------------------------------------------
567:
568: public ScalarExpression newLiteral(QueryExpression qs, Object value) {
569: ScalarExpression expr = new ObjectLiteral(qs, this , value,
570: getType());
571: return expr;
572: }
573:
574: public ScalarExpression newScalarExpression(QueryExpression qs,
575: LogicSetExpression te) {
576: ScalarExpression expr = new ObjectExpression(qs, this, te);
577: return expr;
578: }
579: }
|