001: /**********************************************************************
002: Copyright (c) 2007 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: Contributors:
016: ...
017: **********************************************************************/package org.jpox;
018:
019: import java.io.Serializable;
020: import java.util.ArrayList;
021: import java.util.BitSet;
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.HashSet;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.Set;
029:
030: import org.jpox.exceptions.JPOXUserException;
031: import org.jpox.metadata.AbstractClassMetaData;
032: import org.jpox.metadata.AbstractMemberMetaData;
033: import org.jpox.metadata.FetchGroupMetaData;
034: import org.jpox.metadata.FieldPersistenceModifier;
035: import org.jpox.util.Localiser;
036:
037: /**
038: * FetchPlan for fields for use within JPOX Core.
039: * A FetchPlan has a series of FetchPlanForClass objects being the fetch plan for particular classes.
040: * Each FetchPlanForClass defines a series of fields of that class that are part of the fetch plan.
041: * There are two types of fetch groups under consideration here.
042: * <ul>
043: * <li>Static fetch groups, defined in MetaData (XML/Annotations).</li>
044: * <li>Dynamic fetch groups, defined via an API.</li>
045: * </ul>
046: *
047: * @version $Revision: 1.11 $
048: */
049: public class FetchPlan implements Serializable {
050: /** Localisation utility for output messages */
051: protected static final Localiser LOCALISER = Localiser
052: .getInstance("org.jpox.Localisation");
053:
054: /** Constant defining the fields in the default fetch group. */
055: public static final String DEFAULT = "default";
056:
057: /** Constant defining all fields */
058: public static final String ALL = "all";
059:
060: /** Constant defing no fields. */
061: public static final String NONE = "none";
062:
063: /** Specify that fields that are loaded but not in the current fetch plan should be unloaded prior to detachment. */
064: public static final int DETACH_UNLOAD_FIELDS = 2;
065:
066: /** Specify that fields that are not loaded but are in the current fetch plan should be loaded prior to detachment. */
067: public static final int DETACH_LOAD_FIELDS = 1;
068:
069: /** Fetch size to load all possible. */
070: public static final int FETCH_SIZE_GREEDY = -1;
071:
072: /** Fetch size for the implementation to decide how many to load. */
073: public static final int FETCH_SIZE_OPTIMAL = 0;
074:
075: /** ObjectManagerFactory. */
076: transient final ObjectManagerFactoryImpl omf; // Defined as transient to avoid Serializable problems
077:
078: /** ClassLoader resolver. */
079: transient final ClassLoaderResolver clr; // Defined as transient to avoid Serializable problems
080:
081: /** The "defined" fetch groups in the current FetchPlan. */
082: final Set groups = new HashSet();
083:
084: /** The "dynamic" fetch groups in the current FetchPlan. */
085: transient Set dynamicGroups = null; // Defined as transient to avoid Serializable problems
086:
087: /** The Fetch size. For use when using large result sets. */
088: int fetchSize = FETCH_SIZE_OPTIMAL;
089:
090: /** Options to be used during detachment. Spec 12.7 says that the default is DETACH_LOAD_FIELDS. */
091: int detachmentOptions = FetchPlan.DETACH_LOAD_FIELDS;
092:
093: /** Managed class keyed by ClassMetaData **/
094: final transient Map managedClass = new HashMap();
095:
096: /** Maximum depth to fetch from the root object. */
097: int maxFetchDepth = 1;
098:
099: /** The classes used as the roots for detachment (DetachAllOnCommit). */
100: Class[] detachmentRootClasses = null;
101:
102: /** The instances used as the roots for detachment (DetachAllOnCommit). */
103: Collection detachmentRoots = null;
104:
105: /**
106: * Constructor. Initially has the default fetch group.
107: * @param omf ObjectManagerFactory
108: * @param clr ClassLoader Resolver
109: */
110: public FetchPlan(ObjectManagerFactoryImpl omf,
111: ClassLoaderResolver clr) {
112: this .omf = omf;
113: this .clr = clr;
114: groups.add(FetchPlan.DEFAULT);
115: }
116:
117: /**
118: * Manage the fetch plan for the class
119: * @param cmd AbstractClassMetaData for the class to manage
120: * @return the FetchPlanForClass
121: */
122: public FetchPlanForClass manageFetchPlanForClass(
123: AbstractClassMetaData cmd) {
124: FetchPlanForClass fetchPlanForClass = (FetchPlanForClass) managedClass
125: .get(cmd);
126: if (fetchPlanForClass == null) {
127: fetchPlanForClass = new FetchPlanForClass(cmd, this );
128: managedClass.put(cmd, fetchPlanForClass);
129: }
130: return fetchPlanForClass;
131: }
132:
133: /**
134: * Mark all managed fetch plans to be dirty, so the active fields need to be recomputed.
135: */
136: private void markDirty() {
137: Iterator it = managedClass.values().iterator();
138: while (it.hasNext()) {
139: ((FetchPlanForClass) it.next()).markDirty();
140: }
141: }
142:
143: /**
144: * Access the fetch plan for the class
145: * @param cmd the AbstractClassMetaData
146: * @return the FetchPlanForClass
147: */
148: public synchronized FetchPlanForClass getFetchPlanForClass(
149: AbstractClassMetaData cmd) {
150: return (FetchPlanForClass) managedClass.get(cmd);
151: }
152:
153: /**
154: * Method to add a group to the fetch plan.
155: * @param fetchGroupName The fetch group to add
156: * @return Updated Fetch Plan
157: */
158: public synchronized FetchPlan addGroup(String fetchGroupName) {
159: if (fetchGroupName != null) {
160: boolean changed = groups.add(fetchGroupName);
161: boolean dynChanged = addDynamicGroup(fetchGroupName);
162: if (changed || dynChanged) {
163: markDirty();
164: }
165: }
166: return this ;
167: }
168:
169: /**
170: * Method to remove a group from the fetch plan.
171: * @param fetchGroupName The fetch group to remove
172: * @return Updated Fetch Plan
173: */
174: public synchronized FetchPlan removeGroup(String fetchGroupName) {
175: if (fetchGroupName != null) {
176: boolean changed = false;
177: changed = groups.remove(fetchGroupName);
178: if (dynamicGroups != null) {
179: Iterator iter = dynamicGroups.iterator();
180: while (iter.hasNext()) {
181: FetchGroupImpl grp = (FetchGroupImpl) iter.next();
182: if (grp.getName().equals(fetchGroupName)) {
183: grp.deregisterListener(this ); // Deregister us from this group
184: changed = true;
185: iter.remove();
186: }
187: }
188: }
189: if (changed) {
190: markDirty();
191: }
192: }
193:
194: return this ;
195: }
196:
197: /**
198: * Method to clear the current groups and activate the DFG.
199: * @return The FetchPlan
200: */
201: public synchronized FetchPlan clearGroups() {
202: clearDynamicGroups();
203: groups.clear();
204: markDirty();
205: return this ;
206: }
207:
208: /**
209: * Accessor for the static groups for this FetchPlan.
210: * Doesn't return the dynamic groups.
211: * @return The fetch plan groups (unmodifiable)
212: */
213: public synchronized Set getGroups() {
214: return Collections.unmodifiableSet(new HashSet(groups));
215: }
216:
217: /**
218: * Method to set the groups of the fetch plan.
219: * @param fetchGroupNames The fetch groups
220: * @return Updated Fetch Plan
221: */
222: public synchronized FetchPlan setGroups(Collection fetchGroupNames) {
223: clearDynamicGroups();
224: groups.clear();
225:
226: if (fetchGroupNames != null) {
227: Set g = new HashSet(fetchGroupNames);
228: groups.addAll(g);
229:
230: Iterator iter = fetchGroupNames.iterator();
231: while (iter.hasNext()) {
232: String fetchGroupName = (String) iter.next();
233: addDynamicGroup(fetchGroupName);
234: }
235: }
236:
237: markDirty();
238: return this ;
239: }
240:
241: /**
242: * Method to set the groups using an array.
243: * @param fetchGroupNames Names of the fetch groups
244: * @return The Fetch Plan
245: */
246: public synchronized FetchPlan setGroups(String[] fetchGroupNames) {
247: clearDynamicGroups();
248: groups.clear();
249:
250: if (fetchGroupNames != null) {
251: for (int i = 0; i < fetchGroupNames.length; i++) {
252: groups.add(fetchGroupNames[i]);
253: }
254: for (int i = 0; i < fetchGroupNames.length; i++) {
255: addDynamicGroup(fetchGroupNames[i]);
256: }
257: }
258:
259: markDirty();
260: return this ;
261: }
262:
263: /**
264: * Method to set the fetch group.
265: * @param fetchGroupName Name of the fetch group
266: * @return The Fetch Plan
267: */
268: public synchronized FetchPlan setGroup(String fetchGroupName) {
269: clearDynamicGroups();
270: groups.clear();
271:
272: if (fetchGroupName != null) {
273: groups.add(fetchGroupName);
274: addDynamicGroup(fetchGroupName);
275: }
276:
277: markDirty();
278: return this ;
279: }
280:
281: /**
282: * Convenience method to clear all dynamic groups.
283: */
284: private void clearDynamicGroups() {
285: if (dynamicGroups != null) {
286: Iterator iter = dynamicGroups.iterator();
287: while (iter.hasNext()) {
288: FetchGroupImpl grp = (FetchGroupImpl) iter.next();
289: grp.deregisterListener(this );
290: }
291: dynamicGroups.clear();
292: }
293: }
294:
295: /**
296: * Convenience method to add dynamic fetch groups for the specified name.
297: * @param fetchGroupName Name of fetch group
298: * @return Whether the groups were changed
299: */
300: private boolean addDynamicGroup(String fetchGroupName) {
301: boolean changed = false;
302: FetchGroup[] dynGroups = omf.getFetchGroups(fetchGroupName);
303: if (dynGroups != null) {
304: for (int i = 0; i < dynGroups.length; i++) {
305: if (dynamicGroups == null) {
306: dynamicGroups = new HashSet();
307: }
308: dynamicGroups.add(dynGroups[i]);
309: ((FetchGroupImpl) dynGroups[i]).registerListener(this ); // Register us with this group
310: }
311: changed = true;
312: }
313: return changed;
314: }
315:
316: /**
317: * Method to notify this FetchPlan that the specified FetchGroup has been updated.
318: * <B>JPOX dynamic fetch groups extension</B>
319: * @param group The dynamic FetchGroup
320: * @since 1.2
321: */
322: public void notifyFetchGroupChange(FetchGroup group) {
323: Collection fpClasses = managedClass.values();
324: Iterator iter = fpClasses.iterator();
325: while (iter.hasNext()) {
326: FetchPlanForClass fpClass = (FetchPlanForClass) iter.next();
327: Class cls = clr
328: .classForName(fpClass.cmd.getFullClassName());
329: FetchGroupImpl grp = (FetchGroupImpl) group;
330: if (cls.isAssignableFrom(grp.getFetchGroupClass())
331: || grp.getFetchGroupClass().isAssignableFrom(cls)) {
332: // Mark all potentially related fetch plans dirty so they recalculate
333: fpClass.markDirty();
334: }
335: }
336: }
337:
338: /**
339: * Method to notify this FetchPlan that the specified FetchGroup has been updated.
340: * <B>JPOX dynamic fetch groups extension</B>
341: * @param group The dynamic FetchGroup
342: * @since 1.2
343: */
344: public void notifyFetchGroupRemove(FetchGroup group) {
345: dynamicGroups.remove(group); // Remove the group
346: notifyFetchGroupChange(group); // Recalculate all groups fields
347: }
348:
349: /**
350: * Set the roots for DetachAllOnCommit
351: * @param roots The roots of the detachment graph.
352: * @return The fetch plan with these roots
353: */
354: public FetchPlan setDetachmentRoots(Collection roots) {
355: if (detachmentRootClasses != null || detachmentRoots != null) {
356: throw new JPOXUserException(LOCALISER.msg("006003"));
357: }
358:
359: if (roots == null) {
360: detachmentRoots = null;
361: }
362:
363: detachmentRoots = new ArrayList();
364: detachmentRoots.addAll(roots);
365: return this ;
366: }
367:
368: /**
369: * Accessor for the roots of the detachment graph for DetachAllOnCommit.
370: * @return The roots of the detachment graph.
371: */
372: public Collection getDetachmentRoots() {
373: if (detachmentRoots == null) {
374: return Collections.EMPTY_LIST;
375: }
376: return Collections.unmodifiableCollection(detachmentRoots);
377: }
378:
379: /**
380: * Set the classes used for roots of the detachment graph for DetachAllOnCommit.
381: * @param rootClasses Classes to be used as roots of the detachment graph
382: * @return The fetch plan with these roots
383: */
384: public FetchPlan setDetachmentRootClasses(Class[] rootClasses) {
385: if (detachmentRootClasses != null || detachmentRoots != null) {
386: throw new JPOXUserException(LOCALISER.msg("006003"));
387: }
388:
389: if (rootClasses == null) {
390: detachmentRootClasses = null;
391: return this ;
392: }
393:
394: detachmentRootClasses = new Class[rootClasses.length];
395: for (int i = 0; i < rootClasses.length; i++) {
396: detachmentRootClasses[i] = rootClasses[i];
397: }
398:
399: return this ;
400: }
401:
402: /**
403: * Accessor for the root classes of the detachment graph for DetachAllOnCommit.
404: * @return The classes to be used as the root of the detachment graph.
405: */
406: public Class[] getDetachmentRootClasses() {
407: if (detachmentRootClasses == null) {
408: return new Class[0];
409: }
410:
411: return detachmentRootClasses;
412: }
413:
414: /**
415: * Method called at commit() to clear out the detachment roots.
416: */
417: void resetDetachmentRoots() {
418: detachmentRootClasses = null;
419: detachmentRoots = null;
420: }
421:
422: /**
423: * Mutator for the maximum fetch depth where
424: * -1 implies no restriction on the fetch depth and
425: * 0 is invalid and throws a JDOUserException.
426: * @param max The maximum fetch depth to fetch to
427: */
428: public synchronized FetchPlan setMaxFetchDepth(int max) {
429: if (max == 0) {
430: throw new JPOXUserException(LOCALISER.msg("006002", max));
431: }
432: this .maxFetchDepth = max;
433: return this ;
434: }
435:
436: /**
437: * Accessor for the maximum fetch depth.
438: * @return The maximum fetch depth
439: */
440: public synchronized int getMaxFetchDepth() {
441: return maxFetchDepth;
442: }
443:
444: /**
445: * Method to set the fetch size when using large result sets.
446: * @param fetchSize the size
447: * @return Updated Fetch Plan
448: */
449: public synchronized FetchPlan setFetchSize(int fetchSize) {
450: if (fetchSize != FETCH_SIZE_GREEDY
451: && fetchSize != FETCH_SIZE_OPTIMAL && fetchSize < 0) {
452: // Invalid fetch size so just return
453: return this ;
454: }
455: this .fetchSize = fetchSize;
456: return this ;
457: }
458:
459: /**
460: * Accessor for the fetch size when using large result sets.
461: * @return The size
462: */
463: public synchronized int getFetchSize() {
464: return fetchSize;
465: }
466:
467: /**
468: * Return the options to be used at detachment.
469: * @return Detachment options
470: */
471: public int getDetachmentOptions() {
472: return detachmentOptions;
473: }
474:
475: /**
476: * Set the options to be used at detachment.
477: * @param options The options
478: * @return The updated fetch plan.
479: */
480: public FetchPlan setDetachmentOptions(int options) {
481: detachmentOptions = options;
482: return this ;
483: }
484:
485: /*
486: * (non-Javadoc)
487: * @see java.lang.Object#toString()
488: */
489: public String toString() {
490: return groups.toString();
491: }
492:
493: /**
494: * Returns a copy of this FetchPlan with all settings initialized
495: * @return the FetchPlan
496: */
497: public synchronized FetchPlan getCopy() {
498: FetchPlan fp = new FetchPlan(omf, clr); // Includes DEFAULT
499: fp.maxFetchDepth = maxFetchDepth;
500: fp.groups.remove(FetchPlan.DEFAULT);
501: fp.groups.addAll(this .groups);
502: if (dynamicGroups != null) {
503: fp.dynamicGroups = new HashSet(dynamicGroups);
504: }
505:
506: for (Iterator it = this .managedClass.entrySet().iterator(); it
507: .hasNext();) {
508: Map.Entry entry = (Map.Entry) it.next();
509: AbstractClassMetaData cmd = (AbstractClassMetaData) entry
510: .getKey();
511: FetchPlanForClass fpcls = (FetchPlanForClass) entry
512: .getValue();
513: fp.managedClass.put(cmd, fpcls.getCopy(fp));
514: }
515: fp.fetchSize = this .fetchSize;
516: return fp;
517: }
518:
519: /**
520: * Class managing the fetch plan for a particular class.
521: * This should not use the fields of the enclosing FetchPlan directly, always referring
522: * to them using the "plan" field.
523: */
524: public class FetchPlanForClass {
525: /** Parent FetchPlan. */
526: final FetchPlan plan;
527:
528: /** MetaData for the class that this represents. */
529: final AbstractClassMetaData cmd;
530:
531: /** Fields in the fetch plan for this class. */
532: int[] fieldsInActualFetchPlan;
533:
534: /** Whether the record is dirty and needs the fields recalculating. */
535: boolean dirty = true;
536:
537: /**
538: * Constructor
539: * @param cmd the ClassMetaData
540: * @param fetchPlan the FetchPlan
541: */
542: public FetchPlanForClass(final AbstractClassMetaData cmd,
543: FetchPlan fetchPlan) {
544: super ();
545: this .cmd = cmd;
546: this .plan = fetchPlan;
547: }
548:
549: /**
550: * Accessor for the FetchPlan that this classes plan relates to.
551: * @return The FetchPlan
552: */
553: public final FetchPlan getFetchPlan() {
554: return plan;
555: }
556:
557: /**
558: * Accessor for the ClassMetaData for this classes plan.
559: * @return ClassMetaData for the class represented here
560: */
561: public final AbstractClassMetaData getAbstractClassMetaData() {
562: return cmd;
563: }
564:
565: void markDirty() {
566: dirty = true;
567: }
568:
569: FetchPlanForClass getCopy(FetchPlan impl) {
570: FetchPlanForClass fp = new FetchPlanForClass(cmd, impl);
571: if (this .fieldsInActualFetchPlan != null) {
572: fp.fieldsInActualFetchPlan = new int[this .fieldsInActualFetchPlan.length];
573: for (int i = 0; i < fp.fieldsInActualFetchPlan.length; i++) {
574: fp.fieldsInActualFetchPlan[i] = this .fieldsInActualFetchPlan[i];
575: }
576: }
577: fp.dirty = this .dirty;
578: return fp;
579: }
580:
581: /**
582: * Return whether the specified field is in the fetch plan
583: * @param fieldNumber The field number
584: * @return Whether it is in the FetchPlan
585: */
586: public boolean isFieldInActualFetchPlan(int fieldNumber) {
587: if (dirty) {
588: BitSet fieldsNumber = getFieldsInActualFetchPlanByBitSet();
589: return fieldsNumber.get(fieldNumber);
590: }
591: if (fieldsInActualFetchPlan != null) {
592: for (int i = 0; i < fieldsInActualFetchPlan.length; i++) {
593: if (fieldsInActualFetchPlan[i] == fieldNumber) {
594: return true;
595: }
596: }
597: }
598:
599: return false;
600: }
601:
602: /**
603: * Get all fields in the actual fetch plan
604: * @return an array with the absolute position of the fields
605: */
606: public int[] getFieldsInActualFetchPlan() {
607: if (dirty) {
608: dirty = false;
609: BitSet fieldsNumber = getFieldsInActualFetchPlanByBitSet();
610: int countFieldsInFP = 0;
611: for (int i = 0; i < fieldsNumber.length(); i++) {
612: if (fieldsNumber.get(i)) {
613: countFieldsInFP++;
614: }
615: }
616:
617: fieldsInActualFetchPlan = new int[countFieldsInFP];
618: int nextField = 0;
619: for (int i = 0; i < fieldsNumber.length(); i++) {
620: if (fieldsNumber.get(i)) {
621: fieldsInActualFetchPlan[nextField++] = i;
622: }
623: }
624: }
625: return fieldsInActualFetchPlan;
626: }
627:
628: /**
629: * Method to return the effective depth of this field number in the overall fetch plan.
630: * @param fieldNumber Number of field in this class
631: * @return The (max) recursion depth
632: */
633: public int getMaxRecursionDepthForFieldInCurrentFetchPlan(
634: int fieldNumber) {
635: // prepare array of FetchGroupMetaData from current fetch plan
636: Set currentGroupNames = new HashSet(plan.getGroups());
637: FetchGroupMetaData[] fgmds = cmd
638: .getFetchGroupMetaData(currentGroupNames);
639:
640: // find FetchGroupMetaDatas that contain the field in question
641: Set fetchGroupsContainingField = getFetchGroupsForFieldAbsoluteNumber(
642: fgmds, fieldNumber);
643:
644: // find recursion depth for field in its class <field> definition
645: int recursionDepth = cmd
646: .getMetaDataForManagedMemberAtAbsolutePosition(
647: fieldNumber).getRecursionDepth();
648: if (recursionDepth == AbstractMemberMetaData.UNDEFINED_RECURSION_DEPTH) {
649: recursionDepth = AbstractMemberMetaData.DEFAULT_RECURSION_DEPTH;
650: }
651:
652: // find if it has been overridden in a <fetch-group> definition
653: String fieldName = cmd
654: .getMetaDataForManagedMemberAtAbsolutePosition(
655: fieldNumber).getName();
656: for (Iterator iter = fetchGroupsContainingField.iterator(); iter
657: .hasNext();) {
658: FetchGroupMetaData fgmd = (FetchGroupMetaData) iter
659: .next();
660: AbstractMemberMetaData[] fmds = fgmd
661: .getMemberMetaData();
662: for (int i = 0; i < fmds.length; i++) {
663: AbstractMemberMetaData fmd = fmds[i];
664: if (fmd.getName().equals(fieldName)) {
665: if (fmd.getRecursionDepth() != AbstractMemberMetaData.UNDEFINED_RECURSION_DEPTH) {
666: recursionDepth = fmd.getRecursionDepth();
667: }
668: }
669: }
670: }
671: return recursionDepth;
672: }
673:
674: /**
675: * Get all fields in the actual fetch plan.
676: * This is public for unit testing purposes only
677: * @return an BitSet with the bits set in the absolute position of the fields
678: */
679: public BitSet getFieldsInActualFetchPlanByBitSet() {
680: return getFieldsInActualFetchPlanByBitSet(cmd);
681: }
682:
683: /**
684: * Get all fields in the actual fetch plan for this class and superclasses
685: * @param cmd this AbstractClassMetaData
686: * @return an BitSet with the bits set in the absolute position of the fields
687: */
688: private BitSet getFieldsInActualFetchPlanByBitSet(
689: AbstractClassMetaData cmd) {
690: BitSet bitSet = plan.getFetchPlanForClass(cmd)
691: .getFieldsAbsoluteNumber(
692: cmd.getFetchGroupMetaData());
693: if (cmd.getPersistenceCapableSuperclass() != null) {
694: plan.manageFetchPlanForClass(cmd
695: .getSuperAbstractClassMetaData());
696: bitSet.or(plan.getFetchPlanForClass(
697: cmd.getSuperAbstractClassMetaData())
698: .getFieldsInActualFetchPlanByBitSet(
699: cmd.getSuperAbstractClassMetaData()));
700: } else {
701: // Make sure that we always have the PK fields in the fetch plan = FetchPlanImpl.NONE
702: setNoneFieldNumbers(bitSet);
703: }
704:
705: if (plan.dynamicGroups != null) {
706: // JPOX dynamic fetch groups extension
707: Iterator iter = plan.dynamicGroups.iterator();
708: while (iter.hasNext()) {
709: FetchGroup grp = (FetchGroup) iter.next();
710: if (grp.getClassName().equals(
711: cmd.getFullClassName())) {
712: // Dynamic fetch group applies
713: String[] fields = grp.getFieldNames();
714: for (int i = 0; i < fields.length; i++) {
715: int fieldPos = cmd
716: .getAbsolutePositionOfMember(fields[i]);
717: if (fieldPos >= 0) {
718: bitSet.set(fieldPos);
719: }
720: }
721: }
722: }
723: }
724: return bitSet;
725: }
726:
727: /**
728: * Get the absolute number of the fields for an array of Fetch Group
729: * @param fgmds The Fetch Groups
730: * @return a BitSet with flags set to true in the field number positions
731: */
732: private BitSet getFieldsAbsoluteNumber(
733: FetchGroupMetaData[] fgmds) {
734: BitSet fieldsNumber = new BitSet(0);
735: if (fgmds != null) {
736: for (int i = 0; i < fgmds.length; i++) {
737: if (plan.groups.contains(fgmds[i].getName())) {
738: fieldsNumber
739: .or(getFieldsAbsoluteNumberInFetchGroup(fgmds[i]));
740: }
741: }
742: }
743:
744: if (plan.groups.contains(FetchPlan.DEFAULT)) {
745: setDefaultFieldNumbers(fieldsNumber);
746: }
747: if (plan.groups.contains(FetchPlan.ALL)) {
748: setAllFieldNumbers(fieldsNumber);
749: }
750: if (plan.groups.contains(FetchPlan.NONE)) {
751: setNoneFieldNumbers(fieldsNumber);
752: }
753: return fieldsNumber;
754: }
755:
756: /**
757: * Sets the given list of field numbers to include all the fields defined in the DEFAULT FetchPlan.
758: * @param fieldsNumber list of field numbers
759: */
760: private void setDefaultFieldNumbers(BitSet fieldsNumber) {
761: for (int i = 0; i < cmd.getDFGMemberPositions().length; i++) {
762: fieldsNumber.set(cmd.getDFGMemberPositions()[i]);
763: }
764: }
765:
766: /**
767: * Sets the given list of field numbers to include all the fields defined in the ALL FetchPlan.
768: * @param fieldsNumber list of field numbers
769: */
770: private void setAllFieldNumbers(BitSet fieldsNumber) {
771: for (int i = 0; i < cmd.getNoOfManagedMembers(); i++) {
772: if (cmd.getMetaDataForManagedMemberAtPosition(i)
773: .getPersistenceModifier() != FieldPersistenceModifier.NONE) {
774: fieldsNumber
775: .set(cmd
776: .getAbsoluteMemberPositionForRelativePosition(i));
777: }
778: }
779: }
780:
781: /**
782: * Sets the given list of field numbers to include all the fields defined in the NONE FetchPlan.
783: * @param fieldsNumber list of field numbers
784: */
785: private void setNoneFieldNumbers(BitSet fieldsNumber) {
786: for (int i = 0; i < cmd.getNoOfManagedMembers(); i++) {
787: AbstractMemberMetaData fmd = cmd
788: .getMetaDataForMemberAtRelativePosition(i);
789: if (fmd.isPrimaryKey()) {
790: fieldsNumber.set(fmd.getAbsoluteFieldNumber());
791: }
792: }
793: }
794:
795: /**
796: * Get the absolute field number for a particular Fetch Group
797: * @param fgmd The Fetch Group
798: * @return a list of field numbers
799: */
800: private BitSet getFieldsAbsoluteNumberInFetchGroup(
801: FetchGroupMetaData fgmd) {
802: BitSet fieldsNumber = new BitSet(0);
803: for (int i = 0; i < fgmd.getMemberMetaData().length; i++) {
804: int fieldNumber = getFieldNumber(cmd, fgmd
805: .getMemberMetaData()[i].getName());
806: if (fieldNumber == -1) {
807: throw new JPOXUserException(LOCALISER.msg("006000",
808: fgmd.getMemberMetaData()[i].getName(), fgmd
809: .getName(), cmd.getFullClassName()))
810: .setFatal();
811: }
812: fieldsNumber.set(fieldNumber);
813: }
814: // fields in nested fetch-groups
815: for (int i = 0; i < fgmd.getFetchGroupMetaData().length; i++) {
816: String nestedGroupName = fgmd.getFetchGroupMetaData()[i]
817: .getName();
818: if (nestedGroupName.equals(FetchPlan.DEFAULT)) {
819: setDefaultFieldNumbers(fieldsNumber);
820: } else if (nestedGroupName.equals(FetchPlan.ALL)) {
821: setAllFieldNumbers(fieldsNumber);
822: } else if (nestedGroupName.equals(FetchPlan.NONE)) {
823: setNoneFieldNumbers(fieldsNumber);
824: } else {
825: FetchGroupMetaData nestedFGMD = getFetchGroupMetaData(
826: cmd, nestedGroupName);
827: if (nestedFGMD == null) {
828: throw new JPOXUserException(LOCALISER.msg(
829: "006001",
830: fgmd.getFetchGroupMetaData()[i]
831: .getName(), fgmd.getName(), cmd
832: .getFullClassName()))
833: .setFatal();
834: }
835: fieldsNumber
836: .or(getFieldsAbsoluteNumberInFetchGroup(nestedFGMD));
837: }
838: }
839: return fieldsNumber;
840: }
841:
842: /**
843: * Find the FetchGroupMetaData with the specified name for the class.
844: * @param cmd the AbstractClassMetaData
845: * @param fetchGroupName the fetch group name
846: * @return the FetchGroup MetaData declaration or null if not found
847: */
848: private FetchGroupMetaData getFetchGroupMetaData(
849: AbstractClassMetaData cmd, String fetchGroupName) {
850: FetchGroupMetaData nestedFGMD = null;
851: nestedFGMD = cmd.getFetchGroupMetaData(fetchGroupName);
852: return nestedFGMD;
853: }
854:
855: /**
856: * gets the field number for a fieldname in this or superclasses
857: * @param cmd this AbstractClassMetaData
858: * @param fieldName the field Name
859: * @return the field number. -1 if not found
860: */
861: private int getFieldNumber(AbstractClassMetaData cmd,
862: String fieldName) {
863: int fieldNumber = cmd
864: .getAbsolutePositionOfMember(fieldName);
865: if (fieldNumber == -1
866: && cmd.getPersistenceCapableSuperclass() != null) {
867: fieldNumber = getFieldNumber(cmd
868: .getSuperAbstractClassMetaData(), fieldName);
869: }
870: return fieldNumber;
871: }
872:
873: /**
874: * Get all the fetch groups this field is included
875: * @param fgmds The Fetch Groups
876: * @param absoluteFieldNumber the field absolute number
877: * @return The Fetch Groups
878: */
879: private Set getFetchGroupsForFieldAbsoluteNumber(
880: FetchGroupMetaData[] fgmds, int absoluteFieldNumber) {
881: Set fetchGroups = new HashSet();
882: if (fgmds != null) {
883: for (int i = 0; i < fgmds.length; i++) {
884: for (int j = 0; j < fgmds[i].getMemberMetaData().length; j++) {
885: if (fgmds[i].getMemberMetaData()[j]
886: .getName()
887: .equals(
888: cmd
889: .getMetaDataForManagedMemberAtAbsolutePosition(
890: absoluteFieldNumber)
891: .getName())) {
892: fetchGroups.add(fgmds[i]);
893: }
894: }
895: for (int j = 0; j < fgmds[i]
896: .getFetchGroupMetaData().length; j++) {
897: fetchGroups
898: .addAll(getFetchGroupsForFieldAbsoluteNumber(
899: fgmds[i]
900: .getFetchGroupMetaData(),
901: absoluteFieldNumber));
902: }
903: }
904: }
905: return fetchGroups;
906: }
907:
908: /**
909: * Cache fetch groups by field number, as calculating them in getFetchGroupsForFieldAbsoluteNumber()
910: * is O(n^2) Map<Integer, Set<FetchGroupMetaData>>
911: */
912: Map fetchGroupsByFieldNumber = new HashMap();
913:
914: /**
915: * Whether to call the post load or not. Checks if fields in actual FetchPlan where not previouly loaded
916: * and the post-load is enabled in the metadata
917: * @param loadedFields alredy loaded fields
918: * @return if is to call the postLoad
919: */
920: public boolean isToCallPostLoadFetchPlan(boolean[] loadedFields) {
921: int[] fieldsInActualFetchPlan = getFieldsInActualFetchPlan();
922: for (int i = 0; i < fieldsInActualFetchPlan.length; i++) {
923: final int fieldNumber = fieldsInActualFetchPlan[i];
924: String fieldName = cmd
925: .getMetaDataForManagedMemberAtAbsolutePosition(
926: fieldNumber).getFullFieldName();
927: // if field in actual fetch plan was not previously loaded
928: if (!loadedFields[fieldNumber]) {
929: if (cmd
930: .getMetaDataForManagedMemberAtAbsolutePosition(
931: fieldNumber).isDefaultFetchGroup()
932: && groups.contains(FetchPlan.DEFAULT)) {
933: // to call jdoPostLoad, field must be in default-fetch-group when DFG is active
934: return true;
935: } else {
936: // field must be in a fetch-group which has post-load set to true
937: Integer fieldNumberInteger = new Integer(
938: fieldNumber);
939: Set fetchGroups = (Set) fetchGroupsByFieldNumber
940: .get(fieldNumberInteger);
941: if (fetchGroups == null) {
942: fetchGroups = getFetchGroupsForFieldAbsoluteNumber(
943: cmd.getFetchGroupMetaData(),
944: fieldNumber);
945: // cache those precious results from expensive invocation
946: fetchGroupsByFieldNumber.put(
947: fieldNumberInteger, fetchGroups);
948: }
949: for (Iterator it = fetchGroups.iterator(); it
950: .hasNext();) {
951: FetchGroupMetaData fgmd = (FetchGroupMetaData) it
952: .next();
953: if (fgmd.getPostLoad().booleanValue()) {
954: return true;
955: }
956: }
957:
958: if (plan.dynamicGroups != null) {
959: Class cls = plan.clr.classForName(cmd
960: .getFullClassName());
961: for (Iterator it = plan.dynamicGroups
962: .iterator(); it.hasNext();) {
963: FetchGroupImpl group = (FetchGroupImpl) it
964: .next();
965: if (group.getFetchGroupClass()
966: .isAssignableFrom(cls)
967: && group.hasField(fieldName)
968: && group.getPostLoad()) {
969: return true;
970: }
971: }
972: }
973: }
974: }
975: }
976: return false;
977: }
978: }
979: }
|