home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / VCafe / prosrc.bin / QuerySynchronizer.java < prev    next >
Encoding:
Java Source  |  1998-03-18  |  30.3 KB  |  921 lines

  1. /**
  2.  * @(#)QuerySynchronizer.java
  3.  *
  4.  * Copyright (c) 1997 Symantec Corporation. All Rights Reserved.
  5.  *
  6.  */
  7.  
  8. /**
  9.  * As the name implies, the major function of the QuerySynchronizer class
  10.  * is synchronizing QueryNavigator objects.  This consists of three main parts:
  11.  *
  12.  * <P>- managing separate trees of QueryNavigator objects that are synchronized as a group
  13.  * - synchronizing group save operations
  14.  * - synchronizing group navigation operations
  15.  *
  16.  * <P>Managing Trees
  17.  *
  18.  * <P>The QuerySynchronizer class holds the data that forms trees, performs direct manipulation of this data
  19.  * such as adding and removing nodes, and provides methods for joining and leaving trees.
  20.  * The QueryNavigator class relies on the QuerySynchronizer class for all tree function,
  21.  * such as putting nodes in the correct trees and traversing trees in the correct node order.
  22.  *
  23.  * <P>Synchronizing Save Operations
  24.  *
  25.  * <P>QueryNavigator objects pass control of the flow of execution to their QuerySynchronizer objects
  26.  * at the beginning of any publicly exposed save operation.  From that point forward, the
  27.  * QuerySynchronizer objects guarantee that internal save operations performed by individual
  28.  * QueryNavigator objects occur in the correct order.  Feedback from individual save operations
  29.  * is funneled back through QuerySynchronizer objects.
  30.  *
  31.  * <P>Synchronizing Navigation and Related Operations
  32.  *
  33.  * <P>QueryNavigator objects pass control of the flow of execution to their QuerySynchronizer objects at
  34.  * the beginning of any publicly exposed navigation operation.  From that point forward, the
  35.  * QuerySynchronizer objects guarantee that internal navigation operations and related operations,
  36.  * resolving dirty data and executing new queries, occur in the correct sequence.  Feedback from operations
  37.  * performed by individual QueryNavigator objects is funneled back through QuerySynchronizer objects.
  38.  *
  39.  */
  40.  
  41. package symantec.itools.db.beans.binding;
  42.  
  43. import javax.awt.swing.tree.*;
  44. import java.util.*;
  45. import java.sql.SQLException;
  46.  
  47. public class QuerySynchronizer
  48. {
  49. // CONSTRUCTOR SECTION
  50.  
  51.     /**
  52.      * Default constructor which takes no arguments and is private.
  53.      * Constructor is private because this class manages its own object instances.
  54.      * No other class has or needs access to create or destroy objects.
  55.      * Adds itself to the internal set of object instances.
  56.      *
  57.      * @see addSynchronizer
  58.      */
  59.  
  60.     private QuerySynchronizer()
  61.     {
  62.         addSynchronizer(this);
  63.     }
  64.  
  65. // STATIC SECTION
  66.  
  67. // DATA
  68.  
  69.     // VARIABLES
  70.  
  71.     /**
  72.      * Collection of all synchronizer object instances.
  73.      * There is one object per tree of related synchronizable targets.
  74.      */
  75.     private static QuerySynchronizer[] m_AllSynchronizers = new QuerySynchronizer[255];
  76.  
  77.     /**
  78.      * Collection of all links; used to increase search performance.
  79.      */
  80.     private static Hashtable m_AllLinks = new Hashtable();
  81.  
  82.     /**
  83.      * Used to determine course of action when there are changes.
  84.      * It asks the question should changes be saved.
  85.      * The possible answers are YES, NO, or CANCEL.
  86.      */
  87.     private static SaveChangesWindow m_SaveChangesWindow = new SaveChangesWindow();
  88.  
  89.     // CONSTANTS
  90.  
  91.     // Tree processing actions
  92.  
  93.     /**
  94.      * Save deleted records in children
  95.      */
  96.  
  97.     private final static int SAVE_DELETED_CHILDREN = 1;
  98.  
  99.     /**
  100.      * Save current target
  101.      */
  102.  
  103.     private final static int SAVE_CURRENT = 2;
  104.  
  105.     /**
  106.      * Save deleted in current target
  107.      */
  108.  
  109.     private final static int SAVE_DELETED = 3;
  110.  
  111.     /**
  112.      * Save new or modified children
  113.      */
  114.  
  115.     private final static int SAVE_NEW_OR_MODIFIED_CHILDREN = 4;
  116.  
  117.     /**
  118.      * Commit saves
  119.      */
  120.  
  121.     private final static int COMMIT_STATE = 5;
  122.  
  123.     /**
  124.      * Restart the queries for all children
  125.      */
  126.  
  127.     private final static int QUERY_CHILDREN = 6;
  128.  
  129.     /**
  130.      * Cleare out all children
  131.      */
  132.  
  133.     private final static int CLEAR_CHILDREN = 7;
  134.  
  135.     /**
  136.      * Save new or modified in current target
  137.      */
  138.  
  139.     private final static int SAVE_NEW_OR_MODIFIED = 8;
  140.  
  141.     /**
  142.      * Save new or modified in current target
  143.      */
  144.  
  145.     private final static int CLOSE = 9;
  146.  
  147.  
  148. // PACKAGE ACCESS METHODS
  149.  
  150.     /**
  151.      * Self test; which, traverses trees and prints out each node.
  152.      */
  153.  
  154.     static void testTrees()
  155.     {
  156.         System.out.println("Testing trees!");
  157.         for (int index = 0; index < m_AllSynchronizers.length; index++) {
  158.             if (m_AllSynchronizers[index] != null) {
  159.                 System.out.println(m_AllSynchronizers[index].toString());
  160.             }
  161.         }
  162.     }
  163.  
  164.     /**
  165.      * QueryNavigator objects register with the QuerySynchronizer class by invoking this method.  Since the QueryNavigator objects do not have direct access to QuerySynchronizer objects, this method is static.  The access privilege of this method is at least package.
  166.      *   - This method places QueryNavigators in the appropriate tree
  167.      *   - This method will add distinct JdbcConnection objects
  168.      *   - This method will invoke a method on the QueryNavigator object setting its tree node.
  169.      *   - QueryNavigator objects pass control of the flow of execution by invoking methods on nodes.
  170.      *   - Nodes know what QuerySynchronizer objects own their tree.
  171.      *
  172.      *  <P>Tree Placement Algorithm
  173.      *      - Search globally for master and detail
  174.      *      If detail is found
  175.      *          replace
  176.      *          If master found, merge
  177.      *      Else
  178.      *          If master not found, create new tree
  179.      *          If master alias is blank, fill new tree with detail
  180.      *          Else
  181.      *              create detail
  182.      *              If master not found
  183.      *                  fill new tree with master alias
  184.      *                  add detail to new tree
  185.      *              Else
  186.      *                  merge
  187.      */
  188.  
  189.     synchronized static void addSynchronizable(Synchronizable synchronizable)
  190.         throws IllegalRelationshipException
  191.     {
  192.         String                  masterAlias;
  193.         String                  detailAlias;
  194.         SynchronizerLink        rootLink;
  195.         SynchronizerLink        masterLink;
  196.         SynchronizerLink        detailLink;
  197.         SynchronizerNode        rootNode;
  198.         SynchronizerNode        masterNode;
  199.         SynchronizerNode        detailNode;
  200.         SynchronizerTree        tree;
  201.         QuerySynchronizer       synchronizer;
  202.  
  203.         detailAlias = synchronizable.getAliasName();
  204.         masterAlias = synchronizable.getMasterAliasName();
  205.  
  206.         if (masterAlias == null) {
  207.             masterAlias = "";   // Convention that client may not know about
  208.         }
  209.  
  210.         if (detailAlias == masterAlias) {
  211.             throw new IllegalRelationshipException("Attempt to add a self-referencing master-detail relationship.");
  212.         }
  213.  
  214.         detailLink = findLink(detailAlias);         // Global search...
  215.         if (masterAlias != "") {
  216.             masterLink = findLink(masterAlias);
  217.         }
  218.         else {
  219.             masterLink = null;
  220.         }
  221.  
  222.         if (detailLink != null) {
  223.             if (!detailLink.isOpen() && detailLink.getMasterAlias() != masterAlias) {
  224.                 throw new IllegalRelationshipException("Attempt to alter an existing master-detail relationship.");
  225.             }
  226.             if (masterLink != null && detailLink.getParentLink() != masterLink) {
  227.                 merge(masterLink, detailLink);
  228.             }
  229.         }
  230.         else {
  231.             if (masterLink == null) {
  232.                 synchronizer = new QuerySynchronizer();     // Create new objects
  233.                 rootNode = new SynchronizerNode();
  234.                 tree = new SynchronizerTree(rootNode);
  235.                 rootLink = new QueryNavigatorLink();
  236.                 synchronizer.m_Tree = tree;                 // Set synchronizer references
  237.                 synchronizer.m_RootLink = rootLink;
  238.                 rootNode.setUserObject(rootLink);           // Set root node references
  239.                 rootLink.setNode(rootNode);                 // Set root link references
  240.                 rootLink.setSynchronizer(synchronizer);
  241.             }
  242.             else {
  243.                 synchronizer = null;
  244.             }
  245.  
  246.             if (masterAlias == "") {
  247.                 detailLink = synchronizer.m_RootLink;
  248.             }
  249.             else {
  250.                 detailNode = new SynchronizerNode();        // Create new objects
  251.                 detailLink = new QueryNavigatorLink();
  252.                 detailNode.setUserObject(detailLink);       // Set detail node references
  253.                 detailLink.setNode(detailNode);             // set detail link references
  254.  
  255.                 if (masterLink == null) {
  256.                     masterLink = synchronizer.m_RootLink;
  257.                     masterLink.setAlias(masterAlias);
  258.                     // mini merge...
  259.                     masterNode = (SynchronizerNode)masterLink.getNode();
  260.                     detailNode = (SynchronizerNode)detailLink.getNode();
  261.                     masterNode.add(detailNode);
  262.                     detailLink.setSynchronizer(synchronizer);
  263.                     // end of mine merge
  264.                 }
  265.                 else {
  266.                     merge(masterLink, detailLink);
  267.                 }
  268.             }
  269.         }
  270.  
  271.         detailLink.setSynchronizable(synchronizable);
  272.         synchronizable.setLink(detailLink);
  273.     }
  274.  
  275. // PRIVATE METHODS
  276.  
  277.     /**
  278.      * Search all links for the one with this alias.
  279.      * Not finding a match is not an error.
  280.      * This method exists so that the way in which links are stored does
  281.      * not affect the rest of the methods in this class.
  282.      */
  283.  
  284.     static private SynchronizerLink findLink(Object key)
  285.     {
  286.         SynchronizerLink  link;
  287.  
  288.         if (m_AllLinks.containsKey(key)) {
  289.             link = (SynchronizerLink)m_AllLinks.get(key);
  290.         }
  291.         else {
  292.             link = null;
  293.         }
  294.         return link;
  295.     }
  296.  
  297.     /**
  298.      * Encapsulate storage data structure of m_AllSynchronizers
  299.      * Maybe use a vector; maybe use an array
  300.      */
  301.  
  302.     static private synchronized void addSynchronizer(QuerySynchronizer sync)
  303.     {
  304.         for (int index = 0; index < m_AllSynchronizers.length; index++) {
  305.             if (m_AllSynchronizers[index] == null) {
  306.                 m_AllSynchronizers[index] = sync;
  307.                 return;
  308.             }
  309.         }
  310.         QuerySynchronizer[] tempArray = new QuerySynchronizer[m_AllSynchronizers.length + 255];
  311.         System.arraycopy(m_AllSynchronizers, 0, tempArray, 255, m_AllSynchronizers.length);
  312.         m_AllSynchronizers = tempArray;
  313.     }
  314.  
  315.     /**
  316.      * Encapsulate storage data structure of m_AllSynchronizers
  317.      * Maybe use a vector; maybe use an array
  318.      */
  319.  
  320.     static private synchronized void removeSynchronizer(QuerySynchronizer sync)
  321.     {
  322.         for (int index = 0; index < m_AllSynchronizers.length; index++) {
  323.             if (m_AllSynchronizers[index] == sync) {
  324.                 m_AllSynchronizers[index].removeAllLinks();
  325.                 m_AllSynchronizers[index] = null;
  326.                 return;
  327.             }
  328.         }
  329.     }
  330.  
  331.     /**
  332.      * Links add themselves when their alias is set because
  333.      * the alias is the key.
  334.      */
  335.  
  336.     static synchronized void addLink(SynchronizerLink link)
  337.     {
  338.         Object key = link.getAlias();
  339.         if (!m_AllLinks.contains(key)) {
  340.             m_AllLinks.put(key, link);
  341.         }
  342.     }
  343.  
  344.     /**
  345.      * Links add themselves when their alias is set because
  346.      * the alias is the key.
  347.      */
  348.  
  349.     static synchronized void removeLink(SynchronizerLink link)
  350.     {
  351.         SynchronizerNode    node = (SynchronizerNode)link.getNode();
  352.         SynchronizerNode    parentNode = (SynchronizerNode)node.getParent();
  353.         QuerySynchronizer   synchronizer = link.getSynchronizer();
  354.  
  355.         m_AllLinks.remove(link.getAlias());
  356.  
  357.         if (parentNode != null) {
  358.             try {
  359.                 synchronizer.orderAndProcessNodes(node, CLOSE);
  360.             }
  361.             catch (SQLException ex) {
  362.                 // foo
  363.             }
  364.             parentNode.remove(node);
  365.         }
  366.     }
  367.  
  368.     /**
  369.      * Add detail as child of master
  370.      * Point all of the children to new QuerySynchronizer
  371.      * Recover the detail QSync
  372.      */
  373.  
  374.     static private synchronized void merge(SynchronizerLink masterLink, SynchronizerLink detailLink)
  375.     {
  376.         SynchronizerNode  node;
  377.         SynchronizerLink        link;
  378.         SynchronizerNode  masterNode;
  379.         SynchronizerNode  detailNode;
  380.         QuerySynchronizer       detailSync;
  381.  
  382.         detailSync = detailLink.getSynchronizer();
  383.         if (detailSync != null) {
  384.             removeSynchronizer(detailSync);
  385.         }
  386.  
  387.         masterNode = (SynchronizerNode)masterLink.getNode();
  388.         detailNode = (SynchronizerNode)detailLink.getNode();
  389.         masterNode.add(detailNode);
  390.  
  391.         detailLink.setSynchronizer(masterLink.getSynchronizer());
  392.  
  393.         Enumeration enum = detailNode.children();
  394.         while(enum.hasMoreElements()) {
  395.             node = (SynchronizerNode)enum.nextElement();
  396.             link = (SynchronizerLink)node.getUserObject();
  397.             link.setSynchronizer(masterLink.getSynchronizer());
  398.         }
  399.     }
  400.  
  401.  
  402. // NON-STATIC SECTION
  403.  
  404. // DATA
  405.  
  406.     // Each QuerySynchronizer object contains one and only one
  407.     // QueryNavigatorTree object.  This tree class implements the JFC
  408.     // swing tree interface.  The parent child relationships between tree
  409.     // nodes correspond to the master detail relationships between
  410.     // QueryNavigators objects.  Each node contains a reference to a
  411.     // QueryNavigatorLink object.  The purpose of this link is to hold
  412.     // a spot for a QueryNavigator when the alias is known from a detail
  413.     // QueryNavigator object before the master QueryNavigator object is
  414.     // registered.
  415.  
  416.     /**
  417.     // The tree
  418.      */
  419.     private SynchronizerTree m_Tree = null;
  420.  
  421.     /**
  422.     // The root link
  423.      */
  424.     private SynchronizerLink m_RootLink = null;
  425.  
  426.     /**
  427.     // The connections
  428.      */
  429.     private Hashtable m_AllConnections = null;
  430.  
  431. // METHODS
  432.  
  433.     /**
  434.      * Invoked by the synchronizable object to perform saveAll
  435.      */
  436.     boolean saveAll(SynchronizerLink startLink) throws RelationshipPendingException, ParentInvalidRecordException
  437.     {
  438.         boolean success = saveTree((SynchronizerNode)startLink.getNode(), true);
  439.         if (success) {
  440.             navigate(startLink, Synchronizable.RESTART, null);
  441.         }
  442.         /*
  443.         if (success) {
  444.             try {
  445.                 orderAndProcessNodes(startNode, QUERY_CHILDREN);
  446.             }
  447.             catch (SQLException ex) {
  448.                 // swallow it
  449.             }
  450.         }
  451.         */
  452.         return success;
  453.     }
  454.  
  455.     /**
  456.      * Invoked by the synchronizable object to perform saveAllLevels
  457.      */
  458.     boolean saveAllLevels(SynchronizerLink startLink) throws RelationshipPendingException, ParentInvalidRecordException
  459.     {
  460.         return saveAll((SynchronizerLink)((SynchronizerNode)m_Tree.getRoot()).getUserObject());
  461.     }
  462.  
  463.     /**
  464.      * Invoked by the synchronizable object to perform save
  465.      */
  466.     boolean save(SynchronizerLink startLink) throws RelationshipPendingException, ParentInvalidRecordException
  467.     {
  468.         boolean success = saveTree((SynchronizerNode)startLink.getNode(), false);
  469.         if (success) {
  470.             try {
  471.                 orderAndProcessNodes((SynchronizerNode)startLink.getNode(), QUERY_CHILDREN);
  472.             }
  473.             catch (SQLException ex) {
  474.                 // swallow it
  475.             }
  476.         }
  477.         return success;
  478.     }
  479.  
  480.     /**
  481.      *
  482.      * Execute all neccessary operations in correct order within transactions.
  483.      * Heterogeneous master detail is provided without two-phase commit logic.
  484.      *
  485.      * Order of events:
  486.      * - Begin transactions
  487.      * - Process deleted children
  488.      * - Process current target
  489.      * - Process new or modified children
  490.      * - Commit state
  491.      * - End transactions
  492.      *
  493.      */
  494.  
  495.     private boolean saveTree(SynchronizerNode startNode, boolean saveAll) throws RelationshipPendingException, ParentInvalidRecordException
  496.     {
  497.         boolean                 success;
  498.  
  499.         if (isOpen()) {
  500.             throw new RelationshipPendingException("Attempt to saveAll with a relationship pending.");
  501.         }
  502.  
  503.         if (isParentOnNewRecord((SynchronizerLink)startNode.getUserObject())) {
  504.             throw new ParentInvalidRecordException("Parent record has not been committed. Data changes are not permitted at this time.");
  505.         }
  506.  
  507.         beginAllTransactions();
  508.  
  509.         try {
  510.             orderAndProcessNodes(startNode, SAVE_DELETED_CHILDREN);
  511.             if (saveAll) {
  512.                 orderAndProcessNodes(startNode, SAVE_DELETED);
  513.                 orderAndProcessNodes(startNode, SAVE_NEW_OR_MODIFIED);
  514.             }
  515.             else {
  516.                 orderAndProcessNodes(startNode, SAVE_CURRENT);
  517.             }
  518.             orderAndProcessNodes(startNode, SAVE_NEW_OR_MODIFIED_CHILDREN);
  519.             success = true;
  520.             orderAndProcessNodes(startNode, COMMIT_STATE);
  521.         }
  522.         catch (SQLException ex) {
  523.             System.out.println("Transaction failed due to SQL failure, will be rolled back.");
  524.             success = false;
  525.         }
  526.         catch (Exception ex) {
  527.             success = true;
  528.         }
  529.  
  530.         endAllTransactions(success);
  531.  
  532.         return success;
  533.     }
  534.  
  535.     /**
  536.      * QueryNavigator objects invoke this method to allow QuerySynchronizer
  537.      * objects to orchestrate, control and synchronize all related tasks.
  538.      * The QuerySynchronizer object first determines if any QueryNavigator
  539.      * has dirty data.  There are three ways to resolve such an occurrence.
  540.      *
  541.      * - the data can be saved
  542.      * - the data can be thrown away
  543.      * - the move can be cancelled
  544.      *
  545.      * Once the way is determined, the QuerySynchronizer object executes it.
  546.      * When the occurrence is resolved, the QuerySynchronizer object
  547.      * continues with the navigation.
  548.      *
  549.      * The QuerySynchronizer object invokes the navigation method of the
  550.      * calling QueryNavigator object.  If the navigation is successful,
  551.      * the QuerySynchronizer object invokes the executeQuery methods
  552.      * of other QueryNavigator objects in the tree.
  553.      *
  554.      * - Row is passed
  555.      *
  556.      * @see save
  557.      */
  558.  
  559.     synchronized void navigate(SynchronizerLink startLink, int type, Integer position)
  560.         throws RelationshipPendingException, ParentInvalidRecordException
  561.     {
  562.         int                     resolution;
  563.         boolean                 success;
  564.         Enumeration             orderedEnum;
  565.         SynchronizerLink        link;
  566.         SynchronizerNode  node;
  567.         SynchronizerNode  startNode;
  568.         Synchronizable          target;
  569.         SynchronizerLink        parentLink;
  570.         SynchronizerNode  parentNode;
  571.         Synchronizable          parentTarget;
  572.  
  573.         if (isOpen()) {
  574.             throw new RelationshipPendingException("Attempt to navigate with a relationship pending.");
  575.         }
  576.  
  577.         if (isParentOnNewRecord(startLink)) {
  578.             throw new ParentInvalidRecordException("Parent record has not been committed. Data changes are not permitted at this time.");
  579.         }
  580.  
  581.         startNode = (SynchronizerNode)startLink.getNode();
  582.  
  583.         // Resolve dirty data
  584.         // by means of save, cancel, or continue
  585.         if (isDirty(startLink) && m_SaveChangesWindow != null) {
  586.             resolution = m_SaveChangesWindow.run();
  587.             switch (resolution) {
  588.                 case SaveChangesDialog.YES:
  589.                     success = save(startLink);
  590.                     if (success != true) {
  591.                         return;
  592.                     }
  593.                     break;
  594.                 case SaveChangesDialog.NO:
  595.                 default:
  596.                     break;
  597.                 case SaveChangesDialog.CANCEL:
  598.                     return;
  599.             }
  600.         }
  601.         else {
  602.             resolution = SaveChangesDialog.NO;
  603.         }
  604.  
  605.         try {
  606.  
  607.             // take care of starting target
  608.             link = (SynchronizerLink)startNode.getUserObject();
  609.             target = link.getSynchronizable();
  610.             if (type == Synchronizable.RESTART) {
  611.                 // determine starting position
  612.                 parentNode = (SynchronizerNode)startNode.getParent();
  613.                 if (parentNode != null) {
  614.                     parentLink = (SynchronizerLink)parentNode.getUserObject();
  615.                     parentTarget = parentLink.getSynchronizable();
  616.                 }
  617.                 else {
  618.                     parentTarget = null;
  619.                 }
  620.                 target.executeQuery(parentTarget);
  621.             }
  622.             else {
  623.                 target.navigate(type, position);
  624.             }
  625.  
  626.             // synchronize children
  627.             orderAndProcessNodes(startNode, QUERY_CHILDREN);
  628.  
  629.         }
  630.         catch (SQLException ex) {
  631.         }
  632.     }
  633.  
  634.     /**
  635.      * Debug output
  636.      */
  637.  
  638.     public String toString()
  639.     {
  640.         SynchronizerNode node = (SynchronizerNode)m_Tree.getRoot();
  641.         Enumeration enum = node.enumerateNodes(SynchronizerNode.TOP_DOWN_INCLUSIVE);
  642.         String output = super.toString();
  643.         output += "\nQuerySynchronizer nodes: ";
  644.         while (enum.hasMoreElements()) {
  645.             output += "\n\t" + enum.nextElement().toString();
  646.         }
  647.         return output;
  648.     }
  649.  
  650.     /**
  651.      * Is the root link an open link
  652.      */
  653.  
  654.     private boolean isOpen()
  655.     {
  656.         SynchronizerNode node = (SynchronizerNode)m_Tree.getRoot();
  657.         SynchronizerLink link = (SynchronizerLink)node.getUserObject();
  658.         return link.isOpen();
  659.     }
  660.  
  661.     /**
  662.      * Does this link or any of its children have dirty data?
  663.      */
  664.  
  665.     private boolean isDirty(SynchronizerLink startLink)
  666.     {
  667.         Enumeration             orderedEnum;
  668.         SynchronizerNode  node;
  669.         Synchronizable          target;
  670.         SynchronizerLink        link;
  671.  
  672.         node = (SynchronizerNode)startLink.getNode();
  673.         orderedEnum = node.enumerateNodes(SynchronizerNode.TOP_DOWN_EXCLUSIVE);
  674.  
  675.         while (orderedEnum.hasMoreElements()) {
  676.             node = (SynchronizerNode)orderedEnum.nextElement();
  677.             link = (SynchronizerLink)node.getUserObject();
  678.             target = link.getSynchronizable();
  679.             if (target.isDirty()) {
  680.                 return true;
  681.             }
  682.         }
  683.         return false;
  684.     }
  685.  
  686.     private void beginAllTransactions()
  687.     {
  688.         Enumeration allConnections = enumerateConnections();
  689.         Connection  connection;
  690.  
  691.         while (allConnections.hasMoreElements()) {
  692.             connection = (Connection)allConnections.nextElement();
  693.             connection.beginTransaction();
  694.         }
  695.     }
  696.  
  697.     /**
  698.      *  End transaction.
  699.      */
  700.  
  701.     private void endAllTransactions(boolean commit)
  702.     {
  703.         Enumeration allConnections = enumerateConnections();
  704.         Connection  connection;
  705.  
  706.         while (allConnections.hasMoreElements()) {
  707.             connection = (Connection)allConnections.nextElement();
  708.             connection.endTransaction(commit);
  709.         }
  710.     }
  711.  
  712.     /**
  713.      * Encapsulate storage data structure of m_AllConnections
  714.      * User a hashtable
  715.      */
  716.  
  717.     private synchronized void addConnection(Connection key)
  718.     {
  719.         Integer  useCount;
  720.  
  721.         if (m_AllConnections.containsKey(key)) {
  722.             useCount = (Integer)m_AllConnections.get(key);
  723.             m_AllConnections.remove(key);
  724.             useCount = new Integer(useCount.intValue() + 1);
  725.         }
  726.         else {
  727.             useCount = new Integer(1);
  728.         }
  729.  
  730.         m_AllConnections.put(key, useCount);
  731.     }
  732.  
  733.     /**
  734.      * Encapsulate storage data structure of m_AllConnections
  735.      * Use a hashtable
  736.      */
  737.  
  738.     private synchronized void removeConnection(Connection key)
  739.     {
  740.         Integer  useCount;
  741.  
  742.         useCount = (Integer)m_AllConnections.get(key);
  743.         m_AllConnections.remove(key);
  744.         useCount = new Integer(useCount.intValue() - 1);
  745.  
  746.         // If connection is still referenced, put it back
  747.         if (useCount.intValue() > 0) {
  748.             m_AllConnections.put(key, useCount);
  749.         }
  750.     }
  751.  
  752.     /**
  753.      * Encapsulate storage data structure of m_AllConnections
  754.      * Use a hashtable
  755.      */
  756.  
  757.     private Enumeration enumerateConnections()
  758.     {
  759.         Connection          conn;
  760.         Enumeration         orderedEnum;
  761.         Synchronizable      target;
  762.         SynchronizerLink    link;
  763.         SynchronizerNode    node;
  764.  
  765.         if (m_AllConnections == null) {
  766.             m_AllConnections = new Hashtable();
  767.             node = (SynchronizerNode)m_Tree.getRoot();
  768.             orderedEnum = node.enumerateNodes(SynchronizerNode.TOP_DOWN_INCLUSIVE);
  769.             while (orderedEnum.hasMoreElements()) {
  770.                 node = (SynchronizerNode)orderedEnum.nextElement();
  771.                 link = (SynchronizerLink)node.getUserObject();
  772.                 target = link.getSynchronizable();
  773.                 conn = target.getConnection();
  774.                 addConnection(conn);
  775.             }
  776.         }
  777.         return m_AllConnections.keys();
  778.     }
  779.  
  780.     /**
  781.      * Remove all links.
  782.      */
  783.  
  784.     void removeAllLinks()
  785.     {
  786.         Enumeration         orderedEnum;
  787.         SynchronizerNode    node;
  788.         SynchronizerLink    link;
  789.  
  790.         node = (SynchronizerNode)m_Tree.getRoot();
  791.         orderedEnum = node.enumerateNodes(SynchronizerNode.TOP_DOWN_INCLUSIVE);
  792.         while (orderedEnum.hasMoreElements()) {
  793.             node = (SynchronizerNode)orderedEnum.nextElement();
  794.             link = (SynchronizerLink)node.getUserObject();
  795.             removeLink(link);
  796.         }
  797.     }
  798.  
  799.     /**
  800.      * Check that parent is not in an invalid state.
  801.      * If node has a parent, get it and make sure its state isn't new
  802.      */
  803.  
  804.     boolean isParentOnNewRecord(SynchronizerLink startLink)
  805.     {
  806.         SynchronizerNode  node;
  807.         SynchronizerNode  parentNode;
  808.         SynchronizerLink        parentLink;
  809.         Synchronizable          parentTarget;
  810.         boolean                 parentIsOnNewRecord;
  811.         PersistentObject        object;
  812.  
  813.         node = (SynchronizerNode)startLink.getNode();
  814.         parentNode = (SynchronizerNode)node.getParent();
  815.         if (parentNode != null) {
  816.             parentLink = (SynchronizerLink)parentNode.getUserObject();
  817.             parentTarget = parentLink.getSynchronizable();
  818.             object = (PersistentObject)parentTarget.getCurrentObject();
  819.             if (object == null || object.getMarkedAsNew()) {
  820.                 parentIsOnNewRecord = true;
  821.             }
  822.             else {
  823.                 parentIsOnNewRecord = false;
  824.             }
  825.         }
  826.         else {
  827.             parentIsOnNewRecord = false;
  828.         }
  829.  
  830.         return parentIsOnNewRecord;
  831.     }
  832.  
  833.     /**
  834.      * Order nodes based on the action to be performed and process them in order.
  835.      */
  836.  
  837.     private void orderAndProcessNodes(SynchronizerNode startNode, int action)
  838.         throws SQLException
  839.     {
  840.         int                     order;
  841.         int                     process;
  842.         SynchronizerLink        link;
  843.         SynchronizerNode  node;
  844.         Synchronizable          target;
  845.         Enumeration             path;
  846.         SynchronizerLink        parentLink;
  847.         SynchronizerNode  parentNode;
  848.         Synchronizable          parentTarget;
  849.         PersistentObject        object;
  850.  
  851.         switch (action) {
  852.         case SAVE_DELETED_CHILDREN:
  853.             order = SynchronizerNode.BOTTOM_UP;
  854.             process = Synchronizable.SAVE_DELETED_DIRTY_DATA;
  855.             break;
  856.         case SAVE_CURRENT:
  857.             order = SynchronizerNode.CURRENT_NODE_ONLY;
  858.             process = Synchronizable.SAVE_CURRENT_RECORD;
  859.             break;
  860.         case SAVE_DELETED:
  861.             order = SynchronizerNode.CURRENT_NODE_ONLY;
  862.             process = Synchronizable.SAVE_DELETED_DIRTY_DATA;
  863.             break;
  864.         case SAVE_NEW_OR_MODIFIED:
  865.             order = SynchronizerNode.CURRENT_NODE_ONLY;
  866.             process = Synchronizable.SAVE_ALL_DIRTY_DATA;
  867.             break;
  868.         case SAVE_NEW_OR_MODIFIED_CHILDREN:
  869.             order = SynchronizerNode.TOP_DOWN_EXCLUSIVE;
  870.             process = Synchronizable.SAVE_ALL_DIRTY_DATA;
  871.             break;
  872.         case COMMIT_STATE:
  873.             order = SynchronizerNode.TOP_DOWN_INCLUSIVE;
  874.             process = COMMIT_STATE;
  875.             break;
  876.         case QUERY_CHILDREN:
  877.         default:
  878.             order = SynchronizerNode.TOP_DOWN_EXCLUSIVE;
  879.             link = (SynchronizerLink)startNode.getUserObject();
  880.             target = link.getSynchronizable();
  881.             object = (PersistentObject)target.getCurrentObject();
  882.             if (object == null || object.getMarkedAsNew()) {
  883.                 action = CLEAR_CHILDREN;
  884.                 process = Synchronizable.CLEAR_ALL_DATA;
  885.             }
  886.             else {
  887.                 process = QUERY_CHILDREN;
  888.             }
  889.             break;
  890.         case CLOSE:
  891.             order = SynchronizerNode.TOP_DOWN_EXCLUSIVE;
  892.             process = CLOSE;    // process is ignored here but it will be a compile error not to set it.
  893.             break;
  894.         }
  895.  
  896.         path = startNode.enumerateNodes(order);
  897.  
  898.         while (path.hasMoreElements()) {
  899.             node = (SynchronizerNode)path.nextElement();
  900.             link = (SynchronizerLink)node.getUserObject();
  901.             target = link.getSynchronizable();
  902.             if (action == COMMIT_STATE) {
  903.                 target.commitState();
  904.             }
  905.             else if (action == CLOSE) {
  906.                 link.close();
  907.             }
  908.             else if (action == QUERY_CHILDREN) {
  909.                 parentNode = (SynchronizerNode)node.getParent();
  910.                 if (parentNode != null) {
  911.                     parentLink = (SynchronizerLink)parentNode.getUserObject();
  912.                     parentTarget = parentLink.getSynchronizable();
  913.                     target.executeQuery(parentTarget);
  914.                 }
  915.             }
  916.             else {
  917.                 target.saveAll(process);
  918.             }
  919.         }
  920.     }
  921. }