KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > odmg > ObjectEnvelopeOrdering


1 package org.apache.ojb.odmg;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.util.ArrayList JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import org.apache.commons.lang.ArrayUtils;
24 import org.apache.commons.lang.builder.ToStringBuilder;
25 import org.apache.ojb.broker.Identity;
26 import org.apache.ojb.broker.core.proxy.ProxyHelper;
27 import org.apache.ojb.broker.metadata.ClassDescriptor;
28 import org.apache.ojb.broker.metadata.CollectionDescriptor;
29 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
30 import org.apache.ojb.broker.util.BrokerHelper;
31 import org.apache.ojb.broker.util.logging.Logger;
32 import org.apache.ojb.broker.util.logging.LoggerFactory;
33 import org.apache.ojb.odmg.states.ModificationState;
34
35 /**
36  * <p>Implements an algorithm for reordering the object envelopes of a pending
37  * transaction to minimized the probability of foreign key constraint
38  * violations.</p>
39  *
40  * <p>The algorithm is based on a graph theoretical approach: Each object
41  * envelope is represented by a vertex in a graph. Each possible constraint
42  * on the order of database operations is represented by a directed edge
43  * in this graph, in which the initial vertex represents the object envelope
44  * to be sent to the database first and the terminal index represents the
45  * object envelope that might cause a FK violation if the initial vertex
46  * has not been sent to the database before.</p>
47  *
48  * <p>Additionally the edges in this graph are weighted. This is necessary
49  * because the object envelopes provide only information on the relation
50  * between objects <strong>after</strong> the transaction. FK violations,
51  * however, can also occur due to relations that existed <strong>before</strong>
52  * the transaction. Therefore the algorithm also considers potential relations
53  * between objects due to the fact that an object is of a class that is
54  * the item class of a object or collection reference of another object.
55  * Graph edges representing such potential relationships receive a lower
56  * weight than edges representing concrete relationships that exist in the
57  * current state of the object model.</p>
58  *
59  * <p>Once all graph edges have been established, the algorithm proceeds as
60  * follows:</p>
61  * <ol>
62  * <li>Iterate through all vertices and sum up the weight of all incoming
63  * edges (i.e. those edges whose terminal vertex is the current vertex).</li>
64  * <li>Find the minimum value of this weight sums (ideally this minimum is zero,
65  * meaning that there are object envelopes that can be sent to the
66  * database without risking FK violations)</li>
67  * <li>Add all vertices with a weight sum that is equal to this minimum to the
68  * reordered sequence of object envelopes and remove the vertices
69  * and all connected edges from the graph.</li>
70  * <li>If there are vertices left, repeat steps (1) through (3), otherwise
71  * we are done.
72  * </ol>
73  *
74  * @author Gerhard Grosse
75  * @version $Id: ObjectEnvelopeOrdering.java,v 1.1.2.9 2005/12/21 22:29:21 tomdz Exp $
76  * @since Nov 15, 2004
77  */

78 class ObjectEnvelopeOrdering
79 {
80     private static final int CONCRETE_EDGE_WEIGHT = 3;
81     private static final int CONCRETE_EDGE_WEIGHT_WITH_FK = 4;
82     private static final int POTENTIAL_EDGE_WEIGHT = 1;
83     private static final int POTENTIAL_EDGE_WEIGHT_WITH_FK = 2;
84     private static final Object JavaDoc[] EMPTY_OBJECT_ARRAY = new Object JavaDoc[0];
85
86     private static Logger log = LoggerFactory.getLogger(ObjectEnvelopeOrdering.class);
87
88     private List JavaDoc originalOrder;
89     private Map JavaDoc envelopes;
90
91     private Vertex[] vertices;
92     private List JavaDoc edgeList;
93
94     private Identity[] newOrder;
95
96     /**
97      * Creates an object envelope ordering based on an original ordering
98      * of Identity objects and an Identity-&gt;ObjectEnvelope map
99      * @param originalOrder a list of Identity objects
100      * @param envelopes a map with ObjectEnvelope-s with their respective
101      * Identity-s as key
102      */

103     public ObjectEnvelopeOrdering(List JavaDoc originalOrder, Map JavaDoc envelopes)
104     {
105         this.originalOrder = originalOrder;
106         this.envelopes = envelopes;
107     }
108
109     /**
110      * Reorders the object envelopes. The new order is available from the
111      * <code>ordering</code> property.
112      * @see #getOrdering()
113      */

114     public void reorder()
115     {
116         int newOrderIndex = 0;
117         long t1 = 0, t2 = 0, t3;
118
119         if (log.isDebugEnabled())
120         {
121             t1 = System.currentTimeMillis();
122         }
123         newOrder = new Identity[originalOrder.size()];
124
125         if(log.isDebugEnabled()) log.debug("Orginal order: " + originalOrder);
126         // set up the vertex array in the order the envelopes were added
127
List JavaDoc vertexList = new ArrayList JavaDoc(originalOrder.size());
128         // int vertexIndex = 0;
129
for (Iterator JavaDoc it = originalOrder.iterator(); it.hasNext();)
130         {
131             ObjectEnvelope envelope = (ObjectEnvelope) envelopes.get(it.next());
132             if (envelope.needsUpdate() || envelope.needsInsert() || envelope.needsDelete())
133             {
134                 Vertex vertex = new Vertex(envelope);
135                 vertexList.add(vertex);
136                 if (log.isDebugEnabled())
137                 {
138                     log.debug("Add new Vertex object "+envelope.getIdentity()+" to VertexList");
139                 }
140             }
141             else
142             {
143                 // envelope is clean - just add identity to new order
144
newOrder[newOrderIndex++] = envelope.getIdentity();
145                 if (log.isDebugEnabled())
146                 {
147                     log.debug("Add unmodified object "+envelope.getIdentity()+" to new OrderList");
148                 }
149             }
150         }
151         vertices = (Vertex[]) vertexList.toArray(new Vertex[vertexList.size()]);
152
153         // set up the edges
154
edgeList = new ArrayList JavaDoc(2 * vertices.length);
155         for (int i = 0; i < vertices.length; i++)
156         {
157             addEdgesForVertex(vertices[i]);
158         }
159
160         if (log.isDebugEnabled())
161         {
162             t2 = System.currentTimeMillis();
163             log.debug("Building object envelope graph took " + (t2 - t1) + " ms");
164             log.debug("Object envelope graph contains " + vertices.length + " vertices" + " and " + edgeList.size()
165                     + " edges");
166         }
167
168         int remainingVertices = vertices.length;
169         int iterationCount = 0;
170         while (remainingVertices > 0)
171         {
172             // update iteration count
173
iterationCount++;
174
175             // update incoming edge counts
176
for (Iterator JavaDoc it = edgeList.iterator(); it.hasNext();)
177             {
178                 Edge edge = (Edge) it.next();
179                 if (!edge.isProcessed())
180                 {
181                     if(log.isDebugEnabled())
182                     {
183                         final String JavaDoc msg = "Add weight '"+edge.getWeight()+"' for terminal vertex " + edge.getTerminalVertex() + " of edge " + edge;
184                         log.debug(msg);
185                     }
186                     edge.getTerminalVertex().incrementIncomingEdgeWeight(edge.getWeight());
187                 }
188             }
189
190             // find minimum weight of incoming edges of a vertex
191
int minIncomingEdgeWeight = Integer.MAX_VALUE;
192             for (int i = 0; i < vertices.length; i++)
193             {
194                 Vertex vertex = vertices[i];
195                 if (!vertex.isProcessed() && minIncomingEdgeWeight > vertex.getIncomingEdgeWeight())
196                 {
197                     minIncomingEdgeWeight = vertex.getIncomingEdgeWeight();
198                     if (minIncomingEdgeWeight == 0)
199                     {
200                         // we won't get any lower
201
break;
202                     }
203                 }
204             }
205
206             // process vertices having minimum incoming edge weight
207
int processCount = 0;
208             for (int i = 0; i < vertices.length; i++)
209             {
210                 Vertex vertex = vertices[i];
211                 if (!vertex.isProcessed() && vertex.getIncomingEdgeWeight() == minIncomingEdgeWeight)
212                 {
213                     newOrder[newOrderIndex++] = vertex.getEnvelope().getIdentity();
214                     vertex.markProcessed();
215                     processCount++;
216                     if (log.isDebugEnabled())
217                     {
218                         log.debug("add minimum edge weight - "+minIncomingEdgeWeight
219                                 + ", newOrderList: " + ArrayUtils.toString(newOrder));
220                     }
221                 }
222                 vertex.resetIncomingEdgeWeight();
223             }
224
225             if (log.isDebugEnabled())
226             {
227                 log.debug("Processed " + processCount + " of " + remainingVertices
228                         + " remaining vertices in iteration #" + iterationCount);
229             }
230             remainingVertices -= processCount;
231         }
232
233         if (log.isDebugEnabled())
234         {
235             t3 = System.currentTimeMillis();
236             log.debug("New ordering: " + ArrayUtils.toString(newOrder));
237             log.debug("Processing object envelope graph took " + (t3 - t2) + " ms");
238         }
239
240     }
241
242     /**
243      * Gets the reordered sequence of object envelopes
244      * @return an array of Identity objects representing the opimized sequence
245      * of database operations
246      */

247     public Identity[] getOrdering()
248     {
249         if (newOrder == null)
250         {
251             reorder();
252         }
253         return newOrder;
254     }
255
256     /**
257      * Adds all edges for a given object envelope vertex. All edges are
258      * added to the edgeList map.
259      * @param vertex the Vertex object to find edges for
260      */

261     private void addEdgesForVertex(Vertex vertex)
262     {
263         ClassDescriptor cld = vertex.getEnvelope().getClassDescriptor();
264         Iterator JavaDoc rdsIter = cld.getObjectReferenceDescriptors(true).iterator();
265         while (rdsIter.hasNext())
266         {
267             ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) rdsIter.next();
268             addObjectReferenceEdges(vertex, rds);
269         }
270         Iterator JavaDoc cdsIter = cld.getCollectionDescriptors(true).iterator();
271         while (cdsIter.hasNext())
272         {
273             CollectionDescriptor cds = (CollectionDescriptor) cdsIter.next();
274             addCollectionEdges(vertex, cds);
275         }
276     }
277
278     /**
279      * Finds edges based to a specific object reference descriptor and
280      * adds them to the edge map.
281      * @param vertex the object envelope vertex holding the object reference
282      * @param rds the object reference descriptor
283      */

284     private void addObjectReferenceEdges(Vertex vertex, ObjectReferenceDescriptor rds)
285     {
286         Object JavaDoc refObject = rds.getPersistentField().get(vertex.getEnvelope().getRealObject());
287         Class JavaDoc refClass = rds.getItemClass();
288         for (int i = 0; i < vertices.length; i++)
289         {
290             Edge edge = null;
291             // ObjectEnvelope envelope = vertex.getEnvelope();
292
Vertex refVertex = vertices[i];
293             ObjectEnvelope refEnvelope = refVertex.getEnvelope();
294             if (refObject == refEnvelope.getRealObject())
295             {
296                 edge = buildConcrete11Edge(vertex, refVertex, rds.hasConstraint());
297             }
298             else if (refClass.isInstance(refVertex.getEnvelope().getRealObject()))
299             {
300                 edge = buildPotential11Edge(vertex, refVertex, rds.hasConstraint());
301             }
302             if (edge != null)
303             {
304                 if (!edgeList.contains(edge))
305                 {
306                     edgeList.add(edge);
307                 }
308                 else
309                 {
310                     edge.increaseWeightTo(edge.getWeight());
311                 }
312             }
313         }
314     }
315
316     /**
317      * Finds edges base on a specific collection descriptor (1:n and m:n)
318      * and adds them to the edge map.
319      * @param vertex the object envelope vertex holding the collection
320      * @param cds the collection descriptor
321      */

322     private void addCollectionEdges(Vertex vertex, CollectionDescriptor cds)
323     {
324         ObjectEnvelope envelope = vertex.getEnvelope();
325         Object JavaDoc col = cds.getPersistentField().get(envelope.getRealObject());
326         Object JavaDoc[] refObjects;
327         if (col == null || (ProxyHelper.isCollectionProxy(col) && !ProxyHelper.getCollectionProxy(col).isLoaded()))
328         {
329             refObjects = EMPTY_OBJECT_ARRAY;
330         }
331         else
332         {
333             refObjects = BrokerHelper.getCollectionArray(col);
334         }
335         Class JavaDoc refClass = cds.getItemClass();
336
337         for (int i = 0; i < vertices.length; i++)
338         {
339             Edge edge = null;
340             Vertex refVertex = vertices[i];
341             ObjectEnvelope refEnvelope = refVertex.getEnvelope();
342
343             if (refClass.isInstance(refEnvelope.getRealObject()))
344             {
345                 if (containsObject(refEnvelope.getRealObject(), refObjects))
346                 {
347                     if (cds.isMtoNRelation())
348                     {
349                         edge = buildConcreteMNEdge(vertex, refVertex);
350                     }
351                     else
352                     {
353                         edge = buildConcrete1NEdge(vertex, refVertex);
354                     }
355                 }
356                 else
357                 {
358                     if (cds.isMtoNRelation())
359                     {
360                         edge = buildPotentialMNEdge(vertex, refVertex);
361                     }
362                     else
363                     {
364                         edge = buildPotential1NEdge(vertex, refVertex);
365                     }
366                 }
367             }
368             if (edge != null)
369             {
370                 if (!edgeList.contains(edge))
371                 {
372                     edgeList.add(edge);
373                 }
374                 else
375                 {
376                     edge.increaseWeightTo(edge.getWeight());
377                 }
378             }
379         }
380     }
381
382     /**
383      * Helper method that searches an object array for the occurence of a
384      * specific object based on reference equality
385      * @param searchFor the object to search for
386      * @param searchIn the array to search in
387      * @return true if the object is found, otherwise false
388      */

389     private static boolean containsObject(Object JavaDoc searchFor, Object JavaDoc[] searchIn)
390     {
391         for (int i = 0; i < searchIn.length; i++)
392         {
393             if (searchFor == searchIn[i])
394             {
395                 return true;
396             }
397         }
398         return false;
399     }
400
401     /**
402      * Checks if the database operations associated with two object envelopes
403      * that are related via an 1:1 (or n:1) reference needs to be performed
404      * in a particular order and if so builds and returns a corresponding
405      * directed edge weighted with <code>CONCRETE_EDGE_WEIGHT</code>.
406      * The following cases are considered (* means object needs update, + means
407      * object needs insert, - means object needs to be deleted):
408      * <table>
409      * <tr><td>(1)* -(1:1)-&gt; (2)*</td><td>no edge</td></tr>
410      * <tr><td>(1)* -(1:1)-&gt; (2)+</td><td>(2)-&gt;(1) edge</td></tr>
411      * <tr><td>(1)* -(1:1)-&gt; (2)-</td><td>no edge (cannot occur)</td></tr>
412      * <tr><td>(1)+ -(1:1)-&gt; (2)*</td><td>no edge</td></tr>
413      * <tr><td>(1)+ -(1:1)-&gt; (2)+</td><td>(2)-&gt;(1) edge</td></tr>
414      * <tr><td>(1)+ -(1:1)-&gt; (2)-</td><td>no edge (cannot occur)</td></tr>
415      * <tr><td>(1)- -(1:1)-&gt; (2)*</td><td>no edge</td></tr>
416      * <tr><td>(1)- -(1:1)-&gt; (2)+</td><td>no edge</td></tr>
417      * <tr><td>(1)- -(1:1)-&gt; (2)-</td><td>(1)-&gt;(2) edge</td></tr>
418      * <table>
419      * @param vertex1 object envelope vertex of the object holding the reference
420      * @param vertex2 object envelope vertex of the referenced object
421      * @return an Edge object or null if the two database operations can
422      * be performed in any order
423      */

424     protected Edge buildConcrete11Edge(Vertex vertex1, Vertex vertex2, boolean fkToRef)
425     {
426         ModificationState state1 = vertex1.getEnvelope().getModificationState();
427         ModificationState state2 = vertex2.getEnvelope().getModificationState();
428         if (state1.needsUpdate() || state1.needsInsert())
429         {
430             if (state2.needsInsert())
431             {
432                 // (2) must be inserted before (1) can point to it
433
return new Edge(vertex2, vertex1, fkToRef ? CONCRETE_EDGE_WEIGHT_WITH_FK : CONCRETE_EDGE_WEIGHT);
434             }
435         }
436         else if (state1.needsDelete())
437         {
438             if (state2.needsDelete())
439             {
440                 // (1) points to (2) and must be deleted first
441
return new Edge(vertex1, vertex2, fkToRef ? CONCRETE_EDGE_WEIGHT_WITH_FK : CONCRETE_EDGE_WEIGHT);
442             }
443         }
444         return null;
445     }
446
447     /**
448      * Checks if the database operations associated with two object envelopes
449      * that might have been related via an 1:1 (or n:1) reference before
450      * the current transaction needs to be performed
451      * in a particular order and if so builds and returns a corresponding
452      * directed edge weighted with <code>POTENTIAL_EDGE_WEIGHT</code>.
453      * The following cases are considered (* means object needs update, + means
454      * object needs insert, - means object needs to be deleted):
455      * <table>
456      * <tr><td>(1)* -(1:1)-&gt; (2)*</td><td>no edge</td></tr>
457      * <tr><td>(1)* -(1:1)-&gt; (2)+</td><td>no edge</td></tr>
458      * <tr><td>(1)* -(1:1)-&gt; (2)-</td><td>(1)-&gt;(2) edge</td></tr>
459      * <tr><td>(1)+ -(1:1)-&gt; (2)*</td><td>no edge</td></tr>
460      * <tr><td>(1)+ -(1:1)-&gt; (2)+</td><td>no edge</td></tr>
461      * <tr><td>(1)+ -(1:1)-&gt; (2)-</td><td>no edge</td></tr>
462      * <tr><td>(1)- -(1:1)-&gt; (2)*</td><td>no edge</td></tr>
463      * <tr><td>(1)- -(1:1)-&gt; (2)+</td><td>no edge</td></tr>
464      * <tr><td>(1)- -(1:1)-&gt; (2)-</td><td>(1)-&gt;(2) edge</td></tr>
465      * <table>
466      * @param vertex1 object envelope vertex of the object that might have
467      * hold the reference
468      * @param vertex2 object envelope vertex of the potentially referenced
469      * object
470      * @return an Edge object or null if the two database operations can
471      * be performed in any order
472      */

473     protected Edge buildPotential11Edge(Vertex vertex1, Vertex vertex2, boolean fkToRef)
474     {
475         ModificationState state1 = vertex1.getEnvelope().getModificationState();
476         ModificationState state2 = vertex2.getEnvelope().getModificationState();
477         if (state1.needsUpdate() || state1.needsDelete())
478         {
479             if (state2.needsDelete())
480             {
481                 // old version of (1) might point to (2)
482
return new Edge(vertex1, vertex2, fkToRef ? POTENTIAL_EDGE_WEIGHT_WITH_FK : POTENTIAL_EDGE_WEIGHT);
483             }
484         }
485         return null;
486     }
487
488     /**
489      * Checks if the database operations associated with two object envelopes
490      * that are related via an 1:n collection reference needs to be performed
491      * in a particular order and if so builds and returns a corresponding
492      * directed edge weighted with <code>CONCRETE_EDGE_WEIGHT</code>.
493      * The following cases are considered (* means object needs update, + means
494      * object needs insert, - means object needs to be deleted):
495      * <table>
496      * <tr><td>(1)* -(1:n)-&gt; (2)*</td><td>no edge</td></tr>
497      * <tr><td>(1)* -(1:n)-&gt; (2)+</td><td>no edge</td></tr>
498      * <tr><td>(1)* -(1:n)-&gt; (2)-</td><td>no edge</td></tr>
499      * <tr><td>(1)+ -(1:n)-&gt; (2)*</td><td>(1)-&gt;(2) edge</td></tr>
500      * <tr><td>(1)+ -(1:n)-&gt; (2)+</td><td>(1)-&gt;(2) edge</td></tr>
501      * <tr><td>(1)+ -(1:n)-&gt; (2)-</td><td>no edge (cannot occur)</td></tr>
502      * <tr><td>(1)- -(1:n)-&gt; (2)*</td><td>(2)-&gt;(1) edge</td></tr>
503      * <tr><td>(1)- -(1:n)-&gt; (2)+</td><td>no edge</td></tr>
504      * <tr><td>(1)- -(1:n)-&gt; (2)-</td><td>(2)-&gt;(1) edge</td></tr>
505      * <table>
506      * @param vertex1 object envelope vertex of the object holding the
507      * collection
508      * @param vertex2 object envelope vertex of the object contained in the
509      * collection
510      * @return an Edge object or null if the two database operations can
511      * be performed in any order
512      */

513     protected Edge buildConcrete1NEdge(Vertex vertex1, Vertex vertex2)
514     {
515         ModificationState state1 = vertex1.getEnvelope().getModificationState();
516         ModificationState state2 = vertex2.getEnvelope().getModificationState();
517         if (state1.needsInsert())
518         {
519             if (state2.needsUpdate() || state2.needsInsert())
520             {
521                 // (2) now contains an FK to (1) thus (1) must be inserted first
522
return new Edge(vertex1, vertex2, CONCRETE_EDGE_WEIGHT);
523             }
524         }
525         else if (state1.needsDelete())
526         {
527             if (state2.needsUpdate() || state2.needsDelete())
528             {
529                 // Before deleting (1) give (2) a chance to drop its FK to it
530
return new Edge(vertex2, vertex1, CONCRETE_EDGE_WEIGHT);
531             }
532         }
533         return null;
534     }
535
536     /**
537      * Checks if the database operations associated with two object envelopes
538      * that are might have been related via an 1:n collection reference before
539      * the current transaction needs to be performed
540      * in a particular order and if so builds and returns a corresponding
541      * directed edge weighted with <code>POTENTIAL_EDGE_WEIGHT</code>.
542      * The following cases are considered (* means object needs update, + means
543      * object needs insert, - means object needs to be deleted):
544      * <table>
545      * <tr><td>(1)* -(1:n)-&gt; (2)*</td><td>no edge</td></tr>
546      * <tr><td>(1)* -(1:n)-&gt; (2)+</td><td>no edge</td></tr>
547      * <tr><td>(1)* -(1:n)-&gt; (2)-</td><td>no edge</td></tr>
548      * <tr><td>(1)+ -(1:n)-&gt; (2)*</td><td>no edge</td></tr>
549      * <tr><td>(1)+ -(1:n)-&gt; (2)+</td><td>no edge</td></tr>
550      * <tr><td>(1)+ -(1:n)-&gt; (2)-</td><td>no edge</td></tr>
551      * <tr><td>(1)- -(1:n)-&gt; (2)*</td><td>(2)-&gt;(1) edge</td></tr>
552      * <tr><td>(1)- -(1:n)-&gt; (2)+</td><td>no edge</td></tr>
553      * <tr><td>(1)- -(1:n)-&gt; (2)-</td><td>(2)-&gt;(1) edge</td></tr>
554      * <table>
555      * @param vertex1 object envelope vertex of the object holding the
556      * collection
557      * @param vertex2 object envelope vertex of the object that might have
558      * been contained in the collection
559      * @return an Edge object or null if the two database operations can
560      * be performed in any order
561      */

562     protected Edge buildPotential1NEdge(Vertex vertex1, Vertex vertex2)
563     {
564         ModificationState state1 = vertex1.getEnvelope().getModificationState();
565         ModificationState state2 = vertex2.getEnvelope().getModificationState();
566         if (state1.needsDelete())
567         {
568             if (state2.needsUpdate() || state2.needsDelete())
569             {
570                 // Before deleting (1) give potential previous collection
571
// members a chance to drop their FKs to it
572
return new Edge(vertex2, vertex1, POTENTIAL_EDGE_WEIGHT);
573             }
574         }
575         return null;
576     }
577
578     /**
579      * Checks if the database operations associated with two object envelopes
580      * that are related via an m:n collection reference needs to be performed
581      * in a particular order and if so builds and returns a corresponding
582      * directed edge weighted with <code>CONCRETE_EDGE_WEIGHT</code>.
583      * The following cases are considered (* means object needs update, + means
584      * object needs insert, - means object needs to be deleted):
585      * <table>
586      * <tr><td>(1)* -(m:n)-&gt; (2)*</td><td>no edge</td></tr>
587      * <tr><td>(1)* -(m:n)-&gt; (2)+</td><td>(2)-&gt;(1) edge</td></tr>
588      * <tr><td>(1)* -(m:n)-&gt; (2)-</td><td>no edge (cannot occur)</td></tr>
589      * <tr><td>(1)+ -(m:n)-&gt; (2)*</td><td>no edge</td></tr>
590      * <tr><td>(1)+ -(m:n)-&gt; (2)+</td><td>(2)-&gt;(1) edge</td></tr>
591      * <tr><td>(1)+ -(m:n)-&gt; (2)-</td><td>no edge (cannot occur)</td></tr>
592      * <tr><td>(1)- -(m:n)-&gt; (2)*</td><td>no edge</td></tr>
593      * <tr><td>(1)- -(m:n)-&gt; (2)+</td><td>no edge</td></tr>
594      * <tr><td>(1)- -(m:n)-&gt; (2)-</td><td>(1)-&gt;(2) edge</td></tr>
595      * <table>
596      * @param vertex1 object envelope vertex of the object holding the
597      * collection
598      * @param vertex2 object envelope vertex of the object contained in the
599      * collection
600      * @return an Edge object or null if the two database operations can
601      * be performed in any order
602      */

603     protected Edge buildConcreteMNEdge(Vertex vertex1, Vertex vertex2)
604     {
605         ModificationState state1 = vertex1.getEnvelope().getModificationState();
606         ModificationState state2 = vertex2.getEnvelope().getModificationState();
607         if (state1.needsUpdate() || state1.needsInsert())
608         {
609             if (state2.needsInsert())
610             {
611                 // (2) must be inserted before we can create a link to it
612
return new Edge(vertex2, vertex1, CONCRETE_EDGE_WEIGHT);
613             }
614         }
615         else if (state1.needsDelete())
616         {
617             if (state2.needsDelete())
618             {
619                 // there is a link from (1) to (2) which must be deleted first,
620
// which will happen when deleting (1) - thus:
621
return new Edge(vertex1, vertex2, POTENTIAL_EDGE_WEIGHT);
622             }
623         }
624         return null;
625     }
626
627     /**
628      * Checks if the database operations associated with two object envelopes
629      * that might have been related via an m:n collection reference before
630      * the current transaction needs to be performed
631      * in a particular order and if so builds and returns a corresponding
632      * directed edge weighted with <code>POTENTIAL_EDGE_WEIGHT</code>.
633      * The following cases are considered (* means object needs update, + means
634      * object needs insert, - means object needs to be deleted):
635      * <table>
636      * <tr><td>(1)* -(m:n)-&gt; (2)*</td><td>no edge</td></tr>
637      * <tr><td>(1)* -(m:n)-&gt; (2)+</td><td>no edge</td></tr>
638      * <tr><td>(1)* -(m:n)-&gt; (2)-</td><td>(1)-&gt;(2) edge</td></tr>
639      * <tr><td>(1)+ -(m:n)-&gt; (2)*</td><td>no edge</td></tr>
640      * <tr><td>(1)+ -(m:n)-&gt; (2)+</td><td>no edge</td></tr>
641      * <tr><td>(1)+ -(m:n)-&gt; (2)-</td><td>no edge</td></tr>
642      * <tr><td>(1)- -(m:n)-&gt; (2)*</td><td>no edge</td></tr>
643      * <tr><td>(1)- -(m:n)-&gt; (2)+</td><td>no edge</td></tr>
644      * <tr><td>(1)- -(m:n)-&gt; (2)-</td><td>(1)-&gt;(2) edge</td></tr>
645      * <table>
646      * @param vertex1 object envelope vertex of the object holding the
647      * collection
648      * @param vertex2 object envelope vertex of the object that might have
649      * been contained in the collection
650      * @return an Edge object or null if the two database operations can
651      * be performed in any order
652      */

653     protected Edge buildPotentialMNEdge(Vertex vertex1, Vertex vertex2)
654     {
655         ModificationState state1 = vertex1.getEnvelope().getModificationState();
656         ModificationState state2 = vertex2.getEnvelope().getModificationState();
657         if (state1.needsUpdate() || state1.needsDelete())
658         {
659             if (state2.needsDelete())
660             {
661                 // old version of (1) might comprise a link to (2)
662
return new Edge(vertex1, vertex2, POTENTIAL_EDGE_WEIGHT);
663             }
664         }
665         return null;
666     }
667
668     /**
669      * Represents an edge in the object envelope graph
670      */

671     private static class Edge
672     {
673         private Vertex initial;
674         private Vertex terminal;
675         private Identity initialIdentity;
676         private Identity terminalIdentity;
677         private int weight;
678         private boolean knownToBeProcessed;
679         private int hashCode;
680
681         public Edge(Vertex initial, Vertex terminal, int weight)
682         {
683             this.initial = initial;
684             this.terminal = terminal;
685             this.initialIdentity = initial.getEnvelope().getIdentity();
686             this.terminalIdentity = terminal.getEnvelope().getIdentity();
687             this.weight = weight;
688             this.knownToBeProcessed = false;
689             this.hashCode = initialIdentity.hashCode() + 13 * terminalIdentity.hashCode();
690         }
691
692         public Vertex getInitialVertex()
693         {
694             return initial;
695         }
696
697         public Vertex getTerminalVertex()
698         {
699             return terminal;
700         }
701
702         public boolean connects(Vertex vertex)
703         {
704             return initial == vertex || terminal == vertex;
705         }
706
707         public void increaseWeightTo(int minWeight)
708         {
709             if (weight < minWeight)
710             {
711                 weight = minWeight;
712             }
713         }
714
715         public int getWeight()
716         {
717             return weight;
718         }
719
720         public boolean isProcessed()
721         {
722             if (knownToBeProcessed)
723             {
724                 return true;
725             }
726             else
727             {
728                 boolean processed = initial.isProcessed() || terminal.isProcessed();
729                 if (processed)
730                 {
731                     knownToBeProcessed = true;
732                 }
733                 return processed;
734             }
735         }
736
737         public boolean equals(Object JavaDoc obj)
738         {
739             if (obj instanceof Edge)
740             {
741                 Edge other = (Edge) obj;
742                 return this.initialIdentity.equals(other.initialIdentity)
743                         && this.terminalIdentity.equals(other.terminalIdentity);
744             }
745             else
746             {
747                 return false;
748             }
749         }
750
751         public int hashCode()
752         {
753             return hashCode;
754         }
755
756         public String JavaDoc toString()
757         {
758             return new ToStringBuilder(this)
759                     .append("initial", initialIdentity)
760                     .append("terminal", terminalIdentity)
761                     .append("weight", weight)
762                     .append("processed", knownToBeProcessed)
763                     .toString();
764         }
765     }
766
767     /**
768      * Represents a vertex in the object envelope graph
769      */

770     private static class Vertex
771     {
772         private ObjectEnvelope envelope;
773         private boolean processed;
774         private int incomingEdgeWeight;
775
776         public Vertex(ObjectEnvelope envelope)
777         {
778             this.envelope = envelope;
779             this.incomingEdgeWeight = 0;
780             this.processed = false;
781         }
782
783         public ObjectEnvelope getEnvelope()
784         {
785             return envelope;
786         }
787
788         public void markProcessed()
789         {
790             processed = true;
791         }
792
793         public boolean isProcessed()
794         {
795             return processed;
796         }
797
798         public void resetIncomingEdgeWeight()
799         {
800             incomingEdgeWeight = 0;
801         }
802
803         public void incrementIncomingEdgeWeight(int weight)
804         {
805             incomingEdgeWeight += weight;
806         }
807
808         public int getIncomingEdgeWeight()
809         {
810             return incomingEdgeWeight;
811         }
812
813         public String JavaDoc toString()
814         {
815             return new ToStringBuilder(this)
816                     .append("identity", envelope.getIdentity())
817                     .append("processed", processed)
818                     .append("incomingEdgeWeight", incomingEdgeWeight)
819                     .toString();
820         }
821     }
822
823 }
Popular Tags