KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jga > swing > spreadsheet > Spreadsheet


1 // ============================================================================
2
// $Id: Spreadsheet.java,v 1.16 2005/12/17 04:46:57 davidahall Exp $
3
// Copyright (c) 2004-2005 David A. Hall
4
// ============================================================================
5
// The contents of this file are subject to the Common Development and
6
// Distribution License (CDDL), Version 1.0 (the License); you may not use this
7
// file except in compliance with the License. You should have received a copy
8
// of the the License along with this file: if not, a copy of the License is
9
// available from Sun Microsystems, Inc.
10
//
11
// http://www.sun.com/cddl/cddl.html
12
//
13
// From time to time, the license steward (initially Sun Microsystems, Inc.) may
14
// publish revised and/or new versions of the License. You may not use,
15
// distribute, or otherwise make this file available under subsequent versions
16
// of the License.
17
//
18
// Alternatively, the contents of this file may be used under the terms of the
19
// GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
20
// case the provisions of the LGPL are applicable instead of those above. If you
21
// wish to allow use of your version of this file only under the terms of the
22
// LGPL, and not to allow others to use your version of this file under the
23
// terms of the CDDL, indicate your decision by deleting the provisions above
24
// and replace them with the notice and other provisions required by the LGPL.
25
// If you do not delete the provisions above, a recipient may use your version
26
// of this file under the terms of either the CDDL or the LGPL.
27
//
28
// This library is distributed in the hope that it will be useful,
29
// but WITHOUT ANY WARRANTY; without even the implied warranty of
30
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
31
// ============================================================================
32

33 package net.sf.jga.swing.spreadsheet;
34
35 import java.awt.Component JavaDoc;
36 import java.awt.Container JavaDoc;
37 import java.awt.Dimension JavaDoc;
38 import java.awt.Font JavaDoc;
39 import java.awt.FontMetrics JavaDoc;
40 import java.awt.Graphics JavaDoc;
41 import java.awt.Insets JavaDoc;
42 import java.awt.Point JavaDoc;
43 import java.awt.Rectangle JavaDoc;
44 import java.io.IOException JavaDoc;
45 import java.io.InputStream JavaDoc;
46 import java.io.OutputStream JavaDoc;
47 import java.text.MessageFormat JavaDoc;
48 import java.util.HashMap JavaDoc;
49 import java.util.HashSet JavaDoc;
50 import java.util.Iterator JavaDoc;
51 import java.util.Map JavaDoc;
52 import java.util.Observable JavaDoc;
53 import java.util.Observer JavaDoc;
54 import java.util.Set JavaDoc;
55 import javax.swing.CellRendererPane JavaDoc;
56 import javax.swing.JComponent JavaDoc;
57 import javax.swing.JScrollPane JavaDoc;
58 import javax.swing.JTable JavaDoc;
59 import javax.swing.JViewport JavaDoc;
60 import javax.swing.UIManager JavaDoc;
61 import javax.swing.border.Border JavaDoc;
62 import javax.swing.event.ListSelectionEvent JavaDoc;
63 import javax.swing.table.AbstractTableModel JavaDoc;
64 import javax.swing.table.JTableHeader JavaDoc;
65 import javax.swing.table.TableCellEditor JavaDoc;
66 import javax.swing.table.TableCellRenderer JavaDoc;
67 import javax.swing.table.TableColumn JavaDoc;
68 import javax.swing.table.TableColumnModel JavaDoc;
69 import javax.swing.table.TableModel JavaDoc;
70 import javax.xml.parsers.ParserConfigurationException JavaDoc;
71 import javax.xml.parsers.SAXParser JavaDoc;
72 import javax.xml.parsers.SAXParserFactory JavaDoc;
73 import javax.xml.transform.OutputKeys JavaDoc;
74 import javax.xml.transform.Transformer JavaDoc;
75 import javax.xml.transform.TransformerConfigurationException JavaDoc;
76 import javax.xml.transform.sax.SAXTransformerFactory JavaDoc;
77 import javax.xml.transform.sax.TransformerHandler JavaDoc;
78 import javax.xml.transform.stream.StreamResult JavaDoc;
79 import net.sf.jga.fn.BinaryFunctor;
80 import net.sf.jga.fn.EvaluationException;
81 import net.sf.jga.fn.Generator;
82 import net.sf.jga.fn.UnaryFunctor;
83 import net.sf.jga.fn.adaptor.Constant;
84 import net.sf.jga.fn.adaptor.Identity;
85 import net.sf.jga.parser.IParser;
86 import net.sf.jga.parser.JFXGParser;
87 import net.sf.jga.parser.FunctorRef;
88 import net.sf.jga.parser.GeneratorRef;
89 import net.sf.jga.parser.ParseException;
90 import org.xml.sax.Attributes JavaDoc;
91 import org.xml.sax.InputSource JavaDoc;
92 import org.xml.sax.SAXException JavaDoc;
93 import org.xml.sax.XMLReader JavaDoc;
94 import org.xml.sax.helpers.AttributesImpl JavaDoc;
95 import org.xml.sax.helpers.DefaultHandler JavaDoc;
96
97 /**
98  * Table that contains a sparse-matrix of functors (Generators, specifically).
99  * Each cell in the table can contain a formula, and the table can generate
100  * references to cells to be used in formulas in other cells. Cells in the
101  * matrix can contain constant values and arbitrary Generators, but to support
102  * editing, the calling program must pass an expression that can be parsed
103  * via a JFXGParser.
104  * @see net.sf.jga.parser.JFXGParser
105  * <p>
106  * Copyright &copy; 2004-2005 David A. Hall
107  * @author <a HREF="mailto:davidahall@users.sf.net">David A. Hall</a>
108  */

109
110 // TODO: implement cell ranges as a collection
111

112 // TODO: implement drag/drop of values
113

114 // TODO: make the cells editable by storing their formula's in string form
115
// as well as in parsed form (if possible??)
116

117 // TODO: get error reporting working (renderer should know about cells with
118
// errors)
119

120 // TODO: add a 'formula' mode, where the renderer shows the formula of the
121
// cell rather than its value
122

123 // TODO: allow cells to be 'manually' registered as dependencies -- when a cell's value is set
124
// or manipulated as the result of the formula in some other cell, the first cell does not know
125
// that it has been updated, so it does not notify the table or any of the cells that depend on
126
// it. We can register on all method invocations that start with a 'set' prefix, but (for
127
// example) if a cell contains a point, and another cell calls the point's "move(x,y)" method,
128
// the point won't know that it was updated.
129

130 // TODO: think about assignment statements (include operation/assignment (+=, ...)) where a
131
// cell reference or member reference is allowed on the lhs.
132

133 // TODO: remove the dependency on GenericCellRenderer: cells should 'pre-render' the value
134
// to String (or whatever comes out of the Format functor) when either the value or format is
135
// changed, and serve the cached value to the table model (for performance reasons).
136
// Alternatively, the model may cache such values and clear the cache on value/format changes.
137

138 public class Spreadsheet extends JTable JavaDoc {
139
140     static final long serialVersionUID = -4784933072621672138L;
141     
142     // Parser used to handle formulas. This version adds the 'cell' functor, to provide for cell
143
// references, and the 'row' and 'col' keywords to provide for relative cell addressing
144
transient Parser JavaDoc _parser = new Parser JavaDoc();
145
146     // a pre-cast reference to the model
147
private SpreadsheetTableModel _model;
148
149     // Component used as a row header for the table
150
private RowHeader _rowHeader;
151
152     // Functor used to display status information
153
private UnaryFunctor<String JavaDoc,?> _statusFn = new Identity<String JavaDoc>();
154     
155     // An unfortunate bootstrap hack: we need to allow for arbitrary models
156
// during construction so that the SpreadsheetModel can be non-static and
157
// thus have access to the enclosing Spreadsheet object.
158
private boolean _initialized;
159
160     public Spreadsheet (int rows, int cols){
161         super();
162         _model = new SpreadsheetTableModel(rows, cols);
163         super.setModel(_model);
164
165         setAutoCreateColumnsFromModel(false);
166         setAutoResizeMode(AUTO_RESIZE_OFF);
167         setCellSelectionEnabled(true);
168         setCellEditor(new Cell.Editor());
169
170         TableColumnModel JavaDoc columns = getColumnModel();
171         for (int i = 0; i < cols; ++i) {
172             columns.getColumn(i).setHeaderValue(String.valueOf(i));
173         }
174
175         getTableHeader().setReorderingAllowed(false);
176
177         _rowHeader = new RowHeader(this);
178
179         _parser.bindThis(this);
180
181         _initialized = true;
182     }
183     
184     // - - - - - - - - - -
185
// Spreadsheet Config
186
// - - - - - - - - - -
187

188     public void setColumnCount(int width) {
189         int oldWidth = getColumnCount();
190         doSetColumnCount(oldWidth, width);
191         if (width > 0 && width != oldWidth) {
192             _model.setColumnCount(width);
193         }
194     }
195
196     private void doSetColumnCount(int oldWidth, int width) {
197         if (width > 0 && width != oldWidth) {
198             TableColumnModel JavaDoc columns = getColumnModel();
199             if (oldWidth < width) {
200                 for (int i = oldWidth; i < width; ++i) {
201                     TableColumn JavaDoc column = new TableColumn JavaDoc(i);
202                     column.setHeaderValue(String.valueOf(i));
203                     addColumn(column);
204                 }
205             }
206             else {
207                 for (int i = oldWidth - 1; i >= width; --i) {
208                     removeColumn(columns.getColumn(i));
209                 }
210             }
211         }
212     }
213     
214     public void setRowCount(int height) {
215         if (height > 0 && height != getRowCount()) {
216             _model.setRowCount(height);
217             _rowHeader.setRowCount(height);
218         }
219     }
220     
221     /**
222      * Returns the parser used by the spreadsheet.
223      */

224     public IParser getParser() { return _parser; }
225     
226     // The type of uninitialized cells
227
private Class JavaDoc _defaultType = Integer JavaDoc.class;
228
229     
230     /**
231      * Returns the type of cells that have not be initialized.
232      */

233     public Class JavaDoc getDefaultCellType() { return _defaultType; }
234
235     
236     /**
237      * Sets the type returned by cells that have not been initialized. By default,
238      * this value is java.lang.Integer. If the default value is non-null and is not
239      * an instance of the given type, then the default value will be set to null as
240      * a side-effect of setting the type.
241      */

242     public void setDefaultCellType(Class JavaDoc type) {
243         _defaultType = type;
244         if (_defaultValue != null && !type.isInstance(_defaultValue))
245             _defaultValue = null;
246     }
247
248     
249     // The value of uninitialized cells
250
private Object JavaDoc _defaultValue = new Integer JavaDoc(0);
251
252
253     /**
254      * Returns the default value of cells that have not been initialized.
255      */

256     public Object JavaDoc getDefaultCellValue() { return _defaultValue; }
257     
258
259     /**
260      * Sets the value returned by cells that have not been initialized. By default,
261      * this value is a java.lang.Integer.ZERO. When called, if the new value is not
262      * an instance of the existing default type, then the default type will be changed
263      * to the class of the new value as a side-effect.
264      */

265     public void setDefaultCellValue(Object JavaDoc value) {
266         _defaultValue = value;
267         if (!_defaultType.isInstance(value))
268             _defaultType = value.getClass();
269     }
270
271
272     /**
273      * Sets the option that determines if empty cells and newly created cells are
274      * editable.
275      */

276     public void setEditableByDefault(boolean b) { _model.setEditableByDefault(b); }
277
278     
279     /**
280      * Returns true if empty and newly created cells are editable.
281      */

282     public boolean isEditableByDefault() { return _model.isEditableByDefault(); }
283
284
285     // Flag that controls whether cells are strongly or weakly typed.
286
private boolean _strictType;
287     
288     /**
289      * Returns true if cells strictly enforce types once a type has been set, or if
290      * they allow their types to be changed once set.
291      */

292
293     public boolean isStrictlyTyped() { return _strictType; }
294
295     
296     /**
297      * Sets the option that controls whether cells will be strongly or weakly typed.
298      * Strongly typed cells will not change their types after being set: a new formula
299      * must return the same type as the type that the cell has been assigned. Weakly
300      * typed cells can have their types changed at any time. By default, cells are
301      * weakly typed.
302      */

303
304     public void setStrictTyping(boolean b) { _strictType = b; }
305
306     // - - - - - - - - - -
307
// I/O methods
308
// - - - - - - - - - -
309

310     /**
311      */

312     public void readSpreadsheet(InputStream JavaDoc is) throws IOException JavaDoc {
313         new Reader JavaDoc().readSpreadsheet(is);
314     }
315
316     /**
317      */

318     public void writeSpreadsheet(OutputStream JavaDoc os) throws IOException JavaDoc {
319         new Writer().writeSpreadsheet(os);
320     }
321     
322     // - - - - - - - - - -
323
// GUI methods
324
// - - - - - - - - - -
325

326     /**
327      * Returns the component used as a row header
328      */

329
330     public JComponent JavaDoc getRowHeader() {
331         return _rowHeader;
332     }
333
334     /**
335      * Sets the functor used by the spreadsheet to display status information. This
336      * allows the spreadsheet's container to (for example) route status information
337      * from the spreadsheet to a log file, or to a status bar.
338      */

339
340     public void setStatusHandler(UnaryFunctor<String JavaDoc,?> fn) {
341         _statusFn = fn;
342     }
343
344     /**
345      * Updates the spreadsheet's status message.
346      */

347     public void setStatus(String JavaDoc status) {
348         _statusFn.fn(status);
349     }
350
351     // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
352
// This is temporary, as it really won't support undo/redo -- need to identify
353
// the cell involved and create an UndoableEdit. I think its going to end up
354
// that the Cell class creates the UndoableEdits and sends them to the sheet,
355
// which passes them along to any listeners that may register.
356
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
357

358     private Generator<?> _updateFn;
359
360     public void setUpdateHandler(Generator<?> fn) {
361         _updateFn = fn;
362     }
363     
364     private void fireSpreadsheetUpdated() {
365         if (_updateFn != null)
366             _updateFn.gen();
367     }
368     
369     // - - - - - - - - - -
370
// Cell access methods
371
// - - - - - - - - - -
372

373     /**
374      * Discards all information about the given cell.
375      */

376
377     public void clearCellAt(int row, int col) {
378         Cell cell = getCellIfPresent(row,col);
379         if (cell != null) {
380             cell.clear();
381         }
382     }
383     
384     /**
385      * Returns the cell at the given address, creating one if one does not already
386      * exist.
387      * @throws IndexOutOfBoundsException if the row or column is out of bounds
388      */

389     public Cell getCellAt(int row, int col) {
390         Cell cell = getCellIfPresent(row,col);
391         if (cell == null) {
392             cell = _model.setCell(new Cell(this,row,col));
393         }
394         return cell;
395     }
396
397     /**
398      * Returns the cell at the given address if one exists, or null if it does not
399      * @throws IndexOutOfBoundsException if the row or column is out of bounds
400      */

401     Cell getCellIfPresent(int row, int col) {
402         return _model.getCellAt(row,col);
403     }
404
405     // There's no universal way to convert a constant value of some arbitrary
406
// class into a Generator if the cell is later edited. That is, suppose
407
// we allow the cell to be edited: there's no universal string that we
408
// could put into the editor that we'd be able to parse back out. What
409
// we'd need is the expression that the program uses to generate the value,
410
// which we can then parse into the correct Generator.
411
// For example, suppose a Cell is intended to hold a Color: this method
412
// could be called to set the value to Color.RED, but we can't know how to
413
// generate the expression "Color.RED" in order for it to be edited.
414

415     /**
416      * Builds a read-only cell to hold the given value. If you need the cell
417      * to be editable
418      * @throws IndexOutOfBoundsException if the row or column is out of bounds
419      */

420     public <T> Cell setCellAt(Class JavaDoc<T> type, T value, int row, int col) {
421         return _model.setCellAt(type, new Constant<T>(value), row, col);
422     }
423
424     
425     // TODO: Write a visitor for the generator to create the correct formula (if possible).
426
// In the face of user-supplied functors, that may not work (it may require a number
427
// of registrations for the user-supplied functor).
428

429     /**
430      * Builds a cell to hold the given value
431      * @throws IndexOutOfBoundsException if the row or column is out of bounds
432      */

433     public <T> Cell setCellAt(Class JavaDoc<T> type, Generator<T> gen, int row, int col) {
434         return _model.setCellAt(type, gen, row, col);
435     }
436
437
438 // /**
439
// * Builds a read-only cell to hold the given value
440
// * @throws IndexOutOfBoundsException if the row or column is out of bounds
441
// * @throws IllegalArgumentException if the cell has already been set
442
// */
443
// private <T> Cell setCellAt(Class<T> type, Generator<T> gen, int row, int col) {
444
// // TODO: edit existing cells, changing their types as necessary
445

446 // // @SuppressWarnings
447
// // the Cell returned by getCellAt is non-generic.
448
// Cell cell = getCellAt(row,col);
449
// if (cell != null) {
450
// throw new IllegalArgumentException(cell+" has already been set");
451
// }
452

453 // Point p = new Point(row, col);
454
// cell = new Cell(Spreadsheet.this, type, p, gen);
455
// _model.setCellAt(p, cell);
456
// return cell;
457
// }
458

459     /**
460      * Builds a possibly editable cell to hold the given formula.
461      * @throws IndexOutOfBoundsException if the row or column is out of bounds
462      */

463     public Cell setCellAt(String JavaDoc formula, int row, int col) {
464         return _model.setCellAt(formula, row, col, _model.isEditableByDefault());
465     }
466
467
468     /**
469      * Builds a possibly editable cell to hold the given formula.
470      * @throws IndexOutOfBoundsException if the row or column is out of bounds
471      */

472     public Cell setCellAt(String JavaDoc formula, int row, int col, boolean editable) {
473         return _model.setCellAt(formula, row, col, editable);
474     }
475
476
477 // /**
478
// * Builds a possibly editable cell to hold the given formula
479
// * @throws IndexOutOfBoundsException if the row or column is out of
480
// * bounds
481
// * @throws IllegalArgumentException if the cell has already been set
482
// */
483
// private Cell setCellAt(String formula, int row,int col, boolean editable){
484
// // TODO: edit existing cells, changing their types as necessary
485

486 // // @SuppressWarnings
487
// // the Cell returned by getCellAt is non-generic.
488
// Cell cell = getCellAt(row,col);
489
// if (cell != null) {
490
// throw new IllegalArgumentException(cell+" has already been set");
491
// }
492

493 // Point p = new Point(row, col);
494
// cell = new Cell(this, p, formula, editable);
495
// _model.setCellAt(p, cell);
496
// return cell;
497
// }
498

499     /**
500      * Replaces the CellRenderer for the given cell such that the given
501      * formatting functor is used to present the contents of the cell.
502      * NOTE: Use this method cautiously, as passing a formatter of an
503      * inappropriate type can lead to a class cast exception or to the
504      * Cell reporting '### CLASS ###' condition.
505      */

506     public <T> void setFormatAt(UnaryFunctor<T,String JavaDoc> formatter, int row, int col) {
507         // @SuppressWarnings
508
// the Cell returned by getCellAt is non-generic, so there's no check here
509
// that the cell is of the proper type T. The preferred mechanism is to
510
// set the format of the cell given a Generic reference. The Cell class
511
// catches ClassCastException when it is evaluated, so while this isn't
512
// typesafe, the effects are trapped
513
getCellAt(row,col).setFormat(formatter);
514     }
515
516     /**
517      * Returns the cell with the given name, or null if no cell has the given name.
518      */

519     public Cell getCellByName(String JavaDoc name) {
520         return _model.getCellByName(name);
521     }
522
523     /**
524      * Sets the name of the given cell.
525      * @throws IllegalArgumentException if the name is already in use.
526      */

527     public Cell setCellName(String JavaDoc name, int row, int col) {
528         Cell cell = _model.setCellName(name, row, col);
529         setStatus(cell.toString());
530         return cell;
531     }
532
533
534     /**
535      * Returns a reference to the contents of a given cell
536      */

537     public <T> Generator<T> getReference(Class JavaDoc<T> type, int row, int col) {
538         return _model.getReference(type, row, col);
539     }
540
541
542     // - - - - - - - - - - - - - -
543
// other model wrapper methods
544
// - - - - - - - - - - - - - -
545

546     public void clear() {
547         _model.clear();
548         setRowSelectionInterval(0,0);
549         setColumnSelectionInterval(0,0);
550     }
551
552     // - - - - - - - - - - - - - -
553
// JTable interface
554
// - - - - - - - - - - - - - -
555

556     public TableCellEditor JavaDoc getCellEditor(int row, int col) {
557         Cell cell = getCellIfPresent(row,col);
558         if (cell == null) {
559             return super.getCellEditor(row,col);
560         }
561                 
562         TableCellEditor JavaDoc editor = cell.getEditor();
563         return (editor != null) ? editor : super.getCellEditor(row,col);
564     }
565
566     private TableCellRenderer JavaDoc _defaultRenderer = new Cell.Renderer();
567     
568     public TableCellRenderer JavaDoc getCellRenderer(int row, int col) {
569         Cell cell = getCellIfPresent(row,col);
570         if (cell == null)
571             return _defaultRenderer;
572 // return super.getCellRenderer(row,col);
573

574         TableCellRenderer JavaDoc renderer = cell.getRenderer();
575         return (renderer != null) ? renderer : _defaultRenderer; //super.getCellRenderer(row,col);
576
}
577
578     
579     public void setModel(TableModel JavaDoc model) {
580         // Only true during the constructor: _initialized will be true after the
581
// the constructor is finished. Otherwise, the model must be a
582
// SpreadsheetTableModel
583
if (!_initialized)
584             super.setModel(model);
585         else if (model instanceof SpreadsheetTableModel) {
586             super.setModel(model);
587             SpreadsheetTableModel oldModel = _model;
588             _model = (SpreadsheetTableModel) model;
589             _rowHeader.setRowCount(_model.getRowCount());
590             doSetColumnCount(oldModel.getColumnCount(),_model.getColumnCount());
591         }
592         else {
593             // we _could_ write an adaptor that bridges an arbitrary table model
594
// to a spreadsheet table model.
595
String JavaDoc msg = "Spreadsheet requires SpreadsheetTableModel";
596             throw new IllegalArgumentException JavaDoc(msg);
597         }
598     }
599
600     /**
601      * In addition to the JTable behaviour of this method (which takes care to add the
602      * table's columnheader to an enclosing scrollpane's viewport), add a rowheader to
603      * the enclosing scrollpane's viewport as well.
604      */

605     protected void configureEnclosingScrollPane() {
606         super.configureEnclosingScrollPane();
607         // The base class logic is reproduced: we only want to set the row header under
608
// the same conditions as those in which the column header will be set
609
Container JavaDoc p = getParent();
610         if (p instanceof JViewport JavaDoc) {
611             Container JavaDoc gp = p.getParent();
612             if (gp instanceof JScrollPane JavaDoc) {
613                 JScrollPane JavaDoc scrollPane = (JScrollPane JavaDoc)gp;
614                 JViewport JavaDoc viewport = scrollPane.getViewport();
615                 if (viewport == null || viewport.getView() != this) {
616                     return;
617                 }
618                 
619                 scrollPane.setRowHeaderView(getRowHeader());
620             }
621         }
622     }
623
624     // Overrides the base class to set the status information on selection changes
625
public void valueChanged(ListSelectionEvent JavaDoc e) {
626         if (!e.getValueIsAdjusting())
627             showSelectionStatus();
628         
629         super.valueChanged(e);
630     }
631
632
633     // Overrides the base class to set the status information on selection changes
634
public void columnSelectionChanged(ListSelectionEvent JavaDoc e) {
635         if (!e.getValueIsAdjusting())
636             showSelectionStatus();
637         
638         super.columnSelectionChanged(e);
639     }
640
641     // The last row and column that were written in a status message
642
private int _lastRow = -1, _lastCol = -1;
643
644     // Sends the currently selected cell to the status functor
645
private void showSelectionStatus() {
646         int row = getSelectedRow();
647         int col = getSelectedColumn();
648         if (row < 0 || col < 0)
649             return;
650
651         if (row == _lastRow && col == _lastCol)
652             return;
653         
654         _lastRow = row; _lastCol = col;
655         Cell cell = getCellIfPresent(row, col);
656         if (cell != null)
657             setStatus(cell.toString());
658         else
659             setStatus("cell("+row+","+col+")");
660     }
661
662     // ===========================================
663
// Spreadsheet Model
664
// ===========================================
665

666     private class SpreadsheetTableModel extends AbstractTableModel JavaDoc implements Observer JavaDoc {
667         
668         static final long serialVersionUID = -6455541616661139146L;
669         
670         // The contents of the model: a set of cells keyed by their address...
671
private Map JavaDoc<Point JavaDoc,Cell> _cellmap = new HashMap JavaDoc<Point JavaDoc,Cell>();
672
673         // ... and by their names
674
private Map JavaDoc<String JavaDoc,Cell> _namemap = new HashMap JavaDoc<String JavaDoc,Cell>();
675         
676         // The number of rows in the model
677
private int _numRows;
678
679         // The number of columns in the model
680
private int _numCols;
681
682         /**
683          * Builds a SpreadsheetTableModel of the default size (16x16)
684          */

685         public SpreadsheetTableModel() {
686             this(16, 16);
687         }
688
689         /**
690          * Builds a SpreadsheetTableModel of the given size
691          */

692         public SpreadsheetTableModel(int rows, int cols) {
693             _numRows = rows;
694             _numCols = cols;
695         }
696
697         /**
698          * Removes all information from the model
699          */

700         public void clear() {
701             _cellmap = new HashMap JavaDoc<Point JavaDoc,Cell>();
702             _namemap = new HashMap JavaDoc<String JavaDoc,Cell>();
703             fireTableDataChanged();
704         }
705         
706         /**
707          * Returns the cell at the given address, or null if no such cell
708          * exists.
709          * @throws IndexOutOfBoundsException if the row or column is out of
710          * bounds
711          */

712         public Cell getCellAt(int row, int col) throws IndexOutOfBoundsException JavaDoc {
713             if (checkCellAddress(row,col))
714                 return _cellmap.get(new Point JavaDoc(row, col));
715
716             if (row < 0 || row >= _numRows) {
717                 String JavaDoc msg = "Row " +row +" out of range: 0.." +(_numRows - 1);
718                 throw new IndexOutOfBoundsException JavaDoc(msg);
719             }
720         
721             String JavaDoc msg = "Col " +col +" out of range: 0.." +(_numCols - 1);
722             throw new IndexOutOfBoundsException JavaDoc(msg);
723         }
724
725         /**
726          * Builds a read-only cell to hold the given value
727          * @throws IndexOutOfBoundsException if the row or column is out of
728          * bounds
729          * @throws IllegalArgumentException if the cell has already been set
730          */

731         private <T> Cell setCellAt(Class JavaDoc<T> type, Generator<T> gen, int row, int col) {
732             // TODO: edit existing cells, changing their types as necessary
733

734             // @SuppressWarnings
735
// the Cell returned by is non-generic.
736
Cell cell = getCellAt(row,col);
737             if (cell != null) {
738                 throw new IllegalArgumentException JavaDoc(cell+" has already been set");
739             }
740
741             if (gen == null)
742                 return null;
743
744             return setCell(new Cell(Spreadsheet.this, type, new Point JavaDoc(row, col), gen));
745         }
746
747         /**
748          * Builds a possibly editable cell to hold the given formula
749          * @throws IndexOutOfBoundsException if the row or column is out of
750          * bounds
751          * @throws IllegalArgumentException if the cell has already been set
752          */

753         private Cell setCellAt(String JavaDoc formula, int row,int col, boolean editable){
754             // @SuppressWarnings
755
// the Cell returned by getCellAt is non-generic.
756
Cell cell = getCellAt(row,col);
757             if (cell != null) {
758                 throw new IllegalArgumentException JavaDoc(cell+" has already been set");
759             }
760
761             if (formula == null || "".equals(formula))
762                 return null;
763
764             return setCell(new Cell(Spreadsheet.this, new Point JavaDoc(row, col), formula, editable));
765         }
766
767         /**
768          */

769         private Cell setCell(Cell cell) {
770             String JavaDoc name = cell.getName();
771             if (name != null) {
772                 if (_namemap.get(name) != null) {
773                     String JavaDoc err = "Duplicate cell name "+name;
774                     throw new IllegalArgumentException JavaDoc(err);
775                 }
776             
777                 _namemap.put(name, cell);
778             }
779             
780             cell.addObserver(this);
781
782             Point JavaDoc p = cell.getAddress();
783             _cellmap.put(p, cell);
784             fireTableCellUpdated(p.x, p.y);
785             return cell;
786         }
787
788         /**
789          * Returns a (possibly null) cell whose name is given
790          */

791         private Cell getCellByName(String JavaDoc name) {
792             return _namemap.get(name);
793         }
794
795         /**
796          * Sets the name of a cell
797          */

798         private Cell setCellName(String JavaDoc name, int row, int col) {
799             if (name != null) {
800                 Cell cell = _namemap.get(name);
801                 if (cell != null) {
802                     String JavaDoc err = "Duplicate cell name "+name;
803                     throw new IllegalArgumentException JavaDoc(err);
804                 }
805             }
806             
807             Cell cell = Spreadsheet.this.getCellAt(row, col);
808             String JavaDoc oldname = cell.getName();
809             if (oldname != null)
810                 _namemap.remove(oldname);
811
812             if (name != null)
813                 _namemap.put(name, cell);
814             
815             cell.setName(name);
816             
817             // TODO: need to notify the cell's observers that the name has changed.
818
// This may force a change from an Observable/Observer to the creation
819
// of cell events (CellValueChanged, CellNameChanged, CellFormatChanged)
820
return cell;
821             
822         }
823
824         // NOTE: the Class<T> parm is used by the inferencer to determine the
825
// type of the object returned.
826

827         // NOTE: If editing of cells is implemented by replacing existing cells
828
// with new cells, then this method will have to change a little: the
829
// reference should be served by the spreadsheet and not the cell, so
830
// that the spreadsheet is free to swap cells in and out
831

832         /**
833          * Returns a reference to the contents of a given cell
834          */

835         public <T> Generator<T> getReference(Class JavaDoc<T> type, int row, int col) {
836             Cell cell = getCellAt(row,col);
837             if (cell == null) {
838                 String JavaDoc msg = "Cell({0},{1}) is not yet defined";
839                 throw new IllegalArgumentException JavaDoc(MessageFormat.format(msg, new Object JavaDoc[]{row,col}));
840             }
841             if (type.isAssignableFrom(cell.getType())) {
842
843                 // @SuppressWarnings
844
// the Cell returned by getCellAt is non-generic, as is the reference
845
// that it returns here. The Cell class catches ClassCastException when
846
// it is evaluated, so while this isn't typesafe, the effects are trapped
847
return cell.getReference();
848             }
849
850             String JavaDoc err = "Cannot return reference of type {0} from {1}, whose type is {2}";
851             String JavaDoc msg = MessageFormat.format(err, new Object JavaDoc[]{ type, cell, cell.getType() });
852             throw new ClassCastException JavaDoc(msg);
853         }
854
855         
856         // implementation of the EditableByDefault property
857
private boolean _editableByDefault;
858         private void setEditableByDefault(boolean b) { _editableByDefault = b; }
859         private boolean isEditableByDefault() { return _editableByDefault; }
860
861
862         public void setRowCount(int height) {
863             int oldHeight = _numRows;
864             _numRows = height;
865                 
866             if (oldHeight < height) {
867                 fireTableRowsInserted(oldHeight,height-1);
868             }
869             else {
870                 removeCells();
871                 fireTableRowsDeleted(height,oldHeight-1);
872             }
873         }
874
875         
876         public void setColumnCount(int width) {
877             int oldWidth = _numCols;
878             _numCols = width;
879             if (width < oldWidth) {
880                 removeCells();
881             }
882             
883             fireTableStructureChanged();
884         }
885
886         private void removeCells() {
887             for (Iterator JavaDoc iter = _cellmap.values().iterator(); iter.hasNext(); ) {
888                 Cell cell = (Cell) iter.next();
889                 Point JavaDoc p = cell.getAddress();
890                 if (p.x >= _numRows || p.y >= _numCols) {
891                     iter.remove();
892
893                     String JavaDoc name = cell.getName();
894                     if(name != null)
895                         _namemap.remove(cell.getName());
896                     
897                     cell.unlink();
898                 }
899             }
900         }
901
902         // - - - - - - - - - - -
903
// TableModel interface
904
// - - - - - - - - - - -
905

906         public int getRowCount() {
907             return _numRows;
908         }
909
910         
911         public int getColumnCount() {
912             return _numCols;
913         }
914
915         
916         public Object JavaDoc getValueAt(int row, int col) {
917             if (!checkCellAddress(row, col))
918                 return Cell.REFERENCE_ERR;
919                 
920             Cell cell = _cellmap.get(new Point JavaDoc(row, col));
921             if (cell == null)
922                 return null;
923             
924             Object JavaDoc obj = cell.getValue();
925             return cell.isUndefined() ? "" : cell.isValid() ? obj : cell.getErrorMsg();
926         }
927
928
929         // NOTE: I hook into this method for undo/redo notifications because only
930
// user edits of cell values flows through this method -- programatic updates
931
// to cells (including updates caused by changes to prerequisite cells) don't
932
// flow through the TableModel interface. This is temporary: the Cell class
933
// is likely to grow support for UndoableEdits, and start a chain that ends
934
// up passing them out to the enclosing application
935

936         public void setValueAt(Object JavaDoc value, int row, int col) {
937             Cell cell = getCellAt(row,col);
938             if (cell != null) {
939                 cell.setValue(value);
940             }
941             else if (value != null) {
942                 setCellAt(value.toString(), row, col, true);
943             }
944
945 /*temp*/ fireSpreadsheetUpdated();
946         }
947
948         
949         public boolean isCellEditable(int row, int col) {
950             Cell cell = getCellAt(row, col);
951             if (cell != null)
952                 return cell.isEditable();
953             
954             return _editableByDefault;
955         }
956
957         
958         // - - - - - - - - - - - -
959
// Observer Interface
960
// - - - - - - - - - - - -
961

962         public void update(Observable JavaDoc observable, Object JavaDoc object) {
963             Cell cell = (Cell) observable;
964             Point JavaDoc addr = cell.getAddress();
965             fireTableCellUpdated(addr.x, addr.y);
966         }
967     
968         // - - - - - - - - - - - -
969
// implementation details
970
// - - - - - - - - - - - -
971

972         private boolean checkCellAddress(int row, int col) {
973             return row >= 0 && row < _numRows && col >=0 && col < _numCols;
974         }
975     }
976
977     // ===========================================
978
// Spreadsheet Parser
979
// ===========================================
980

981     class Parser extends JFXGParser {
982         private Cell _crntCell;
983         
984         private Parser() { super(); }
985
986         public Generator parseGenerator(Cell cell, String JavaDoc str) throws ParseException {
987             _crntCell = cell;
988             try {
989                 return super.parseGenerator(str);
990             }
991             finally {
992                 _crntCell = null;
993             }
994             
995         }
996
997         /**
998          * Suppresses the binding method: inside a spreadsheet, 'this' always refers to
999          * the spreadsheet itself.
1000         */

1001        public void bindThis(Object JavaDoc thisBinding) {
1002            super.bindThis(Spreadsheet.this);
1003        }
1004
1005
1006        /**
1007         * Overrides the base parser class mechanism for resolving methods, in order to
1008         * protect certain methods from abuse.
1009         */

1010        protected FunctorRef resolveMethodName(FunctorRef prefix, String JavaDoc name, FunctorRef[] args)
1011            throws ParseException
1012        {
1013            // If the prefix is not a constant reference to this spreadsheet, defer to the superclass
1014
if (prefix.getReferenceType() != FunctorRef.CONSTANT )
1015                return super.resolveMethodName(prefix, name, args);
1016
1017            // It has to be a CONSTANT to get here, so the cast is known safe
1018
if (((GeneratorRef) prefix).getFunctor().gen() != Spreadsheet.this)
1019                return super.resolveMethodName(prefix, name, args);
1020
1021            // We've established that the prefix is a constant reference to this spreadsheet.
1022
return super.resolveMethodName(prefix, name, args);
1023        }
1024
1025        
1026        /**
1027         * Implements the <i>row</i>, and <i>col</i> keywords.
1028         */

1029        protected FunctorRef reservedWord(String JavaDoc word) throws ParseException {
1030            if (word.equals("row")) {
1031                return new GeneratorRef(new Constant(_crntCell.getAddress().x), Integer JavaDoc.class);
1032            }
1033
1034            if (word.equals("col")) {
1035                return new GeneratorRef(new Constant(_crntCell.getAddress().y), Integer JavaDoc.class);
1036            }
1037
1038            return super.reservedWord(word);
1039        }
1040
1041        
1042        /**
1043         * Implements the <i>cell</i> keyword.
1044         */

1045        protected FunctorRef reservedFunction(String JavaDoc name, FunctorRef[] args) throws ParseException {
1046            if (name.equals("cell")) {
1047                if (args.length == 1 && args[0] instanceof GeneratorRef &&
1048                    args[0].getReturnType().equals(String JavaDoc.class))
1049                {
1050                    String JavaDoc refname = (String JavaDoc) ((GeneratorRef) args[0]).getFunctor().gen();
1051                    Cell cell = getCellByName(refname);
1052                    if (cell == null) {
1053                        throw new ParseException("Unknown Cell Name: " + refname);
1054                    }
1055
1056                    return new GeneratorRef(cell.getReference(), cell.getType());
1057                }
1058                
1059                if (args.length != 2 ||
1060                    !(args[0] instanceof GeneratorRef) ||
1061                    !(args[1] instanceof GeneratorRef) ||
1062                    !(args[0].getReturnType().equals(Integer JavaDoc.class)) ||
1063                    !(args[1].getReturnType().equals(Integer JavaDoc.class)))
1064                    throw new ParseException ("Cell Reference requires row, col arguments");
1065
1066                int row = ((Integer JavaDoc) ((GeneratorRef) args[0]).getFunctor().gen()).intValue();
1067                int col = ((Integer JavaDoc) ((GeneratorRef) args[1]).getFunctor().gen()).intValue();
1068                Cell cell = getCellAt(row, col);
1069                return new GeneratorRef(cell.getReference(), cell.getType());
1070            }
1071
1072            return super.reservedFunction(name, args);
1073        }
1074    }
1075
1076    // ===========================================
1077
// SpreadsheetWriter
1078
// ===========================================
1079

1080    public class Writer {
1081        private TransformerHandler JavaDoc _handler;
1082        private Transformer JavaDoc _xformer;
1083        private Set JavaDoc<Cell> _cellsWritten;
1084        
1085        public Writer() throws IOException JavaDoc {
1086            try {
1087                SAXTransformerFactory JavaDoc tf = (SAXTransformerFactory JavaDoc) SAXTransformerFactory.newInstance();
1088                _handler = tf.newTransformerHandler();
1089
1090                Transformer JavaDoc xformer = _handler.getTransformer();
1091                xformer.setOutputProperty(OutputKeys.ENCODING,"ISO-8859-1");
1092                
1093                // TODO: link in the DTD, ideally as a resource loaded from the jar,
1094
// I don't want to have to post the DTD on the site, as that will break
1095
// code if/when the project is moved.
1096

1097// xformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,"doc/hacksheet.dtd");
1098
xformer.setOutputProperty(OutputKeys.INDENT,"yes");
1099            }
1100            catch (TransformerConfigurationException JavaDoc x) {
1101                IOException JavaDoc iox = new IOException JavaDoc(x.getMessage());
1102                iox.initCause(x);
1103                throw iox;
1104            }
1105        }
1106        
1107        
1108        public void writeSpreadsheet(OutputStream JavaDoc os) throws IOException JavaDoc {
1109            try {
1110                // Set up all the JAXP overhead
1111
StreamResult JavaDoc stream = new StreamResult JavaDoc(os);
1112                _handler.setResult(stream);
1113                _handler.startDocument();
1114
1115                AttributesImpl JavaDoc atts = new AttributesImpl JavaDoc();
1116                int rows = getRowCount();
1117                int cols = getColumnCount();
1118                
1119                // Start the spreadsheet document
1120
//
1121
// <hacksheet vers="%d" rows="%d" cols="%d" defaultType="%s" defaultValue="%s">
1122
atts.clear();
1123                atts.addAttribute("","","vers","", "0.1.0");
1124                atts.addAttribute("","","rows","", String.valueOf(rows));
1125                atts.addAttribute("","","cols","", String.valueOf(cols));
1126            
1127                Class JavaDoc defaultType = getDefaultCellType();
1128                if ( ! defaultType.equals(Integer JavaDoc.class)) {
1129                    atts.addAttribute("","","defaultType","",defaultType.getName());
1130                }
1131                
1132                Object JavaDoc defaultValue = getDefaultCellValue();
1133                if ( ! defaultValue.equals(0)) {
1134                    atts.addAttribute("","","defaultValue","",getDefaultCellValue().toString());
1135                }
1136            
1137                _handler.startElement("","","hacksheet",atts);
1138                
1139                _cellsWritten = new HashSet JavaDoc<Cell>(_model._cellmap.values().size() * 4 / 3);
1140                writeCells(_model._cellmap.values().iterator());
1141                
1142                _handler.endElement("","","hacksheet");
1143                _handler.endDocument();
1144            }
1145            catch (SAXException JavaDoc x) {
1146                IOException JavaDoc iox = new IOException JavaDoc(x.getMessage());
1147                iox.initCause(x);
1148                throw iox;
1149            }
1150        }
1151
1152        private void writeCells(Iterator JavaDoc<Cell> cells) throws SAXException JavaDoc {
1153            while (cells.hasNext()) {
1154                Cell cell = cells.next();
1155                if (!_cellsWritten.contains(cell)) {
1156                    writeCells(cell.dependsOn());
1157                    writeCell(cell);
1158                }
1159            }
1160        }
1161
1162        private void writeCell(Cell cell) throws SAXException JavaDoc {
1163            AttributesImpl JavaDoc atts = new AttributesImpl JavaDoc();
1164        
1165            String JavaDoc name = cell.getName();
1166            if (name != null && name.trim().length() != 0)
1167                atts.addAttribute("","","id","",cell.getName());
1168            
1169            Point JavaDoc p = cell.getAddress();
1170            atts.addAttribute("","","row","", String.valueOf(p.x));
1171            atts.addAttribute("","","col","", String.valueOf(p.y));
1172            atts.addAttribute("","","type","", cell.getType().getName());
1173            atts.addAttribute("","","editable","",String.valueOf(cell.isEditable()));
1174        
1175            String JavaDoc formula = cell.getFormula();
1176            _handler.startElement("","","cell",atts);
1177
1178            atts.clear();
1179            _handler.startElement("","","formula",atts);
1180            _handler.characters(formula.toCharArray(),0,formula.length());
1181            _handler.endElement("","","formula");
1182            _handler.endElement("","","cell");
1183
1184            _cellsWritten.add(cell);
1185        }
1186    }
1187
1188    // ===========================================
1189
// SpreadsheetReader
1190
// ===========================================
1191

1192    public class Reader extends DefaultHandler JavaDoc{
1193        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1194
1195        
1196        public void readSpreadsheet(InputStream JavaDoc is) throws IOException JavaDoc {
1197            try {
1198                createParser().parse(new InputSource JavaDoc(is));
1199            }
1200            catch (SAXException JavaDoc x) {
1201                IOException JavaDoc iox = new IOException JavaDoc(x.getMessage());
1202                iox.initCause(x);
1203                throw iox;
1204            }
1205            catch (ParserConfigurationException JavaDoc x) {
1206                IOException JavaDoc iox = new IOException JavaDoc(x.getMessage());
1207                iox.initCause(x);
1208                throw iox;
1209            }
1210        }
1211
1212
1213        private Cell _crntCell;
1214        
1215        public void startElement(String JavaDoc nsURI, String JavaDoc localname, String JavaDoc qname, Attributes JavaDoc attr)
1216            throws SAXException JavaDoc
1217        {
1218            if (qname.equals("hacksheet")) {
1219                String JavaDoc vers = attr.getValue("vers");
1220                // TODO: write a version check
1221

1222                int rows = Integer.parseInt(attr.getValue("rows"));
1223                int cols = Integer.parseInt(attr.getValue("cols"));
1224                SpreadsheetTableModel model = new SpreadsheetTableModel(rows, cols);
1225                SpreadsheetTableModel oldModel = _model;
1226                setModel(model);
1227            }
1228            else if (qname.equals("cell")) {
1229                int row = Integer.parseInt(attr.getValue("row"));
1230                int col = Integer.parseInt(attr.getValue("col"));
1231                _crntCell = new Cell(Spreadsheet.this, row, col);
1232                _crntCell.setEditable(Boolean.valueOf(attr.getValue("editable")));
1233                String JavaDoc name = attr.getValue("id");
1234                if (name != null) {
1235                    _crntCell.setName(name);
1236                }
1237            }
1238            else if (qname.equals("formula")) {
1239                buf.delete(0, buf.length());
1240            }
1241            else
1242                throw new SAXException JavaDoc("unknown tag \""+qname+"\"");
1243        }
1244
1245        public void endElement(String JavaDoc nsURI, String JavaDoc localname, String JavaDoc qname)
1246            throws SAXException JavaDoc
1247        {
1248            if (qname.equals("hacksheet")) {
1249            }
1250            else if (qname.equals("cell")) {
1251                _model.setCell(_crntCell);
1252            }
1253            else if (qname.equals("formula")) {
1254                _crntCell.setFormula(buf.toString());
1255                buf.delete(0, buf.length());
1256            }
1257            else
1258                throw new SAXException JavaDoc("unknown tag \""+qname+"\"");
1259        }
1260
1261
1262        public void characters(char[] ch, int start, int ln) throws SAXException JavaDoc {
1263            for (int i = 0; i < ln; ++i) {
1264                buf.append(ch[start+i]);
1265            }
1266        }
1267
1268        
1269        public XMLReader JavaDoc createParser() throws SAXException JavaDoc, ParserConfigurationException JavaDoc {
1270            SAXParserFactory JavaDoc spf = SAXParserFactory.newInstance();
1271            spf.setValidating(false);
1272                
1273            SAXParser JavaDoc saxParser = spf.newSAXParser();
1274            XMLReader JavaDoc xmlrd = saxParser.getXMLReader();
1275            xmlrd.setContentHandler (this);
1276            xmlrd.setErrorHandler (this);
1277            return xmlrd;
1278        }
1279    }
1280}
1281
1282// ===========================================
1283
// RowHeader
1284
// ===========================================
1285

1286class RowHeader extends JComponent JavaDoc {
1287    static final long serialVersionUID = -1375303876648436931L;
1288
1289    private JTable JavaDoc _table;
1290    private TableCellRenderer JavaDoc _renderer;
1291    private JTableHeader JavaDoc _header;
1292    private CellRendererPane JavaDoc _rendererPane;
1293    private Font JavaDoc _headerFont;
1294
1295    public RowHeader(JTable JavaDoc table) {
1296        _table = table;
1297        _header = table.getTableHeader();
1298        _renderer = _header.getDefaultRenderer();
1299        _rendererPane = new CellRendererPane JavaDoc();
1300        add(_rendererPane);
1301        
1302        Component JavaDoc rendererComponent =
1303            _renderer.getTableCellRendererComponent(_table, "0", false, false, 0, -1);
1304
1305        _headerFont = rendererComponent.getFont();
1306        
1307        setFont(_headerFont);
1308        setBackground(rendererComponent.getBackground());
1309        setForeground(rendererComponent.getForeground());
1310// setRowCount(table.getRowCount());
1311
}
1312    
1313    public TableCellRenderer JavaDoc getRenderer() { return _renderer; }
1314    public void setRenderer(TableCellRenderer JavaDoc renderer) { _renderer = renderer; }
1315
1316    public void setRowCount(int count) {
1317        resize(getPreferredSize());
1318    }
1319    
1320    public Dimension JavaDoc getPreferredSize() {
1321        Border JavaDoc border = (Border JavaDoc) UIManager.getDefaults().get("TableHeader.cellBorder");
1322        Insets JavaDoc insets = border.getBorderInsets(_header);
1323        FontMetrics JavaDoc metrics = getFontMetrics(_headerFont);
1324        Dimension JavaDoc dim = new Dimension JavaDoc( metrics.stringWidth("99999") +insets.right +insets.left,
1325                                       _table.getRowHeight() * _table.getRowCount());
1326        return dim;
1327    }
1328
1329    
1330    protected void paintComponent(Graphics JavaDoc g) {
1331
1332        // TODO: this is naive: we should take the clipping region into account and
1333
// only paint the visible rows
1334

1335        Rectangle JavaDoc cellRect = new Rectangle JavaDoc(0,0,getWidth(),_table.getRowHeight(0));
1336        int rowMargin = _header.getColumnModel().getColumnMargin() - 1;
1337        for (int i = 0; i < _table.getRowCount(); ++i) {
1338            int rowHeight = _table.getRowHeight(i);
1339            cellRect.height = rowHeight - rowMargin;
1340            paintCell(g, cellRect, i);
1341            cellRect.y += rowHeight;
1342        }
1343    }
1344    
1345    private void paintCell(Graphics JavaDoc g, Rectangle JavaDoc cellRect, int rowIndex) {
1346        Component JavaDoc component =
1347            _renderer.getTableCellRendererComponent(_table,rowIndex,false,false,rowIndex,-1);
1348        
1349        _rendererPane.paintComponent(g, component, this, cellRect.x, cellRect.y,
1350                                     cellRect.width, cellRect.height, true);
1351    }
1352}
1353    
1354
Popular Tags