Back to index

wims  3.65+svn20090927
SharpTableModel.java
Go to the documentation of this file.
00001 /*
00002  * @(#)SharpTable.java
00003  * 
00004  * $Id: SharpTableModel.java,v 1.91 2002/08/08 05:15:07 huaz Exp $
00005  *
00006  * Created on October 14, 2000, 8:10 PM
00007 */
00008 package SharpTools;
00009 
00010 import java.util.*;
00011 import java.io.*;
00012 import javax.swing.*;
00013 import javax.swing.table.*; 
00014 import javax.swing.event.TableModelListener; 
00015 import javax.swing.event.TableModelEvent; 
00016 import javax.swing.event.TableColumnModelEvent;
00017 
00029 public class SharpTableModel extends DefaultTableModel {
00030     
00033     private boolean modified;
00036     private boolean passwordModified;
00037     
00039     private SharpTools sharp;
00042     private History history;
00043 
00048     public SharpTableModel(SharpTools sharp) {
00049         super();
00050 
00051        // initialize state to unmodified and file to untitled
00052        modified = false;
00053        this.sharp = sharp;
00054     }
00055 
00068     public SharpTableModel(SharpTools sharp, int numRows, int numColumns) {
00069        
00070         super(numRows, numColumns+1);
00071         //initialize row headers
00072        for(int row = SharpTools.baseRow; row < numRows; row++) {
00073             Cell temp = new Cell(new Integer(row+1));
00074             super.setValueAt(temp, row, 0);
00075         }
00076 
00077         //initialize column headers
00078        /*
00079        for (int col = 1; col < numColumns; col++) {
00080            Cell temp = new Cell(Node.translateColumn(col));
00081            super.setValueAt(temp, 0, col);
00082        }
00083        */
00084         for(int row = SharpTools.baseRow; row < numRows; row++)
00085            for (int col = SharpTools.baseCol; col < numColumns+1; col++)
00086               // we initialize it here
00087               super.setValueAt(new Cell(""), row, col);
00088 
00089         // initialize state to unmodified and file to untitled
00090        modified = false;
00091        this.sharp = sharp;
00092     }
00093 
00104     public SharpTableModel(SharpTools sharp, Object[][] data) {
00105 
00106         this(sharp, data.length + SharpTools.baseRow/*+ 1*/,
00107             data[0].length + SharpTools.baseCol);
00108 
00109         /* load the data */
00110         for(int i = SharpTools.baseRow; i < data.length; i++) {
00111             for(int j = SharpTools.baseCol; j < data[i].length; j++) {
00112                 doSetValueAt(data[i][j], i, j);
00113             }
00114         }
00115 
00116         // initialize state to unmodified and file to untitled
00117        modified = false;
00118        //     file = new File("Untitled");
00119        this.sharp = sharp;
00120     }
00121 
00125     public String getColumnName(int col) {
00126        Debug.println("name of column "+col);
00127        if (col < SharpTools.baseCol)
00128            return "";
00129        else
00130            return String.valueOf(Node.translateColumn(col));
00131     }
00132     
00137     void setHistory(History h) {
00138        history = h;
00139     }
00140     
00158     public Cell getCellAt(int aRow, int aColumn) {
00159        
00160         /* check for out of bounds */
00161        if (aRow < 0 || aRow >= getRowCount() ||
00162            aColumn < 0 || aColumn >= getColumnCount()) {
00163            return null;
00164        }
00165 
00166         Cell temp = (Cell)super.getValueAt(aRow, aColumn);
00167        
00168        return temp;
00169     }
00170 
00171     
00181     public Object getValueAt(int aRow, int aColumn) {
00182         return getCellAt(aRow, aColumn);
00183     }
00184     
00185 
00195     public void recalculate(int aRow, int aColumn) {
00196         if (isFormula(aRow, aColumn)) {
00197 
00198            try {
00199               Debug.println("recalculate");
00200               Number eVal = Formula.evaluate(this, aRow, aColumn);
00201               //we set the value here
00202               getCellAt(aRow, aColumn).setValue(eVal);
00203            }
00204            catch (ParserException e) {
00205                 //set value as the appropriate error message
00206               getCellAt(aRow, aColumn).setValue(e);
00207            }
00208         }
00209     }
00210 
00217     public void recalculate(CellPoint x) {
00218         recalculate(x.getRow(), x.getCol());
00219     }
00220 
00221     
00230     private static Object fieldParser(String input, CellPoint c) {
00231         if (input == null)
00232            return new String("");  
00233        
00234         int row = c.getRow();
00235         int col = c.getCol();
00236         
00237        /* try making it a formula */
00238        if (input.startsWith("=")) {
00239            Formula form = null;        
00240        
00241             //create formula and its value and put in a cell
00242            try {
00243                return new Formula(input.substring(1), row, col);
00244             }catch (ParserException e) {
00245               // no parsing
00246               return new Formula(input.substring(1), row, col, e);
00247            }
00248               
00249         }else {       
00250            /* try making it a number */
00251            try {
00252               //            try {
00253               //                return new Integer(input);
00254               //            }
00255               //            catch (NumberFormatException e) {
00256                   return new Float(input);
00257                   //        }
00258             }catch (NumberFormatException e2) {
00259               /* all else fails treat as string */
00260               return input;
00261             }
00262         }
00263     }
00264        
00271     public static Object fieldParser(String input) {
00272         if (input == null)
00273            return new String("");  
00274        
00275        /* try making it a number */
00276        try {
00277            return new Float(input);
00278         }catch (NumberFormatException e) {
00279            /* all else fails treat as string */
00280            return input;
00281         }
00282     }
00283 
00284     
00299     public void setValueAt(Object aValue, int aRow, int aColumn) {
00300        CellPoint point = new CellPoint(aRow, aColumn);  
00301        history.add(this, new CellRange(point, point));
00302        doSetValueAt(aValue, aRow, aColumn);
00303     }
00304     
00313     public void doSetValueAt(Object aValue, int aRow, int aColumn) {
00314 
00315        if (aValue == null)
00316            aValue = new String("");       
00317        
00318        if (aValue instanceof String) {
00319 
00320            String input = (String)aValue;
00321            
00322            /* try making it a formula */
00323            if (input.startsWith("=")) {
00324               Formula form = null;     
00325               
00326               //create formula and its value and put in a cell
00327               try {
00328                   form = new Formula(input.substring(1),
00329                                    aRow, aColumn);
00330                   setCellAt(form, aRow, aColumn);
00331               }
00332               catch (ParserException e) {
00333                   // no parsing
00334                   form = new Formula(input.substring(1), aRow, aColumn, e);
00335                   setCellAt(form, aRow, aColumn);
00336                   getCellAt(aRow, aColumn).setValue(e);
00337               }
00338               
00339            }else {
00340 
00341               //                try {
00342               //                Integer idata = new Integer(input);
00343               //                setCellAt(idata, aRow, aColumn);
00344               //            }
00345                 
00346                 /* if it begins with "=" but invalid just
00347                  * treat as a string
00348                  */
00349               
00350               //            catch (NumberFormatException e) {
00351                   /* try making it a number */
00352                   try {
00353                      Float data = new Float(input);
00354                      setCellAt(data, aRow, aColumn);
00355                   }
00356                 
00357                   /* if it begins with "=" but invalid just
00358                    * treat as a string
00359                    */
00360               
00361                   catch (NumberFormatException e2) {
00362                      /* all else fails treat as string */
00363                      setCellAt(aValue, aRow, aColumn);
00364                   }
00365                   //        }
00366            }
00367         }else {
00368             /* it is an object, assume it is exactly
00369              * what should the cell be set to
00370              */
00371             if (aValue instanceof Formula) {
00372                 try {
00373                   Formula form = (Formula)aValue;
00374                   setCellAt(new Formula(form, aRow, aColumn), aRow, aColumn);
00375                 }catch (ParserException e) {
00376                    //           errorMessage(e.toString());
00377                     Formula form2 = (Formula)aValue;
00378                     form2 = new Formula(form2.toString(), aRow, aColumn, e);
00379                   setCellAt(form2, aRow, aColumn);
00380                   getCellAt(aRow, aColumn).setValue(e);
00381                 }
00382             }else {           
00383                 setCellAt(aValue, aRow, aColumn);
00384             }
00385         }
00386     }
00387     
00395     public void setCellAt(Object input, int aRow, int aColumn) {
00396         Cell temp = getCellAt(aRow, aColumn);
00397 
00398        /* if for some reason value out of bounds ignore */
00399         if (temp != null) {
00400 
00401            //always remove references old formula referred to
00402            removeRefs(aRow, aColumn);
00403             
00404            //insert new formula
00405            if (input instanceof Formula) {
00406               temp.setFormula((Formula)input);
00407 
00408               if (isLoop(new CellPoint(aRow, aColumn))) {
00409                   ParserException loop = new ParserException("#LOOP?");
00410                     Formula form2 = new Formula(input.toString(), aRow, aColumn, loop);
00411                   setCellAt(form2, aRow, aColumn);
00412                   getCellAt(aRow, aColumn).setValue(loop);
00413                   updateRefs(aRow, aColumn);
00414                   return;
00415                 }else {
00416                   
00417                   addRefs(aRow, aColumn);
00418                   recalculate(aRow, aColumn);
00419                   updateRefs(aRow, aColumn);
00420 
00421                 }               
00422             }else {
00423                 //treat as normal data cell
00424               temp.setData(input);
00425               updateRefs(aRow, aColumn);
00426             }
00427        }
00428     }
00429 
00430     
00438     public void setRange(CellRange range, Object[][] data) {
00439         
00440         /* Loop through the paste range */
00441         for (int i = range.getStartRow(); i <= range.getEndRow(); i++) {
00442             for (int j = range.getStartCol(); j <= range.getEndCol(); j++) {
00443                 
00444                 //calculate the corresponding entry in data array
00445                 int x = i - range.getStartRow();
00446                 int y = j - range.getStartCol();
00447                 
00448                 //place data entry at that place
00449                 doSetValueAt(data[x][y], i, j);
00450             }
00451         }
00452     }
00453         
00454         
00465     public void setRange(CellRange range, Cell[][] data, boolean byValue) {
00466         
00467         /* there may be formula so if byValue is true paste evaluated formula 
00468          * value into the range as a data cell
00469          */
00470         if (byValue) {
00471             for (int i = range.getStartRow(); i <= range.getEndRow(); i++) {
00472                 for (int j = range.getStartCol(); j <= range.getEndCol(); j++) {
00473                     
00474                     int x = i - range.getStartRow();
00475                     int y = j - range.getStartCol();
00476                     
00477                     //get only value of a formula cell not formula
00478                   doSetValueAt(data[x][y].getValue(), i, j);
00479                 }
00480             }
00481         }else {
00482             for (int i = range.getStartRow(); i <= range.getEndRow(); i++) {
00483                 for (int j = range.getStartCol(); j <= range.getEndCol(); j++){
00484                    
00485                     int x = i - range.getStartRow();
00486                     int y = j - range.getStartCol();
00487                     Cell info = data[x][y];
00488                     
00489                     //paste new formula to recalculate
00490                     if (info.isFormula()) {
00491                         doSetValueAt(info.getFormula(), i, j);
00492                     }else {
00493                         doSetValueAt(info.getValue(), i, j);
00494                     }
00495                 }
00496             }
00497         }
00498     }
00499     
00506     public void clearRange(CellRange range) {           
00507         fill(range, null);
00508     }
00509     
00510     
00511     protected void fillRange(CellRange range, String s) {
00512         fill(range, SharpTableModel.fieldParser(s, range.getminCorner()));
00513     }
00514     
00523     protected void fill(CellRange range, Object input) {
00524         
00525         //loop through range
00526         for (int i = range.getStartRow(); i <= range.getEndRow(); i++) {
00527             for (int j = range.getStartCol(); j <= range.getEndCol(); j++) {
00528                 doSetValueAt(input, i, j);
00529             }
00530         }
00531     }
00532     
00541     public CellPoint look(CellPoint begin, Object goal, boolean matchCase,
00542                           boolean matchCell) {
00543         int startRow = begin.getRow();
00544         int startCol = begin.getCol();
00545        
00546        if ((goal instanceof String) && !matchCase && matchCell) {
00547             String objective = (String)goal;
00548            for(int i = startCol; i < getColumnCount(); i++) {
00549               if (objective.equalsIgnoreCase(
00550                                 getCellAt(startRow, i).getValue().toString())) {
00551                   return new CellPoint(startRow, i);
00552               }
00553            }
00554            
00555            for(int i = startRow + 1; i < getRowCount(); i++) {
00556               for(int j = 1; j < getColumnCount(); j++) {
00557                   if (objective.equalsIgnoreCase(
00558                                 getCellAt(i, j).getValue().toString())) {
00559                      return new CellPoint(i, j);
00560                   }
00561               }
00562            }
00563            
00564            return null;
00565             
00566        }else {
00567             if ((goal instanceof String) && !matchCell) {
00568                 String objective = (String)goal;
00569                for(int i = startCol; i < getColumnCount(); i++) {
00570                   String test = getCellAt(startRow, i).getValue().toString();
00571                     
00572                     if (!matchCase) {
00573                         objective = objective.toUpperCase();
00574                         test = test.toUpperCase();
00575                     }
00576                   for(int k = 0; k < test.length(); k++) {
00577                      if (test.startsWith(objective, k)) {
00578                          return new CellPoint(startRow, i);
00579                      }
00580                          
00581                     }
00582                 }                      
00583                   
00584               for(int i = startRow + 1; i < getRowCount(); i++) {
00585                   for(int j = 1; j < getColumnCount(); j++) {
00586                      String test = getCellAt(i, j).getValue().toString();
00587                      if (!matchCase) {
00588                          objective = objective.toUpperCase();
00589                          test = test.toUpperCase();
00590                         }
00591                      for(int k = 0; k < test.length(); k++) {
00592                          if (test.startsWith(objective, k)) {
00593                             return new CellPoint(i, j);
00594                             }
00595                         }
00596                     }
00597                 }
00598                      
00599               return null;
00600             }else {                
00601               for(int i = startCol; i < getColumnCount(); i++) {
00602                   if (goal.equals(getCellAt(startRow, i).getValue())) {
00603                      return new CellPoint(startRow, i);
00604                   }
00605               }
00606               
00607               for(int i = startRow + 1; i < getRowCount(); i++) {
00608                   for(int j = 1; j < getColumnCount(); j++) {
00609                      if (goal.equals(getCellAt(i, j).getValue())) {
00610                          return new CellPoint(i, j);
00611                      }
00612                   }
00613               }
00614                      
00615               return null;
00616             }
00617         }
00618     }
00619     
00627     public Cell[][] getRange(CellRange range) {
00628         
00629         //get dimensions of range
00630         Cell[][] board = new Cell[range.getHeight()][range.getWidth()];
00631         
00632         //copy the cells    
00633         for (int i = range.getStartRow(); i <= range.getEndRow(); i++) {
00634             for (int j = range.getStartCol(); j <= range.getEndCol(); j++) {
00635                 
00636                 //translate to coordinates in copy array
00637                 int x = i - range.getStartRow();
00638                 int y = j - range.getStartCol();
00639                 
00640                 Cell field = getCellAt(i, j);
00641              
00642                 /* if it is a formula copy both the value and the formula
00643                  * The value will be useful with a paste by value 
00644                  */
00645                 if (field.isFormula()) {
00646                     try {
00647                         Formula form = new Formula(field.getFormula(), i, j);
00648                         board[x][y] = new Cell(form, field.getValue(), null);
00649                     }catch (ParserException e) {
00650                         /* if there is a problem, always treat formula
00651                          * as a string.
00652                          */
00653                         board[x][y] = new Cell(field.getFormula().toString());
00654                     }
00655                 }else {
00656                     //value cells have immutable objects
00657                     board[x][y] = new Cell(field.getValue());
00658                 }
00659             }
00660         }
00661         return board;
00662     }
00663 
00675     public void addRefs(int aRow, int aColumn) {
00676         if (isFormula(aRow, aColumn)) {
00677             Formula temp = getCellAt(aRow, aColumn).getFormula();
00678             TreeSet list = temp.getDependency();
00679          
00680             /* use formula's dependency to find cells
00681              * that need to notify it if their values
00682              * change
00683              */
00684             Iterator it = list.iterator();
00685            CellPoint thisRef = new CellPoint(aRow, aColumn);
00686            while (it.hasNext()) {
00687                 CellPoint update = (CellPoint)it.next();
00688               Cell field = getCellAt(update.getRow(), update.getCol());
00689               
00690                 //test of cell found was out of bounds
00691               if (field != null)
00692                   field.addRef(thisRef);
00693             }
00694         }
00695     }
00696     
00706     public void removeRefs(int aRow, int aColumn) {
00707         if (isFormula(aRow, aColumn)) {
00708             Formula temp = getCellAt(aRow, aColumn).getFormula();
00709             TreeSet list = temp.getDependency();
00710            
00711             /* use formula dependcy list to go to cells that it references
00712              * then remove its entry form their reference list
00713              */
00714             Iterator it = list.iterator();
00715            CellPoint thisRef = new CellPoint(aRow, aColumn);
00716            while (it.hasNext()) {
00717                 CellPoint update = (CellPoint)it.next();
00718                 Cell field = getCellAt(update.getRow(), update.getCol());
00719                 
00720               if (field != null)
00721                   field.removeRef(thisRef);
00722                 
00723             }
00724         }
00725     }
00726 
00735     public void updateRefs(int aRow, int aColumn) {
00736        
00737         Cell temp = getCellAt(aRow, aColumn);
00738        if (temp == null)
00739            return;
00740 
00741        TreeSet set = getRefs(aRow, aColumn);
00742 
00743        // mark it as "needsRecalc";
00744        Iterator it = set.iterator();
00745        while (it.hasNext()) {
00746            CellPoint point = (CellPoint)it.next();
00747            Formula formula = getCellAt(point.getRow(), point.getCol()).getFormula();
00748            
00749            formula.setNeedsRecalc(true);
00750            // make sure JTable refreshes it
00751            //fireTableCellUpdated(point.getRow(), point.getCol());
00752        }
00753 
00754        // recalculate
00755        it = set.iterator();
00756        while (it.hasNext()) {
00757            CellPoint point = (CellPoint)it.next();
00758            try {
00759               getNumericValueAt(point.getRow(), point.getCol());
00760            }
00761            catch (ParserException e) {
00762            }
00763            
00764            // make sure JTable refreshes it
00765            fireTableCellUpdated(point.getRow(), point.getCol());
00766        }
00767        
00768         //make sure to tell JTable things have changed
00769        fireTableCellUpdated(aRow, aColumn);
00770     }
00771 
00780     private TreeSet getRefs(int row, int col) {
00781        TreeSet set = new TreeSet();
00782        getRefs(row, col, set);
00783        return set;
00784     }
00785     
00796     private void getRefs(int row, int col, TreeSet set) {
00797 
00798        Cell cell = getCellAt(row, col);
00799        if (cell == null || !cell.hasRefs())
00800            return;
00801        
00802        Iterator it = cell.getRefs().iterator();
00803        while (it.hasNext()) {
00804            CellPoint point = (CellPoint)it.next();
00805            set.add(point);
00806            getRefs(point.getRow(), point.getCol(), set);
00807        }      
00808     }
00809     
00819     public void recalculateAll() {
00820        for (int i = 1; i < getRowCount(); i++)
00821            for (int j = 1; j < getColumnCount(); j++) {
00822               addRefs(i, j);
00823               recalculate(i, j);
00824            }
00825     }
00826 
00827     
00831     public void removeColumn(CellRange deletionRange) {
00832 
00833         /* since the insertion point is given by a selected cell
00834          * there will never be an out of bounds error
00835          */
00836         
00837         /* first column to delete */
00838         int col = deletionRange.getStartCol();
00839         
00840         /* number of columns to delete including col */
00841         int removeNum = deletionRange.getWidth();
00842         
00843         //last entry of table
00844         int lastRow = getRowCount() - 1;
00845         int lastCol = getColumnCount() - 1;
00846         
00847         
00848         /* everything to the right of the columns to remove
00849          * need to be copied to be shifted right
00850          */
00851         CellRange range = new CellRange(SharpTools.baseRow, lastRow,
00852                                         col + removeNum, lastCol);
00853        
00854         
00855         SharpClipboard scrap = new SharpClipboard(this, range, true);
00856         
00857         JTable table = sharp.getTable();
00858         for(int i = 0; i < removeNum; i++) {
00859             // delete old column
00860             removeColumn();
00861            TableColumnModel tm = table.getColumnModel();
00862            tm.removeColumn(tm.getColumn(tm.getColumnCount() - 1));
00863        }
00864         
00865         //shift clipboard elements right
00866         scrap.paste(this, new CellPoint(SharpTools.baseRow, col));
00867 
00868        //     updateRefs(refs);
00869        recalculateAll();
00870        
00871        // set selection
00872        if (table.getSelectedColumnCount() == 0) {
00873            setSelection(new CellRange(SharpTools.baseRow, SharpTools.baseRow,
00874                                    col, col));
00875        }
00876 
00877         //    fireTableStructureChanged();
00878        sharp.setBaseColumnWidth(0);
00879     }
00880     
00881     private void removeColumn() {
00882 
00883         int lastRow = getRowCount() - 1;
00884         int lastCol = getColumnCount() - 1;
00885         
00886         //remove the data from cells to delete to maintain references
00887         clearRange(new CellRange(SharpTools.baseRow, lastRow, lastCol, lastCol));
00888         
00889         Iterator it = dataVector.iterator();
00890         while (it.hasNext()) {
00891             /* Since deleting B makes C the
00892              * new B, the reference lists in cells of old "B"  
00893              * should not change. So, we only shift the data in cells right
00894              * and deleted the last columns.
00895              */
00896             Vector temp = (Vector)it.next();
00897             temp.removeElementAt(getColumnCount() - 1);
00898         }
00899         
00900         //update inherited field from  DefaultTableModel
00901         columnIdentifiers.removeElementAt(columnIdentifiers.size() - 1);
00902         
00903         // Notification is generated within the GUI
00904     }
00905     
00912     public void insertColumn(CellRange insertRange) {
00913         /* since the insertion point is given by a selected cell
00914          * there will never be an out of bounds error
00915          */
00916         Debug.println("insertRange: "+insertRange);
00917         /* start inserting at this coordinate */
00918         int col = insertRange.getStartCol();
00919         
00920         /* number of columns to insert including col */
00921         int insertNum = insertRange.getWidth();
00922         
00923         //the coordinates of the last cell in table
00924         int lastRow = getRowCount() - 1;
00925         int lastCol = getColumnCount() - 1;
00926         
00927         /* everything right to new columns must be shifted right
00928          * so cut them to the clipboard. So if col is "C" then it
00929          * is also copied. The max is a guard for inserting 
00930          * before the label column.
00931          */
00932         CellRange range = new CellRange(SharpTools.baseRow, lastRow, Math.max(col, SharpTools.baseCol), lastCol);
00933         SharpClipboard scrap = new SharpClipboard(this, range, true);
00934 
00935         JTable table = sharp.getTable();
00936        TableColumnModel tm = table.getColumnModel();
00937        TableColumn column = tm.getColumn(col);
00938         //add the new columns to the end
00939         for(int i = 0; i < insertNum; i++) {
00940            int curCol = lastCol+i+1;
00941             addColumn();
00942            TableColumn newcol = new TableColumn(curCol, 
00943                                            column.getPreferredWidth());
00944            //     TableColumn column = tm.getColumn(tm.getColumnCount() - 1);
00945            //            TableColumn newcol = new TableColumn(tm.getColumnCount(), 
00946            //                                                 column.getPreferredWidth());
00947             newcol.setHeaderValue(Node.translateColumn(curCol));
00948            tm.addColumn(newcol);
00949         }
00950         
00951         //shift relevant columns left
00952         scrap.paste(this, new CellPoint(SharpTools.baseRow, col + insertNum));
00953 
00954        recalculateAll();
00955        // set selection
00956 
00957        if (table.getSelectedColumnCount() == 0) {
00958            setSelection(new CellRange(SharpTools.baseRow, SharpTools.baseRow,
00959                                    col, col));       
00960        }
00961 
00962        //     sharp.setBaseColumnWidth();
00963        //fireTableStructureChanged();
00964        sharp.setBaseColumnWidth(0);
00965     }
00966     
00971     private void addColumn() {
00972         
00973         columnIdentifiers.addElement(null);
00974 
00975         /* Initialize the new column */
00976         Iterator it = dataVector.iterator();
00977         
00978         //Give column the appropriate label 
00979         if (it.hasNext()) {
00980             Cell temp = new Cell(Node.translateColumn(getColumnCount() - 1));
00981             ((Vector)it.next()).addElement(temp);
00982         }
00983         
00984         //initialize cells
00985         while (it.hasNext()) {
00986             ((Vector)it.next()).addElement(new Cell(""));
00987         }
00988 
00989         // Generate notification
00990        /*        newColumnsAdded(new TableModelEvent(this, 0, getRowCount() - 1,
00991                 getColumnCount() - 1, TableModelEvent.INSERT));
00992        */
00993     }    
00994         
00998     public void removeRow(CellRange deletionRange) {
00999         /* since the insertion point is given by a selected cell
01000          * there will never be an out of bounds error
01001          */
01002         clearRange(deletionRange);
01003         
01004         /* first row to delete */
01005         int row = deletionRange.getStartRow();
01006         
01007         /* number of rows to delete including the first */
01008         int removeNum = deletionRange.getHeight();
01009         
01010         //coordinates of last cell in spreadsheet
01011         int lastRow = getRowCount() - 1;
01012         int lastCol = getColumnCount() - 1;
01013         
01014         //everything lower than rows to remove must be copied to be shifted
01015         CellRange range = new CellRange(row + removeNum, lastRow,
01016                                    SharpTools.baseCol, lastCol);
01017         SharpClipboard scrap = new SharpClipboard(this, range, true);
01018            
01019         for(int i = 0; i < removeNum; i++) {
01020             super.removeRow(getRowCount() - 1);
01021         }
01022         
01023         
01024         //shift relevent rows up
01025         scrap.paste(this, new CellPoint(row, SharpTools.baseCol));    
01026 
01027        recalculateAll();
01028        
01029        JTable table = sharp.getTable();
01030        // set selection
01031        if (table.getSelectedColumnCount() == 0) {
01032            setSelection(new CellRange(row, row, SharpTools.baseCol, SharpTools.baseCol));
01033        }
01034     }
01035 
01041     public void insertRow(CellRange insertRange) {
01042         /* since the insertion point is given by a selected cell
01043          * there will never be an out of bounds error
01044          */
01045                           
01046         /* insert starting at this coordinate */
01047        int row = insertRange.getStartRow();
01048         
01049         /* number of rows to insert including row */
01050        int insertNum = insertRange.getHeight();
01051         
01052         //coordinates of last cell of table
01053        int lastRow = getRowCount() - 1;
01054        int lastCol = getColumnCount() - 1;
01055 
01056         /* copy things below these new rows
01057          * The max is to prevent inserts above the column headers
01058          */
01059        CellRange range = new CellRange(Math.max(row, SharpTools.baseRow), lastRow, SharpTools.baseCol, lastCol);
01060         SharpClipboard scrap = new SharpClipboard(this, range, true);
01061         
01062         //add the rows to the end
01063         for(int i = 0; i < insertNum; i++) {
01064             addRow();
01065         }
01066         
01067         //shift old rows down
01068         scrap.paste(this, new CellPoint(row + insertNum, SharpTools.baseCol));
01069 
01070        recalculateAll();
01071        
01072        JTable table = sharp.getTable();
01073        // set selection
01074        if (table.getSelectedColumnCount() == 0) {
01075            setSelection(new CellRange(row, row, SharpTools.baseCol, SharpTools.baseCol));
01076        }
01077 
01078     }
01079 
01080 
01084     private void addRow() {
01085         
01086         //create a new row with appropriate label
01087         Vector rowData = new Vector();
01088         rowData.add(0, new Cell(new Integer(getRowCount() + 1)));
01089         
01090         //add it to the table
01091         super.addRow(rowData);
01092         
01093         for(int i = 1; i < getColumnCount(); i++) {
01094             super.setValueAt(new Cell(""), getRowCount() - 1, i);
01095         }
01096     }
01097 
01110     public Number getNumericValueAt(int row, int col) throws ParserException {
01111         Cell cell = getCellAt(row, col);
01112        if (cell != null) {
01113            int type = cell.getType();
01114            if (type == Cell.FORMULA) {
01115               Object value = cell.getValue();
01116               Formula form = cell.getFormula();
01117               // if need recalc
01118               if (form.needsRecalc()) {
01119                   try {
01120                      value = form.evaluate(this, row, col);
01121                      cell.setValue(value);
01122                   }
01123                   catch (ParserException e) {
01124                      cell.setValue(e);
01125                      value = e;
01126                   }         
01127               }
01128               
01129               if (value instanceof ParserException)
01130                   throw (ParserException)value;
01131               else
01132                   return (Number)cell.getValue();
01133            }
01134            else if (type == Cell.NUMBER)
01135               return (Number)cell.getValue();
01136            else
01137               return new Float(0);
01138        }
01139        else
01140        // a string or null
01141        //     return new Float(0);
01142            throw new ParserException("#REFS?");
01143 
01144     }
01145 
01153     public boolean isFormula(int aRow, int aColumn) {
01154         Cell temp = getCellAt(aRow, aColumn);
01155         return ((temp != null) && (temp.getType() == Cell.FORMULA));
01156     }
01157         
01165     public boolean isCellEditable(int row, int column) {
01166        //        return !((row == 0) || (column == 0));
01167        return column != 0;
01168     }
01169 
01178     public Class getColumnClass(int c) {
01179         
01180         /* only cell objects in this TableModel */
01181         return Cell.class;
01182     }    
01183 
01189     private boolean isLoop(CellPoint cell) {
01190        return isLoop(cell, new TreeSet());
01191     }
01192 
01198     private boolean isLoop(CellPoint cell, TreeSet set) {
01199        if (set.contains(cell))
01200            return true;
01201        
01202        Cell objCell = getCellAt(cell.getRow(), cell.getCol());
01203        if (objCell == null)
01204            return false;
01205        Formula formula = objCell.getFormula();
01206        if (formula == null)
01207            return false;
01208 
01209        set.add(cell);
01210        
01211        Iterator it = formula.getDependency().iterator();
01212        while (it.hasNext()) {
01213            CellPoint newCell = (CellPoint)it.next();
01214            boolean ret = isLoop(newCell, set);
01215            if (ret)
01216               return true;
01217        }
01218        
01219        set.remove(cell);
01220        return false;
01221     }
01222 
01228     public void setModified(boolean modified) {
01229        this.modified = modified|passwordModified;
01230        // enable/disable the "Save" button
01231        sharp.checkSaveState();
01232     }
01239     public void setPasswordModified(boolean modified) {
01240        passwordModified = modified;
01241        setModified(this.modified);
01242        // enable/disable the "Save" button
01243     }
01244 
01250     public boolean isModified() {
01251        return modified;
01252     }
01253 
01259     public JTable getTable() {
01260        return sharp.getTable();
01261     }
01262 
01268     public void setSelection(CellRange sel) {
01269        JTable table = sharp.getTable();   
01270        // validate sel
01271        int maxRow = table.getRowCount()-1;
01272        int maxCol = table.getColumnCount()-1;
01273 
01274        int startRow = sel.getStartRow();
01275        int startCol = sel.getStartCol();
01276        int endRow = sel.getEndRow();
01277        int endCol = sel.getEndCol();
01278 
01279        table.setColumnSelectionInterval(Math.min(startCol, maxCol),
01280                                     Math.min(endCol, maxCol));
01281        table.setRowSelectionInterval(Math.min(startRow, maxRow),
01282                                   Math.min(endRow, maxRow));
01283                                   
01284     }
01285 
01293     public boolean isDeletionSafe(CellRange range, boolean byRow) {
01294        int rowOff, colOff;
01295        CellRange needCheck;
01296        
01297        if (byRow) {
01298            rowOff = -range.getHeight();
01299            colOff = 0;
01300            if (range.getEndRow() == getRowCount()-1)
01301               return true;
01302            
01303            needCheck = new CellRange(range.getEndRow()+1, getRowCount()-1,
01304                                   SharpTools.baseCol, getColumnCount()-1);
01305        }
01306        else {
01307            rowOff = 0;
01308            colOff = -range.getWidth();
01309            if (range.getEndCol() == getColumnCount()-1)
01310               return true;
01311            needCheck = new CellRange(SharpTools.baseRow, getRowCount()-1,
01312                                   range.getEndCol()+1, getColumnCount()-1);
01313        }
01314 
01315        for (int i = needCheck.getStartRow(); i <= needCheck.getEndRow(); i++)
01316            for (int j = needCheck.getStartCol(); j <= needCheck.getEndCol();
01317                j++) {
01318               Cell cell = getCellAt(i, j);
01319               if (cell.isFormula() &&
01320                   !Formula.isSafe(cell.getFormula(), rowOff, colOff)) {
01321                   Debug.println("relative addresses become invalid");
01322                   return false;
01323               }
01324            }
01325 
01326        return true;
01327        
01328     }
01329 
01341     public String toString(CellRange range, boolean byValue) {
01342        StringBuffer sbf=new StringBuffer();
01343        for (int i=range.getStartRow(); i<=range.getEndRow(); i++) {
01344            for (int j=range.getStartCol(); j<=range.getEndCol(); j++) {
01345               if (byValue) {
01346                   sbf.append(getValueAt(i, j));
01347               }
01348               else {
01349                   Cell cell = getCellAt(i, j);
01350                   if(cell != null)
01351                      sbf.append(cell.toString());
01352               }
01353               
01354               if (j<range.getEndCol())
01355                   sbf.append("\t");
01356            }
01357            sbf.append("\n");
01358        }
01359 
01360        String text = sbf.toString();
01361        return text;
01362     }
01363 
01364     public String to_WIMS(CellRange range, boolean byValue ,boolean matrix) {
01365        StringBuffer sbf=new StringBuffer();
01366        Number n=null;
01367        String row_sep="\t";
01368        String col_sep="\n";
01369        
01370        if(matrix){
01371            row_sep=",";
01372            col_sep=";";
01373            sbf.append("[");
01374        }
01375        else
01376        {
01377            row_sep="\t";
01378            col_sep="\n";
01379        }
01380        for (int i=range.getStartRow(); i<=range.getEndRow(); i++) {
01381            for (int j=range.getStartCol(); j<=range.getEndCol(); j++) {
01382               n=null;
01383               if(isFormula(i,j)){
01384                   try { n = getNumericValueAt(i,j);}                                                                                                                              
01385                   catch (ParserException e) {System.out.println("hmmm this should not happen"); }
01386               }
01387               if(n != null){
01388                   sbf.append(n);
01389               }
01390               else
01391               {
01392                   if(byValue){
01393                      sbf.append(getValueAt(i, j));
01394                   }
01395                   else
01396                   {
01397                      Cell cell = getCellAt(i, j);
01398                      // if cell is not empty
01399                      if(cell != null){sbf.append(cell.toString());}
01400                   }
01401               }
01402               if (j<range.getEndCol())
01403                   sbf.append(row_sep);
01404            }
01405            sbf.append(col_sep);
01406        }
01407        if(matrix){sbf.append("]");}
01408 
01409        String text = sbf.toString();
01410        return text;
01411     }
01412 
01424     void fromString(String text, int rowOff, int colOff, CellRange range) {
01425 
01426        try {
01427            BufferedReader in = new BufferedReader(new StringReader(text));
01428            String line;
01429            int row = range.getStartRow();
01430        
01431            while (row <= range.getEndRow()) {
01432               line = in.readLine();
01433               
01434               int index;
01435               int prev = 0;
01436               
01437               // set col to startCol before each loop
01438               int col = range.getStartCol();
01439               String value;
01440               
01441               while (col <= range.getEndCol()) {
01442                   index = line.indexOf('\t', prev);
01443                   if (index >= 0) {
01444                      value = line.substring(prev, index);
01445                   }
01446                   else {
01447                      value = line.substring(prev);
01448                   }
01449                   
01450                   if (value.startsWith("=")) {
01451                             // need to fix relative address
01452                      value = 
01453                          Formula.fixRelAddr(value.substring(1),
01454                                           rowOff, colOff);
01455                      if (value == null)
01456                          value = new String("=$REFS$0");
01457                      else
01458                          value = "="+value;
01459                   }
01460                   
01461                   doSetValueAt(value, row, col);
01462                   
01463                   prev = index+1;
01464                   // increment column number
01465                   col++;
01466                   
01467                   if (index == -1)
01468                      break;
01469               }
01470               
01471               row++;
01472            }
01473        }
01474        catch (Exception e) {           
01475        }
01476     }
01477     
01485     public String toString() {
01486        return toString(new CellRange(SharpTools.baseRow, getRowCount()-1,
01487                                   SharpTools.baseCol, getColumnCount()-1), false);
01488     }
01489     public String toWIMS(boolean matrix) {
01490        return to_WIMS(new CellRange(SharpTools.baseRow, getRowCount()-1,
01491                                   SharpTools.baseCol, getColumnCount()-1), false, matrix);
01492     }
01493     
01500     static public CellPoint getSize(String input) {
01501        
01502        BufferedReader in = new BufferedReader(new StringReader(input));
01503        String line;
01504        int rowcount = 0;
01505        int colcount = 0;
01506 
01507        try {
01508        
01509            while ((line = in.readLine()) != null) {
01510               rowcount++;
01511               // initialize new tokenizer on line with tab delimiter. 
01512               //            tokenizer = new StringTokenizer(line, "\t");
01513               int index;
01514               int prev = 0;
01515               
01516               // set col to 1 before each loop
01517               int col = 0;
01518               
01519               while (true) {
01520                   index = line.indexOf('\t', prev);            
01521                   prev = index+1;
01522                   // increment column number
01523                   col++;
01524                   
01525                   if (index == -1)
01526                      break;
01527               }
01528 
01529               if (colcount < col)
01530                   colcount = col;
01531            }
01532        }
01533        catch (Exception e) {
01534            return null;
01535        }
01536 
01537        return new CellPoint(rowcount, colcount);
01538     }
01539 
01540 
01551     public void sort(CellRange area, int primary, int second, boolean isRow, 
01552               boolean ascend, boolean tiebreaker) {
01553 
01554         
01555         /* original data order will be saved here 
01556          * and placed on clipboard for undo
01557          */
01558         SharpClipboard[] data;
01559         if (isRow) {
01560             data = new SharpClipboard[area.getWidth()];
01561             for(int i = 0; i < data.length; i++) {
01562                 CellRange temp = new CellRange(area.getStartRow(),
01563                                                area.getEndRow(),
01564                                                area.getStartCol() + i,
01565                                                area.getStartCol() + i);
01566                 data[i] = new SharpClipboard(this, temp, false);
01567             }
01568         }else {
01569             data = new SharpClipboard[area.getHeight()];
01570             for(int i = 0; i < data.length; i++) {
01571                 CellRange temp = new CellRange(area.getStartRow() + i,
01572                                                area.getStartRow() + i,
01573                                                area.getStartCol(),
01574                                                area.getEndCol());
01575                 data[i] = new SharpClipboard(this, temp, false);
01576             }
01577         }
01578                                             
01579         
01580         /* We are going to do the sort within the world of the data array
01581          * First, we do index sorting to create an index array.
01582          * Then according to the index array, we paste the entries in data
01583          * back in the sorted order.
01584          */
01585         
01586         //do index sorting
01587         int[] indices = internalSort(area, primary, second, isRow, ascend, tiebreaker);
01588             
01589         //paste accordingly
01590         if (isRow) {
01591            for (int i = area.getStartCol(); i <= area.getEndCol(); i++){
01592               //point to paste at
01593                 CellPoint point = new CellPoint(area.getStartRow(), i);
01594                 
01595                 int y = i - area.getStartCol();
01596                 data[indices[y] - area.getStartCol()].paste(this, point);
01597             }
01598         }else {
01599             for (int i = area.getStartRow(); i <= area.getEndRow(); i++) {
01600               //point to paste at
01601                 CellPoint point = new CellPoint(i, area.getStartCol());
01602                 
01603                 int y = i - area.getStartRow();
01604                 data[indices[y] - area.getStartRow()].paste(this, point);
01605             }
01606         }
01607     }
01608     
01620     private int compareLines(int primary, boolean isRow, boolean ascending, 
01621                              int i, int j) {
01622  
01623             Cell x = getCriteria(primary, i, isRow);
01624             Cell y = getCriteria(primary, j, isRow);
01625             return x.compare(y, ascending);
01626     }
01627 
01635     private Cell getCriteria(int interest,
01636                           int i, boolean isRow) {
01637        if (isRow) {
01638            return getCellAt(interest, i);
01639        }else {
01640            return getCellAt(i, interest);
01641        }
01642     }
01643 
01647     private boolean rightOrder(int primary, int second, boolean isRow, int i, 
01648                             int j, boolean ascend, boolean order) {
01649        
01650         //compare by first criteria                            
01651         int result = compareLines(primary, isRow, ascend, i, j);
01652         
01653         //if equal, use second as tiebreaker
01654         if (result == 0) {
01655             result = compareLines(second, isRow, order, i, j);
01656 
01657             if (order) {
01658               return (result < 0);
01659             }else {
01660               return (result > 0);
01661             }
01662         //otherwise just return results from primary criteria
01663         }else {
01664             if (ascend) {
01665               return (result < 0);
01666            }else {
01667                 return (result > 0);
01668             }
01669        }
01670     }
01671     
01684     private int[] internalSort(CellRange area, int primary, int second, 
01685                             boolean isRow, boolean ascend, 
01686                             boolean tiebreaker) {
01687         //initialize index array
01688         int[] index;
01689        if (isRow) {
01690            index = new int[area.getWidth()];
01691            for(int i = 0; i < index.length; i++) {
01692               index[i] = i + area.getStartCol();
01693            }
01694        }else {
01695            index = new int[area.getHeight()];
01696            for(int i = 0; i < index.length; i++) {
01697               index[i] = i + area.getStartRow();
01698            }
01699        }
01700         
01701         int j;
01702         
01703         for (int p = 1; p < index.length ; p++) {
01704            int tmp = index[p];
01705            
01706             for (j = p; ((j > 0) && rightOrder(primary, second, isRow, tmp, 
01707                                              index[j - 1], ascend, tiebreaker)); 
01708                j--) {
01709                 index[j] = index[j - 1];
01710             }
01711            index[j] = tmp;
01712         }        
01713         return index;
01714     }
01715 
01721     public boolean isEmptyCell(int row, int col) {
01722        return getCellAt(row, col).getValue().equals("");
01723            
01724     }    
01725 }