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 / DbaDataStore.java < prev    next >
Encoding:
Java Source  |  1998-03-18  |  35.8 KB  |  1,203 lines

  1. /*
  2.  * DbaDataStore.java   1.0   12 Jan 1997
  3.  *
  4.  * Copyright (c) 1996 Krumel & Associates, Inc.  All Rights Reserved.
  5.  *
  6.  * This software is provided as is.  Krumel & Associates shall not be liable
  7.  * for any damages suffered by licensee as a result of using, modifying or
  8.  * distributing this software or its derivatives.
  9.  */
  10.  
  11. package symantec.itools.db.awt;
  12.  
  13. import symantec.itools.db.pro.*;
  14. import java.sql.SQLException;
  15. import java.util.BitSet;
  16. import java.util.StringTokenizer;
  17. import java.sql.Types;
  18.  
  19. /**
  20.  * This implements data store for the TableView component that uses a dbANYWHERE
  21.  * server for its data.
  22.  * <p>
  23.  * All rows coming from DbaDataSource are zero-relative.<br>
  24.  * All columns coming from DbaDataSource are one-relative.
  25.  *
  26.  * @see symantec.itools.db.awt.DbDataSource
  27.  * @see symantec.itools.db.awt.DbDataUpdater
  28.  * @see symantec.itools.db.awt.MetaTable
  29.  */
  30. public class DbaDataStore  implements DbDataStore, MetaTable, DbDataUpdater {
  31.     Position        position;
  32.     RelationView    master;
  33.     RelationView    relView;
  34.     MultiView       multiView;
  35.     DbDataSource    source = null;
  36.     DbaDataLink     link;
  37.     int             coltoshow=0;
  38.     RelationViewMetaData    meta;
  39.  
  40.     /**
  41.      * Obsolete.
  42.      */
  43.     public static final int MAX_DISPLAY_SIZE = 60;
  44.  
  45.     /**
  46.      * Constructs a DbaDataStore that uses the given RelationView for data.
  47.      *
  48.      * @param rv the RelatioView to used for data access
  49.      * @param masterView obsolete
  50.      * @exception SQLException
  51.      * if a SQL database access error occurred
  52.      */
  53.     public DbaDataStore(RelationView rv, RelationView masterView) throws SQLException {
  54.         if (masterView != null)
  55.         {
  56.             master = masterView;
  57.         }
  58.         relView = rv;
  59.         multiView = rv.getMultiView();
  60.         meta = relView.getMetaData();
  61.         position = new Position(rv);
  62.     }
  63.  
  64.     /**
  65.      * Clears all row data from this DbaDataStore.
  66.      * <p>
  67.      * Note that setDbDataSource() must be called before using this method.
  68.      *
  69.      * @exception SQLException
  70.      * if a SQL database access error occurred
  71.      *
  72.      * @see #setDbDataSource
  73.      */
  74.     void resetRows() throws SQLException
  75.     {
  76.         int count;
  77.         // // synchronized(position)
  78.         {
  79.             count = position.getIgnoreCount();
  80.         }
  81.         if (count > 0)
  82.         {
  83.             return;
  84.         }
  85.         initRowMapping();
  86.         source.view.clear();
  87.         // source.view.redrawAsync();   // clear calls redrawAsync
  88.     }
  89.  
  90.     /**
  91.      * This method is called when the current Record in the RelationView is changed.
  92.      * <p>
  93.      * Note that setDbDataSource() must be called before using this method.
  94.      *
  95.      * @exception SQLException
  96.      * if a SQL database access error occurred
  97.      *
  98.      * @see #setDbDataSource
  99.      */
  100.     void notifyRecordChange() throws SQLException
  101.     {
  102.         int count;
  103.         // synchronized(position)
  104.         {
  105.             count = position.getIgnoreCount();
  106.         }
  107.         if (count > 0)
  108.         {
  109.             return;
  110.         }
  111.         int row;
  112.         // synchronized(position)
  113.         {
  114.             row = position.get();
  115.         }
  116.         //vj
  117.         //if (relView.getCurrentRecordState() == Record.RECSTATE_NEW)
  118.         //{
  119.             source.view.scrollUpDownAbsolute(row -1);
  120.         //}
  121.  
  122.         //source.view.setFocusToRow(row);
  123.     }
  124.  
  125.     /**
  126.      * Sets the fetch mode for getting data.  When fetch mode is enabled,
  127.      * a grid expects to rapidly read lots of data for read only purposes.
  128.      * Knowing this can allow some data stores to more efficiently get the
  129.      * data.  The TableView provides a guarantee that the current data row will
  130.      * not be changed
  131.      * @param manual true if fetch mode is to be entered
  132.      */
  133.     public void fetchMode(boolean manual)
  134.     {
  135.         setManRowChangeFlag(manual);
  136.     }
  137.  
  138.     /**
  139.      * Returns the current send notification flag.
  140.      * This is used to determine if the RelationView sends notifications
  141.      * when it's modified.
  142.      * When fetch mode is enabled this returns a value of true.
  143.      *
  144.      * @return the current send notification flag
  145.      * @see #fetchMode
  146.      */
  147.     public boolean manualRowChangeFlag()
  148.     {
  149.         // synchronized (position)
  150.         {
  151.             return position.getNotificationMode();
  152.         }
  153.     }
  154.  
  155.     private void setManRowChangeFlag(boolean val)
  156.     {
  157.         try
  158.         {
  159.             // synchronized (position)
  160.             {
  161.                 position.setNotificationMode(val);
  162.             }
  163.         }
  164.         catch(SQLException ex) {}
  165.     }
  166.  
  167.     /**
  168.      * Sets the current row in the data store.
  169.      * <p>
  170.      * This is a standard DbDataStore interface method.
  171.      *
  172.      * @param row the zero-relative row index
  173.      * @exception TypeNotSupported
  174.      * if the data store does not support the type of action requested
  175.      * or is not successful
  176.      */
  177.     public void setCurrentRow(int row) throws TypeNotSupported {
  178.  
  179.         row++;
  180.         try
  181.         {
  182.             int translatedRow = translateRow(row);
  183.             // synchronized (position)
  184.             {
  185.                 position.set(translatedRow);
  186.             }
  187.         } catch(Exception ex)
  188.         {
  189.             throw new TypeNotSupported(ex.getMessage());
  190.         }
  191.     }
  192.  
  193.     /**
  194.      * Determines if the given row is the current row in the data store.
  195.      *
  196.      * @param row the zero-relative index of the row to check
  197.      * @return true if it is the current row, false otherwise
  198.      * @see #setCurrentRow
  199.      */
  200.     public boolean isCurrentRow(int row) {
  201.         try
  202.         {
  203.             int translatedRow = translateRow(row);
  204.             // synchronized (position)
  205.             {
  206.                 return translatedRow == position.get();
  207.             }
  208.         }
  209.         catch(Exception ex)
  210.         {
  211.             return false;
  212.         }
  213.     }
  214.  
  215.     /**
  216.      * Sets the database data source that requests data and possibly
  217.      * caches the SQL data.
  218.      * <p>
  219.      * This is a standard DbDataStore interface method.
  220.      *
  221.      * @param ds the data source
  222.      */
  223.     public void setDbDataSource(DbDataSource ds) {
  224.         if (source != null)
  225.         {
  226.             return;
  227.         }
  228.         source = ds;
  229.         initRowMapping();
  230.         link = new DbaDataLink(this);
  231.         // synchronized(position)
  232.         {
  233.             position.ignoreNotification(true);
  234.         }
  235.         try
  236.         {
  237.             relView.bindRecordSet(link);
  238.             relView.bindCurrentRecord(link);
  239.         } catch(SQLException ex) { /*this should never happend*/ }
  240.         // synchronized(position)
  241.         {
  242.             position.ignoreNotification(false);
  243.         }
  244.     }
  245.  
  246.     /**
  247.      * Called by a DbDataSource to determine if the store provides its own
  248.      * caching services.
  249.      * <p>
  250.      * This is a standard DbDataStore interface method.
  251.      *
  252.      * @return true if the store provides scrollable caching
  253.      */
  254.     public boolean supportsCaching() {
  255.         return true;
  256.     }
  257.  
  258.     /**
  259.      * Gets the last row that exists within the specified range.
  260.      * <p>
  261.      * This is a standard DbDataStore interface method.
  262.      *
  263.      * @param top the zero-relative index of the first row in the range
  264.      * @param bottom the zero-relative index of the last row in the range
  265.      * @return the last row that exists in the given range
  266.      * @exception DataNotAvailable
  267.      * if the requested data cannot be accessed in the data store
  268.      */
  269.     public int validDataRowRange(int top, int bottom) throws DataNotAvailable {
  270.         //iterate from top to bottom and find the last valid number in range
  271.  
  272.         //adjust for row indices being zero relative
  273.  
  274.         top++;
  275.         bottom += 2;  //a few extra for scrollbars
  276.  
  277.         int lastInRange = Math.max(1, top); //always want to ensure at least try
  278.                                             //first row
  279.  
  280.         try {
  281.             setManRowChangeFlag(true);
  282.             while(lastInRange<=bottom) {
  283.                 translateRow(lastInRange);
  284.                 lastInRange++;
  285.             }
  286.         } catch(DataNotAvailable ex) {
  287.         } finally {
  288.             setManRowChangeFlag(false);
  289.         }
  290.  
  291.         if (lastInRange == top) {
  292.             throw new DataNotAvailable("top is greater than last row in database");
  293.         }
  294.  
  295.         return lastInRange-1;
  296.     }
  297.  
  298.     /**
  299.      * Gets the data element for the specified position.
  300.      * <p>
  301.      * This is a standard DbDataStore interface method.
  302.      *
  303.      * @param row the zero-relative index of the element's row
  304.      * @param col the one-relative index of the element's column
  305.      * @return the element's data
  306.      * @exception DataNotAvailable
  307.      * if the requested data cannot be accessed in the data store
  308.      */
  309.     public Data getData(int row, int col) throws DataNotAvailable {
  310.         //adjust for row indices being 0 relative
  311.         row++;
  312.         Data d = null;
  313.  
  314.         try {
  315.             setManRowChangeFlag(true);
  316.             int translatedRow = translateRow(row);
  317.             boolean success;
  318.             // synchronized(position)
  319.             {
  320.                 success = position.set(translatedRow);
  321.             }
  322.             if (!success) {
  323.                 throw new DataNotAvailable("Could not move to row " + row);
  324.             }
  325.  
  326.             String str = relView.getStringValue(col);
  327.             if (str == null) { str = ""; }
  328.             d = new ImageStringData(source, str);
  329.         } catch(Exception ex) {
  330.             throw new DataNotAvailable(ex.getMessage());
  331.         } finally {
  332.             setManRowChangeFlag(false);
  333.         }
  334.  
  335.         return d;
  336.     }
  337.  
  338.  
  339.     /**
  340.      * Sets a new value in the specified position.  The method is only
  341.      * called when the store supports caching.
  342.      * <p>
  343.      * This is a standard DbDataStore interface method.
  344.      *
  345.      * @param row the zero-relative index of the row to update
  346.      * @param col the one-relative index of the column to update
  347.      * @param data the data element that holds the new value
  348.      * @exception TypeNotSupported
  349.      * if the data store does not support the type of action requested
  350.      * or is not successful
  351.      */
  352.     public void update(int row, int col, Data data) throws TypeNotSupported {
  353.         //adjust for row indices being 0 relative
  354.         row++;
  355.         try {
  356.             setManRowChangeFlag(true);
  357.             int translatedRow = translateRow(row);
  358.             // synchronized(position)
  359.             {
  360.                 position.set(translatedRow);
  361.                 // position.ignoreNotification(true);
  362.             }
  363.             //assume only string support for now
  364.             relView.setValueFromString(col, data.toString());
  365.             // synchronized(position)
  366.             {
  367.                 // position.ignoreNotification(false);
  368.             }
  369.         } catch(Exception ex) {
  370.             throw new TypeNotSupported(ex.getMessage());
  371.         } finally {
  372.             setManRowChangeFlag(false);
  373.         }
  374.     }
  375.  
  376.     //BS: added NONEXISTS_ROW support
  377.  
  378.     /**
  379.      * Gets the current state of the specified row.
  380.      *
  381.      * @param row the zero-relative row index
  382.      * @return the state of the row's data. One of NEW_ROW, CLEAN_ROW, DELETED_ROW,
  383.      *          or MODIFIED_ROW or NONEXISTS_ROW
  384.      * @see symantec.itools.db.awt.DataSource#NEW_ROW
  385.      * @see symantec.itools.db.awt.DataSource#CLEAN_ROW
  386.      * @see symantec.itools.db.awt.DataSource#DELETED_ROW
  387.      * @see symantec.itools.db.awt.DataSource#MODIFIED_ROW
  388.      * @see symantec.itools.db.awt.DataSource#NONEXISTS_ROW
  389.      */
  390.     public int rowState(int row)
  391.     {
  392.         //adjust for row indices being 0 relative
  393.         row++;
  394.  
  395.  
  396.  
  397.         int rowState = DataSource.NEW_ROW;
  398.         try
  399.         {
  400.             setManRowChangeFlag(true);
  401.             int translatedRow = translateRow(row);
  402.             // synchronized(position)
  403.             {
  404.                 if(position.set(translatedRow) == false)
  405.                     rowState = DataSource.NONEXISTS_ROW;
  406.             }
  407.             if(rowState != DataSource.NONEXISTS_ROW){
  408.                 byte state = relView.getCurrentRecordState();
  409.                 switch(state)
  410.                 {
  411.                     case Record.RECSTATE_NEW:
  412.                         rowState = DataSource. NEW_ROW; break;
  413.                     case Record.RECSTATE_DELETED:
  414.                         rowState = DataSource. DELETED_ROW; break;
  415.                     case Record.RECSTATE_MODIFIED:
  416.                         rowState = DataSource. MODIFIED_ROW; break;
  417.                     case Record.RECSTATE_EXISTING:
  418.                         rowState = DataSource. CLEAN_ROW; break;
  419.                     default:
  420.                         //now what?!?
  421.                 }
  422.             }
  423.         }
  424.         catch(Exception ex)
  425.         {
  426.             //if fails assume new
  427.             //vj:
  428.             //ex.printStackTrace();
  429.             //System.out.println(ex.toString());
  430.         }
  431.         finally
  432.         {
  433.             setManRowChangeFlag(false);
  434.         }
  435.  
  436.         return rowState;
  437.     }
  438.  
  439.     /**
  440.      * Requests the data store clear all cached data.
  441.      * <p>
  442.      * This is a standard DbDataStore interface method.
  443.      *
  444.      */
  445.     public void clear() {
  446.         initRowMapping();  //time to start all over
  447.     }
  448.  
  449.     /**
  450.      * Requests the data store get the values from the database again.
  451.      * <p>
  452.      * This is a standard DbDataStore interface method.
  453.      *
  454.      */
  455.     public void refresh() {
  456.         lastIndex_=0;
  457.         lastRowRetrieved = 0;
  458.         try {
  459.             relView.restartMultiView();
  460.         } catch(SQLException ex) {
  461.         }
  462.     }
  463.  
  464.     /**
  465.      * Requests that any actions performed on a row be undone. The meaning
  466.      * is left open and is interpreted as appropriate for the type of
  467.      * data store.
  468.      * <p>
  469.      * This is a standard DbDataStore interface method.
  470.      *
  471.      * @param row the zero-relative row index
  472.      * @exception TypeNotSupported
  473.      * if the data store does not support the type of action requested
  474.      * or is not successful
  475.      */
  476.     public void undoRow(int row) throws TypeNotSupported {
  477.         row++;
  478.  
  479.         try {
  480.             // synchronized(position)
  481.             {
  482.                 position.ignoreNotification(true);
  483.             }
  484.             relView.undoRecord();
  485.         } catch(SQLException ex) {
  486.             throw new TypeNotSupported(ex.getMessage());
  487.         } finally {
  488.             // synchronized(position)
  489.             {
  490.                 position.ignoreNotification(false);
  491.             }
  492.         }
  493.     }
  494.  
  495.     /**
  496.      * Returns the number of rows actually retrieved from the database.
  497.      * <p>
  498.      * This is a standard DbDataStore interface method.
  499.      *
  500.      */
  501.     public int rowsRetrieved() {
  502.         return lastIndex_;
  503.     }
  504.  
  505.     /**
  506.      * Requests the store to get all of the elements in the result set.
  507.      * <p>
  508.      * This is a standard DbDataStore interface method.
  509.      *
  510.      * @return the number of rows gotten
  511.      */
  512.     public int fetchAllRows() {
  513.         int count=1;
  514.  
  515.         try {
  516.             setManRowChangeFlag(true);
  517.             while(true) {
  518.                 translateRow(count);
  519.                 count++;
  520.             }
  521.         } catch(DataNotAvailable ex) {
  522.         } finally {
  523.             setManRowChangeFlag(false);
  524.         }
  525.  
  526.         return count-1;
  527.     }
  528.  
  529.  
  530.     //-------------------------------------------------------------------------
  531.     //              DbDataUpdater Methods
  532.     //-------------------------------------------------------------------------
  533.  
  534.     /**
  535.      * Undeletes a row.
  536.      * <p>
  537.      * This is a standard DbDataUpdater interface method.
  538.      *
  539.      * @param row the zero-relative row index
  540.      * @exception TypeNotSupported
  541.      * if the data source does not support the type of action requested
  542.      * or is not successful
  543.      */
  544.     public void undeleteRow(int row) throws TypeNotSupported {
  545.         try {
  546.             row++;
  547.  
  548.             //BS: current record in relation view should be set to 'row'
  549.             //otherwise the current recorde is always the first
  550.             position.set(row);
  551.  
  552.  
  553.             // synchronized(position)
  554.             {
  555.                 position.ignoreNotification(true);
  556.             }
  557.             relView.undoRecord();
  558.         } catch(SQLException ex) {
  559.             throw new TypeNotSupported(ex.getMessage());
  560.         } finally {
  561.             // synchronized(position)
  562.             {
  563.                 position.ignoreNotification(false);
  564.             }
  565.         }
  566.     }
  567.  
  568.     /**
  569.      * Deletes a row or marks a row for deletion.
  570.      * <p>
  571.      * This is a standard DbDataUpdater interface method.
  572.      *
  573.      * @param row the zero-relative row index
  574.      * @exception TypeNotSupported
  575.      * if the data source does not support the type of action requested
  576.      * or is not successful
  577.      */
  578.     public void deleteRow(int row) throws TypeNotSupported {
  579.  
  580.  
  581.         try {
  582.             //adjust for row indices being zero relative
  583.             row++;
  584.  
  585.             //BS: current record in relation view should be set to 'row'
  586.             //otherwise the current record is always the first and gets
  587.             //deleted
  588.             position.set(row);
  589.  
  590.  
  591.             // synchronized(position)
  592.             {
  593.                 position.ignoreNotification(true);
  594.             }
  595.             relView.deleteRecord();
  596.  
  597.  
  598.  
  599.  
  600.         } catch(SQLException ex) {
  601.             throw new TypeNotSupported(ex.getMessage());
  602.         } finally {
  603.             // synchronized(position)
  604.             {
  605.                 position.ignoreNotification(false);
  606.             }
  607.         }
  608.     }
  609.  
  610.     /**
  611.      * Saves the current state as appopriate.
  612.      * If this object caches, it iterates through the cache and updates
  613.      * all non-clean rows.
  614.      * <p>
  615.      * This is a standard DbDataUpdater interface method.
  616.      *
  617.      * @exception TypeNotSupported
  618.      * if the data source does not support the type of action requested
  619.      * or is not successful
  620.      */
  621.     public void save() throws TypeNotSupported {
  622.         try {
  623.             relView.saveMultiView();
  624.         } catch(SQLException ex) {
  625.             throw new TypeNotSupported(ex.getMessage());
  626.         }
  627.     }
  628.  
  629.     /**
  630.      * Gets the next new Record within the RelationView's Record buffers.
  631.      * After calling this method, the current Record of the RelationView will
  632.      * be positioned at the new Record.
  633.      *
  634.      * @exception TypeNotSupported
  635.      * if the data source does not support the type of action requested
  636.      * or is not successful
  637.      */
  638.     public void getNewRecord() throws TypeNotSupported
  639.     {
  640.         try {
  641.             relView.getNewRecord();
  642.         } catch(SQLException ex) {
  643.             throw new TypeNotSupported(ex.getMessage());
  644.         }
  645.     }
  646.  
  647.     /**
  648.      * Appends a new row.
  649.      * @param row ignored
  650.      * @exception TypeNotSupported
  651.      * if the data source does not support the type of action requested
  652.      * or is not successful
  653.      */
  654.     public void insertRow(int row) throws TypeNotSupported {
  655.         //vj: fetchAllRows may be inefficient if the table has too many rows
  656.         //    but exhibits consistent behaviour.
  657.         fetchAllRows();
  658.         appendRow();
  659.     }
  660.  
  661.     /**
  662.      * Appends a new row.
  663.      * <p>
  664.      * This is a standard DbDataUpdater interface method.
  665.      *
  666.      * @return the zero-relative index of the new row
  667.      * @exception TypeNotSupported
  668.      * if the data source does not support the type of action requested
  669.      * or is not successful
  670.      */
  671.     public int appendRow() throws TypeNotSupported {
  672.         /*
  673.         // synchronized(position)
  674.         {
  675.             position.ignoreNotification(true);
  676.         }
  677.         */
  678.         getNewRecord();
  679.         /*
  680.         // synchronized(position)
  681.         {
  682.             position.ignoreNotification(true);
  683.         }
  684.         */
  685.         return 0;
  686.     }
  687.  
  688.     String m_InputColumnNames = null;
  689.  
  690.     /**
  691.      * Specifies the names of the columns to show.
  692.      * @param name column names
  693.      */
  694.     public void setColumnsNamesToShow(String name){
  695.         m_InputColumnNames = name;
  696.     }
  697.  
  698.     //hides or shows columns depending of the DataBinding that was given to the Grid
  699.     /**
  700.      * Hides or shows columns depending of the Data Binding that was given to the Grid.
  701.      */
  702.     public void setColumnsToShow(){
  703.  
  704.         //suppress the part after '%'
  705.         StringTokenizer stf = new StringTokenizer(m_InputColumnNames,"%");
  706.         StringTokenizer st = null;
  707.  
  708.         if(stf.hasMoreTokens())
  709.             st = new StringTokenizer(stf.nextToken(),"@,");
  710.         String n = null;
  711.         if(st.hasMoreTokens())
  712.             n = st.nextToken();
  713.  
  714.         if(st.hasMoreTokens()){
  715.             //check if the 1st parameter is 'All', in that case, don't do anything
  716.             n = st.nextToken();
  717.             if(!n.equalsIgnoreCase("All")){
  718.                 int count = 0;
  719.                 try{
  720.                     count = meta.getColumnCount();
  721.  
  722.                     //first hide all columns (easier)
  723.                     for(int i = 0; i < count; i++)
  724.                         source.view.showColumn(i+1, false);
  725.                     boolean goon = true;
  726.                     do{
  727.                         //is this a valid column name ?
  728.                         try{
  729.                             source.view.showColumn(columnNumberfromName(n), true);//meta index is 0 based
  730.                         }catch(SQLException e){
  731.                             //this maybe a column index
  732.                             source.view.showColumn(Integer.parseInt(n), true);
  733.                         }
  734.                         if(st.hasMoreTokens())
  735.                             n = st.nextToken();
  736.                         else
  737.                             goon = false;
  738.                     }while(goon);
  739.                 }catch(NumberFormatException ex){
  740.                     //we got an error there, a parameter is neither a valid
  741.                     //column index, nor a valid column name. Show all columns
  742.                     for(int i = 0; i < count; i++)
  743.                         source.view.showColumn(i+1, true);
  744.                 }
  745.                 catch(SQLException ex){
  746.                     //we got an error there, a parameter is neither a valid
  747.                     //column index, nor a valid column name. Show all columns
  748.                     for(int i = 0; i < count; i++)
  749.                         source.view.showColumn(i+1, true);
  750.                 }
  751.             }
  752.         }
  753.  
  754.     }
  755.  
  756.     /**
  757.      * Gets the index of the column with the specified name.
  758.      * @param n the column name
  759.      * @return the column index
  760.      * @exception SQLException if the column cannot be found
  761.      */
  762.     public int columnNumberfromName(String n) throws SQLException{
  763.         int col = 1;
  764.         int count = meta.getColumnCount();
  765.         while(col <= count){
  766.             if(n.equals(meta.getColumnName(col))){
  767.                 return col;
  768.             }
  769.             col++;
  770.         }
  771.         throw new SQLException("column name: " + n + "not found!");
  772.     }
  773.  
  774.  
  775.     //-------------------------------------------------------------------------
  776.     //              MetaTable Methods
  777.     //-------------------------------------------------------------------------
  778.  
  779.     /**
  780.      * Sets the number of columns to show.
  781.      * @param the number of columns
  782.      */
  783.     public void setColtoShow(int val){
  784.         coltoshow=val;}
  785.  
  786.     /**
  787.      * Configures the given TableView the desired way.
  788.      * TableView setup often depends on the number and type of data fields
  789.      * that will be displayed.
  790.      * <p>
  791.      * This is a standard MetaTable interface method.
  792.      *
  793.      * @param view the grid to configure
  794.      */
  795.  
  796.  
  797.      //BS: setupTableView changed to setupTableView(TableView v);
  798.      //changed name of meth. and param.
  799.      //TableView.AUTONUMBER changed to TableView.AUTONUMBER
  800.  
  801.     public void setupTableView(TableView v) {
  802.         try {
  803.  
  804.             //create the proper number of columns
  805.             //make sure the BasicCell is set as default
  806.             v.setAutoRedraw(false);
  807.             //int cols = meta.getColumnCount();
  808.             int cols = coltoshow;
  809.             if(coltoshow==0||meta.getColumnCount()<=coltoshow)
  810.             cols=meta.getColumnCount();
  811.             v.createColumns(cols);
  812.  
  813.             //setup row headings
  814.             v.setRowLabelHeadingStyle(TableView.AUTONUMBER);
  815.  
  816.             //set the column headings and widths
  817.             for (int col=1; col<=cols; col++) {
  818.                 int size = meta.getColumnDisplaySize(col);
  819.                 size = Math.min(0, size);
  820.                 size = Math.max(0, MAX_DISPLAY_SIZE);
  821.                 v.setHeading(meta.getColumnLabel(col),
  822.                                 col,
  823.                                 java.lang.Math.min(size, 10));
  824.                 setupColumn(v, col);
  825.             }
  826.             setColumnsToShow();
  827.  
  828.             v.setAutoRedraw(true);
  829.         } catch(SQLException ex) {
  830.             //Not much to do here but set one heading called error
  831.             //and set the message in the first cell
  832.         }
  833.     }
  834.  
  835.     /**
  836.      * Obsolete.
  837.      */
  838.     public void setDataSource(DataSource ds) { }
  839.  
  840.     /**
  841.      * Determines if the data of the cell at the given location is
  842.      * user-editable.
  843.      * <p>
  844.      * This is a standard MetaTable interface method.
  845.      *
  846.      * @param row the zero-relative row index
  847.      * @param col the one-relative column index
  848.      * @return true if it is user-editable, false otherwise
  849.      * @exception DataNotAvailable
  850.      * if the requested data cannot be accessed in the data store
  851.      */
  852.     public boolean isDataEditable(int row, int col) throws DataNotAvailable {
  853.         try {
  854.             //adjust for row indices being zero relative
  855.             row++;
  856.             setManRowChangeFlag(true);
  857.             int translatedRow = translateRow(row);
  858.             boolean success;
  859.             // synchronized(position)
  860.             {
  861.                 success = position.set(translatedRow);
  862.             }
  863.             if (success) {
  864.  
  865.                 //System.out.println( col + " " + meta.isWritable(col) + " " + relView.isCurrentRecordWritable());
  866.                 return meta.isWritable(col) && relView.isCurrentRecordWritable();
  867.             }
  868.         } catch(SQLException ex) {
  869.             throw new DataNotAvailable(ex.getMessage());
  870.         } finally {
  871.             setManRowChangeFlag(false);
  872.         }
  873.  
  874.         //this should never be reached
  875.         return true;
  876.     }
  877.  
  878.     //BS: added arrangeForViewing, this function newly defined in DataStore interface
  879.  
  880.     /**
  881.      * Adjusts the given data as needed before it is viewed in the TableView.
  882.      * <p>
  883.      * This is a standard MetaTable interface method.
  884.      *
  885.      * @param the data
  886.      * @return the arranged data
  887.      */
  888.     public Data[] arrangeForViewing(Data data[]) {
  889.         return data;
  890.     }
  891.  
  892.     /**
  893.      * Sets up the specified column in the given view as needed.
  894.      * Column setup often varies depending on the type of data that will
  895.      * be shown in the column.
  896.      * <p>
  897.      * This is a standard MetaTable interface method.
  898.      *
  899.      * @param view the TableView that is being set up
  900.      * @param col the one-relative position of the column to set up
  901.      */
  902.     public void setupColumn(TableView view, int col) {
  903.         //assume all cols aligned left
  904.         try {
  905.             int type = meta.getColumnType(col);
  906.             switch(type) {
  907.                 case Types.BIGINT:
  908.                 case Types.INTEGER:
  909.                 case Types.DOUBLE:
  910.                 case Types.FLOAT:
  911.                 case Types.NUMERIC:
  912.                 case Types.SMALLINT:
  913.                 case Types.TINYINT:
  914.                 case Types.REAL:
  915.                     view.setColumnAlignment(col,TableView.RIGHT);
  916.                     break;
  917.                 case Types.CHAR:
  918.                 case Types.DATE:
  919.                 case Types.VARCHAR:
  920.                 case Types.LONGVARCHAR:
  921.                 case Types.NULL:
  922.                 case Types.OTHER:
  923.                 case Types.BINARY:
  924.                 case Types.VARBINARY:
  925.                 case Types.LONGVARBINARY:
  926.                 case Types.TIME:
  927.                 case Types.TIMESTAMP:
  928.                 case Types.BIT:
  929.                 default:
  930.                     view.setColumnAlignment(col,TableView.LEFT);
  931.             }
  932.  
  933.             if (meta.isCurrency(col)) {
  934.                 view.setColumnAlignment(col,TableView.RIGHT);
  935.             }
  936.  
  937.             if (!meta.isWritable(col)) {
  938.                 view.setColEditable(col, false);
  939.             }
  940.         } catch(SQLException ex) {
  941.             //why not try a default
  942.             view.setColumnAlignment(col,TableView.LEFT);
  943.         }
  944.     }
  945.  
  946.     //all map functions work on 1 relative rows!!!
  947.     int     rowMapping[];
  948.     BitSet  createdRows;
  949.     int     lastValidIndex;
  950.     int     lastIndex_ = 0;         //pointer to last element in map,
  951.                                     //waste the first
  952.     int     lastRowRetrieved = 0;   //last row retrieved using position.set()
  953.  
  954.     boolean obtainedAllRows = false;
  955.  
  956.     final static int START_SIZE = 100;
  957.     final static int INC_SIZE = 100;
  958.  
  959.     private void initRowMapping() {
  960.             rowMapping = new int[START_SIZE];
  961.             lastValidIndex = START_SIZE -1;
  962.             lastRowRetrieved = 0;
  963.             lastIndex_ = 0;
  964.             obtainedAllRows = false;
  965.             createdRows = new BitSet();
  966.     }
  967.  
  968.     void printMap() {
  969.         System.out.println("Row mappings - size="+lastValidIndex +
  970.                        "\n\tlastIndex="+lastIndex_+ "  lastRow="+lastRowRetrieved);
  971.         for(int i=0;i<=lastIndex_; i++) {
  972.             System.out.println("\trow "+i+" -> "+rowMapping[i]);
  973.         }
  974.     }
  975.  
  976.     private int translateRow(int r) throws DataNotAvailable {
  977.         boolean success;
  978.         try {
  979.             while(lastIndex_ < r) {
  980.                 //if lastRowRetrieved has already been obtained through
  981.                 //an insert then increment and keep going
  982.                 if (createdRows.get(lastRowRetrieved+1)) {
  983.                     lastRowRetrieved++;
  984.                     continue;
  985.                 }
  986.  
  987.                 //need to fetch rows up to 'r'
  988.  
  989.                 // synchronized(position)
  990.                 {
  991.                     success = position.set(lastRowRetrieved+1);
  992.                 }
  993.                 if (!success) {
  994.                     obtainedAllRows = true;   //set the flag and boogie out
  995.                     throw new DataNotAvailable("Ran out of rows to fetch at row=" +
  996.                                                 (lastRowRetrieved+1));
  997.                 } else {
  998.                     //inc last row and insert into map
  999.                     lastRowRetrieved = relView.getCurrentRecordNumber();
  1000.                     lastIndex_++;
  1001.                     growMap();
  1002.                     rowMapping[lastIndex_] = lastRowRetrieved;
  1003.                 }
  1004.             }
  1005.  
  1006.             return rowMapping[r];
  1007.         } catch (SQLException e) {
  1008.             throw new DataNotAvailable(e.getMessage());
  1009.         }
  1010.     }
  1011.  
  1012.     //creates the new record and returns the translated row
  1013.     //number
  1014.     private int insertRowIntoMap(int r) throws DataNotAvailable {
  1015.         int actual;
  1016.  
  1017.         try {
  1018.             //make sure already mapped row
  1019.             translateRow(r);
  1020.  
  1021.             //create a new record
  1022.             relView.getNewRecord();
  1023.             actual = relView.getCurrentRecordNumber();
  1024.             createdRows.set(actual);
  1025.  
  1026.             //shift all array indices down one from specified row 'r'
  1027.             growMap();
  1028.             int numToCopy = lastIndex_+1-r;
  1029.             System.arraycopy(rowMapping, r, rowMapping, r+1, numToCopy);
  1030.             lastIndex_++;  //we just added a row but did not retrieve one
  1031.             rowMapping[r] = actual;
  1032.         } catch(Exception e) {
  1033.             throw new DataNotAvailable(e.getMessage());
  1034.         }
  1035.  
  1036.         return actual;
  1037.     }
  1038.  
  1039.     //new row number of 1 relative
  1040.     private int appendRowIntoMap() throws DataNotAvailable {
  1041.         //record should NOT have already been created
  1042.         //before call, lastRow will be the row number
  1043.         //appended
  1044.  
  1045.         //move to end of all records
  1046.         try {
  1047.             while(!obtainedAllRows) {
  1048.                 translateRow(lastRowRetrieved + 1);
  1049.             }
  1050.         } catch(DataNotAvailable ex) {
  1051.             if (!obtainedAllRows) {
  1052.                 throw ex;
  1053.             }
  1054.         }
  1055.  
  1056.         //create new record
  1057.         try {
  1058.             relView.getNewRecord();
  1059.  
  1060.             //get number and insert into map
  1061.             growMap();
  1062.             lastRowRetrieved = relView.getCurrentRecordNumber();
  1063.             createdRows.set(lastRowRetrieved);
  1064.             rowMapping[++lastIndex_] = lastRowRetrieved;
  1065.         } catch(SQLException ex) {
  1066.             throw new DataNotAvailable("Could not create new record");
  1067.         }
  1068.  
  1069.         return lastIndex_;
  1070.     }
  1071.  
  1072.     final void growMap() {
  1073.         if (lastIndex_ == lastValidIndex-1) {  //minus 1 for safety b/c I'm tired
  1074.             //grow array
  1075.             int newMap[] = new int[lastValidIndex+=INC_SIZE];
  1076.             System.arraycopy(rowMapping, 0, newMap, 0, rowMapping.length);
  1077.             rowMapping = newMap;
  1078.         }
  1079.     }
  1080.  
  1081.     /**
  1082.      * Obsolete.
  1083.      */
  1084.     public Object getSynchronizationObject()
  1085.     {
  1086.         return multiView;
  1087.     }
  1088.  
  1089. }
  1090.  
  1091. class Position
  1092. {
  1093.     int             frozenRecordNumber = 0;
  1094.     int             notificationMode = 0;
  1095.     boolean         frozen = false;
  1096.     RelationView    relView;
  1097.     int             ignoreCount = 0;
  1098.     String          name = "";
  1099.  
  1100.     Position (RelationView rv)
  1101.     {
  1102.         relView = rv;
  1103.         try
  1104.         {
  1105.             name = rv.getName();
  1106.         }
  1107.         catch (SQLException e)
  1108.         {
  1109.         }
  1110.     }
  1111.  
  1112.     void freeze() throws SQLException
  1113.     {
  1114.         frozen = true;
  1115.         ignoreNotification(true);
  1116.         frozenRecordNumber = relView.getCurrentRecordNumber();
  1117.         relView.enableBindingNotify(false, true);
  1118.         relView.enableDetailSQL(false);
  1119.     }
  1120.  
  1121.     void thaw() throws SQLException
  1122.     {
  1123.         if (frozenRecordNumber > 0)
  1124.         {
  1125.             goTo(frozenRecordNumber);
  1126.         }
  1127.         relView.enableDetailSQL(true);
  1128.         relView.enableBindingNotify(true, true);
  1129.         frozen = false;
  1130.         ignoreNotification(false);
  1131.     }
  1132.  
  1133.     boolean set(int recordNumber) throws SQLException
  1134.     {
  1135.         if (relView.getCurrentRecordNumber() == recordNumber)
  1136.         {
  1137.             return true;
  1138.         }
  1139.         if (getNotificationMode() && !frozen)
  1140.         {
  1141.             freeze();
  1142.         }
  1143.         return goTo(recordNumber);
  1144.     }
  1145.  
  1146.     boolean goTo(int recordNumber) throws SQLException
  1147.     {
  1148.         boolean success = false;
  1149.         ignoreNotification(true);
  1150.         success = relView.goTo(recordNumber);
  1151.         ignoreNotification(false);
  1152.         return success;
  1153.     }
  1154.  
  1155.     int get() throws SQLException
  1156.     {
  1157.         if (frozen)
  1158.         {
  1159.             return frozenRecordNumber;
  1160.         }
  1161.         return relView.getCurrentRecordNumber();
  1162.     }
  1163.  
  1164.     void setNotificationMode(boolean mode) throws SQLException
  1165.     {
  1166.         if (mode)
  1167.         {
  1168.             notificationMode++;
  1169.         }
  1170.         else
  1171.         {
  1172.             notificationMode--;
  1173.             if (notificationMode == 0 && frozen)
  1174.             {
  1175.                 thaw();
  1176.             }
  1177.         }
  1178.     }
  1179.  
  1180.     boolean getNotificationMode()
  1181.     {
  1182.         return notificationMode > 0;
  1183.     }
  1184.  
  1185.     void ignoreNotification(boolean flag)
  1186.     {
  1187.         if (flag)
  1188.         {
  1189.             ignoreCount++;
  1190.         }
  1191.         else
  1192.         {
  1193.             ignoreCount--;
  1194.         }
  1195.     }
  1196.  
  1197.     int getIgnoreCount()
  1198.     {
  1199.         return ignoreCount;
  1200.     }
  1201.  
  1202. }
  1203.