001: /*
002: * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>.
003: *
004: * This file is part of
005: * CleanSheets - a spreadsheet application for the Java platform.
006: *
007: * CleanSheets is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU General Public License as published by
009: * the Free Software Foundation; either version 2 of the License, or
010: * (at your option) any later version.
011: *
012: * CleanSheets is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with CleanSheets; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package csheets.core;
022:
023: import java.io.IOException;
024: import java.io.ObjectInputStream;
025: import java.io.ObjectOutputStream;
026: import java.util.ArrayList;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.SortedSet;
032: import java.util.TreeSet;
033:
034: import csheets.core.formula.compiler.FormulaCompilationException;
035: import csheets.ext.Extension;
036: import csheets.ext.ExtensionManager;
037: import csheets.ext.SpreadsheetExtension;
038:
039: /**
040: * The implementation of the <code>Spreadsheet</code> interface.
041: * @author Einar Pehrson
042: */
043: public class SpreadsheetImpl implements Spreadsheet {
044:
045: /** The unique version identifier used for serialization */
046: private static final long serialVersionUID = 7010464744129096272L;
047:
048: /** The base of the titles of new spreadsheets */
049: public static final String BASE_TITLE = "Sheet ";
050:
051: /** The workbook to which the spreadsheet belongs */
052: private Workbook workbook;
053:
054: /** The cells that have been instantiated */
055: private SortedSet<Cell> cells = new TreeSet<Cell>();
056:
057: /** The title of the spreadsheet */
058: private String title;
059:
060: /** The number of columns in the spreadsheet */
061: private int columns = 0;
062:
063: /** The number of rows in the spreadsheet */
064: private int rows = 0;
065:
066: /** The cell listeners that have been registered on the cell */
067: private transient List<CellListener> cellListeners = new ArrayList<CellListener>();
068:
069: /** The cell listener that forwards events from all cells */
070: private transient CellListener eventForwarder = new EventForwarder();
071:
072: /** The spreadsheet extensions that have been instantiated */
073: private transient Map<String, SpreadsheetExtension> extensions = new HashMap<String, SpreadsheetExtension>();
074:
075: /**
076: * Creates a new spreadsheet.
077: * @param workbook the workbook to which the spreadsheet belongs
078: * @param title the title of the spreadsheet
079: */
080: SpreadsheetImpl(Workbook workbook, String title) {
081: this .workbook = workbook;
082: this .title = title;
083: }
084:
085: /**
086: * Creates a new spreadsheet, in which cells are initialized with data from
087: * the given content matrix.
088: * @param workbook the workbook to which the spreadsheet belongs
089: * @param title the title of the spreadsheet
090: * @param content the contents of the cells in the spreadsheet
091: */
092: SpreadsheetImpl(Workbook workbook, String title, String[][] content) {
093: this (workbook, title);
094: rows = content.length;
095: for (int row = 0; row < content.length; row++) {
096: int columns = content[row].length;
097: if (this .columns < columns)
098: this .columns = columns;
099: for (int column = 0; column < columns; column++) {
100: try {
101: Cell cell = new CellImpl(this , new Address(column,
102: row), content[row][column]);
103: cell.addCellListener(eventForwarder);
104: cells.add(cell);
105: } catch (FormulaCompilationException e) {
106: }
107: }
108: }
109: }
110:
111: /*
112: * LOCATION
113: */
114:
115: public Workbook getWorkbook() {
116: return workbook;
117: }
118:
119: public String getTitle() {
120: return title;
121: }
122:
123: public void setTitle(String title) {
124: this .title = title;
125: // fireTitleChanged();
126: }
127:
128: /*
129: * DIMENSIONS
130: */
131:
132: public int getColumnCount() {
133: return columns;
134: }
135:
136: public int getRowCount() {
137: return rows;
138: }
139:
140: /*
141: * CELLS
142: */
143:
144: public Cell getCell(Address address) {
145: // Updates spreadsheet dimensions
146: if (address.getRow() > rows)
147: rows = address.getRow();
148: if (address.getColumn() > columns)
149: columns = address.getColumn();
150:
151: // Looks for a previously used cell with this address
152: for (Cell cell : cells)
153: if (address.equals(cell.getAddress()))
154: return cell;
155:
156: // If the cell has never been requested, create a new one
157: Cell cell = new CellImpl(this , address);
158: cell.addCellListener(eventForwarder);
159: cells.add(cell);
160: return cell;
161: }
162:
163: public Cell getCell(int column, int row) {
164: return getCell(new Address(column, row));
165: }
166:
167: public SortedSet<Cell> getCells(Address address1, Address address2) {
168: // Sorts addresses
169: if (address1.compareTo(address2) > 0) {
170: Address tempAddress = address1;
171: address1 = address2;
172: address2 = tempAddress;
173: }
174:
175: // Builds the set
176: SortedSet<Cell> cells = new TreeSet<Cell>();
177: for (int column = address1.getColumn(); column <= address2
178: .getColumn(); column++)
179: for (int row = address1.getRow(); row <= address2.getRow(); row++)
180: cells.add(getCell(new Address(column, row)));
181:
182: return cells;
183: }
184:
185: public Cell[] getColumn(int index) {
186: Cell[] column = new Cell[rows];
187: for (int row = 0; row < row; row++)
188: column[row] = getCell(new Address(index, row));
189: return column;
190: }
191:
192: public Cell[] getRow(int index) {
193: Cell[] row = new Cell[columns];
194: for (int column = 0; column < columns; column++)
195: row[column] = getCell(new Address(column, index));
196: return row;
197: }
198:
199: public Iterator<Cell> iterator() {
200: return cells.iterator();
201: }
202:
203: /*
204: * EVENT HANDLING
205: */
206:
207: public void addCellListener(CellListener listener) {
208: cellListeners.add(listener);
209: }
210:
211: public void removeCellListener(CellListener listener) {
212: cellListeners.remove(listener);
213: }
214:
215: public CellListener[] getCellListeners() {
216: return cellListeners.toArray(new CellListener[cellListeners
217: .size()]);
218: }
219:
220: /**
221: * A cell listener that forwards events from all cells to registered listeners.
222: */
223: private class EventForwarder implements CellListener {
224:
225: /**
226: * Creates a new event forwarder.
227: */
228: public EventForwarder() {
229: }
230:
231: public void valueChanged(Cell cell) {
232: for (CellListener listener : cellListeners)
233: listener.valueChanged(cell);
234: }
235:
236: public void contentChanged(Cell cell) {
237: for (CellListener listener : cellListeners)
238: listener.contentChanged(cell);
239: }
240:
241: public void dependentsChanged(Cell cell) {
242: for (CellListener listener : cellListeners)
243: listener.dependentsChanged(cell);
244: }
245:
246: public void cellCleared(Cell cell) {
247: for (CellListener listener : cellListeners)
248: listener.cellCleared(cell);
249: }
250:
251: public void cellCopied(Cell cell, Cell source) {
252: for (CellListener listener : cellListeners)
253: listener.cellCopied(cell, source);
254: }
255: }
256:
257: /*
258: * EXTENSIONS
259: */
260:
261: public Spreadsheet getExtension(String name) {
262: // Looks for an existing spreadsheet extension
263: SpreadsheetExtension extension = extensions.get(name);
264: if (extension == null) {
265: // Creates a new spreadsheet extension
266: Extension x = ExtensionManager.getInstance().getExtension(
267: name);
268: if (x != null) {
269: extension = x.extend(this );
270: if (extension != null)
271: extensions.put(name, extension);
272: }
273: }
274: return extension;
275: }
276:
277: /*
278: * GENERAL
279: */
280:
281: /**
282: * Customizes deserialization by catching exceptions when extensions
283: * are not found.
284: * @param stream the object input stream from which the object is to be read
285: * @throws IOException If any of the usual Input/Output related exceptions occur
286: * @throws ClassNotFoundException If the class of a serialized object cannot be found.
287: */
288: private void readObject(ObjectInputStream stream)
289: throws IOException, ClassNotFoundException {
290: stream.defaultReadObject();
291:
292: // Sets up event forwarder
293: eventForwarder = new EventForwarder();
294: for (Cell cell : cells)
295: cell.addCellListener(eventForwarder);
296: cellListeners = new ArrayList<CellListener>();
297:
298: // Reads extensions
299: extensions = new HashMap<String, SpreadsheetExtension>();
300: int extCount = stream.readInt();
301: for (int i = 0; i < extCount; i++) {
302: try {
303: SpreadsheetExtension extension = (SpreadsheetExtension) stream
304: .readObject();
305: extensions.put(extension.getName(), extension);
306: } catch (ClassNotFoundException e) {
307: System.err.println(e);
308: }
309: }
310: }
311:
312: /**
313: * Customizes serialization, by writing extensions separately.
314: * @param stream the object output stream to which the object is to be written
315: * @throws IOException If any of the usual Input/Output related exceptions occur
316: */
317: private void writeObject(ObjectOutputStream stream)
318: throws IOException {
319: stream.defaultWriteObject();
320:
321: // Writes extensions
322: stream.writeInt(extensions.size());
323: for (SpreadsheetExtension extension : extensions.values())
324: stream.writeObject(extension);
325: }
326: }
|