001: /**********************************************************************
002: Copyright (c) 2006 Erik Bengtson 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: Contributors:
016: ...
017: **********************************************************************/package org.jpox.jdo;
018:
019: import java.io.Serializable;
020: import java.lang.reflect.Constructor;
021: import java.lang.reflect.Field;
022: import java.lang.reflect.Modifier;
023: import java.util.Collection;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Map;
027:
028: import javax.jdo.JDOHelper;
029: import javax.jdo.identity.ByteIdentity;
030: import javax.jdo.identity.CharIdentity;
031: import javax.jdo.identity.IntIdentity;
032: import javax.jdo.identity.LongIdentity;
033: import javax.jdo.identity.ObjectIdentity;
034: import javax.jdo.identity.ShortIdentity;
035: import javax.jdo.identity.SingleFieldIdentity;
036: import javax.jdo.identity.StringIdentity;
037: import javax.jdo.spi.Detachable;
038: import javax.jdo.spi.JDOImplHelper;
039: import javax.jdo.spi.PersistenceCapable;
040:
041: import org.jpox.ClassLoaderResolver;
042: import org.jpox.ObjectManager;
043: import org.jpox.PersistenceConfiguration;
044: import org.jpox.StateManager;
045: import org.jpox.api.ApiAdapter;
046: import org.jpox.exceptions.JPOXException;
047: import org.jpox.jdo.state.LifeCycleStateFactory;
048: import org.jpox.metadata.AbstractClassMetaData;
049: import org.jpox.metadata.AbstractMemberMetaData;
050: import org.jpox.metadata.IdentityType;
051: import org.jpox.metadata.InvalidPrimaryKeyException;
052: import org.jpox.state.JDOStateManagerImpl;
053: import org.jpox.state.LifeCycleState;
054: import org.jpox.util.ClassUtils;
055: import org.jpox.util.JPOXLogger;
056: import org.jpox.util.Localiser;
057:
058: /**
059: * Adapter for the JDO API, to allow the JPOX core runtime to expose multiple APIs to clients.
060: *
061: * @version $Revision: 1.32 $
062: */
063: public class JDOAdapter implements ApiAdapter {
064: protected static final Localiser LOCALISER = Localiser
065: .getInstance("org.jpox.Localisation");
066:
067: // ------------------------------ Object Lifecycle --------------------------------
068:
069: /**
070: * Method to create a new StateManager for the ObjectManager and class.
071: * @param om ObjectManager
072: * @param acmd MetaData for the class/interface
073: */
074: public StateManager newStateManager(ObjectManager om,
075: AbstractClassMetaData acmd) {
076: return new JDOStateManagerImpl(om, acmd);
077: }
078:
079: /**
080: * Returns the LifeCycleState for the state constant.
081: * @param stateType the type as integer
082: * @return the type as LifeCycleState object
083: */
084: public LifeCycleState getLifeCycleState(int stateType) {
085: return LifeCycleStateFactory.getLifeCycleState(stateType);
086: }
087:
088: /**
089: * Accessor for whether the passed object is persistent.
090: * @param obj The object
091: * @return Whether it is persistent
092: */
093: public boolean isPersistent(Object obj) {
094: // Relay through to JDOHelper
095: return JDOHelper.isPersistent(obj);
096: }
097:
098: /**
099: * Accessor for whether the passed object is new.
100: * @param obj The object
101: * @return Whether it is new
102: */
103: public boolean isNew(Object obj) {
104: // Relay through to JDOHelper
105: return JDOHelper.isNew(obj);
106: }
107:
108: /**
109: * Accessor for whether the passed object is dirty.
110: * @param obj The object
111: * @return Whether it is dirty
112: */
113: public boolean isDirty(Object obj) {
114: // Relay through to JDOHelper
115: return JDOHelper.isDirty(obj);
116: }
117:
118: /**
119: * Accessor for whether the passed object is deleted.
120: * @param obj The object
121: * @return Whether it is deleted
122: */
123: public boolean isDeleted(Object obj) {
124: // Relay through to JDOHelper
125: return JDOHelper.isDeleted(obj);
126: }
127:
128: /**
129: * Accessor for whether the passed object is detached.
130: * @param obj The object
131: * @return Whether it is detached
132: */
133: public boolean isDetached(Object obj) {
134: // Relay through to JDOHelper
135: return JDOHelper.isDetached(obj);
136: }
137:
138: /**
139: * Accessor for whether the passed object is transactional.
140: * @param obj The object
141: * @return Whether it is transactional
142: */
143: public boolean isTransactional(Object obj) {
144: // Relay through to JDOHelper
145: return JDOHelper.isTransactional(obj);
146: }
147:
148: /**
149: * Method to return if the passed object is persistable using this API.
150: * Returns whether the object is an instance of javax.jdo.spi.PersistenceCapable.
151: * @param obj The object
152: * @return Whether it is persistable
153: */
154: public boolean isPersistable(Object obj) {
155: if (obj == null) {
156: return false;
157: }
158:
159: return (obj instanceof PersistenceCapable);
160: }
161:
162: /**
163: * Utility method to check if the specified class is of a type that can be persisted for this API.
164: * Checks that it implements javax.jdo.spi.PersistenceCapable.
165: * @param cls The class to check
166: * @return Whether the class is persistable
167: */
168: public boolean isPersistable(Class cls) {
169: if (cls == null) {
170: return false;
171: }
172: return (PersistenceCapable.class.isAssignableFrom(cls));
173: }
174:
175: /**
176: * Method to return if the passed object is detachable using this API.
177: * Returns whether the object is an instance of javax.jdo.spi.Detachable.
178: * @param obj The object
179: * @return Whether it is detachable
180: */
181: public boolean isDetachable(Object obj) {
182: if (obj == null) {
183: return false;
184: }
185:
186: return (obj instanceof Detachable);
187: }
188:
189: /**
190: * Accessor for the object state.
191: * @param obj Object
192: * @return The state ("persistent-clean", "detached-dirty" etc)
193: */
194: public String getObjectState(Object obj) {
195: return JPOXJDOHelper.getObjectStateAsString(obj);
196: }
197:
198: // ------------------------------ Object Identity --------------------------------
199:
200: /**
201: * Method to return the object identity for the passed persistable object.
202: * Returns null if it is not persistable, or has no identity.
203: * @param obj The object
204: * @return The identity
205: */
206: public Object getIdForObject(Object obj) {
207: if (!isPersistable(obj)) {
208: return null;
209: }
210: return ((PersistenceCapable) obj).jdoGetObjectId();
211: }
212:
213: /**
214: * Method to return the object version for the passed persistable object.
215: * Returns null if it is not persistable, or not versioned.
216: * @param obj The object
217: * @return The version
218: */
219: public Object getVersionForObject(Object obj) {
220: if (!isPersistable(obj)) {
221: return null;
222: }
223: return ((PersistenceCapable) obj).jdoGetVersion();
224: }
225:
226: /**
227: * Utility to check if a primary-key class is valid.
228: * Will throw a InvalidPrimaryKeyException if it is invalid, otherwise returning true.
229: * @param pkClass The Primary Key class
230: * @param cmd AbstractClassMetaData for the PersistenceCapable class
231: * @param clr the ClassLoaderResolver
232: * @param noOfPkFields Number of primary key fields
233: * @return Whether it is valid
234: */
235: public boolean isValidPrimaryKeyClass(Class pkClass,
236: AbstractClassMetaData cmd, ClassLoaderResolver clr,
237: int noOfPkFields) {
238: // When using inner class, must be static
239: if (ClassUtils.isInnerClass(pkClass.getName())
240: && !Modifier.isStatic(pkClass.getModifiers())) {
241: throw new InvalidPrimaryKeyException(LOCALISER, "019000",
242: cmd.getFullClassName(), pkClass.getName());
243: }
244:
245: // Must be public
246: if (!Modifier.isPublic(pkClass.getModifiers())) {
247: throw new InvalidPrimaryKeyException(LOCALISER, "019001",
248: cmd.getFullClassName(), pkClass.getName());
249: }
250:
251: // Must implement Serializable
252: if (!Serializable.class.isAssignableFrom(pkClass)) {
253: throw new InvalidPrimaryKeyException(LOCALISER, "019002",
254: cmd.getFullClassName(), pkClass.getName());
255: }
256:
257: // a). JDO's SingleFieldIdentity class
258: if (isSingleFieldIdentityClass(pkClass.getName())) {
259: if (noOfPkFields != 1) {
260: throw new InvalidPrimaryKeyException(LOCALISER,
261: "019003", cmd.getFullClassName());
262: }
263: }
264: // b). Users own primary key class
265: else {
266: // Must have public default constructor
267: try {
268: Constructor constructor = pkClass
269: .getConstructor(new Class[0]);
270: if (constructor == null
271: || !Modifier.isPublic(constructor
272: .getModifiers())) {
273: throw new InvalidPrimaryKeyException(LOCALISER,
274: "019004", cmd.getFullClassName(), pkClass
275: .getName());
276: }
277: } catch (NoSuchMethodException ex) {
278: throw new InvalidPrimaryKeyException(LOCALISER,
279: "019004", cmd.getFullClassName(), pkClass
280: .getName());
281: }
282:
283: // Must have public String arg constructor
284: try {
285: Constructor constructor = pkClass
286: .getConstructor(new Class[] { String.class });
287: if (constructor == null
288: || !Modifier.isPublic(constructor
289: .getModifiers())) {
290: throw new InvalidPrimaryKeyException(LOCALISER,
291: "019005", cmd.getFullClassName(), pkClass
292: .getName());
293: }
294: } catch (NoSuchMethodException nsme) {
295: }
296:
297: // Must override toString() method
298: try {
299: java.lang.reflect.Method method = pkClass.getMethod(
300: "toString", new Class[0]);
301: if (method == null
302: || !Modifier.isPublic(method.getModifiers())
303: || method.getDeclaringClass().equals(
304: Object.class)) {
305: throw new InvalidPrimaryKeyException(LOCALISER,
306: "019006", cmd.getFullClassName(), pkClass
307: .getName());
308: }
309: } catch (NoSuchMethodException nsme) {
310: }
311:
312: // Must override hashCode() method
313: try {
314: java.lang.reflect.Method method = pkClass.getMethod(
315: "hashCode", new Class[0]);
316: if (method == null
317: || method.getDeclaringClass().equals(
318: Object.class)) {
319: throw new InvalidPrimaryKeyException(LOCALISER,
320: "019007", cmd.getFullClassName(), pkClass
321: .getName());
322: }
323: } catch (NoSuchMethodException nsme) {
324: }
325:
326: // Must override equals(Object) method
327: try {
328: java.lang.reflect.Method method = pkClass.getMethod(
329: "equals", new Class[] { Object.class });
330: if (method == null
331: || method.getDeclaringClass().equals(
332: Object.class)) {
333: throw new InvalidPrimaryKeyException(LOCALISER,
334: "019008", cmd.getFullClassName(), pkClass
335: .getName());
336: }
337: } catch (NoSuchMethodException nsme) {
338: }
339:
340: // Check the field types of the objectid-class
341: int noPkFields = processPrimaryKeyClass(pkClass, cmd, clr);
342: Collection super classes = ClassUtils
343: .getSuperclasses(pkClass);
344: if (super classes != null && super classes.size() > 0) {
345: Iterator super classIter = super classes.iterator();
346: while (super classIter.hasNext()) {
347: Class super cls = (Class) super classIter.next();
348: noPkFields += processPrimaryKeyClass(super cls, cmd,
349: clr);
350: }
351: }
352:
353: // No of Primary Key fields and no of fields in
354: // objectid-class must concur
355: if (noOfPkFields != noPkFields
356: && cmd.getIdentityType() == IdentityType.APPLICATION) {
357: throw new InvalidPrimaryKeyException(LOCALISER,
358: "019015", cmd.getFullClassName(), pkClass
359: .getName(), "" + noOfPkFields, ""
360: + noPkFields);
361: }
362: }
363:
364: return true;
365: }
366:
367: /**
368: * Convenience method to process a PK class and return the number of valid fields found.
369: * Throws InvalidPrimaryKeyException if a field is invalid
370: * @param pkClass The PK class.
371: * @param cmd MetaData for the class that this is the PK for
372: * @return The number of PK fields
373: */
374: private int processPrimaryKeyClass(Class pkClass,
375: AbstractClassMetaData cmd, ClassLoaderResolver clr) {
376: int noOfPkFields = 0;
377:
378: Field[] fieldsInPkClass = pkClass.getDeclaredFields();
379: for (int i = 0; i < fieldsInPkClass.length; i++) {
380: if (!Modifier.isStatic(fieldsInPkClass[i].getModifiers())) {
381: // All non-static fields must be serializable
382: if (!fieldsInPkClass[i].getType().isPrimitive()
383: && !(Serializable.class)
384: .isAssignableFrom(fieldsInPkClass[i]
385: .getType())) {
386: throw new InvalidPrimaryKeyException(LOCALISER,
387: "019009", cmd.getFullClassName(), pkClass
388: .getName(), fieldsInPkClass[i]
389: .getName());
390: }
391:
392: // All non-static fields must be public
393: if (!Modifier.isPublic(fieldsInPkClass[i]
394: .getModifiers())) {
395: throw new InvalidPrimaryKeyException(LOCALISER,
396: "019010", cmd.getFullClassName(), pkClass
397: .getName(), fieldsInPkClass[i]
398: .getName());
399: }
400:
401: // non-static fields of objectid-class include
402: // persistence-capable object field
403: AbstractMemberMetaData fieldInPcClass = cmd
404: .getMetaDataForMember(fieldsInPkClass[i]
405: .getName());
406: boolean found_field = false;
407: if (fieldInPcClass == null) {
408: throw new InvalidPrimaryKeyException(LOCALISER,
409: "019011", cmd.getFullClassName(), pkClass
410: .getName(), fieldsInPkClass[i]
411: .getName());
412: }
413:
414: // check if the field in objectid-class has the same type as the
415: // Type declared in the PersistenceCapable class
416: if (fieldInPcClass.getTypeName().equals(
417: fieldsInPkClass[i].getType().getName())) {
418: found_field = true;
419: }
420:
421: // Check for primary key field that is PC (Compound Identity - aka Identifying Relations)
422: if (!found_field) {
423: String fieldTypePkClass = fieldsInPkClass[i]
424: .getType().getName();
425: AbstractClassMetaData ref_cmd = cmd
426: .getMetaDataManager()
427: .getMetaDataForClassInternal(
428: fieldInPcClass.getType(), clr);
429: if (ref_cmd == null) {
430: throw new InvalidPrimaryKeyException(LOCALISER,
431: "019012", cmd.getFullClassName(),
432: pkClass.getName(), fieldsInPkClass[i]
433: .getName(), fieldInPcClass
434: .getType().getName());
435: }
436: if (ref_cmd.getObjectidClass() == null) {
437: //Single Field Identity
438: if (isSingleFieldIdentityClass(fieldTypePkClass)) {
439: throw new InvalidPrimaryKeyException(
440: LOCALISER, "019014", cmd
441: .getFullClassName(),
442: pkClass.getName(),
443: fieldsInPkClass[i].getName(),
444: fieldTypePkClass, ref_cmd
445: .getFullClassName());
446: }
447: }
448: if (!fieldTypePkClass.equals(ref_cmd
449: .getObjectidClass())) {
450: throw new InvalidPrimaryKeyException(LOCALISER,
451: "019013", cmd.getFullClassName(),
452: pkClass.getName(), fieldsInPkClass[i]
453: .getName(), fieldTypePkClass,
454: ref_cmd.getObjectidClass());
455: }
456: found_field = true;
457: }
458: if (!found_field) {
459: throw new InvalidPrimaryKeyException(LOCALISER,
460: "019012", cmd.getFullClassName(), pkClass
461: .getName(), fieldsInPkClass[i]
462: .getName(), fieldInPcClass
463: .getType().getName());
464: }
465:
466: noOfPkFields++;
467: }
468: }
469:
470: return noOfPkFields;
471: }
472:
473: /**
474: * Accessor for whether the passed identity is a valid single-field application-identity for this API.
475: * @return Whether it is valid
476: */
477: public boolean isSingleFieldIdentity(Object id) {
478: return (id instanceof SingleFieldIdentity);
479: }
480:
481: /**
482: * Checks whether the passed class name is valid for a single field application-identity for this API.
483: * @param className the full class name
484: * @return Whether it is a single field class
485: */
486: public boolean isSingleFieldIdentityClass(String className) {
487: if (className == null || className.length() < 1) {
488: return false;
489: }
490:
491: return (className
492: .equals(JDOClassNameConstants.JAVAX_JDO_IDENTITY_BYTE_IDENTITY)
493: || className
494: .equals(JDOClassNameConstants.JAVAX_JDO_IDENTITY_CHAR_IDENTITY)
495: || className
496: .equals(JDOClassNameConstants.JAVAX_JDO_IDENTITY_INT_IDENTITY)
497: || className
498: .equals(JDOClassNameConstants.JAVAX_JDO_IDENTITY_LONG_IDENTITY)
499: || className
500: .equals(JDOClassNameConstants.JAVAX_JDO_IDENTITY_OBJECT_IDENTITY)
501: || className
502: .equals(JDOClassNameConstants.JAVAX_JDO_IDENTITY_SHORT_IDENTITY) || className
503: .equals(JDOClassNameConstants.JAVAX_JDO_IDENTITY_STRING_IDENTITY));
504: }
505:
506: /**
507: * Accessor for the class name to use for identities when there is a single Long/long field.
508: * @return Class name of identity class
509: */
510: public String getSingleFieldIdentityClassNameForLong() {
511: return JDOClassNameConstants.JAVAX_JDO_IDENTITY_LONG_IDENTITY;
512: }
513:
514: /**
515: * Accessor for the class name to use for identities when there is a single Integer/int field.
516: * @return Class name of identity class
517: */
518: public String getSingleFieldIdentityClassNameForInt() {
519: return JDOClassNameConstants.JAVAX_JDO_IDENTITY_INT_IDENTITY;
520: }
521:
522: /**
523: * Accessor for the class name to use for identities when there is a single Short/short field.
524: * @return Class name of identity class
525: */
526: public String getSingleFieldIdentityClassNameForShort() {
527: return JDOClassNameConstants.JAVAX_JDO_IDENTITY_SHORT_IDENTITY;
528: }
529:
530: /**
531: * Accessor for the class name to use for identities when there is a single Byte/byte field.
532: * @return Class name of identity class
533: */
534: public String getSingleFieldIdentityClassNameForByte() {
535: return JDOClassNameConstants.JAVAX_JDO_IDENTITY_BYTE_IDENTITY;
536: }
537:
538: /**
539: * Accessor for the class name to use for identities when there is a single Character/char field.
540: * @return Class name of identity class
541: */
542: public String getSingleFieldIdentityClassNameForChar() {
543: return JDOClassNameConstants.JAVAX_JDO_IDENTITY_CHAR_IDENTITY;
544: }
545:
546: /**
547: * Accessor for the class name to use for identities when there is a single String field.
548: * @return Class name of identity class
549: */
550: public String getSingleFieldIdentityClassNameForString() {
551: return JDOClassNameConstants.JAVAX_JDO_IDENTITY_STRING_IDENTITY;
552: }
553:
554: /**
555: * Accessor for the class name to use for identities when there is a single Object field.
556: * @return Class name of identity class
557: */
558: public String getSingleFieldIdentityClassNameForObject() {
559: return JDOClassNameConstants.JAVAX_JDO_IDENTITY_OBJECT_IDENTITY;
560: }
561:
562: /**
563: * Accessor for the target class for the specified single field application-identity.
564: * @param id The identity
565: * @return The target class
566: */
567: public Class getTargetClassForSingleFieldIdentity(Object id) {
568: if (id instanceof SingleFieldIdentity) {
569: return ((SingleFieldIdentity) id).getTargetClass();
570: }
571: return null;
572: }
573:
574: /**
575: * Accessor for the target class name for the specified single field identity.
576: * @param id The identity
577: * @return The target class name
578: */
579: public String getTargetClassNameForSingleFieldIdentity(Object id) {
580: if (id instanceof SingleFieldIdentity) {
581: return ((SingleFieldIdentity) id).getTargetClassName();
582: }
583: return null;
584: }
585:
586: /**
587: * Accessor for the key object for the specified single field application-identity.
588: * @param id The identity
589: * @return The key object
590: */
591: public Object getTargetKeyForSingleFieldIdentity(Object id) {
592: if (id instanceof SingleFieldIdentity) {
593: return ((SingleFieldIdentity) id).getKeyAsObject();
594: }
595: return null;
596: }
597:
598: /**
599: * Accessor for the type of the single field application-identity key given the single field identity type.
600: * @param idType Single field identity type
601: * @return key type
602: */
603: public Class getKeyTypeForSingleFieldIdentityType(Class idType) {
604: if (idType == null) {
605: return null;
606: }
607: if (!isSingleFieldIdentityClass(idType.getName())) {
608: return null;
609: }
610:
611: if (LongIdentity.class.isAssignableFrom(idType)) {
612: return Long.class;
613: } else if (IntIdentity.class.isAssignableFrom(idType)) {
614: return Integer.class;
615: } else if (ShortIdentity.class.isAssignableFrom(idType)) {
616: return Short.class;
617: } else if (ByteIdentity.class.isAssignableFrom(idType)) {
618: return Byte.class;
619: } else if (CharIdentity.class.isAssignableFrom(idType)) {
620: return Character.class;
621: } else if (StringIdentity.class.isAssignableFrom(idType)) {
622: return String.class;
623: } else if (ObjectIdentity.class.isAssignableFrom(idType)) {
624: return Object.class;
625: }
626: return null;
627: }
628:
629: /**
630: * Utility to create a new SingleFieldIdentity using reflection when you know the
631: * type of the PersistenceCapable, and also which SingleFieldIdentity, and the value of the key.
632: * @param idType Type of SingleFieldIdentity
633: * @param pcType Type of the PersistenceCapable
634: * @param value The value for the identity (the Long, or Int, or ... etc).
635: * @return Single field identity
636: * @throws JPOXException if invalid input is received
637: */
638: public Object getNewSingleFieldIdentity(Class idType, Class pcType,
639: Object value) {
640: if (idType == null) {
641: throw new JPOXException(LOCALISER.msg("029001", pcType))
642: .setFatal();
643: }
644: if (pcType == null) {
645: throw new JPOXException(LOCALISER.msg("029000", idType))
646: .setFatal();
647: }
648: if (value == null) {
649: throw new JPOXException(LOCALISER.msg("029003", idType,
650: pcType)).setFatal();
651: }
652: if (!SingleFieldIdentity.class.isAssignableFrom(idType)) {
653: throw new JPOXException(LOCALISER.msg("029002", idType
654: .getName(), pcType.getName())).setFatal();
655: }
656:
657: SingleFieldIdentity id = null;
658: Class keyType = null;
659: if (idType == LongIdentity.class) {
660: keyType = Long.class;
661: if (!(value instanceof Long)) {
662: throw new JPOXException(LOCALISER.msg("029004", idType
663: .getName(), pcType.getName(), value.getClass()
664: .getName(), "Long")).setFatal();
665: }
666: } else if (idType == IntIdentity.class) {
667: keyType = Integer.class;
668: if (!(value instanceof Integer)) {
669: throw new JPOXException(LOCALISER.msg("029004", idType
670: .getName(), pcType.getName(), value.getClass()
671: .getName(), "Integer")).setFatal();
672: }
673: } else if (idType == StringIdentity.class) {
674: keyType = String.class;
675: if (!(value instanceof String)) {
676: throw new JPOXException(LOCALISER.msg("029004", idType
677: .getName(), pcType.getName(), value.getClass()
678: .getName(), "String")).setFatal();
679: }
680: } else if (idType == ByteIdentity.class) {
681: keyType = Byte.class;
682: if (!(value instanceof Byte)) {
683: throw new JPOXException(LOCALISER.msg("029004", idType
684: .getName(), pcType.getName(), value.getClass()
685: .getName(), "Byte")).setFatal();
686: }
687: } else if (idType == ShortIdentity.class) {
688: keyType = Short.class;
689: if (!(value instanceof Short)) {
690: throw new JPOXException(LOCALISER.msg("029004", idType
691: .getName(), pcType.getName(), value.getClass()
692: .getName(), "Short")).setFatal();
693: }
694: } else if (idType == CharIdentity.class) {
695: keyType = Character.class;
696: if (!(value instanceof Character)) {
697: throw new JPOXException(LOCALISER.msg("029004", idType
698: .getName(), pcType.getName(), value.getClass()
699: .getName(), "Character")).setFatal();
700: }
701: } else {
702: // ObjectIdentity
703: keyType = Object.class;
704: }
705:
706: try {
707: Class[] ctrArgs = new Class[] { Class.class, keyType };
708: Constructor ctr = idType.getConstructor(ctrArgs);
709:
710: Object[] args = new Object[] { pcType, value };
711: id = (SingleFieldIdentity) ctr.newInstance(args);
712: } catch (Exception e) {
713: JPOXLogger.PERSISTENCE
714: .error("Error encountered while creating SingleFieldIdentity instance of type \""
715: + idType.getName() + "\"");
716: JPOXLogger.PERSISTENCE.error(e);
717:
718: return null;
719: }
720:
721: return id;
722: }
723:
724: /**
725: * Method to create a new object identity for the passed object with the supplied MetaData.
726: * Only applies to application-identity cases.
727: * @param pc The persistable object
728: * @param cmd Its metadata
729: * @return The new identity object
730: */
731: public Object getNewApplicationIdentityObjectId(Object pc,
732: AbstractClassMetaData cmd) {
733: if (pc == null || cmd == null) {
734: return null;
735: }
736:
737: Object id = ((PersistenceCapable) pc).jdoNewObjectIdInstance();
738: if (!cmd.usesSingleFieldIdentityClass()) {
739: ((PersistenceCapable) pc).jdoCopyKeyFieldsToObjectId(id);
740: }
741: return id;
742: }
743:
744: /**
745: * Method to return a new object identity for the specified class, and key (possibly toString() output).
746: * @param cls Persistable class
747: * @param key form of the object id
748: * @return The object identity
749: */
750: public Object getNewApplicationIdentityObjectId(Class cls,
751: Object key) {
752: return JDOImplHelper.getInstance()
753: .newObjectIdInstance(cls, key);
754: }
755:
756: // ------------------------------ Persistence --------------------------------
757:
758: /**
759: * Whether the API allows (re-)persistence of a deleted object.
760: * @return Whether you can call persist on a deleted object
761: */
762: public boolean allowPersistOfDeletedObject() {
763: // JDO doesnt allow re-persist of a deleted object
764: return false;
765: }
766:
767: /**
768: * Whether the API allows deletion of a non-persistent object.
769: * @return Whether you can call delete on an object not yet persisted
770: */
771: public boolean allowDeleteOfNonPersistentObject() {
772: // JDO requires an exception throwing on attempts to delete transient objects
773: return false;
774: }
775:
776: /**
777: * Whether the API allows reading a field of a deleted object.
778: * @return Whether you can read after deleting
779: */
780: public boolean allowReadFieldOfDeletedObject() {
781: return false;
782: }
783:
784: /**
785: * Whether the API requires clearing of the fields of an object when it is deleted.
786: * @return Whether to clear loaded fields at delete
787: */
788: public boolean clearLoadedFlagsOnDeleteObject() {
789: return true;
790: }
791:
792: /**
793: * Returns the default cascade-persist. JDO defaults to persisting by reachability.
794: * @return The default value for cascade-persist (true)
795: */
796: public boolean getDefaultCascadePersistForField() {
797: return true;
798: }
799:
800: /**
801: * Returns the default cascade-update setting. JDO defaults to updating by reachability.
802: * @return The default cascade-update (true)
803: */
804: public boolean getDefaultCascadeUpdateForField() {
805: return true;
806: }
807:
808: /**
809: * Returns the default cascade-delete setting. JDO defaults to not deleting by reachability (unless using delete dependent)
810: * @return The default cascade-delete (false)
811: */
812: public boolean getDefaultCascadeDeleteForField() {
813: return false;
814: }
815:
816: /**
817: * Returns the default cascade-refresh setting.
818: * @return The default cascade-refresh (false)
819: */
820: public boolean getDefaultCascadeRefreshForField() {
821: return false;
822: }
823:
824: /**
825: * Method to return the default factory properties for this API.
826: * @return The default props
827: */
828: public Map getDefaultFactoryProperties() {
829: Map props = new HashMap();
830: props.put(PersistenceConfiguration.IDENTIFIER_FACTORY_PROPERTY,
831: "jpox"); // JPOX identifier naming
832: props
833: .put(
834: PersistenceConfiguration.QUERY_ALLOW_ALL_SQL_STATEMENTS,
835: "false"); // JDO SQL has to start SELECT
836: props
837: .put(
838: PersistenceConfiguration.JDO_NONTRANSACTIONAL_READ_PROPERTY,
839: "false");
840: props
841: .put(
842: PersistenceConfiguration.JDO_DETACHALLONCOMMIT_PROPERTY,
843: "false");
844: props
845: .put(
846: PersistenceConfiguration.PERSISTENCE_BY_REACHABILITY_AT_COMMIT,
847: "true");
848: props
849: .put(
850: PersistenceConfiguration.STRING_DEFAULT_LENGTH_PROPERTY,
851: "256");
852: return props;
853: }
854:
855: /**
856: * Accessor for the object id from the StateManager for this object.
857: * @param sm StateManager
858: * @return object id
859: */
860: public Object getObjectId(StateManager sm) {
861: return sm.getObjectId((PersistenceCapable) sm.getObject());
862: }
863:
864: /**
865: * Accessor for the version from the StateManager for this object.
866: * @param sm StateManager
867: */
868: public Object getVersion(StateManager sm) {
869: return sm.getVersion((PersistenceCapable) sm.getObject());
870: }
871:
872: /**
873: * Accessor for whether a field is loaded for the object managed by the StateManager
874: * @param sm StateManager
875: * @param fieldNumber Number of the field
876: * @return Whether it is loaded
877: */
878: public boolean isLoaded(StateManager sm, int fieldNumber) {
879: return sm.isLoaded((PersistenceCapable) sm.getObject(),
880: fieldNumber);
881: }
882:
883: /**
884: * Accessor for the persistence manager for this object.
885: * The returned object will be a JDO PersistenceManager or null if not managed.
886: * @param obj The object
887: * @return The persistence manager
888: */
889: public Object getPersistenceManager(Object obj) {
890: if (obj instanceof PersistenceCapable) {
891: return ((PersistenceCapable) obj)
892: .jdoGetPersistenceManager();
893: }
894: return null;
895: }
896:
897: /**
898: * Method to make the field dirty in the object.
899: * @param obj Object
900: * @param fieldName Name of the field to make dirty
901: */
902: public void makeFieldDirty(Object obj, String fieldName) {
903: ((PersistenceCapable) obj).jdoMakeDirty(fieldName);
904: }
905: }
|