1 /*********************************************************************
2 *
3 * Copyright (C) 2001 Andrew Khan
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ***************************************************************************/
19
20 package jxl.write.biff;
21
22 import common.Assert;
23 import common.Logger;
24
25 import jxl.Cell;
26 import jxl.CellFeatures;
27 import jxl.Sheet;
28 import jxl.biff.DataValidation;
29 import jxl.biff.DataValiditySettingsRecord;
30 import jxl.biff.FormattingRecords;
31 import jxl.biff.IntegerHelper;
32 import jxl.biff.NumFormatRecordsException;
33 import jxl.biff.Type;
34 import jxl.biff.WritableRecordData;
35 import jxl.biff.XFRecord;
36 import jxl.biff.drawing.ComboBox;
37 import jxl.biff.drawing.Comment;
38 import jxl.format.CellFormat;
39 import jxl.write.WritableCell;
40 import jxl.write.WritableCellFeatures;
41 import jxl.write.WritableWorkbook;
42
43 /**
44 * Abstract class which stores the common data used for cells, such
45 * as row, column and formatting information.
46 * Any record which directly represents the contents of a cell, such
47 * as labels and numbers, are derived from this class
48 * data store
49 */
50 public abstract class CellValue extends WritableRecordData
51 implements WritableCell
52 {
53 /**
54 * The logger
55 */
56 private static Logger logger = Logger.getLogger(CellValue.class);
57
58 /**
59 * The row in the worksheet at which this cell is located
60 */
61 private int row;
62
63 /**
64 * The column in the worksheet at which this cell is located
65 */
66 private int column;
67
68 /**
69 * The format applied to this cell
70 */
71 private XFRecord format;
72
73 /**
74 * A handle to the formatting records, used in case we want
75 * to change the format of the cell once it has been added
76 * to the spreadsheet
77 */
78 private FormattingRecords formattingRecords;
79
80 /**
81 * A flag to indicate that this record is already referenced within
82 * a worksheet
83 */
84 private boolean referenced;
85
86 /**
87 * A handle to the sheet
88 */
89 private WritableSheetImpl sheet;
90
91 /**
92 * The cell features
93 */
94 private WritableCellFeatures features;
95
96 /**
97 * Internal copied flag, to prevent cell features being added multiple
98 * times to the drawing array
99 */
100 private boolean copied;
101
102 /**
103 * Constructor used when building writable cells from the Java API
104 *
105 * @param c the column
106 * @param t the type indicator
107 * @param r the row
108 */
109 protected CellValue(Type t, int c, int r)
110 {
111 this(t, c, r, WritableWorkbook.NORMAL_STYLE);
112 copied = false;
113 }
114
115 /**
116 * Constructor used when creating a writable cell from a read-only cell
117 * (when copying a workbook)
118 *
119 * @param c the cell to clone
120 * @param t the type of this cell
121 */
122 protected CellValue(Type t, Cell c)
123 {
124 this(t, c.getColumn(), c.getRow());
125 copied = true;
126
127 format = (XFRecord) c.getCellFormat();
128
129 if (c.getCellFeatures() != null)
130 {
131 features = new WritableCellFeatures(c.getCellFeatures());
132 features.setWritableCell(this);
133 }
134 }
135
136 /**
137 * Overloaded constructor used when building writable cells from the
138 * Java API which also takes a format
139 *
140 * @param c the column
141 * @param t the cell type
142 * @param r the row
143 * @param st the format to apply to this cell
144 */
145 protected CellValue(Type t, int c, int r, CellFormat st)
146 {
147 super(t);
148 row = r;
149 column = c;
150 format = (XFRecord) st;
151 referenced = false;
152 copied = false;
153 }
154
155 /**
156 * Copy constructor
157 *
158 * @param c the column
159 * @param t the cell type
160 * @param r the row
161 * @param cv the value to copy
162 */
163 protected CellValue(Type t, int c, int r, CellValue cv)
164 {
165 super(t);
166 row = r;
167 column = c;
168 format = cv.format;
169 referenced = false;
170 copied = false; // used during a deep copy, so the cell features need
171 // to be added again
172
173 if (cv.features != null)
174 {
175 features = new WritableCellFeatures(cv.features);
176 features.setWritableCell(this);
177 }
178 }
179
180 /**
181 * An API function which sets the format to apply to this cell
182 *
183 * @param cf the format to apply to this cell
184 */
185 public void setCellFormat(CellFormat cf)
186 {
187 format = (XFRecord) cf;
188
189 // If the referenced flag has not been set, this cell has not
190 // been added to the spreadsheet, so we don't need to perform
191 // any further logic
192 if (!referenced)
193 {
194 return;
195 }
196
197 // The cell has already been added to the spreadsheet, so the
198 // formattingRecords reference must be initialized
199 Assert.verify(formattingRecords != null);
200
201 addCellFormat();
202 }
203
204 /**
205 * Returns the row number of this cell
206 *
207 * @return the row number of this cell
208 */
209 public int getRow()
210 {
211 return row;
212 }
213
214 /**
215 * Returns the column number of this cell
216 *
217 * @return the column number of this cell
218 */
219 public int getColumn()
220 {
221 return column;
222 }
223
224 /**
225 * Indicates whether or not this cell is hidden, by virtue of either
226 * the entire row or column being collapsed
227 *
228 * @return TRUE if this cell is hidden, FALSE otherwise
229 */
230 public boolean isHidden()
231 {
232 ColumnInfoRecord cir = sheet.getColumnInfo(column);
233
234 if (cir != null && cir.getWidth() == 0)
235 {
236 return true;
237 }
238
239 RowRecord rr = sheet.getRowInfo(row);
240
241 if (rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()))
242 {
243 return true;
244 }
245
246 return false;
247 }
248
249 /**
250 * Gets the data to write to the output file
251 *
252 * @return the binary data
253 */
254 public byte[] getData()
255 {
256 byte[] mydata = new byte[6];
257 IntegerHelper.getTwoBytes(row, mydata, 0);
258 IntegerHelper.getTwoBytes(column, mydata, 2);
259 IntegerHelper.getTwoBytes(format.getXFIndex(), mydata, 4);
260 return mydata;
261 }
262
263 /**
264 * Called when the cell is added to the worksheet in order to indicate
265 * that this object is already added to the worksheet
266 * This method also verifies that the associated formats and formats
267 * have been initialized correctly
268 *
269 * @param fr the formatting records
270 * @param ss the shared strings used within the workbook
271 * @param s the sheet this is being added to
272 */
273 void setCellDetails(FormattingRecords fr, SharedStrings ss,
274 WritableSheetImpl s)
275 {
276 referenced = true;
277 sheet = s;
278 formattingRecords = fr;
279
280 addCellFormat();
281 addCellFeatures();
282 }
283
284 /**
285 * Internal method to see if this cell is referenced within the workbook.
286 * Once this has been placed in the workbook, it becomes immutable
287 *
288 * @return TRUE if this cell has been added to a sheet, FALSE otherwise
289 */
290 final boolean isReferenced()
291 {
292 return referenced;
293 }
294
295 /**
296 * Gets the internal index of the formatting record
297 *
298 * @return the index of the format record
299 */
300 final int getXFIndex()
301 {
302 return format.getXFIndex();
303 }
304
305 /**
306 * API method which gets the format applied to this cell
307 *
308 * @return the format for this cell
309 */
310 public CellFormat getCellFormat()
311 {
312 return format;
313 }
314
315 /**
316 * Increments the row of this cell by one. Invoked by the sheet when
317 * inserting rows
318 */
319 void incrementRow()
320 {
321 row++;
322
323 if (features != null)
324 {
325 Comment c = features.getCommentDrawing();
326 if (c != null)
327 {
328 c.setX(column);
329 c.setY(row);
330 }
331 }
332 }
333
334 /**
335 * Decrements the row of this cell by one. Invoked by the sheet when
336 * removing rows
337 */
338 void decrementRow()
339 {
340 row--;
341
342 if (features != null)
343 {
344 Comment c = features.getCommentDrawing();
345 if ( c!= null)
346 {
347 c.setX(column);
348 c.setY(row);
349 }
350
351 if (features.hasDropDown())
352 {
353 logger.warn("need to change value for drop down drawing");
354 }
355 }
356 }
357
358 /**
359 * Increments the column of this cell by one. Invoked by the sheet when
360 * inserting columns
361 */
362 void incrementColumn()
363 {
364 column++;
365
366 if (features != null)
367 {
368 Comment c = features.getCommentDrawing();
369 if (c != null)
370 {
371 c.setX(column);
372 c.setY(row);
373 }
374 }
375
376 }
377
378 /**
379 * Decrements the column of this cell by one. Invoked by the sheet when
380 * removing columns
381 */
382 void decrementColumn()
383 {
384 column--;
385
386 if (features != null)
387 {
388 Comment c = features.getCommentDrawing();
389 if (c != null)
390 {
391 c.setX(column);
392 c.setY(row);
393 }
394 }
395
396 }
397
398 /**
399 * Called when a column is inserted on the specified sheet. Notifies all
400 * RCIR cells of this change. The default implementation here does nothing
401 *
402 * @param s the sheet on which the column was inserted
403 * @param sheetIndex the sheet index on which the column was inserted
404 * @param col the column number which was inserted
405 */
406 void columnInserted(Sheet s, int sheetIndex, int col)
407 {
408 }
409
410 /**
411 * Called when a column is removed on the specified sheet. Notifies all
412 * RCIR cells of this change. The default implementation here does nothing
413 *
414 * @param s the sheet on which the column was inserted
415 * @param sheetIndex the sheet index on which the column was inserted
416 * @param col the column number which was inserted
417 */
418 void columnRemoved(Sheet s, int sheetIndex, int col)
419 {
420 }
421
422 /**
423 * Called when a row is inserted on the specified sheet. Notifies all
424 * RCIR cells of this change. The default implementation here does nothing
425 *
426 * @param s the sheet on which the column was inserted
427 * @param sheetIndex the sheet index on which the column was inserted
428 * @param row the column number which was inserted
429 */
430 void rowInserted(Sheet s, int sheetIndex, int row)
431 {
432 }
433
434 /**
435 * Called when a row is inserted on the specified sheet. Notifies all
436 * RCIR cells of this change. The default implementation here does nothing
437 *
438 * @param s the sheet on which the row was removed
439 * @param sheetIndex the sheet index on which the column was removed
440 * @param row the column number which was removed
441 */
442 void rowRemoved(Sheet s, int sheetIndex, int row)
443 {
444 }
445
446 /**
447 * Accessor for the sheet containing this cell
448 *
449 * @return the sheet containing this cell
450 */
451 protected WritableSheetImpl getSheet()
452 {
453 return sheet;
454 }
455
456 /**
457 * Adds the format information to the shared records. Performs the necessary
458 * checks (and clones) to ensure that the formats are not shared.
459 * Called from setCellDetails and setCellFormat
460 */
461 private void addCellFormat()
462 {
463 // Check to see if the format is one of the shared Workbook defaults. If
464 // so, then get hold of the Workbook's specific instance
465 Styles styles = sheet.getWorkbook().getStyles();
466 format = styles.getFormat(format);
467
468 try
469 {
470 if (!format.isInitialized())
471 {
472 formattingRecords.addStyle(format);
473 }
474 }
475 catch (NumFormatRecordsException e)
476 {
477 logger.warn("Maximum number of format records exceeded. Using " +
478 "default format.");
479 format = styles.getNormalStyle();
480 }
481 }
482
483 /**
484 * Accessor for the cell features
485 *
486 * @return the cell features or NULL if this cell doesn't have any
487 */
488 public CellFeatures getCellFeatures()
489 {
490 return features;
491 }
492
493 /**
494 * Accessor for the cell features
495 *
496 * @return the cell features or NULL if this cell doesn't have any
497 */
498 public WritableCellFeatures getWritableCellFeatures()
499 {
500 return features;
501 }
502
503 /**
504 * Sets the cell features
505 *
506 * @param cf the cell features
507 */
508 public void setCellFeatures(WritableCellFeatures cf)
509 {
510 if (features != null)
511 {
512 logger.warn("current cell features not null - overwriting");
513 }
514
515 features = cf;
516 cf.setWritableCell(this);
517
518 // If the cell is already on the worksheet, then add the cell features
519 // to the workbook
520 if (referenced)
521 {
522 addCellFeatures();
523 }
524 }
525
526 /**
527 * Handles any addition cell features, such as comments or data
528 * validation. Called internally from this class when a cell is
529 * added to the workbook, and also externally from BaseCellFeatures
530 * following a call to setComment
531 */
532 public final void addCellFeatures()
533 {
534 if (features == null)
535 {
536 return;
537 }
538
539 if (copied == true)
540 {
541 copied = false;
542
543 /*
544 if (features.hasDataValidation())
545 {
546 DataValidation dv = sheet.getDataValidation();
547 DataValiditySettingsRecord dvsr =
548 dv.getDataValiditySettings(column, row);
549 features.setValidationSettings(dvsr);
550 }
551 else
552 {
553 // For comments, make sure we go the whole nine yards when
554 // the comment gets added to the sheet
555 copied = false;
556 }
557 */
558
559 return;
560 }
561
562 if (features.getComment() != null)
563 {
564 Comment comment = new Comment(features.getComment(),
565 column, row);
566 comment.setWidth(features.getCommentWidth());
567 comment.setHeight(features.getCommentHeight());
568 sheet.addDrawing(comment);
569 sheet.getWorkbook().addDrawing(comment);
570 features.setCommentDrawing(comment);
571 }
572
573 if (features.hasDataValidation())
574 {
575 try
576 {
577 features.getDVParser().setCell(column,
578 row,
579 sheet.getWorkbook(),
580 sheet.getWorkbook(),
581 sheet.getWorkbookSettings());
582 }
583 catch (jxl.biff.formula.FormulaException e)
584 {
585 e.printStackTrace();
586 Assert.verify(false);
587 }
588 sheet.addValidationCell(this);
589 if (!features.hasDropDown())
590 {
591 return;
592 }
593
594 // Get the combo box drawing object for list validations
595 if (sheet.getComboBox() == null)
596 {
597 ComboBox cb = new ComboBox();
598 sheet.addDrawing(cb);
599 sheet.getWorkbook().addDrawing(cb);
600 sheet.setComboBox(cb);
601 }
602
603 features.setComboBox(sheet.getComboBox());
604 }
605 }
606
607 /**
608 * Removes the cell features from the Workbook/Worksheet. Called when
609 * a cell is being removed/replaced from a worksheet
610 */
611 public final void removeCellFeatures()
612 {
613 if (features == null)
614 {
615 return;
616 }
617
618 // Remove the comment
619 features.removeComment();
620
621 // Remove the data validation
622 features.removeDataValidation();
623 }
624
625
626 /**
627 * Called by the cell features to remove a comment
628 *
629 * @param c the comment to remove
630 */
631 public final void removeComment(Comment c)
632 {
633 sheet.removeDrawing(c);
634 }
635
636 /**
637 * Called by the cell features to remove the data validation
638 */
639 public final void removeDataValidation()
640 {
641 sheet.removeDataValidation(this);
642 }
643
644 /**
645 * Called when doing a copy of a writable object to indicate the source
646 * was writable than a read only copy and certain things (most notably
647 * the comments will need to be re-evaluated)
648 *
649 * @param boolean the copied flag
650 */
651 final void setCopied(boolean c)
652 {
653 copied = c;
654 }
655
656 }