001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.metadata;
012:
013: import com.versant.core.common.Debug;
014: import com.versant.core.metadata.parser.JdoElement;
015: import com.versant.core.metadata.parser.JdoExtension;
016: import com.versant.core.metadata.parser.JdoExtensionKeys;
017:
018: import java.util.*;
019:
020: import com.versant.core.common.BindingSupportImpl;
021:
022: /**
023: * This is used by MetaDataBuilder to construct fetch groups. Putting all
024: * the fetch group stuff in one file should add some much needed sanity.
025: */
026: public class FetchGroupBuilder {
027:
028: private final ModelMetaData jmd;
029: private final boolean sendCurrentForFGWithSecFields;
030: private final boolean readObjectBeforeWrite;
031:
032: public FetchGroupBuilder(ModelMetaData jmd,
033: boolean sendCurrentForFGWithSecFields,
034: boolean readObjectBeforeWrite) {
035: this .jmd = jmd;
036: this .sendCurrentForFGWithSecFields = sendCurrentForFGWithSecFields;
037: this .readObjectBeforeWrite = readObjectBeforeWrite;
038: }
039:
040: protected StoreFetchGroup createStoreFetchGroup() {
041: return null;
042: }
043:
044: /**
045: * Return the first fetch-group extension in a or null if none.
046: */
047: public JdoExtension findFetchGroupExt(JdoExtension[] a) {
048: if (a == null)
049: return null;
050: int n = a.length;
051: for (int i = 0; i < n; i++) {
052: if (a[i].key == JdoExtensionKeys.FETCH_GROUP)
053: return a[i];
054: }
055: return null;
056: }
057:
058: /**
059: * Build all the fetch groups. This must be called once all the fields for
060: * each class have been created (including fake fields etc.).
061: */
062: public void buildFetchGroups(boolean quiet) {
063: ClassMetaData[] classes = jmd.classes;
064: int clen = classes.length;
065:
066: // create all the groups with their field arrays
067: if (Debug.DEBUG)
068: System.out.println("MDB-FGB: Creating fetch groups");
069: for (int i = 0; i < clen; i++) {
070: ClassMetaData cmd = classes[i];
071: try {
072: if (cmd.pcSuperMetaData == null)
073: createFetchGroups(cmd, quiet);
074: } catch (RuntimeException e) {
075: cmd.addError(e, quiet);
076: }
077: }
078:
079: // resolve next-fetch-group references etc
080: if (Debug.DEBUG) {
081: System.out
082: .println("MDB-FGB: Resolving next-fetch-group references");
083: }
084: for (int i = 0; i < clen; i++) {
085: ClassMetaData cmd = classes[i];
086: try {
087: processFetchGroups(cmd);
088: } catch (RuntimeException e) {
089: cmd.addError(e, quiet);
090: }
091: }
092:
093: // Set the sendFieldsOnFetch on FetchGroup's with secondaryField's
094: // if required by the DataStore for the class. Also sets the
095: // hasPrimaryFields flag on all FetchGroups.
096: for (int i = 0; i < clen; i++) {
097: ClassMetaData cmd = classes[i];
098: // todo Commenting out these lines appeared to make no difference
099: // if (cmd.pcSuperMetaData != null && cmd.vdsClass == null) {
100: // continue;
101: // }
102: FetchGroup[] fetchGroups = cmd.fetchGroups;
103: if (fetchGroups == null) {
104: continue;
105: }
106: for (int j = fetchGroups.length - 1; j >= 0; j--) {
107: FetchGroup fg = fetchGroups[j];
108: if (sendCurrentForFGWithSecFields
109: && !fg.hasPrimaryFields(true)
110: && fg.hasSecondaryFields()) {
111: fg.setSendFieldsOnFetch(true);
112: }
113: if (fg.hasPrimaryFields(false)) {
114: fg.setHasPrimaryFields(true);
115: }
116: }
117: }
118: }
119:
120: /**
121: * Create all groups for cmd with their field arrays and then recursively
122: * create groups for its subclasses.
123: */
124: private void createFetchGroups(ClassMetaData cmd, boolean quiet) {
125: if (cmd.fields == null) {
126: return; // no fields due to some previous error
127: }
128:
129: ArrayList groups = cmd.fgTmp = new ArrayList();
130: HashMap nameGroupMap = cmd.nameGroupMap = new HashMap();
131:
132: FetchGroup dfg = createDefaultFetchGroup(cmd);
133: groups.add(dfg);
134: nameGroupMap.put(dfg.name, dfg);
135:
136: FetchGroup retrieveFG = createRetrieveFetchGroup(cmd);
137: groups.add(retrieveFG);
138: nameGroupMap.put(retrieveFG.name, retrieveFG);
139:
140: FetchGroup allColsFG = createAllColumnsFetchGroup(cmd);
141: groups.add(allColsFG);
142: nameGroupMap.put(allColsFG.name, allColsFG);
143:
144: FetchGroup dfgNoFakes = createFetchGroupDefaultNoFakes(cmd);
145: groups.add(dfgNoFakes);
146: nameGroupMap.put(dfgNoFakes.name, dfgNoFakes);
147:
148: // if you add new groups here add a corresponding line to the block
149: // towards the end of this method that caches special groups in
150: // ClassMetaData
151: addNotNull(createRefFetchGroup(cmd), groups, nameGroupMap);
152: addNotNull(createDepFetchGroup(cmd), groups, nameGroupMap);
153: addNotNull(createReqFetchGroup(cmd), groups, nameGroupMap);
154: addNotNull(createManagedManyToManyFetchGroup(cmd), groups,
155: nameGroupMap);
156:
157: // find all the user defined groups
158: JdoElement[] elements = cmd.jdoClass.elements;
159: int elementsLen = elements.length;
160: for (int j = 0; j < elementsLen; j++) {
161: JdoElement element = elements[j];
162: if (!(element instanceof JdoExtension))
163: continue;
164: JdoExtension e = (JdoExtension) element;
165: if (e.key != JdoExtensionKeys.FETCH_GROUP)
166: continue;
167: FetchGroup g = new FetchGroup(cmd, e.getString(),
168: createStoreFetchGroup());
169: try {
170: if (g.name.equals(FetchGroup.DFG_NAME)) {
171: throw BindingSupportImpl.getInstance().runtime(
172: "The group name '" + FetchGroup.DFG_NAME
173: + "' is reserved for the "
174: + "default fetch group\n"
175: + e.getContext());
176: }
177: if (g.name.equals(FetchGroup.REF_NAME)) {
178: throw BindingSupportImpl.getInstance().runtime(
179: "The group name '" + FetchGroup.REF_NAME
180: + "' is reserved internal use\n"
181: + e.getContext());
182: }
183: if (nameGroupMap.containsKey(g.name)) {
184: throw BindingSupportImpl.getInstance().runtime(
185: "There is already a group called: '"
186: + g.name + "'\n" + e.getContext());
187: }
188: nameGroupMap.put(g.name, g);
189: g.extension = e;
190: processFetchGroupFields(cmd, g, quiet);
191: groups.add(g);
192: } catch (RuntimeException e1) {
193: cmd.addError(e1, quiet);
194: }
195: }
196:
197: // create an empty group for each group from our parent class that
198: // has not been extended i.e. group defined here with same name
199: if (cmd.pcSuperMetaData != null) {
200: List list = cmd.pcSuperMetaData.fgTmp;
201: for (int i = 0; i < list.size(); i++) {
202: FetchGroup sg = (FetchGroup) list.get(i);
203: if (nameGroupMap.containsKey(sg.name))
204: continue;
205: FetchGroup g = new FetchGroup(cmd, sg.name,
206: createStoreFetchGroup());
207: g.fields = new FetchGroupField[0];
208: groups.add(g);
209: nameGroupMap.put(g.name, g);
210: }
211: }
212:
213: // Fill in the fetchGroup for all fields creating new groups for fields
214: // without a group that are not in the default fetch group. If the
215: // field is a pass1 field then the optimistic locking field (if any)
216: // is included in the group.
217: FieldMetaData[] fields = cmd.fields;
218: int fieldsLen = fields.length;
219: for (int i = 0; i < fieldsLen; i++) {
220: FieldMetaData fmd = fields[i];
221: if (fmd.jdoField != null && fmd.jdoField.extensions != null) {
222: try {
223: JdoExtension e = findFetchGroupExt(fmd.jdoField.extensions);
224: if (e != null) {
225: String gname = e.getString();
226: fmd.fetchGroup = (FetchGroup) nameGroupMap
227: .get(gname);
228: if (fmd.fetchGroup == null) {
229: throw BindingSupportImpl.getInstance()
230: .runtime(
231: "No such fetch-group: '"
232: + gname + "'\n"
233: + e.getContext());
234: }
235: }
236: } catch (RuntimeException e1) {
237: fmd.addError(e1, quiet);
238: }
239: }
240: if (fmd.fetchGroup == null) {
241: if (fmd.defaultFetchGroup) {
242: fmd.fetchGroup = dfg;
243: } else {
244: String name = "_" + fmd.name + cmd.qname;
245: for (int j = 2; nameGroupMap.containsKey(name); j++) {
246: name = "_" + fmd.name + j;
247: }
248: FetchGroup g = new FetchGroup(cmd, name,
249: createStoreFetchGroup());
250:
251: ArrayList a = new ArrayList();
252: if (fmd.isEmbeddedRef()) {
253: //must add the fake fields to the fg
254: if (fmd.embeddedFmds != null) {
255: for (int j = 0; j < fmd.embeddedFmds.length; j++) {
256: FieldMetaData embeddedFmd = fmd.embeddedFmds[j];
257: a.add(new FetchGroupField(embeddedFmd));
258: }
259: }
260: } else {
261: FetchGroupField fgf = createFetchGroupFieldWithPrefetch(fmd);
262: a.add(fgf);
263: }
264:
265: // classes with storeAllFields set also read all fields
266: // so there is no need to add the optimistic locking field
267: // to fetch groups
268: if (fmd.primaryField && !cmd.storeAllFields
269: && cmd.optimisticLockingField != null) {
270: a.add(new FetchGroupField(
271: cmd.optimisticLockingField));
272: }
273:
274: g.fields = new FetchGroupField[a.size()];
275: a.toArray(g.fields);
276:
277: groups.add(g);
278: nameGroupMap.put(g.name, g);
279: fmd.fetchGroup = g;
280: }
281: }
282: }
283:
284: // dig out some popular fetchgroups for quick access
285: cmd.refFetchGroup = (FetchGroup) nameGroupMap
286: .get(FetchGroup.REF_NAME);
287: cmd.depFetchGroup = (FetchGroup) nameGroupMap
288: .get(FetchGroup.DEP_NAME);
289: cmd.reqFetchGroup = (FetchGroup) nameGroupMap
290: .get(FetchGroup.REQ_NAME);
291: cmd.managedManyToManyFetchGroup = (FetchGroup) nameGroupMap
292: .get(FetchGroup.MANY_TO_MANY_NAME);
293:
294: // process all of our subclasses
295: ClassMetaData[] pcSubclasses = cmd.pcSubclasses;
296: if (pcSubclasses != null) {
297: for (int i = pcSubclasses.length - 1; i >= 0; i--) {
298: createFetchGroups(cmd.pcSubclasses[i], quiet);
299: }
300: }
301:
302: if (cmd.pcSuperMetaData == null) {
303: Collections.sort(cmd.fgTmp);
304: if (cmd.pcSubclasses != null) {
305: doSubFGs(cmd);
306: }
307:
308: }
309:
310: if (cmd.pcSuperMetaData == null) {
311: createFGArrays(cmd);
312: }
313: }
314:
315: /**
316: * Create a FGF for a field with prefetching enabled if that makes sense
317: * for the store.
318: */
319: protected FetchGroupField createFetchGroupFieldWithPrefetch(
320: FieldMetaData fmd) {
321: return new FetchGroupField(fmd);
322: }
323:
324: private void addNotNull(FetchGroup g, ArrayList groups,
325: HashMap nameGroupMap) {
326: if (g != null) {
327: groups.add(g);
328: nameGroupMap.put(g.name, g);
329: }
330: }
331:
332: private void doSubFGs(ClassMetaData cmd) {
333: ClassMetaData[] subCmds = cmd.pcSubclasses;
334: if (subCmds == null)
335: return;
336: ArrayList list = new ArrayList();
337: for (int i = 0; i < subCmds.length; i++) {
338: ClassMetaData subCmd = subCmds[i];
339: Collections.sort(subCmd.fgTmp);
340: list.clear();
341: for (int j = 0; j < cmd.fgTmp.size(); j++) {
342: FetchGroup fetchGroup = (FetchGroup) cmd.fgTmp.get(j);
343: FetchGroup subFG = (FetchGroup) subCmd.nameGroupMap
344: .get(fetchGroup.name);
345: if (j == 0) {
346: if (!fetchGroup.name.equals(FetchGroup.DFG_NAME)
347: || !subFG.name.equals(FetchGroup.DFG_NAME)) {
348: throw BindingSupportImpl.getInstance()
349: .internal("DFG broken");
350: }
351: }
352: list.add(subFG);
353: }
354: subCmd.fgTmp.removeAll(list);
355: list.addAll(subCmd.fgTmp);
356: subCmd.fgTmp.clear();
357: subCmd.fgTmp.addAll(list);
358: doSubFGs(subCmd);
359: }
360: }
361:
362: private void createFGArrays(ClassMetaData cmd) {
363: indexFGs(cmd);
364: ClassMetaData[] cmds = cmd.pcSubclasses;
365: if (cmds == null)
366: return;
367: for (int i = 0; i < cmds.length; i++) {
368: ClassMetaData aCmd = cmds[i];
369: createFGArrays(aCmd);
370: }
371: }
372:
373: private void indexFGs(ClassMetaData cmd) {
374: int ng = cmd.fgTmp.size();
375: FetchGroup[] fga = cmd.fetchGroups = new FetchGroup[ng];
376: FetchGroup[] sfga = cmd.sortedFetchGroups = new FetchGroup[ng];
377: cmd.fgTmp.toArray(fga);
378: cmd.fgTmp.toArray(sfga);
379: Arrays.sort(sfga);
380: for (int i = fga.length - 1; i >= 0; i--) {
381: if (fga[i].index == -1) {
382: fga[i].index = i;
383: }
384: }
385: }
386:
387: /**
388: * Create the default fetch group for cmd.
389: */
390: private FetchGroup createDefaultFetchGroup(ClassMetaData cmd) {
391: FieldMetaData[] fields = cmd.fields;
392: FetchGroup g = new FetchGroup(cmd, FetchGroup.DFG_NAME,
393: createStoreFetchGroup());
394: int n = fields.length;
395: ArrayList a = new ArrayList(n);
396: for (int i = 0; i < n; i++) {
397: FieldMetaData fmd = fields[i];
398: if (!fmd.defaultFetchGroup || fmd.primaryKey)
399: continue;
400: FetchGroupField fgf = new FetchGroupField(fmd);
401: a.add(fgf);
402: }
403: n = a.size();
404: g.fields = new FetchGroupField[n];
405: a.toArray(g.fields);
406: return g;
407: }
408:
409: /**
410: * This creates a fetch group that contains only default fetch group fields
411: * but no fake fields must be added here.
412: */
413: private FetchGroup createFetchGroupDefaultNoFakes(ClassMetaData cmd) {
414: FetchGroup g = new FetchGroup(cmd,
415: FetchGroup.DFG_NAME_NO_FAKES, createStoreFetchGroup());
416: FieldMetaData[] fields = cmd.fields;
417: int n = fields.length;
418: ArrayList a = new ArrayList(n);
419: for (int i = 0; i < n; i++) {
420: FieldMetaData fmd = fields[i];
421: if (fmd.defaultFetchGroup && !fmd.fake) {
422: FetchGroupField fgf = new FetchGroupField(fmd);
423: a.add(fgf);
424: }
425: }
426: n = a.size();
427: g.fields = new FetchGroupField[n];
428: a.toArray(g.fields);
429: return g;
430: }
431:
432: /**
433: * Create the retrieve fetch group for cmd.
434: */
435: private FetchGroup createRetrieveFetchGroup(ClassMetaData cmd) {
436: FetchGroup g = new FetchGroup(cmd, FetchGroup.RETRIEVE_NAME,
437: createStoreFetchGroup());
438: FieldMetaData[] fields = cmd.fields;
439: int n = fields.length;
440: ArrayList a = new ArrayList(n);
441: for (int i = 0; i < n; i++) {
442: FieldMetaData fmd = fields[i];
443: if (fmd.persistenceModifier != MDStatics.PERSISTENCE_MODIFIER_PERSISTENT)
444: continue;
445: FetchGroupField fgf = createFetchGroupFieldWithPrefetch(fmd);
446: a.add(fgf);
447: }
448: n = a.size();
449: g.fields = new FetchGroupField[n];
450: a.toArray(g.fields);
451: return g;
452: }
453:
454: /**
455: * Create the all columns fetch group for cmd. This implementation just
456: * creates a dummy empty fetch group.
457: */
458: protected FetchGroup createAllColumnsFetchGroup(ClassMetaData cmd) {
459: FetchGroup g = new FetchGroup(cmd, FetchGroup.ALL_COLS_NAME,
460: createStoreFetchGroup());
461: g.fields = new FetchGroupField[0];
462: return g;
463: }
464:
465: /**
466: * Create the ref fetch group for cmd or return null if cmd does not
467: * have any fields that reference other objects. Polyref and collection
468: * fields are included. This does not fill in the nextFetchGroup and
469: * nextKeyFetchGroup for the fields as this can only be done once all
470: * the groups have been created.
471: */
472: private FetchGroup createRefFetchGroup(ClassMetaData cmd) {
473: FieldMetaData[] fields = cmd.fields;
474: int n = fields.length;
475: ArrayList a = new ArrayList(n);
476: for (int i = 0; i < n; i++) {
477: FieldMetaData fmd = fields[i];
478: if (fmd.isDirectRef() && fmd.isFake()
479: && fmd.inverseFieldMetaData != null) {
480: continue;
481: }
482: switch (fmd.category) {
483: case MDStatics.CATEGORY_POLYREF:
484: a.add(new FetchGroupField(fmd));
485: break;
486: case MDStatics.CATEGORY_COLLECTION:
487: case MDStatics.CATEGORY_MAP:
488: if (fmd.elementType == Object.class
489: || fmd.keyType == Object.class
490: || (fmd.elementType != null && fmd.elementType
491: .isInterface())
492: || (fmd.keyType != null && fmd.keyType
493: .isInterface())) {
494: a.add(new FetchGroupField(fmd));
495: break;
496: }
497: default:
498: ClassMetaData refmd = fmd.getRefOrValueClassMetaData();
499: if (refmd != null || fmd.keyTypeMetaData != null) {
500: a.add(new FetchGroupField(fmd));
501: }
502: }
503: }
504: n = a.size();
505: if (n == 0) {
506: return null;
507: }
508: FetchGroup g = new FetchGroup(cmd, FetchGroup.REF_NAME,
509: createStoreFetchGroup());
510: g.fields = new FetchGroupField[n];
511: a.toArray(g.fields);
512: return g;
513: }
514:
515: /**
516: * Create the dependent fetch group for cmd or return null if cmd does not
517: * have any fields that reference dependent objects. This does not fill
518: * in the nextFetchGroup and nextKeyFetchGroup for the fields as this can
519: * only be done once all the groups have been created.
520: */
521: private FetchGroup createDepFetchGroup(ClassMetaData cmd) {
522: FieldMetaData[] fields = cmd.fields;
523: int n = fields.length;
524: ArrayList a = new ArrayList(n);
525: for (int i = 0; i < n; i++) {
526: FieldMetaData fmd = fields[i];
527: if (!fmd.dependentValues && !fmd.dependentKeys)
528: continue;
529: if (fmd.category != MDStatics.CATEGORY_POLYREF) {
530: ClassMetaData refmd = fmd.getRefOrValueClassMetaData();
531: if (refmd == null && fmd.keyTypeMetaData == null)
532: continue;
533: }
534: a.add(new FetchGroupField(fmd));
535: }
536: n = a.size();
537: if (n == 0)
538: return null;
539: FetchGroup g = new FetchGroup(cmd, FetchGroup.DEP_NAME,
540: createStoreFetchGroup());
541: g.fields = new FetchGroupField[n];
542: a.toArray(g.fields);
543: return g;
544: }
545:
546: /**
547: * Create the fetch group containing required all the fields that must
548: * be filled in the original state when persisting changes to instances.
549: * This includes all autoset version fields ans well as autoset timestamp
550: * fields used to implement optimistic locking.
551: */
552: private FetchGroup createReqFetchGroup(ClassMetaData cmd) {
553: FieldMetaData[] fields = cmd.fields;
554: int n = fields.length;
555: ArrayList a = new ArrayList(n);
556: if (readObjectBeforeWrite) {
557: for (int i = 0; i < n; i++) {
558: FieldMetaData fmd = fields[i];
559: if (fmd.primaryField) {
560: FetchGroupField fgf = new FetchGroupField(fmd);
561: fgf.doNotFetchObject = true;
562: a.add(fgf);
563: }
564: }
565: } else {
566: for (int i = 0; i < n; i++) {
567: FieldMetaData fmd = fields[i];
568: if (isReqField(cmd, fmd)) {
569: a.add(new FetchGroupField(fmd));
570: }
571: }
572: }
573: n = a.size();
574: if (n == 0)
575: return null;
576: FetchGroup g = new FetchGroup(cmd, FetchGroup.REQ_NAME,
577: createStoreFetchGroup());
578: g.fields = new FetchGroupField[n];
579: a.toArray(g.fields);
580: return g;
581: }
582:
583: /**
584: * Is fmd required before changes to an instance can be persisted?
585: */
586: private boolean isReqField(ClassMetaData cmd, FieldMetaData fmd) {
587: if (fmd.autoSet == MDStatics.AUTOSET_NO) {
588: return false;
589: }
590: if (fmd.typeCode == MDStatics.DATE) {
591: return fmd == cmd.optimisticLockingField;
592: }
593: return true;
594: }
595:
596: /**
597: * Create the fetch group containing all the managed many-to-many fields
598: * and all the required fields. No group is created if there are no
599: * managed many-to-many fields.
600: */
601: private FetchGroup createManagedManyToManyFetchGroup(
602: ClassMetaData cmd) {
603: int manyToManyCount = 0;
604: FieldMetaData[] fields = cmd.fields;
605: int n = fields.length;
606: ArrayList a = new ArrayList(n);
607: for (int i = 0; i < n; i++) {
608: FieldMetaData fmd = fields[i];
609: if (fmd.isManyToMany && fmd.managed || isReqField(cmd, fmd)) {
610: if (fmd.isManyToMany)
611: manyToManyCount++;
612: a.add(new FetchGroupField(fmd));
613: }
614: }
615: if (manyToManyCount == 0)
616: return null;
617: FetchGroup g = new FetchGroup(cmd,
618: FetchGroup.MANY_TO_MANY_NAME, createStoreFetchGroup());
619: g.fields = new FetchGroupField[a.size()];
620: a.toArray(g.fields);
621: return g;
622: }
623:
624: /**
625: * Process the groups extension to find all of its fields. This does
626: * not resolve next-fetch-group extensions as this can only be done
627: * when all groups have been created.
628: */
629: private void processFetchGroupFields(ClassMetaData cmd,
630: FetchGroup g, boolean quite) {
631: JdoExtension e = g.extension;
632: HashSet fieldNameSet = new HashSet(17);
633: ArrayList fields = new ArrayList();
634: JdoExtension[] nested = e.nested;
635: if (nested == null)
636: return;
637: int nn = nested.length;
638: boolean noFgFields = true;
639: for (int k = 0; k < nn; k++) {
640: JdoExtension ne = nested[k];
641: if (ne.key != JdoExtensionKeys.FIELD_NAME)
642: continue;
643: String fname = ne.getString();
644: if (fieldNameSet.contains(fname)) {
645: throw BindingSupportImpl.getInstance().runtime(
646: "Field is already in group: '" + fname + "'\n"
647: + ne.getContext());
648: }
649: noFgFields = false;
650: //don't add pk fields to fg's
651: FieldMetaData fmd = cmd.getFieldMetaData(fname);
652: if (fmd == null) {
653: cmd.addError(BindingSupportImpl.getInstance().runtime(
654: "Field does not exist: '" + fname + "'\n"
655: + ne.getContext()), quite);
656: continue;
657: }
658: if (fmd.primaryKey)
659: continue;
660: // if (fmd.jdbcField.isSharedWithPK()) continue;
661:
662: fieldNameSet.add(fname);
663: FetchGroupField f = new FetchGroupField(fmd);
664: f.extension = ne;
665: fields.add(f);
666: }
667:
668: //add the fields that should be in all the fg's
669: FieldMetaData[] fmds = cmd.fields;
670: int n = fmds.length;
671: for (int i = 0; i < n; i++) {
672: FieldMetaData fmd = fmds[i];
673: if (!fmd.secondaryField && fmd.includeInAllFGs()
674: && !fieldNameSet.contains(fmd.name)) {
675: fieldNameSet.add(fmd.name);
676: FetchGroupField fgf = new FetchGroupField(fmd);
677: fields.add(fgf);
678: }
679: }
680:
681: int nf = fields.size();
682: if (noFgFields && nf == 0) {
683: throw BindingSupportImpl.getInstance().runtime(
684: "Fetch group does not contain any fields: '"
685: + g.name + "'\n" + e.getContext());
686: }
687: g.fields = new FetchGroupField[nf];
688: fields.toArray(g.fields);
689: }
690:
691: /**
692: * Process extensions for all fields in all groups. This resolves
693: * next-fetch-group and next-key-fetch-group references.
694: */
695: private void processFetchGroups(ClassMetaData cmd) {
696: processRefFetchGroup(cmd);
697: FetchGroup[] groups = cmd.fetchGroups;
698: if (groups == null) {
699: return;
700: }
701: FetchGroup dep = cmd.refFetchGroup;
702: int ng = groups.length;
703: for (int i = 0; i < ng; i++) {
704: FetchGroup group = groups[i];
705: if (group == dep)
706: continue;
707: FetchGroupField[] fields = group.fields;
708: if (fields == null)
709: continue;
710: int nf = fields.length;
711: for (int j = 0; j < nf; j++) {
712: FetchGroupField f = fields[j];
713: ClassMetaData refmd = f.fmd
714: .getRefOrValueClassMetaData();
715: ClassMetaData keymd = f.fmd.keyTypeMetaData;
716: JdoExtension[] nested = f.extension == null ? null
717: : f.extension.nested;
718: if (nested != null) {
719: int nl = nested.length;
720: for (int k = 0; k < nl; k++) {
721: JdoExtension e = nested[k];
722: switch (e.key) {
723: case JdoExtensionKeys.NEXT_FETCH_GROUP:
724: processNextFetchGroup(f, e, refmd);
725: break;
726: case JdoExtensionKeys.NEXT_KEY_FETCH_GROUP:
727: processNextKeyFetchGroup(f, e, keymd);
728: break;
729: default:
730: if (e.isCommon()) {
731: MetaDataBuilder
732: .throwUnexpectedExtension(e);
733: }
734: }
735: ;
736: }
737: }
738: if (f.nextFetchGroup == null && refmd != null
739: && refmd.fetchGroups != null) {
740: f.nextFetchGroup = refmd.fetchGroups[0];
741: }
742: if (f.nextKeyFetchGroup == null && keymd != null
743: && keymd.fetchGroups != null) {
744: f.nextKeyFetchGroup = keymd.fetchGroups[0];
745: }
746: }
747: }
748: }
749:
750: private void processNextFetchGroup(FetchGroupField f,
751: JdoExtension e, ClassMetaData refmd) {
752: if (f.nextFetchGroup != null) {
753: throw BindingSupportImpl.getInstance().runtime(
754: "Only one next-fetch-group extension is allowed\n"
755: + e.getContext());
756: }
757: if (refmd == null) {
758: throw BindingSupportImpl.getInstance().runtime(
759: "Field does not reference a PC class\n"
760: + e.getContext());
761: }
762: FetchGroup g = refmd.getFetchGroup(e.getString());
763: if (g == null) {
764: throw BindingSupportImpl.getInstance().runtime(
765: "Fetch group '" + e.getString()
766: + "' not found in class " + refmd.qname
767: + "\n" + e.getContext());
768: }
769: f.nextFetchGroup = g;
770: }
771:
772: private void processNextKeyFetchGroup(FetchGroupField f,
773: JdoExtension e, ClassMetaData keymd) {
774: if (f.nextKeyFetchGroup != null) {
775: throw BindingSupportImpl.getInstance().runtime(
776: "Only one next-key-fetch-group extension is allowed\n"
777: + e.getContext());
778: }
779: if (keymd == null) {
780: throw BindingSupportImpl.getInstance().runtime(
781: "Field key does not reference a PC class\n"
782: + e.getContext());
783: }
784: FetchGroup g = keymd.getFetchGroup(e.getString());
785: if (g == null) {
786: throw BindingSupportImpl.getInstance().runtime(
787: "Fetch group '" + e.getString()
788: + "' not found in class " + keymd.qname
789: + "\n" + e.getContext());
790: }
791: f.nextKeyFetchGroup = g;
792: }
793:
794: /**
795: * Fill in the nextFetchGroup and nextKeyFetchGroup references for
796: * all the fields in the refFetchGroup for cmd (if any). Only dependent
797: * object references are followed.
798: */
799: private void processRefFetchGroup(ClassMetaData cmd) {
800: FetchGroup g = cmd.refFetchGroup;
801: if (g == null)
802: return;
803: FetchGroupField[] fields = g.fields;
804: int nf = fields.length;
805: for (int j = 0; j < nf; j++) {
806: FetchGroupField f = fields[j];
807: FieldMetaData fmd = f.fmd;
808: // only follow dependent object references for this group
809: if (fmd.dependentValues) {
810: ClassMetaData refmd = fmd.getRefOrValueClassMetaData();
811: if (refmd != null)
812: f.nextFetchGroup = refmd.refFetchGroup;
813: }
814: if (fmd.dependentKeys) {
815: ClassMetaData keymd = fmd.keyTypeMetaData;
816: if (keymd != null)
817: f.nextKeyFetchGroup = keymd.refFetchGroup;
818: }
819: }
820: }
821: }
|