001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.internal.descriptors;
038:
039: import java.util.*;
040: import oracle.toplink.essentials.exceptions.*;
041: import oracle.toplink.essentials.indirection.*;
042: import oracle.toplink.essentials.internal.helper.*;
043: import oracle.toplink.essentials.mappings.*;
044: import oracle.toplink.essentials.internal.sessions.AbstractSession;
045: import oracle.toplink.essentials.descriptors.ClassDescriptor;
046:
047: /**
048: * This class provides a generic way of using the descriptor information
049: * to traverse an object graph.
050: * Define a subclass, or an inner class, that implements at least
051: * #iterate(Object) to implement a new traversal
052: * feature without having to change the mapping classes or the object builder.
053: * It provides functionality such as a cascading depth, a stack of visited object,
054: * and a collection of the visited objects.
055: *
056: * NOTE:
057: * If this works nicely the merge manager, remote traversals, and maybe
058: * even aspects of the commit manager could be converted to use this class.
059: */
060: public abstract class DescriptorIterator {
061: public static final int NoCascading = 1;
062: public static final int CascadePrivateParts = 2;
063: public static final int CascadeAllParts = 3;
064: protected IdentityHashtable visitedObjects;
065: protected Stack visitedStack;
066: protected AbstractSession session;
067: protected DatabaseMapping currentMapping;
068: protected ClassDescriptor currentDescriptor;
069: protected Object result;// this is a work area, typically used as a Collecting Parm
070: protected boolean shouldIterateOverIndirectionObjects;
071: protected boolean shouldIterateOverUninstantiatedIndirectionObjects;
072: protected boolean shouldIterateOverWrappedObjects;
073: protected boolean shouldIterateOnIndirectionObjects;
074: protected boolean shouldIterateOnAggregates;
075: protected boolean shouldIterateOnPrimitives;
076: protected boolean shouldBreak;
077: protected int cascadeDepth;// see static constants below
078:
079: /**
080: * Construct a typical iterator:
081: * iterate over all the objects
082: * process the objects contained by "value holders"...
083: * ...but only if they have already been instantiated...
084: * ...and don't process the "value holders" themselves
085: * process "wrapped" objects
086: * skip aggregate objects
087: * skip primitives (Strings, Dates, Integers, etc.)
088: */
089: public DescriptorIterator() {
090: // 2612538 - the default size of IdentityHashtable (32) is appropriate
091: this .visitedObjects = new IdentityHashtable();
092: this .visitedStack = new Stack();
093: this .cascadeDepth = CascadeAllParts;
094: this .shouldIterateOverIndirectionObjects = true;// process the objects contained by ValueHolders...
095: this .shouldIterateOverUninstantiatedIndirectionObjects = false;// ...but only if they have already been instantiated...
096: this .shouldIterateOnIndirectionObjects = false;// ...and don't process the ValueHolders themselves
097: this .shouldIterateOverWrappedObjects = true;// process "wrapped" objects
098: this .shouldIterateOnAggregates = false;
099: this .shouldIterateOnPrimitives = false;
100: this .shouldBreak = false;
101: }
102:
103: public int getCascadeDepth() {
104: return cascadeDepth;
105: }
106:
107: public ClassDescriptor getCurrentDescriptor() {
108: return currentDescriptor;
109: }
110:
111: public DatabaseMapping getCurrentMapping() {
112: return currentMapping;
113: }
114:
115: /**
116: * Fetch and return the descriptor for the specified object.
117: */
118: protected ClassDescriptor getDescriptorFor(Object object) {
119: ClassDescriptor result = getSession().getDescriptor(object);
120: if (result == null) {
121: throw DescriptorException.missingDescriptor(object
122: .getClass().getName());
123: }
124: return result;
125: }
126:
127: public Object getResult() {
128: return result;
129: }
130:
131: public AbstractSession getSession() {
132: return session;
133: }
134:
135: /**
136: * Return the second-to-last object visited.
137: */
138: public Object getVisitedGrandparent() {
139: Object parent = getVisitedStack().pop();
140: Object result = getVisitedStack().peek();
141: getVisitedStack().push(parent);
142: return result;
143: }
144:
145: public IdentityHashtable getVisitedObjects() {
146: return visitedObjects;
147: }
148:
149: /**
150: * Return the last object visited.
151: */
152: public Object getVisitedParent() {
153: return getVisitedStack().peek();
154: }
155:
156: public Stack getVisitedStack() {
157: return visitedStack;
158: }
159:
160: /**
161: * Iterate an aggregate object
162: * (i.e. an object that is the target of an AggregateMapping).
163: * Override this method if appropriate.
164: */
165: protected void internalIterateAggregateObject(Object aggregateObject) {
166: iterate(aggregateObject);
167: }
168:
169: /**
170: * Iterate an indirect container (IndirectList or IndirectMap).
171: * Override this method if appropriate.
172: */
173: protected void internalIterateIndirectContainer(
174: IndirectContainer container) {
175: iterate(container);
176: }
177:
178: /**
179: * Iterate a primitive object (String, Date, Integer, etc.).
180: * Override this method if appropriate.
181: */
182: protected void internalIteratePrimitive(Object primitiveValue) {
183: iterate(primitiveValue);
184: }
185:
186: /**
187: * Iterate a (a non-Aggregate) reference object.
188: * Override this method if appropriate.
189: */
190: protected void internalIterateReferenceObject(Object referenceObject) {
191: iterate(referenceObject);
192: }
193:
194: /**
195: * Iterate a value holder.
196: * Override this method if appropriate.
197: */
198: protected void internalIterateValueHolder(
199: ValueHolderInterface valueHolder) {
200: iterate(valueHolder);
201: }
202:
203: /**
204: * To define a new iterator create a subclass and define at least this method.
205: * Given an object or set of the objects, this method will be called on those
206: * objects and any object connected to them by using the descriptors to
207: * traverse the object graph.
208: * Override the assorted #internalIterate*() methods if appropriate.
209: */
210: protected abstract void iterate(Object object);
211:
212: /**
213: * Iterate on the mapping's reference object and
214: * recursively iterate on the reference object's
215: * reference objects.
216: * This is used for aggregate and aggregate collection mappings, which are not iterated on by default.
217: */
218: public void iterateForAggregateMapping(Object aggregateObject,
219: DatabaseMapping mapping, ClassDescriptor descriptor) {
220: if (aggregateObject == null) {
221: return;
222: }
223: setCurrentMapping(mapping);
224: // aggregate descriptors are passed in because they could be part of an inheritance tree
225: setCurrentDescriptor(descriptor);
226:
227: if (shouldIterateOnAggregates()) {// false by default
228: internalIterateAggregateObject(aggregateObject);
229: if (shouldBreak()) {
230: setShouldBreak(false);
231: return;
232: }
233: }
234:
235: iterateReferenceObjects(aggregateObject);
236: }
237:
238: /**
239: * Iterate on the indirection object for its mapping.
240: */
241: public void iterateIndirectContainerForMapping(
242: IndirectContainer container, DatabaseMapping mapping) {
243: setCurrentMapping(mapping);
244: setCurrentDescriptor(null);
245:
246: if (shouldIterateOnIndirectionObjects()) {// false by default
247: internalIterateIndirectContainer(container);
248: }
249:
250: if (shouldIterateOverUninstantiatedIndirectionObjects()
251: || (shouldIterateOverIndirectionObjects() && container
252: .isInstantiated())) {
253: // force instantiation only if specified
254: mapping.iterateOnRealAttributeValue(this , container);
255: }
256: }
257:
258: /**
259: * Iterate on the primitive value for its mapping.
260: */
261: public void iteratePrimitiveForMapping(Object primitiveValue,
262: DatabaseMapping mapping) {
263: if (primitiveValue == null) {
264: return;
265: }
266: setCurrentMapping(mapping);
267: setCurrentDescriptor(null);
268:
269: if (shouldIterateOnPrimitives()) {// false by default
270: internalIteratePrimitive(primitiveValue);
271: }
272: }
273:
274: /**
275: * Iterate on the mapping's reference object and
276: * recursively iterate on the reference object's
277: * reference objects.
278: */
279: public void iterateReferenceObjectForMapping(
280: Object referenceObject, DatabaseMapping mapping) {
281: if (!(shouldCascadeAllParts() || (shouldCascadePrivateParts() && mapping
282: .isPrivateOwned()))) {
283: return;
284: }
285:
286: // When using wrapper policy in EJB the iteration can stop in certain cases,
287: // this is because EJB forces beans to be registered anyway and clone identity can be violated
288: // and the violated clones references to session objects should not be traversed.
289: ClassDescriptor rd = mapping.getReferenceDescriptor();
290: if ((!shouldIterateOverWrappedObjects()) && (rd != null)
291: && (rd.hasWrapperPolicy())) {
292: return;
293: }
294: if (referenceObject == null) {
295: return;
296: }
297:
298: // Check if already processed.
299: if (getVisitedObjects().containsKey(referenceObject)) {
300: return;
301: }
302:
303: getVisitedObjects().put(referenceObject, referenceObject);
304: setCurrentMapping(mapping);
305: setCurrentDescriptor(getDescriptorFor(referenceObject));
306:
307: internalIterateReferenceObject(referenceObject);
308: if (shouldBreak()) {
309: setShouldBreak(false);
310: return;
311: }
312:
313: iterateReferenceObjects(referenceObject);
314: }
315:
316: /**
317: * Iterate over the sourceObject's reference objects,
318: * updating the visited stack appropriately.
319: */
320: protected void iterateReferenceObjects(Object sourceObject) {
321: getVisitedStack().push(sourceObject);
322: getCurrentDescriptor().getObjectBuilder().iterate(this );
323: getVisitedStack().pop();
324: }
325:
326: /**
327: * Iterate on the value holder for its mapping.
328: */
329: public void iterateValueHolderForMapping(
330: ValueHolderInterface valueHolder, DatabaseMapping mapping) {
331: setCurrentMapping(mapping);
332: setCurrentDescriptor(null);
333:
334: if (shouldIterateOnIndirectionObjects()) {// false by default
335: internalIterateValueHolder(valueHolder);
336: }
337:
338: if (shouldIterateOverUninstantiatedIndirectionObjects()
339: || (shouldIterateOverIndirectionObjects() && valueHolder
340: .isInstantiated())) {
341: // force instantiation only if specified
342: mapping.iterateOnRealAttributeValue(this , valueHolder
343: .getValue());
344: }
345: }
346:
347: public void setCascadeDepth(int cascadeDepth) {
348: this .cascadeDepth = cascadeDepth;
349: }
350:
351: public void setCurrentDescriptor(ClassDescriptor currentDescriptor) {
352: this .currentDescriptor = currentDescriptor;
353: }
354:
355: public void setCurrentMapping(DatabaseMapping currentMapping) {
356: this .currentMapping = currentMapping;
357: }
358:
359: public void setResult(Object result) {
360: this .result = result;
361: }
362:
363: public void setSession(AbstractSession session) {
364: this .session = session;
365: }
366:
367: public void setShouldBreak(boolean shouldBreak) {
368: this .shouldBreak = shouldBreak;
369: }
370:
371: /**
372: * Set whether the aggregate reference objects themselves
373: * should be processed. (The objects referenced by the aggregate
374: * objects will be processed either way.)
375: */
376: public void setShouldIterateOnAggregates(
377: boolean shouldIterateOnAggregates) {
378: this .shouldIterateOnAggregates = shouldIterateOnAggregates;
379: }
380:
381: /**
382: * Set whether the indirection objects themselves (e.g. the ValueHolders)
383: * should be processed.
384: */
385: public void setShouldIterateOnIndirectionObjects(
386: boolean shouldIterateOnIndirectionObjects) {
387: this .shouldIterateOnIndirectionObjects = shouldIterateOnIndirectionObjects;
388: }
389:
390: /**
391: * Set whether to process primitive reference objects
392: * (e.g. Strings, Dates, ints).
393: */
394: public void setShouldIterateOnPrimitives(
395: boolean shouldIterateOnPrimitives) {
396: this .shouldIterateOnPrimitives = shouldIterateOnPrimitives;
397: }
398:
399: /**
400: * Set whether to process the objects contained by indirection objects
401: * (e.g. a ValueHolder's value) - but *without* instantiating them.
402: * @see #setShouldIterateOverUninstantiatedIndirectionObjects()
403: */
404: public void setShouldIterateOverIndirectionObjects(
405: boolean shouldIterateOverIndirectionObjects) {
406: this .shouldIterateOverIndirectionObjects = shouldIterateOverIndirectionObjects;
407: }
408:
409: /**
410: * Set whether to *instantiate* and process the objects
411: * contained by indirection objects (e.g. a ValueHolder's value).
412: */
413: public void setShouldIterateOverUninstantiatedIndirectionObjects(
414: boolean shouldIterateOverUninstantiatedIndirectionObjects) {
415: this .shouldIterateOverUninstantiatedIndirectionObjects = shouldIterateOverUninstantiatedIndirectionObjects;
416: }
417:
418: public void setShouldIterateOverWrappedObjects(
419: boolean shouldIterateOverWrappedObjects) {
420: this .shouldIterateOverWrappedObjects = shouldIterateOverWrappedObjects;
421: }
422:
423: public void setVisitedObjects(IdentityHashtable visitedObjects) {
424: this .visitedObjects = visitedObjects;
425: }
426:
427: protected void setVisitedStack(Stack visitedStack) {
428: this .visitedStack = visitedStack;
429: }
430:
431: public boolean shouldBreak() {
432: return shouldBreak;
433: }
434:
435: public boolean shouldCascadeAllParts() {
436: return getCascadeDepth() == CascadeAllParts;
437: }
438:
439: public boolean shouldCascadeNoParts() {
440: return (getCascadeDepth() == NoCascading);
441: }
442:
443: public boolean shouldCascadePrivateParts() {
444: return (getCascadeDepth() == CascadeAllParts)
445: || (getCascadeDepth() == CascadePrivateParts);
446: }
447:
448: /**
449: * Return whether the aggregate reference objects themselves
450: * should be processed. (The objects referenced by the aggregate
451: * objects will be processed either way.)
452: */
453: public boolean shouldIterateOnAggregates() {
454: return shouldIterateOnAggregates;
455: }
456:
457: /**
458: * Return whether the indirection objects themselves (e.g. the ValueHolders)
459: * should be processed.
460: */
461: public boolean shouldIterateOnIndirectionObjects() {
462: return shouldIterateOnIndirectionObjects;
463: }
464:
465: /**
466: * Return whether to process primitive reference objects
467: * (e.g. Strings, Dates, ints).
468: */
469: public boolean shouldIterateOnPrimitives() {
470: return shouldIterateOnPrimitives;
471: }
472:
473: /**
474: * Return whether to process the objects contained by indirection objects
475: * (e.g. a ValueHolder's value) - but *without* instantiating them.
476: * @see #shouldIterateOverUninstantiatedIndirectionObjects()
477: */
478: public boolean shouldIterateOverIndirectionObjects() {
479: return shouldIterateOverIndirectionObjects;
480: }
481:
482: /**
483: * Return whether to *instantiate* and process the objects
484: * contained by indirection objects (e.g. a ValueHolder's value).
485: */
486: public boolean shouldIterateOverUninstantiatedIndirectionObjects() {
487: return shouldIterateOverUninstantiatedIndirectionObjects;
488: }
489:
490: public boolean shouldIterateOverWrappedObjects() {
491: return shouldIterateOverWrappedObjects;
492: }
493:
494: /**
495: * This is the root method called to start the iteration.
496: */
497: public void startIterationOn(Object sourceObject) {
498: getVisitedObjects().put(sourceObject, sourceObject);
499: setCurrentMapping(null);
500: setCurrentDescriptor(getSession().getDescriptor(sourceObject));
501:
502: iterate(sourceObject);
503:
504: // start the recursion
505: if ((getCurrentDescriptor() != null)
506: && (!shouldCascadeNoParts()) && !this.shouldBreak()) {
507: iterateReferenceObjects(sourceObject);
508: }
509: }
510: }
|