Source Code Cross Referenced for Spreadsheet.java in  » Development » jga-Generic-Algorithms » net » sf » jga » swing » spreadsheet » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Development » jga Generic Algorithms » net.sf.jga.swing.spreadsheet 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        // ============================================================================
0002:        // $Id: Spreadsheet.java,v 1.16 2005/12/17 04:46:57 davidahall Exp $
0003:        // Copyright (c) 2004-2005  David A. Hall
0004:        // ============================================================================
0005:        // The contents of this file are subject to the Common Development and
0006:        // Distribution License (CDDL), Version 1.0 (the License); you may not use this 
0007:        // file except in compliance with the License.  You should have received a copy
0008:        // of the the License along with this file: if not, a copy of the License is 
0009:        // available from Sun Microsystems, Inc.
0010:        //
0011:        // http://www.sun.com/cddl/cddl.html
0012:        //
0013:        // From time to time, the license steward (initially Sun Microsystems, Inc.) may
0014:        // publish revised and/or new versions of the License.  You may not use,  
0015:        // distribute, or otherwise make this file available under subsequent versions 
0016:        // of the License.
0017:        // 
0018:        // Alternatively, the contents of this file may be used under the terms of the 
0019:        // GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
0020:        // case the provisions of the LGPL are applicable instead of those above. If you 
0021:        // wish to allow use of your version of this file only under the terms of the 
0022:        // LGPL, and not to allow others to use your version of this file under the 
0023:        // terms of the CDDL, indicate your decision by deleting the provisions above 
0024:        // and replace them with the notice and other provisions required by the LGPL. 
0025:        // If you do not delete the provisions above, a recipient may use your version 
0026:        // of this file under the terms of either the CDDL or the LGPL.
0027:        // 
0028:        // This library is distributed in the hope that it will be useful,
0029:        // but WITHOUT ANY WARRANTY; without even the implied warranty of
0030:        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
0031:        // ============================================================================
0032:
0033:        package net.sf.jga.swing.spreadsheet;
0034:
0035:        import java.awt.Component;
0036:        import java.awt.Container;
0037:        import java.awt.Dimension;
0038:        import java.awt.Font;
0039:        import java.awt.FontMetrics;
0040:        import java.awt.Graphics;
0041:        import java.awt.Insets;
0042:        import java.awt.Point;
0043:        import java.awt.Rectangle;
0044:        import java.io.IOException;
0045:        import java.io.InputStream;
0046:        import java.io.OutputStream;
0047:        import java.text.MessageFormat;
0048:        import java.util.HashMap;
0049:        import java.util.HashSet;
0050:        import java.util.Iterator;
0051:        import java.util.Map;
0052:        import java.util.Observable;
0053:        import java.util.Observer;
0054:        import java.util.Set;
0055:        import javax.swing.CellRendererPane;
0056:        import javax.swing.JComponent;
0057:        import javax.swing.JScrollPane;
0058:        import javax.swing.JTable;
0059:        import javax.swing.JViewport;
0060:        import javax.swing.UIManager;
0061:        import javax.swing.border.Border;
0062:        import javax.swing.event.ListSelectionEvent;
0063:        import javax.swing.table.AbstractTableModel;
0064:        import javax.swing.table.JTableHeader;
0065:        import javax.swing.table.TableCellEditor;
0066:        import javax.swing.table.TableCellRenderer;
0067:        import javax.swing.table.TableColumn;
0068:        import javax.swing.table.TableColumnModel;
0069:        import javax.swing.table.TableModel;
0070:        import javax.xml.parsers.ParserConfigurationException;
0071:        import javax.xml.parsers.SAXParser;
0072:        import javax.xml.parsers.SAXParserFactory;
0073:        import javax.xml.transform.OutputKeys;
0074:        import javax.xml.transform.Transformer;
0075:        import javax.xml.transform.TransformerConfigurationException;
0076:        import javax.xml.transform.sax.SAXTransformerFactory;
0077:        import javax.xml.transform.sax.TransformerHandler;
0078:        import javax.xml.transform.stream.StreamResult;
0079:        import net.sf.jga.fn.BinaryFunctor;
0080:        import net.sf.jga.fn.EvaluationException;
0081:        import net.sf.jga.fn.Generator;
0082:        import net.sf.jga.fn.UnaryFunctor;
0083:        import net.sf.jga.fn.adaptor.Constant;
0084:        import net.sf.jga.fn.adaptor.Identity;
0085:        import net.sf.jga.parser.IParser;
0086:        import net.sf.jga.parser.JFXGParser;
0087:        import net.sf.jga.parser.FunctorRef;
0088:        import net.sf.jga.parser.GeneratorRef;
0089:        import net.sf.jga.parser.ParseException;
0090:        import org.xml.sax.Attributes;
0091:        import org.xml.sax.InputSource;
0092:        import org.xml.sax.SAXException;
0093:        import org.xml.sax.XMLReader;
0094:        import org.xml.sax.helpers.AttributesImpl;
0095:        import org.xml.sax.helpers.DefaultHandler;
0096:
0097:        /**
0098:         * Table that contains a sparse-matrix of functors (Generators, specifically).
0099:         * Each cell in the table can contain a formula, and the table can generate
0100:         * references to cells to be used in formulas in other cells.  Cells in the
0101:         * matrix can contain constant values and arbitrary Generators, but to support
0102:         * editing, the calling program must pass an expression that can be parsed
0103:         * via a JFXGParser.
0104:         * @see net.sf.jga.parser.JFXGParser
0105:         * <p>
0106:         * Copyright &copy; 2004-2005  David A. Hall
0107:         * @author <a href="mailto:davidahall@users.sf.net">David A. Hall</a>
0108:         */
0109:
0110:        // TODO: implement cell ranges as a collection
0111:        // TODO: implement drag/drop of values
0112:        // TODO: make the cells editable by storing their formula's in string form
0113:        // as well as in parsed form (if possible??)
0114:        // TODO: get error reporting working (renderer should know about cells with
0115:        // errors)
0116:        // TODO: add a 'formula' mode, where the renderer shows the formula of the
0117:        // cell rather than its value
0118:        // TODO: allow cells to be 'manually' registered as dependencies -- when a cell's value is set
0119:        // or manipulated as the result of the formula in some other cell, the first cell does not know
0120:        // that it has been updated, so it does not notify the table or any of the cells that depend on
0121:        // it.  We can register on all method invocations that start with a 'set' prefix, but (for
0122:        // example) if a cell contains a point, and another cell calls the point's "move(x,y)" method,
0123:        // the point won't know that it was updated.
0124:        // TODO: think about assignment statements (include operation/assignment (+=, ...)) where a
0125:        // cell reference or member reference is allowed on the lhs.
0126:        // TODO: remove the dependency on GenericCellRenderer: cells should 'pre-render' the value
0127:        // to String (or whatever comes out of the Format functor) when either the value or format is
0128:        // changed, and serve the cached value to the table model (for performance reasons).
0129:        // Alternatively, the model may cache such values and clear the cache on value/format changes.
0130:        public class Spreadsheet extends JTable {
0131:
0132:            static final long serialVersionUID = -4784933072621672138L;
0133:
0134:            // Parser used to handle formulas.  This version adds the 'cell' functor, to provide for cell
0135:            // references, and the 'row' and 'col' keywords to provide for relative cell addressing
0136:            transient Parser _parser = new Parser();
0137:
0138:            // a pre-cast reference to the model
0139:            private SpreadsheetTableModel _model;
0140:
0141:            // Component used as a row header for the table
0142:            private RowHeader _rowHeader;
0143:
0144:            // Functor used to display status information
0145:            private UnaryFunctor<String, ?> _statusFn = new Identity<String>();
0146:
0147:            // An unfortunate bootstrap hack: we need to allow for arbitrary models
0148:            // during construction so that the SpreadsheetModel can be non-static and
0149:            // thus have access to the enclosing Spreadsheet object.
0150:            private boolean _initialized;
0151:
0152:            public Spreadsheet(int rows, int cols) {
0153:                super ();
0154:                _model = new SpreadsheetTableModel(rows, cols);
0155:                super .setModel(_model);
0156:
0157:                setAutoCreateColumnsFromModel(false);
0158:                setAutoResizeMode(AUTO_RESIZE_OFF);
0159:                setCellSelectionEnabled(true);
0160:                setCellEditor(new Cell.Editor());
0161:
0162:                TableColumnModel columns = getColumnModel();
0163:                for (int i = 0; i < cols; ++i) {
0164:                    columns.getColumn(i).setHeaderValue(String.valueOf(i));
0165:                }
0166:
0167:                getTableHeader().setReorderingAllowed(false);
0168:
0169:                _rowHeader = new RowHeader(this );
0170:
0171:                _parser.bindThis(this );
0172:
0173:                _initialized = true;
0174:            }
0175:
0176:            // - - - - - - - - - -
0177:            // Spreadsheet Config
0178:            // - - - - - - - - - -
0179:
0180:            public void setColumnCount(int width) {
0181:                int oldWidth = getColumnCount();
0182:                doSetColumnCount(oldWidth, width);
0183:                if (width > 0 && width != oldWidth) {
0184:                    _model.setColumnCount(width);
0185:                }
0186:            }
0187:
0188:            private void doSetColumnCount(int oldWidth, int width) {
0189:                if (width > 0 && width != oldWidth) {
0190:                    TableColumnModel columns = getColumnModel();
0191:                    if (oldWidth < width) {
0192:                        for (int i = oldWidth; i < width; ++i) {
0193:                            TableColumn column = new TableColumn(i);
0194:                            column.setHeaderValue(String.valueOf(i));
0195:                            addColumn(column);
0196:                        }
0197:                    } else {
0198:                        for (int i = oldWidth - 1; i >= width; --i) {
0199:                            removeColumn(columns.getColumn(i));
0200:                        }
0201:                    }
0202:                }
0203:            }
0204:
0205:            public void setRowCount(int height) {
0206:                if (height > 0 && height != getRowCount()) {
0207:                    _model.setRowCount(height);
0208:                    _rowHeader.setRowCount(height);
0209:                }
0210:            }
0211:
0212:            /**
0213:             * Returns the parser used by the spreadsheet.
0214:             */
0215:            public IParser getParser() {
0216:                return _parser;
0217:            }
0218:
0219:            // The type of uninitialized cells
0220:            private Class _defaultType = Integer.class;
0221:
0222:            /**
0223:             * Returns the type of cells that have not be initialized.
0224:             */
0225:            public Class getDefaultCellType() {
0226:                return _defaultType;
0227:            }
0228:
0229:            /**
0230:             * Sets the type returned by cells that have not been initialized.  By default,
0231:             * this value is java.lang.Integer.  If the default value is non-null and is not
0232:             * an instance of the given type, then the default value will be set to null as
0233:             * a side-effect of setting the type.
0234:             */
0235:            public void setDefaultCellType(Class type) {
0236:                _defaultType = type;
0237:                if (_defaultValue != null && !type.isInstance(_defaultValue))
0238:                    _defaultValue = null;
0239:            }
0240:
0241:            // The value of uninitialized cells
0242:            private Object _defaultValue = new Integer(0);
0243:
0244:            /**
0245:             * Returns the default value of cells that have not been initialized.
0246:             */
0247:            public Object getDefaultCellValue() {
0248:                return _defaultValue;
0249:            }
0250:
0251:            /**
0252:             * Sets the value returned by cells that have not been initialized.  By default,
0253:             * this value is a java.lang.Integer.ZERO.  When called, if the new value is not
0254:             * an instance of the existing default type, then the default type will be changed
0255:             * to the class of the new value as a side-effect.
0256:             */
0257:            public void setDefaultCellValue(Object value) {
0258:                _defaultValue = value;
0259:                if (!_defaultType.isInstance(value))
0260:                    _defaultType = value.getClass();
0261:            }
0262:
0263:            /**
0264:             * Sets the option that determines if empty cells and newly created cells are
0265:             * editable.
0266:             */
0267:            public void setEditableByDefault(boolean b) {
0268:                _model.setEditableByDefault(b);
0269:            }
0270:
0271:            /**
0272:             * Returns true if empty and newly created cells are editable.
0273:             */
0274:            public boolean isEditableByDefault() {
0275:                return _model.isEditableByDefault();
0276:            }
0277:
0278:            // Flag that controls whether cells are strongly or weakly typed.
0279:            private boolean _strictType;
0280:
0281:            /**
0282:             * Returns true if cells strictly enforce types once a type has been set, or if
0283:             * they allow their types to be changed once set.
0284:             */
0285:
0286:            public boolean isStrictlyTyped() {
0287:                return _strictType;
0288:            }
0289:
0290:            /**
0291:             * Sets the option that controls whether cells will be strongly or weakly typed.
0292:             * Strongly typed cells will not change their types after being set: a new formula
0293:             * must return the same type as the type that the cell has been assigned.  Weakly
0294:             * typed cells can have their types changed at any time.  By default, cells are
0295:             * weakly typed.
0296:             */
0297:
0298:            public void setStrictTyping(boolean b) {
0299:                _strictType = b;
0300:            }
0301:
0302:            // - - - - - - - - - -
0303:            // I/O methods
0304:            // - - - - - - - - - -
0305:
0306:            /**
0307:             */
0308:            public void readSpreadsheet(InputStream is) throws IOException {
0309:                new Reader().readSpreadsheet(is);
0310:            }
0311:
0312:            /**
0313:             */
0314:            public void writeSpreadsheet(OutputStream os) throws IOException {
0315:                new Writer().writeSpreadsheet(os);
0316:            }
0317:
0318:            // - - - - - - - - - -
0319:            // GUI methods
0320:            // - - - - - - - - - -
0321:
0322:            /**
0323:             * Returns the component used as a row header
0324:             */
0325:
0326:            public JComponent getRowHeader() {
0327:                return _rowHeader;
0328:            }
0329:
0330:            /**
0331:             * Sets the functor used by the spreadsheet to display status information.  This
0332:             * allows the spreadsheet's container to (for example) route status information
0333:             * from the spreadsheet to a log file, or to a status bar.
0334:             */
0335:
0336:            public void setStatusHandler(UnaryFunctor<String, ?> fn) {
0337:                _statusFn = fn;
0338:            }
0339:
0340:            /**
0341:             * Updates the spreadsheet's status message.
0342:             */
0343:            public void setStatus(String status) {
0344:                _statusFn.fn(status);
0345:            }
0346:
0347:            // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
0348:            // This is temporary, as it really won't support undo/redo -- need to identify
0349:            // the cell involved and create an UndoableEdit.  I think its going to end up
0350:            // that the Cell class creates the UndoableEdits and sends them to the sheet,
0351:            // which passes them along to any listeners that may register.
0352:            // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
0353:
0354:            private Generator<?> _updateFn;
0355:
0356:            public void setUpdateHandler(Generator<?> fn) {
0357:                _updateFn = fn;
0358:            }
0359:
0360:            private void fireSpreadsheetUpdated() {
0361:                if (_updateFn != null)
0362:                    _updateFn.gen();
0363:            }
0364:
0365:            // - - - - - - - - - -
0366:            // Cell access methods
0367:            // - - - - - - - - - -
0368:
0369:            /**
0370:             * Discards all information about the given cell.
0371:             */
0372:
0373:            public void clearCellAt(int row, int col) {
0374:                Cell cell = getCellIfPresent(row, col);
0375:                if (cell != null) {
0376:                    cell.clear();
0377:                }
0378:            }
0379:
0380:            /**
0381:             * Returns the cell at the given address, creating one if one does not already
0382:             * exist.
0383:             * @throws IndexOutOfBoundsException if the row or column is out of bounds
0384:             */
0385:            public Cell getCellAt(int row, int col) {
0386:                Cell cell = getCellIfPresent(row, col);
0387:                if (cell == null) {
0388:                    cell = _model.setCell(new Cell(this , row, col));
0389:                }
0390:                return cell;
0391:            }
0392:
0393:            /**
0394:             * Returns the cell at the given address if one exists, or null if it does not
0395:             * @throws IndexOutOfBoundsException if the row or column is out of bounds
0396:             */
0397:            Cell getCellIfPresent(int row, int col) {
0398:                return _model.getCellAt(row, col);
0399:            }
0400:
0401:            // There's no universal way to convert a constant value of some arbitrary
0402:            // class into a Generator if the cell is later edited.  That is, suppose
0403:            // we allow the cell to be edited: there's no universal string that we
0404:            // could put into the editor that we'd be able to parse back out.  What
0405:            // we'd need is the expression that the program uses to generate the value,
0406:            // which we can then parse into the correct Generator.
0407:            // For example, suppose a Cell is intended to hold a Color: this method
0408:            // could be called to set the value to Color.RED, but we can't know how to
0409:            // generate the expression "Color.RED" in order for it to be edited.
0410:
0411:            /**
0412:             * Builds a read-only cell to hold the given value.  If you need the cell
0413:             * to be editable
0414:             * @throws IndexOutOfBoundsException if the row or column is out of bounds
0415:             */
0416:            public <T> Cell setCellAt(Class<T> type, T value, int row, int col) {
0417:                return _model.setCellAt(type, new Constant<T>(value), row, col);
0418:            }
0419:
0420:            // TODO: Write a visitor for the generator to create the correct formula (if possible).
0421:            // In the face of user-supplied functors, that may not work (it may require a number
0422:            // of registrations for the user-supplied functor).
0423:
0424:            /**
0425:             * Builds a cell to hold the given value
0426:             * @throws IndexOutOfBoundsException if the row or column is out of bounds
0427:             */
0428:            public <T> Cell setCellAt(Class<T> type, Generator<T> gen, int row,
0429:                    int col) {
0430:                return _model.setCellAt(type, gen, row, col);
0431:            }
0432:
0433:            //     /**
0434:            //      * Builds a read-only cell to hold the given value
0435:            //      * @throws IndexOutOfBoundsException if the row or column is out of bounds
0436:            //      * @throws IllegalArgumentException if the cell has already been set
0437:            //      */
0438:            //     private <T> Cell setCellAt(Class<T> type, Generator<T> gen, int row, int col) {
0439:            //         // TODO: edit existing cells, changing their types as necessary
0440:
0441:            //         // @SuppressWarnings
0442:            //         // the Cell returned by getCellAt is non-generic.
0443:            //         Cell cell = getCellAt(row,col);
0444:            //         if (cell != null) {
0445:            //             throw new IllegalArgumentException(cell+" has already been set");
0446:            //         }
0447:
0448:            //         Point p = new Point(row, col);
0449:            //         cell = new Cell(Spreadsheet.this, type, p, gen);
0450:            //         _model.setCellAt(p, cell);
0451:            //         return cell;
0452:            //     }
0453:
0454:            /**
0455:             * Builds a possibly editable cell to hold the given formula.
0456:             * @throws IndexOutOfBoundsException if the row or column is out of bounds
0457:             */
0458:            public Cell setCellAt(String formula, int row, int col) {
0459:                return _model.setCellAt(formula, row, col, _model
0460:                        .isEditableByDefault());
0461:            }
0462:
0463:            /**
0464:             * Builds a possibly editable cell to hold the given formula.
0465:             * @throws IndexOutOfBoundsException if the row or column is out of bounds
0466:             */
0467:            public Cell setCellAt(String formula, int row, int col,
0468:                    boolean editable) {
0469:                return _model.setCellAt(formula, row, col, editable);
0470:            }
0471:
0472:            //      /**
0473:            //       * Builds a possibly editable cell to hold the given formula
0474:            //       * @throws IndexOutOfBoundsException if the row or column is out of
0475:            //       * bounds
0476:            //       * @throws IllegalArgumentException if the cell has already been set
0477:            //       */
0478:            //      private Cell setCellAt(String formula, int row,int col, boolean editable){
0479:            //          // TODO: edit existing cells, changing their types as necessary
0480:
0481:            //          // @SuppressWarnings
0482:            //          // the Cell returned by getCellAt is non-generic.
0483:            //          Cell cell = getCellAt(row,col);
0484:            //          if (cell != null) {
0485:            //              throw new IllegalArgumentException(cell+" has already been set");
0486:            //          }
0487:
0488:            //          Point p = new Point(row, col);
0489:            //          cell = new Cell(this, p, formula, editable);
0490:            //          _model.setCellAt(p, cell);
0491:            //          return cell;
0492:            //      }
0493:
0494:            /**
0495:             * Replaces the CellRenderer for the given cell such that the given
0496:             * formatting functor is used to present the contents of the cell.
0497:             * NOTE: Use this method cautiously, as passing a formatter of an
0498:             * inappropriate type can lead to a class cast exception or to the
0499:             * Cell reporting '### CLASS ###' condition.
0500:             */
0501:            public <T> void setFormatAt(UnaryFunctor<T, String> formatter,
0502:                    int row, int col) {
0503:                // @SuppressWarnings
0504:                // the Cell returned by getCellAt is non-generic, so there's no check here
0505:                // that the cell is of the proper type T.  The preferred mechanism is to
0506:                // set the format of the cell given a Generic reference.  The Cell class
0507:                // catches ClassCastException when it is evaluated, so while this isn't
0508:                // typesafe, the effects are trapped
0509:                getCellAt(row, col).setFormat(formatter);
0510:            }
0511:
0512:            /**
0513:             * Returns the cell with the given name, or null if no cell has the given name.
0514:             */
0515:            public Cell getCellByName(String name) {
0516:                return _model.getCellByName(name);
0517:            }
0518:
0519:            /**
0520:             * Sets the name of the given cell.
0521:             * @throws IllegalArgumentException if the name is already in use.
0522:             */
0523:            public Cell setCellName(String name, int row, int col) {
0524:                Cell cell = _model.setCellName(name, row, col);
0525:                setStatus(cell.toString());
0526:                return cell;
0527:            }
0528:
0529:            /**
0530:             * Returns a reference to the contents of a given cell
0531:             */
0532:            public <T> Generator<T> getReference(Class<T> type, int row, int col) {
0533:                return _model.getReference(type, row, col);
0534:            }
0535:
0536:            // - - - - - - - - - - - - - -
0537:            // other model wrapper methods
0538:            // - - - - - - - - - - - - - -
0539:
0540:            public void clear() {
0541:                _model.clear();
0542:                setRowSelectionInterval(0, 0);
0543:                setColumnSelectionInterval(0, 0);
0544:            }
0545:
0546:            // - - - - - - - - - - - - - -
0547:            // JTable interface
0548:            // - - - - - - - - - - - - - -
0549:
0550:            public TableCellEditor getCellEditor(int row, int col) {
0551:                Cell cell = getCellIfPresent(row, col);
0552:                if (cell == null) {
0553:                    return super .getCellEditor(row, col);
0554:                }
0555:
0556:                TableCellEditor editor = cell.getEditor();
0557:                return (editor != null) ? editor : super 
0558:                        .getCellEditor(row, col);
0559:            }
0560:
0561:            private TableCellRenderer _defaultRenderer = new Cell.Renderer();
0562:
0563:            public TableCellRenderer getCellRenderer(int row, int col) {
0564:                Cell cell = getCellIfPresent(row, col);
0565:                if (cell == null)
0566:                    return _defaultRenderer;
0567:                //             return super.getCellRenderer(row,col);
0568:
0569:                TableCellRenderer renderer = cell.getRenderer();
0570:                return (renderer != null) ? renderer : _defaultRenderer; //super.getCellRenderer(row,col);
0571:            }
0572:
0573:            public void setModel(TableModel model) {
0574:                // Only true during the constructor: _initialized will be true after the
0575:                // the constructor is finished.  Otherwise, the model must be a
0576:                // SpreadsheetTableModel
0577:                if (!_initialized)
0578:                    super .setModel(model);
0579:                else if (model instanceof  SpreadsheetTableModel) {
0580:                    super .setModel(model);
0581:                    SpreadsheetTableModel oldModel = _model;
0582:                    _model = (SpreadsheetTableModel) model;
0583:                    _rowHeader.setRowCount(_model.getRowCount());
0584:                    doSetColumnCount(oldModel.getColumnCount(), _model
0585:                            .getColumnCount());
0586:                } else {
0587:                    // we _could_ write an adaptor that bridges an arbitrary table model
0588:                    // to a spreadsheet table model.
0589:                    String msg = "Spreadsheet requires SpreadsheetTableModel";
0590:                    throw new IllegalArgumentException(msg);
0591:                }
0592:            }
0593:
0594:            /**
0595:             * In addition to the JTable behaviour of this method (which takes care to add the
0596:             * table's columnheader to an enclosing scrollpane's viewport), add a rowheader to
0597:             * the enclosing scrollpane's viewport as well.
0598:             */
0599:            protected void configureEnclosingScrollPane() {
0600:                super .configureEnclosingScrollPane();
0601:                // The base class logic is reproduced: we only want to set the row header under
0602:                // the same conditions as those in which the column header will be set
0603:                Container p = getParent();
0604:                if (p instanceof  JViewport) {
0605:                    Container gp = p.getParent();
0606:                    if (gp instanceof  JScrollPane) {
0607:                        JScrollPane scrollPane = (JScrollPane) gp;
0608:                        JViewport viewport = scrollPane.getViewport();
0609:                        if (viewport == null || viewport.getView() != this ) {
0610:                            return;
0611:                        }
0612:
0613:                        scrollPane.setRowHeaderView(getRowHeader());
0614:                    }
0615:                }
0616:            }
0617:
0618:            // Overrides the base class to set the status information on selection changes
0619:            public void valueChanged(ListSelectionEvent e) {
0620:                if (!e.getValueIsAdjusting())
0621:                    showSelectionStatus();
0622:
0623:                super .valueChanged(e);
0624:            }
0625:
0626:            // Overrides the base class to set the status information on selection changes
0627:            public void columnSelectionChanged(ListSelectionEvent e) {
0628:                if (!e.getValueIsAdjusting())
0629:                    showSelectionStatus();
0630:
0631:                super .columnSelectionChanged(e);
0632:            }
0633:
0634:            // The last row and column that were written in a status message
0635:            private int _lastRow = -1, _lastCol = -1;
0636:
0637:            // Sends the currently selected cell to the status functor
0638:            private void showSelectionStatus() {
0639:                int row = getSelectedRow();
0640:                int col = getSelectedColumn();
0641:                if (row < 0 || col < 0)
0642:                    return;
0643:
0644:                if (row == _lastRow && col == _lastCol)
0645:                    return;
0646:
0647:                _lastRow = row;
0648:                _lastCol = col;
0649:                Cell cell = getCellIfPresent(row, col);
0650:                if (cell != null)
0651:                    setStatus(cell.toString());
0652:                else
0653:                    setStatus("cell(" + row + "," + col + ")");
0654:            }
0655:
0656:            // ===========================================
0657:            // Spreadsheet Model
0658:            // ===========================================
0659:
0660:            private class SpreadsheetTableModel extends AbstractTableModel
0661:                    implements  Observer {
0662:
0663:                static final long serialVersionUID = -6455541616661139146L;
0664:
0665:                // The contents of the model: a set of cells keyed by their address...
0666:                private Map<Point, Cell> _cellmap = new HashMap<Point, Cell>();
0667:
0668:                // ... and by their names
0669:                private Map<String, Cell> _namemap = new HashMap<String, Cell>();
0670:
0671:                // The number of rows in the model
0672:                private int _numRows;
0673:
0674:                // The number of columns in the model
0675:                private int _numCols;
0676:
0677:                /**
0678:                 * Builds a SpreadsheetTableModel of the default size (16x16)
0679:                 */
0680:                public SpreadsheetTableModel() {
0681:                    this (16, 16);
0682:                }
0683:
0684:                /**
0685:                 * Builds a SpreadsheetTableModel of the given size
0686:                 */
0687:                public SpreadsheetTableModel(int rows, int cols) {
0688:                    _numRows = rows;
0689:                    _numCols = cols;
0690:                }
0691:
0692:                /**
0693:                 * Removes all information from the model
0694:                 */
0695:                public void clear() {
0696:                    _cellmap = new HashMap<Point, Cell>();
0697:                    _namemap = new HashMap<String, Cell>();
0698:                    fireTableDataChanged();
0699:                }
0700:
0701:                /**
0702:                 * Returns the cell at the given address, or null if no such cell
0703:                 * exists.
0704:                 * @throws IndexOutOfBoundsException if the row or column is out of
0705:                 * bounds
0706:                 */
0707:                public Cell getCellAt(int row, int col)
0708:                        throws IndexOutOfBoundsException {
0709:                    if (checkCellAddress(row, col))
0710:                        return _cellmap.get(new Point(row, col));
0711:
0712:                    if (row < 0 || row >= _numRows) {
0713:                        String msg = "Row " + row + " out of range: 0.."
0714:                                + (_numRows - 1);
0715:                        throw new IndexOutOfBoundsException(msg);
0716:                    }
0717:
0718:                    String msg = "Col " + col + " out of range: 0.."
0719:                            + (_numCols - 1);
0720:                    throw new IndexOutOfBoundsException(msg);
0721:                }
0722:
0723:                /**
0724:                 * Builds a read-only cell to hold the given value
0725:                 * @throws IndexOutOfBoundsException if the row or column is out of
0726:                 * bounds
0727:                 * @throws IllegalArgumentException if the cell has already been set
0728:                 */
0729:                private <T> Cell setCellAt(Class<T> type, Generator<T> gen,
0730:                        int row, int col) {
0731:                    // TODO: edit existing cells, changing their types as necessary
0732:
0733:                    // @SuppressWarnings
0734:                    // the Cell returned by  is non-generic.
0735:                    Cell cell = getCellAt(row, col);
0736:                    if (cell != null) {
0737:                        throw new IllegalArgumentException(cell
0738:                                + " has already been set");
0739:                    }
0740:
0741:                    if (gen == null)
0742:                        return null;
0743:
0744:                    return setCell(new Cell(Spreadsheet.this , type, new Point(
0745:                            row, col), gen));
0746:                }
0747:
0748:                /**
0749:                 * Builds a possibly editable cell to hold the given formula
0750:                 * @throws IndexOutOfBoundsException if the row or column is out of
0751:                 * bounds
0752:                 * @throws IllegalArgumentException if the cell has already been set
0753:                 */
0754:                private Cell setCellAt(String formula, int row, int col,
0755:                        boolean editable) {
0756:                    // @SuppressWarnings
0757:                    // the Cell returned by getCellAt is non-generic.
0758:                    Cell cell = getCellAt(row, col);
0759:                    if (cell != null) {
0760:                        throw new IllegalArgumentException(cell
0761:                                + " has already been set");
0762:                    }
0763:
0764:                    if (formula == null || "".equals(formula))
0765:                        return null;
0766:
0767:                    return setCell(new Cell(Spreadsheet.this , new Point(row,
0768:                            col), formula, editable));
0769:                }
0770:
0771:                /**
0772:                 */
0773:                private Cell setCell(Cell cell) {
0774:                    String name = cell.getName();
0775:                    if (name != null) {
0776:                        if (_namemap.get(name) != null) {
0777:                            String err = "Duplicate cell name " + name;
0778:                            throw new IllegalArgumentException(err);
0779:                        }
0780:
0781:                        _namemap.put(name, cell);
0782:                    }
0783:
0784:                    cell.addObserver(this );
0785:
0786:                    Point p = cell.getAddress();
0787:                    _cellmap.put(p, cell);
0788:                    fireTableCellUpdated(p.x, p.y);
0789:                    return cell;
0790:                }
0791:
0792:                /**
0793:                 * Returns a (possibly null) cell whose name is given
0794:                 */
0795:                private Cell getCellByName(String name) {
0796:                    return _namemap.get(name);
0797:                }
0798:
0799:                /**
0800:                 * Sets the name of a cell
0801:                 */
0802:                private Cell setCellName(String name, int row, int col) {
0803:                    if (name != null) {
0804:                        Cell cell = _namemap.get(name);
0805:                        if (cell != null) {
0806:                            String err = "Duplicate cell name " + name;
0807:                            throw new IllegalArgumentException(err);
0808:                        }
0809:                    }
0810:
0811:                    Cell cell = Spreadsheet.this .getCellAt(row, col);
0812:                    String oldname = cell.getName();
0813:                    if (oldname != null)
0814:                        _namemap.remove(oldname);
0815:
0816:                    if (name != null)
0817:                        _namemap.put(name, cell);
0818:
0819:                    cell.setName(name);
0820:
0821:                    // TODO: need to notify the cell's observers that the name has changed.
0822:                    // This may force a change from an Observable/Observer to the creation
0823:                    // of cell events (CellValueChanged, CellNameChanged, CellFormatChanged)
0824:                    return cell;
0825:
0826:                }
0827:
0828:                // NOTE: the Class<T> parm is used by the inferencer to determine the
0829:                // type of the object returned.
0830:
0831:                // NOTE: If editing of cells is implemented by replacing existing cells
0832:                // with new cells, then this method will have to change a little: the
0833:                // reference should be served by the spreadsheet and not the cell, so
0834:                // that the spreadsheet is free to swap cells in and out
0835:
0836:                /**
0837:                 * Returns a reference to the contents of a given cell
0838:                 */
0839:                public <T> Generator<T> getReference(Class<T> type, int row,
0840:                        int col) {
0841:                    Cell cell = getCellAt(row, col);
0842:                    if (cell == null) {
0843:                        String msg = "Cell({0},{1}) is not yet defined";
0844:                        throw new IllegalArgumentException(MessageFormat
0845:                                .format(msg, new Object[] { row, col }));
0846:                    }
0847:                    if (type.isAssignableFrom(cell.getType())) {
0848:
0849:                        // @SuppressWarnings
0850:                        // the Cell returned by getCellAt is non-generic, as is the reference
0851:                        // that it returns here.  The Cell class catches ClassCastException when
0852:                        // it is evaluated, so while this isn't typesafe, the effects are trapped
0853:                        return cell.getReference();
0854:                    }
0855:
0856:                    String err = "Cannot return reference of type {0} from {1}, whose type is {2}";
0857:                    String msg = MessageFormat.format(err, new Object[] { type,
0858:                            cell, cell.getType() });
0859:                    throw new ClassCastException(msg);
0860:                }
0861:
0862:                // implementation of the EditableByDefault property
0863:                private boolean _editableByDefault;
0864:
0865:                private void setEditableByDefault(boolean b) {
0866:                    _editableByDefault = b;
0867:                }
0868:
0869:                private boolean isEditableByDefault() {
0870:                    return _editableByDefault;
0871:                }
0872:
0873:                public void setRowCount(int height) {
0874:                    int oldHeight = _numRows;
0875:                    _numRows = height;
0876:
0877:                    if (oldHeight < height) {
0878:                        fireTableRowsInserted(oldHeight, height - 1);
0879:                    } else {
0880:                        removeCells();
0881:                        fireTableRowsDeleted(height, oldHeight - 1);
0882:                    }
0883:                }
0884:
0885:                public void setColumnCount(int width) {
0886:                    int oldWidth = _numCols;
0887:                    _numCols = width;
0888:                    if (width < oldWidth) {
0889:                        removeCells();
0890:                    }
0891:
0892:                    fireTableStructureChanged();
0893:                }
0894:
0895:                private void removeCells() {
0896:                    for (Iterator iter = _cellmap.values().iterator(); iter
0897:                            .hasNext();) {
0898:                        Cell cell = (Cell) iter.next();
0899:                        Point p = cell.getAddress();
0900:                        if (p.x >= _numRows || p.y >= _numCols) {
0901:                            iter.remove();
0902:
0903:                            String name = cell.getName();
0904:                            if (name != null)
0905:                                _namemap.remove(cell.getName());
0906:
0907:                            cell.unlink();
0908:                        }
0909:                    }
0910:                }
0911:
0912:                // - - - - - - - - - - -
0913:                // TableModel interface
0914:                // - - - - - - - - - - -
0915:
0916:                public int getRowCount() {
0917:                    return _numRows;
0918:                }
0919:
0920:                public int getColumnCount() {
0921:                    return _numCols;
0922:                }
0923:
0924:                public Object getValueAt(int row, int col) {
0925:                    if (!checkCellAddress(row, col))
0926:                        return Cell.REFERENCE_ERR;
0927:
0928:                    Cell cell = _cellmap.get(new Point(row, col));
0929:                    if (cell == null)
0930:                        return null;
0931:
0932:                    Object obj = cell.getValue();
0933:                    return cell.isUndefined() ? "" : cell.isValid() ? obj
0934:                            : cell.getErrorMsg();
0935:                }
0936:
0937:                // NOTE: I hook into this method for undo/redo notifications because only
0938:                // user edits of cell values flows through this method -- programatic updates
0939:                // to cells (including updates caused by changes to prerequisite cells) don't
0940:                // flow through the TableModel interface.  This is temporary: the Cell class
0941:                // is likely to grow support for UndoableEdits, and start a chain that ends
0942:                // up passing them out to the enclosing application
0943:
0944:                public void setValueAt(Object value, int row, int col) {
0945:                    Cell cell = getCellAt(row, col);
0946:                    if (cell != null) {
0947:                        cell.setValue(value);
0948:                    } else if (value != null) {
0949:                        setCellAt(value.toString(), row, col, true);
0950:                    }
0951:
0952:                    /*temp*/fireSpreadsheetUpdated();
0953:                }
0954:
0955:                public boolean isCellEditable(int row, int col) {
0956:                    Cell cell = getCellAt(row, col);
0957:                    if (cell != null)
0958:                        return cell.isEditable();
0959:
0960:                    return _editableByDefault;
0961:                }
0962:
0963:                // - - - - - - - - - - - -
0964:                // Observer Interface
0965:                // - - - - - - - - - - - -
0966:
0967:                public void update(Observable observable, Object object) {
0968:                    Cell cell = (Cell) observable;
0969:                    Point addr = cell.getAddress();
0970:                    fireTableCellUpdated(addr.x, addr.y);
0971:                }
0972:
0973:                // - - - - - - - - - - - -
0974:                // implementation details
0975:                // - - - - - - - - - - - -
0976:
0977:                private boolean checkCellAddress(int row, int col) {
0978:                    return row >= 0 && row < _numRows && col >= 0
0979:                            && col < _numCols;
0980:                }
0981:            }
0982:
0983:            // ===========================================
0984:            // Spreadsheet Parser
0985:            // ===========================================
0986:
0987:            class Parser extends JFXGParser {
0988:                private Cell _crntCell;
0989:
0990:                private Parser() {
0991:                    super ();
0992:                }
0993:
0994:                public Generator parseGenerator(Cell cell, String str)
0995:                        throws ParseException {
0996:                    _crntCell = cell;
0997:                    try {
0998:                        return super .parseGenerator(str);
0999:                    } finally {
1000:                        _crntCell = null;
1001:                    }
1002:
1003:                }
1004:
1005:                /**
1006:                 * Suppresses the binding method: inside a spreadsheet, 'this' always refers to
1007:                 * the spreadsheet itself.
1008:                 */
1009:                public void bindThis(Object this Binding) {
1010:                    super .bindThis(Spreadsheet.this );
1011:                }
1012:
1013:                /**
1014:                 * Overrides the base parser class mechanism for resolving methods, in order to
1015:                 * protect certain methods from abuse.  
1016:                 */
1017:                protected FunctorRef resolveMethodName(FunctorRef prefix,
1018:                        String name, FunctorRef[] args) throws ParseException {
1019:                    // If the prefix is not a constant reference to this spreadsheet, defer to the superclass
1020:                    if (prefix.getReferenceType() != FunctorRef.CONSTANT)
1021:                        return super .resolveMethodName(prefix, name, args);
1022:
1023:                    // It has to be a CONSTANT to get here, so the cast is known safe
1024:                    if (((GeneratorRef) prefix).getFunctor().gen() != Spreadsheet.this )
1025:                        return super .resolveMethodName(prefix, name, args);
1026:
1027:                    // We've established that the prefix is a constant reference to this spreadsheet.
1028:                    return super .resolveMethodName(prefix, name, args);
1029:                }
1030:
1031:                /**
1032:                 * Implements the <i>row</i>, and <i>col</i> keywords.
1033:                 */
1034:                protected FunctorRef reservedWord(String word)
1035:                        throws ParseException {
1036:                    if (word.equals("row")) {
1037:                        return new GeneratorRef(new Constant(_crntCell
1038:                                .getAddress().x), Integer.class);
1039:                    }
1040:
1041:                    if (word.equals("col")) {
1042:                        return new GeneratorRef(new Constant(_crntCell
1043:                                .getAddress().y), Integer.class);
1044:                    }
1045:
1046:                    return super .reservedWord(word);
1047:                }
1048:
1049:                /**
1050:                 * Implements the <i>cell</i>  keyword.
1051:                 */
1052:                protected FunctorRef reservedFunction(String name,
1053:                        FunctorRef[] args) throws ParseException {
1054:                    if (name.equals("cell")) {
1055:                        if (args.length == 1 && args[0] instanceof  GeneratorRef
1056:                                && args[0].getReturnType().equals(String.class)) {
1057:                            String refname = (String) ((GeneratorRef) args[0])
1058:                                    .getFunctor().gen();
1059:                            Cell cell = getCellByName(refname);
1060:                            if (cell == null) {
1061:                                throw new ParseException("Unknown Cell Name: "
1062:                                        + refname);
1063:                            }
1064:
1065:                            return new GeneratorRef(cell.getReference(), cell
1066:                                    .getType());
1067:                        }
1068:
1069:                        if (args.length != 2
1070:                                || !(args[0] instanceof  GeneratorRef)
1071:                                || !(args[1] instanceof  GeneratorRef)
1072:                                || !(args[0].getReturnType()
1073:                                        .equals(Integer.class))
1074:                                || !(args[1].getReturnType()
1075:                                        .equals(Integer.class)))
1076:                            throw new ParseException(
1077:                                    "Cell Reference requires row, col arguments");
1078:
1079:                        int row = ((Integer) ((GeneratorRef) args[0])
1080:                                .getFunctor().gen()).intValue();
1081:                        int col = ((Integer) ((GeneratorRef) args[1])
1082:                                .getFunctor().gen()).intValue();
1083:                        Cell cell = getCellAt(row, col);
1084:                        return new GeneratorRef(cell.getReference(), cell
1085:                                .getType());
1086:                    }
1087:
1088:                    return super .reservedFunction(name, args);
1089:                }
1090:            }
1091:
1092:            // ===========================================
1093:            // SpreadsheetWriter
1094:            // ===========================================
1095:
1096:            public class Writer {
1097:                private TransformerHandler _handler;
1098:                private Transformer _xformer;
1099:                private Set<Cell> _cellsWritten;
1100:
1101:                public Writer() throws IOException {
1102:                    try {
1103:                        SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory
1104:                                .newInstance();
1105:                        _handler = tf.newTransformerHandler();
1106:
1107:                        Transformer xformer = _handler.getTransformer();
1108:                        xformer.setOutputProperty(OutputKeys.ENCODING,
1109:                                "ISO-8859-1");
1110:
1111:                        // TODO: link in the DTD, ideally as a resource loaded from the jar,
1112:                        // I don't want to have to post the DTD on the site, as that will break
1113:                        // code if/when the project is moved.
1114:
1115:                        //                 xformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,"doc/hacksheet.dtd");
1116:                        xformer.setOutputProperty(OutputKeys.INDENT, "yes");
1117:                    } catch (TransformerConfigurationException x) {
1118:                        IOException iox = new IOException(x.getMessage());
1119:                        iox.initCause(x);
1120:                        throw iox;
1121:                    }
1122:                }
1123:
1124:                public void writeSpreadsheet(OutputStream os)
1125:                        throws IOException {
1126:                    try {
1127:                        // Set up all the JAXP overhead
1128:                        StreamResult stream = new StreamResult(os);
1129:                        _handler.setResult(stream);
1130:                        _handler.startDocument();
1131:
1132:                        AttributesImpl atts = new AttributesImpl();
1133:                        int rows = getRowCount();
1134:                        int cols = getColumnCount();
1135:
1136:                        // Start the spreadsheet document
1137:                        //
1138:                        // <hacksheet vers="%d" rows="%d" cols="%d" defaultType="%s" defaultValue="%s">
1139:                        atts.clear();
1140:                        atts.addAttribute("", "", "vers", "", "0.1.0");
1141:                        atts.addAttribute("", "", "rows", "", String
1142:                                .valueOf(rows));
1143:                        atts.addAttribute("", "", "cols", "", String
1144:                                .valueOf(cols));
1145:
1146:                        Class defaultType = getDefaultCellType();
1147:                        if (!defaultType.equals(Integer.class)) {
1148:                            atts.addAttribute("", "", "defaultType", "",
1149:                                    defaultType.getName());
1150:                        }
1151:
1152:                        Object defaultValue = getDefaultCellValue();
1153:                        if (!defaultValue.equals(0)) {
1154:                            atts.addAttribute("", "", "defaultValue", "",
1155:                                    getDefaultCellValue().toString());
1156:                        }
1157:
1158:                        _handler.startElement("", "", "hacksheet", atts);
1159:
1160:                        _cellsWritten = new HashSet<Cell>(_model._cellmap
1161:                                .values().size() * 4 / 3);
1162:                        writeCells(_model._cellmap.values().iterator());
1163:
1164:                        _handler.endElement("", "", "hacksheet");
1165:                        _handler.endDocument();
1166:                    } catch (SAXException x) {
1167:                        IOException iox = new IOException(x.getMessage());
1168:                        iox.initCause(x);
1169:                        throw iox;
1170:                    }
1171:                }
1172:
1173:                private void writeCells(Iterator<Cell> cells)
1174:                        throws SAXException {
1175:                    while (cells.hasNext()) {
1176:                        Cell cell = cells.next();
1177:                        if (!_cellsWritten.contains(cell)) {
1178:                            writeCells(cell.dependsOn());
1179:                            writeCell(cell);
1180:                        }
1181:                    }
1182:                }
1183:
1184:                private void writeCell(Cell cell) throws SAXException {
1185:                    AttributesImpl atts = new AttributesImpl();
1186:
1187:                    String name = cell.getName();
1188:                    if (name != null && name.trim().length() != 0)
1189:                        atts.addAttribute("", "", "id", "", cell.getName());
1190:
1191:                    Point p = cell.getAddress();
1192:                    atts.addAttribute("", "", "row", "", String.valueOf(p.x));
1193:                    atts.addAttribute("", "", "col", "", String.valueOf(p.y));
1194:                    atts.addAttribute("", "", "type", "", cell.getType()
1195:                            .getName());
1196:                    atts.addAttribute("", "", "editable", "", String
1197:                            .valueOf(cell.isEditable()));
1198:
1199:                    String formula = cell.getFormula();
1200:                    _handler.startElement("", "", "cell", atts);
1201:
1202:                    atts.clear();
1203:                    _handler.startElement("", "", "formula", atts);
1204:                    _handler.characters(formula.toCharArray(), 0, formula
1205:                            .length());
1206:                    _handler.endElement("", "", "formula");
1207:                    _handler.endElement("", "", "cell");
1208:
1209:                    _cellsWritten.add(cell);
1210:                }
1211:            }
1212:
1213:            // ===========================================
1214:            // SpreadsheetReader 
1215:            // ===========================================
1216:
1217:            public class Reader extends DefaultHandler {
1218:                StringBuffer buf = new StringBuffer();
1219:
1220:                public void readSpreadsheet(InputStream is) throws IOException {
1221:                    try {
1222:                        createParser().parse(new InputSource(is));
1223:                    } catch (SAXException x) {
1224:                        IOException iox = new IOException(x.getMessage());
1225:                        iox.initCause(x);
1226:                        throw iox;
1227:                    } catch (ParserConfigurationException x) {
1228:                        IOException iox = new IOException(x.getMessage());
1229:                        iox.initCause(x);
1230:                        throw iox;
1231:                    }
1232:                }
1233:
1234:                private Cell _crntCell;
1235:
1236:                public void startElement(String nsURI, String localname,
1237:                        String qname, Attributes attr) throws SAXException {
1238:                    if (qname.equals("hacksheet")) {
1239:                        String vers = attr.getValue("vers");
1240:                        // TODO: write a version check
1241:
1242:                        int rows = Integer.parseInt(attr.getValue("rows"));
1243:                        int cols = Integer.parseInt(attr.getValue("cols"));
1244:                        SpreadsheetTableModel model = new SpreadsheetTableModel(
1245:                                rows, cols);
1246:                        SpreadsheetTableModel oldModel = _model;
1247:                        setModel(model);
1248:                    } else if (qname.equals("cell")) {
1249:                        int row = Integer.parseInt(attr.getValue("row"));
1250:                        int col = Integer.parseInt(attr.getValue("col"));
1251:                        _crntCell = new Cell(Spreadsheet.this , row, col);
1252:                        _crntCell.setEditable(Boolean.valueOf(attr
1253:                                .getValue("editable")));
1254:                        String name = attr.getValue("id");
1255:                        if (name != null) {
1256:                            _crntCell.setName(name);
1257:                        }
1258:                    } else if (qname.equals("formula")) {
1259:                        buf.delete(0, buf.length());
1260:                    } else
1261:                        throw new SAXException("unknown tag \"" + qname + "\"");
1262:                }
1263:
1264:                public void endElement(String nsURI, String localname,
1265:                        String qname) throws SAXException {
1266:                    if (qname.equals("hacksheet")) {
1267:                    } else if (qname.equals("cell")) {
1268:                        _model.setCell(_crntCell);
1269:                    } else if (qname.equals("formula")) {
1270:                        _crntCell.setFormula(buf.toString());
1271:                        buf.delete(0, buf.length());
1272:                    } else
1273:                        throw new SAXException("unknown tag \"" + qname + "\"");
1274:                }
1275:
1276:                public void characters(char[] ch, int start, int ln)
1277:                        throws SAXException {
1278:                    for (int i = 0; i < ln; ++i) {
1279:                        buf.append(ch[start + i]);
1280:                    }
1281:                }
1282:
1283:                public XMLReader createParser() throws SAXException,
1284:                        ParserConfigurationException {
1285:                    SAXParserFactory spf = SAXParserFactory.newInstance();
1286:                    spf.setValidating(false);
1287:
1288:                    SAXParser saxParser = spf.newSAXParser();
1289:                    XMLReader xmlrd = saxParser.getXMLReader();
1290:                    xmlrd.setContentHandler(this );
1291:                    xmlrd.setErrorHandler(this );
1292:                    return xmlrd;
1293:                }
1294:            }
1295:        }
1296:
1297:        // ===========================================
1298:        // RowHeader
1299:        // ===========================================
1300:
1301:        class RowHeader extends JComponent {
1302:            static final long serialVersionUID = -1375303876648436931L;
1303:
1304:            private JTable _table;
1305:            private TableCellRenderer _renderer;
1306:            private JTableHeader _header;
1307:            private CellRendererPane _rendererPane;
1308:            private Font _headerFont;
1309:
1310:            public RowHeader(JTable table) {
1311:                _table = table;
1312:                _header = table.getTableHeader();
1313:                _renderer = _header.getDefaultRenderer();
1314:                _rendererPane = new CellRendererPane();
1315:                add(_rendererPane);
1316:
1317:                Component rendererComponent = _renderer
1318:                        .getTableCellRendererComponent(_table, "0", false,
1319:                                false, 0, -1);
1320:
1321:                _headerFont = rendererComponent.getFont();
1322:
1323:                setFont(_headerFont);
1324:                setBackground(rendererComponent.getBackground());
1325:                setForeground(rendererComponent.getForeground());
1326:                //         setRowCount(table.getRowCount());
1327:            }
1328:
1329:            public TableCellRenderer getRenderer() {
1330:                return _renderer;
1331:            }
1332:
1333:            public void setRenderer(TableCellRenderer renderer) {
1334:                _renderer = renderer;
1335:            }
1336:
1337:            public void setRowCount(int count) {
1338:                resize(getPreferredSize());
1339:            }
1340:
1341:            public Dimension getPreferredSize() {
1342:                Border border = (Border) UIManager.getDefaults().get(
1343:                        "TableHeader.cellBorder");
1344:                Insets insets = border.getBorderInsets(_header);
1345:                FontMetrics metrics = getFontMetrics(_headerFont);
1346:                Dimension dim = new Dimension(metrics.stringWidth("99999")
1347:                        + insets.right + insets.left, _table.getRowHeight()
1348:                        * _table.getRowCount());
1349:                return dim;
1350:            }
1351:
1352:            protected void paintComponent(Graphics g) {
1353:
1354:                // TODO: this is naive: we should take the clipping region into account and
1355:                // only paint the visible rows
1356:
1357:                Rectangle cellRect = new Rectangle(0, 0, getWidth(), _table
1358:                        .getRowHeight(0));
1359:                int rowMargin = _header.getColumnModel().getColumnMargin() - 1;
1360:                for (int i = 0; i < _table.getRowCount(); ++i) {
1361:                    int rowHeight = _table.getRowHeight(i);
1362:                    cellRect.height = rowHeight - rowMargin;
1363:                    paintCell(g, cellRect, i);
1364:                    cellRect.y += rowHeight;
1365:                }
1366:            }
1367:
1368:            private void paintCell(Graphics g, Rectangle cellRect, int rowIndex) {
1369:                Component component = _renderer.getTableCellRendererComponent(
1370:                        _table, rowIndex, false, false, rowIndex, -1);
1371:
1372:                _rendererPane.paintComponent(g, component, this , cellRect.x,
1373:                        cellRect.y, cellRect.width, cellRect.height, true);
1374:            }
1375:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.