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.common.OID;
015: import com.versant.core.common.State;
016: import com.versant.core.metadata.parser.JdoExtension;
017:
018: import java.io.PrintStream;
019: import java.io.Serializable;
020: import java.util.HashSet;
021: import java.util.Set;
022:
023: import com.versant.core.common.BindingSupportImpl;
024:
025: /**
026: * A group of fields from a class that are retrieved together. This holds
027: * the field and assorted store specific options.
028: */
029: public final class FetchGroup implements Serializable, Comparable {
030:
031: public static final FetchGroupField[] EMPTY_FETCHGROUP_FIELDS = new FetchGroupField[0];
032: public static final int[] EMPTY_INT_ARRAY = new int[0];
033:
034: /**
035: * This is the name reserved for the default fetch group.
036: */
037: public static final String DFG_NAME = "default";
038: /**
039: * This is the name reserved for the default fetch group without fake fields.
040: */
041: public static final String DFG_NAME_NO_FAKES = "defaultNoFakes";
042: /**
043: * This is the fetchGroup to load all data.
044: */
045: public static final String RETRIEVE_NAME = "jdoGenieRetrieveFG";
046: /**
047: * This is the fetchGroup to load all columns in the class table.
048: */
049: public static final String ALL_COLS_NAME = "_jdoall";
050: /**
051: * This is the name reserved for the fetch group containing all the
052: * reference and collection fields for reachability searching.
053: */
054: public static final String REF_NAME = "_jdoref";
055: /**
056: * This is the name reserved for the fetch group containing all the
057: * reference fields used to complete one-to-many relationships.
058: */
059: public static final String DETAIL_NAME = "_jdodetail";
060: /**
061: * This is the name reserved for the fetch group containing all the
062: * direct references and dependent fields. All instances in the delete
063: * graph must contain at least this group. This will always be
064: * a superset of the DEP_NAME group.
065: *
066: * @see #DEP_NAME
067: */
068: public static final String DEL_NAME = "_jdodel";
069: /**
070: * This is the name reserved for the fetch group containing all the
071: * depedent reference and collection fields for delete reachability
072: * searching.
073: */
074: public static final String DEP_NAME = "_jdodep";
075: /**
076: * This is the name reserved for the fetch group containing all the
077: * fields that must be filled in the original state (e.g. jdoVersion etc.)
078: * when persisting changes to instances.
079: */
080: public static final String REQ_NAME = "_jdoreq";
081: /**
082: * This is the name reserved for the fetch group containing all the
083: * many-to-many fields that must be cleared when deleting an instance.
084: *
085: * @see ClassMetaData#managedManyToManyFetchGroup
086: */
087: public static final String MANY_TO_MANY_NAME = "_manytomany";
088:
089: /**
090: * The name of this group.
091: */
092: public String name;
093: /**
094: * The class this group belongs to.
095: */
096: public ClassMetaData classMetaData;
097: /**
098: * Our index in our classes fetchGroups array. This is -1 for dynamically
099: * created fetch groups as they are not in the fetchGroups array.
100: *
101: * @see ClassMetaData#fetchGroups
102: */
103: public int index = -1;
104: /**
105: * The parsed meta data for this group (null if none i.e. automatically
106: * generated fetch group e.g. the default fetch group).
107: */
108: public JdoExtension extension;
109: /**
110: * The fields in this group in fieldNo order.
111: */
112: public FetchGroupField[] fields;
113: /**
114: * The state field no's of the fetchGroup.
115: */
116: public int[] stateFieldNos;
117: /**
118: * The corresponding fetch group from our superclass or null if none.
119: */
120: public FetchGroup super FetchGroup;
121: /**
122: * The sub fetch groups from our subclasses or null if none.
123: */
124: public FetchGroup[] subFetchGroups;
125: /**
126: * Send any available State data for the instance being fetched along
127: * with the fetch call. This is used for fields that have some data
128: * stored with the instance itself and some data stored elsewhere e.g.
129: * collections on VDS.
130: */
131: public boolean sendFieldsOnFetch;
132: /**
133: * Does this fetch group or any other fetch group in the heirachy
134: * contain any fields with primaryField true?
135: */
136: public boolean hasPrimaryFields;
137: /**
138: * Extra store specific info attached to this fetch group.
139: */
140: public transient StoreFetchGroup storeFetchGroup;
141:
142: private boolean canUseParallelFetch;
143: private boolean canUseParallelFetchDone;
144:
145: /**
146: * The total number of main table columns in the fetch group.
147: */
148: public int jdbcTotalCols;
149:
150: /**
151: * This is a fgf for a jdbc collection field that must be cross joined.
152: * Maps is not supported.
153: */
154: public FetchGroupField crossJoinedCollectionField;
155:
156: public FetchGroup(ClassMetaData classMetaData, String name,
157: StoreFetchGroup sfg) {
158: this .classMetaData = classMetaData;
159: this .name = name;
160: this .storeFetchGroup = sfg;
161: if (sfg != null) {
162: sfg.setFetchGroup(this );
163: }
164: }
165:
166: /**
167: * Sort by name except for the default fetch group which is always first.
168: * Do not change this ordering.
169: */
170: public int compareTo(Object o) {
171: if (name == DFG_NAME)
172: return -1;
173: return name.compareTo(((FetchGroup) o).name);
174: }
175:
176: /**
177: * Add a field to this group. This is used to add fake fields created by
178: * stores to hold extra information (e.g. row version column values for
179: * the JDBC store).
180: */
181: public void add(FieldMetaData fmd) {
182: int n = fields.length;
183: FetchGroupField[] a = new FetchGroupField[n + 1];
184: System.arraycopy(fields, 0, a, 0, n);
185: a[n] = new FetchGroupField(fmd);
186: fields = a;
187: if (storeFetchGroup != null) {
188: storeFetchGroup.fieldAdded(fmd);
189: }
190: }
191:
192: /**
193: * Is the field part of this group?
194: */
195: public boolean contains(FieldMetaData fmd) {
196: for (int i = fields.length - 1; i >= 0; i--) {
197: FetchGroupField f = fields[i];
198: if (f.fmd == fmd)
199: return true;
200: }
201: return false;
202: }
203:
204: public String toString() {
205: return "FetchGroup@" + System.identityHashCode(this ) + ": "
206: + name;
207: }
208:
209: /**
210: * Finish initialization of this fetch group.
211: */
212: public void finish() {
213: if (fields != null) {
214: // init the stateFieldNos array
215: int nf = fields.length;
216: stateFieldNos = new int[nf];
217: for (int i = nf - 1; i >= 0; i--) {
218: stateFieldNos[i] = fields[i].fmd.stateFieldNo;
219: }
220: } else {
221: fields = EMPTY_FETCHGROUP_FIELDS;
222: stateFieldNos = EMPTY_INT_ARRAY;
223: }
224:
225: // find the super fetch group (if any)
226: if (name != null) {
227: ClassMetaData pcmd = classMetaData.pcSuperMetaData;
228: if (pcmd != null) {
229: super FetchGroup = pcmd.getFetchGroup(name);
230: }
231: }
232:
233: if (storeFetchGroup != null) {
234: storeFetchGroup.finish();
235: }
236: }
237:
238: /**
239: * Get the state fetch group index of this group.
240: *
241: * @see State
242: */
243: public int getStateIndex() {
244: return classMetaData.super FetchGroupCount + index;
245: }
246:
247: public boolean isRefFG() {
248: return name.equals("_jdoref");
249: }
250:
251: /**
252: * Make sure this fetchGroup is for the available class of OID or one
253: * of its superclasses. Returns the most derived usable group i.e. if
254: * this method is called with a group for class Base and the available
255: * class from the OID is a subclass of base then the corresponding
256: * sub group will be returned.
257: */
258: public FetchGroup resolve(OID oid, ModelMetaData jmd) {
259: // make sure the fetch group is for the available meta data of oid
260: ClassMetaData acmd = oid.getAvailableClassMetaData();
261: ClassMetaData gcmd = classMetaData;
262: if (gcmd == acmd)
263: return this ;
264: for (ClassMetaData cmd = acmd; cmd != gcmd;) {
265: cmd = cmd.pcSuperMetaData;
266: if (cmd == null) {
267: throw BindingSupportImpl.getInstance().internal(
268: "Fetch group " + this + " (" + classMetaData
269: + ") does not match OID " + oid + " ("
270: + acmd + ")");
271: }
272: }
273: return acmd.getFetchGroup(name);
274: }
275:
276: /**
277: * Returns the most derived usable group i.e. if this method is called
278: * with a group for class Base and availableCmd is a subclass of base
279: * then the corresponding sub group will be returned.
280: */
281: public FetchGroup resolve(ClassMetaData availableCmd) {
282: if (availableCmd == classMetaData)
283: return this ;
284: return availableCmd.getFetchGroup(name);
285: }
286:
287: public void dump() {
288: dump(Debug.OUT, "");
289: }
290:
291: public void dump(PrintStream out, String indent) {
292: out.println(indent + "FetchGroup " + this );
293: String is = indent + " ";
294: out.println(is + "classMetaData = " + classMetaData);
295: out.println(is + "index = " + index);
296: out.println(is + "getStateIndex() = " + getStateIndex());
297: out.println(is + "superFetchGroup = " + super FetchGroup);
298: if (subFetchGroups != null) {
299: for (int i = 0; i < subFetchGroups.length; i++) {
300: FetchGroup sg = subFetchGroups[i];
301: out.println(is + "subFetchGroups[" + i + "] = "
302: + sg.classMetaData.qname + " " + sg);
303: }
304: } else {
305: out.println(is + "subFetchGroups is null");
306: }
307: if (fields != null) {
308: for (int i = 0; i < fields.length; i++) {
309: out.println(is + "fields[" + i + "] " + fields[i]);
310: }
311: }
312: if (stateFieldNos != null) {
313: for (int i = 0; i < stateFieldNos.length; i++) {
314: out.println(is + "stateField[" + i + "] no = "
315: + stateFieldNos[i]);
316: }
317: }
318: }
319:
320: /**
321: * Can this fetch group make use of parallel fetching of collections
322: * and maps? This will recursively check fetch groups we reference
323: * and so on.
324: */
325: public boolean canUseParallelFetch() {
326: if (canUseParallelFetchDone)
327: return canUseParallelFetch;
328: return canUseParallelFetchImp(new HashSet());
329: }
330:
331: private boolean canUseParallelFetchImp(Set fgs) {
332: if (fgs.contains(this ))
333: return canUseParallelFetch;
334:
335: fgs.add(this );
336: for (int i = fields.length - 1; i >= 0; i--) {
337: FetchGroupField fgf = fields[i];
338: int cat = fgf.fmd.category;
339: if (cat == MDStatics.CATEGORY_COLLECTION
340: || cat == MDStatics.CATEGORY_MAP) {
341: canUseParallelFetch = true;
342: break;
343: } else if (cat == MDStatics.CATEGORY_REF
344: && fgf.nextFetchGroup.canUseParallelFetchImp(fgs)) {
345: canUseParallelFetch = true;
346: break;
347: }
348: }
349: //give the superclass fg a change to calculate
350: if (super FetchGroup != null) {
351: super FetchGroup.canUseParallelFetchImp(fgs);
352: //if we are false then take superfetch groups property
353: if (!canUseParallelFetch)
354: canUseParallelFetch = super FetchGroup.canUseParallelFetch;
355: }
356:
357: canUseParallelFetchDone = true;
358: return canUseParallelFetch;
359: }
360:
361: /**
362: * Does this fetch group or any of its sub fetch groups contain any
363: * fields with secondaryField true?
364: */
365: public boolean hasSecondaryFields() {
366: if (fields != null) {
367: for (int i = fields.length - 1; i >= 0; i--) {
368: if (fields[i].fmd.secondaryField)
369: return true;
370: }
371: }
372: if (subFetchGroups != null) {
373: for (int i = subFetchGroups.length - 1; i >= 0; i--) {
374: if (subFetchGroups[i].hasSecondaryFields())
375: return true;
376: }
377: }
378: return false;
379: }
380:
381: /**
382: * Does this fetch group or any of its sub fetch groups contain any
383: * fields with primaryField true? This will search the heirachy i.e.
384: * it does not check the hasPrimaryFields flag. If nonFake is true then
385: * only fields with fake == true are not considered.
386: */
387: public boolean hasPrimaryFields(boolean nonFake) {
388: if (fields == null)
389: return false;
390: for (int i = fields.length - 1; i >= 0; i--) {
391: final FieldMetaData fmd = fields[i].fmd;
392: if (fmd.primaryField && (!nonFake || !fmd.fake)) {
393: return true;
394: }
395: }
396: if (subFetchGroups != null) {
397: for (int i = subFetchGroups.length - 1; i >= 0; i--) {
398: if (subFetchGroups[i].hasPrimaryFields(false)) {
399: return true;
400: }
401: }
402: }
403: return false;
404: }
405:
406: /**
407: * Set the sendFieldsOnFetch flag for us and all of our sub
408: * fetch groups recursively.
409: */
410: public void setSendFieldsOnFetch(boolean on) {
411: this .sendFieldsOnFetch = on;
412: if (subFetchGroups != null) {
413: for (int i = subFetchGroups.length - 1; i >= 0; i--) {
414: subFetchGroups[i].setSendFieldsOnFetch(on);
415: }
416: }
417: }
418:
419: /**
420: * Set the hasPrimaryFields flag on us and all of our sub fetch groups
421: * recursively.
422: */
423: public void setHasPrimaryFields(boolean on) {
424: this .hasPrimaryFields = on;
425: if (subFetchGroups != null) {
426: for (int i = subFetchGroups.length - 1; i >= 0; i--) {
427: subFetchGroups[i].setHasPrimaryFields(on);
428: }
429: }
430: }
431: }
|