KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jxl > write > biff > WritableSheetImpl


1 /*********************************************************************
2 *
3 * Copyright (C) 2002 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 java.io.IOException JavaDoc;
23 import java.util.TreeSet JavaDoc;
24 import java.util.Comparator JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.ArrayList JavaDoc;
27
28 import common.Assert;
29 import common.Logger;
30
31 import jxl.Cell;
32 import jxl.LabelCell;
33 import jxl.NumberCell;
34 import jxl.CellType;
35 import jxl.Sheet;
36 import jxl.LabelCell;
37 import jxl.DateCell;
38 import jxl.BooleanCell;
39 import jxl.Hyperlink;
40 import jxl.Range;
41 import jxl.SheetSettings;
42 import jxl.WorkbookSettings;
43 import jxl.CellView;
44 import jxl.Image;
45 import jxl.HeaderFooter;
46 import jxl.format.CellFormat;
47 import jxl.format.Border;
48 import jxl.format.BorderLineStyle;
49 import jxl.format.PageOrientation;
50 import jxl.format.PaperSize;
51 import jxl.write.Blank;
52 import jxl.write.WritableWorkbook;
53 import jxl.write.WritableSheet;
54 import jxl.write.WritableCell;
55 import jxl.write.Label;
56 import jxl.write.Number;
57 import jxl.write.DateTime;
58 import jxl.write.WriteException;
59 import jxl.write.Boolean;
60 import jxl.write.WritableHyperlink;
61 import jxl.write.WritableImage;
62
63 import jxl.biff.IntegerHelper;
64 import jxl.biff.WritableRecordData;
65 import jxl.biff.FormattingRecords;
66 import jxl.biff.EmptyCell;
67 import jxl.biff.XFRecord;
68 import jxl.biff.NumFormatRecordsException;
69 import jxl.biff.FormulaData;
70 import jxl.biff.SheetRangeImpl;
71 import jxl.biff.IndexMapping;
72 import jxl.biff.WorkspaceInformationRecord;
73
74 import jxl.biff.drawing.Chart;
75 import jxl.biff.drawing.DrawingGroupObject;
76 import jxl.biff.drawing.Drawing;
77
78 /**
79  * A writable sheet. This class contains implementation of all the
80  * writable sheet methods which may be invoke by the API
81  */

82 class WritableSheetImpl implements WritableSheet
83 {
84   /**
85    * The logger
86    */

87   private static Logger logger = Logger.getLogger(WritableSheetImpl.class);
88     
89   /**
90    * The name of this sheet
91    */

92   private String JavaDoc name;
93   /**
94    * A handle to the output file which the binary data is written to
95    */

96   private File outputFile;
97   /**
98    * The rows within this sheet
99    */

100   private RowRecord[] rows;
101   /**
102    * A handle to workbook format records
103    */

104   private FormattingRecords formatRecords;
105   /**
106    * A handle to the shared strings used by this workbook
107    */

108   private SharedStrings sharedStrings;
109
110   /**
111    * The list of non-default column formats
112    */

113   private TreeSet JavaDoc columnFormats;
114
115   /**
116    * The list of hyperlinks
117    */

118   private ArrayList JavaDoc hyperlinks;
119
120   /**
121    * The list of merged ranged
122    */

123   private MergedCells mergedCells;
124
125   /**
126    * A number of rows. This is a count of the maximum row number + 1
127    */

128   private int numRows;
129
130   /**
131    * The number of columns. This is a count of the maximum column number + 1
132    */

133   private int numColumns;
134
135   /**
136    * The environment specific print record, copied from the read spreadsheet
137    */

138   private PLSRecord plsRecord;
139
140   /**
141    * The buttons property set
142    */

143   private ButtonPropertySetRecord buttonPropertySet;
144
145   /**
146    * A flag indicating that this sheet is a chart only
147    */

148   private boolean chartOnly;
149
150   /**
151    * The data validations on this page
152    */

153   private DataValidation dataValidation;
154
155   /**
156    * Array of row page breaks
157    */

158   private ArrayList JavaDoc rowBreaks;
159
160   /**
161    * The drawings on this sheet
162    */

163   private ArrayList JavaDoc drawings;
164
165   /**
166    * The images on this sheet. This is a subset of the drawings list
167    */

168   private ArrayList JavaDoc images;
169
170   /**
171    * Drawings modified flag. Set to true if the drawings list has
172    * been modified
173    */

174   private boolean drawingsModified;
175
176   /**
177    * The settings for this sheet
178    */

179   private SheetSettings settings;
180
181   /**
182    * The sheet writer engine
183    */

184   private SheetWriter sheetWriter;
185
186   /**
187    * The settings for the workbook
188    */

189   private WorkbookSettings workbookSettings;
190
191   /**
192    * The workbook
193    */

194   private WritableWorkbookImpl workbook;
195
196   /**
197    * The amount by which to grow the rows array
198    */

199   private final static int rowGrowSize = 10;
200
201   /**
202    * The maximum number of rows excel allows in a worksheet
203    */

204   private final static int numRowsPerSheet = 65536;
205
206   /**
207    * The maximum number of characters permissible for a sheet name
208    */

209   private final static int maxSheetNameLength = 31;
210
211   /**
212    * The illegal characters for a sheet name
213    */

214   private final static char[] illegalSheetNameCharacters =
215     new char[] {'*', ':', '?', '\\'};
216
217   /**
218    * The supported file types
219    */

220   private static final String JavaDoc[] imageTypes = new String JavaDoc[] {"png"};
221
222   /**
223    * The comparator for column info record
224    */

225   private static class ColumnInfoComparator implements Comparator JavaDoc
226   {
227     /**
228      * Equals method
229      *
230      * @param o the object to compare
231      * @return TRUE if equal, FALSE otherwise
232      */

233     public boolean equals(Object JavaDoc o)
234     {
235       return o == this;
236     }
237
238     /**
239      * Comparison function for to ColumnInfoRecords
240      *
241      * @param o2 first object to compare
242      * @param o1 second object to compare
243      * @return the result of the comparison
244      */

245     public int compare(Object JavaDoc o1, Object JavaDoc o2)
246     {
247       if (o1 == o2)
248       {
249         return 0;
250       }
251
252       Assert.verify(o1 instanceof ColumnInfoRecord);
253       Assert.verify(o2 instanceof ColumnInfoRecord);
254       
255       ColumnInfoRecord ci1 = (ColumnInfoRecord) o1;
256       ColumnInfoRecord ci2 = (ColumnInfoRecord) o2;
257
258       return ci1.getColumn() - ci2.getColumn();
259     }
260   }
261
262   /**
263    * Constructor
264    *
265    * @param fr the formatting records used by the workbook
266    * @param of the output file to write the binary data
267    * @param f the fonts used by the workbook
268    * @param n the name of this sheet
269    * @param ss the shared strings used by the workbook
270    * @param ws the workbook settings
271    */

272   public WritableSheetImpl(String JavaDoc n,
273                            File of,
274                            FormattingRecords fr,
275                            SharedStrings ss,
276                            WorkbookSettings ws,
277                            WritableWorkbookImpl ww)
278   {
279     name = validateName(n);
280     outputFile = of;
281     rows = new RowRecord[0];
282     numRows = 0;
283     numColumns = 0;
284     chartOnly = false;
285     workbook = ww;
286
287     formatRecords = fr;
288     sharedStrings = ss;
289     workbookSettings = ws;
290     drawingsModified = false;
291     columnFormats = new TreeSet JavaDoc(new ColumnInfoComparator());
292     hyperlinks = new ArrayList JavaDoc();
293     mergedCells = new MergedCells(this);
294     rowBreaks = new ArrayList JavaDoc();
295     drawings = new ArrayList JavaDoc();
296     images = new ArrayList JavaDoc();
297     settings = new SheetSettings();
298
299
300     sheetWriter = new SheetWriter(outputFile,
301                                   this,
302                                   workbookSettings);
303   }
304
305   /**
306    * Returns the cell specified at this row and at this column
307    *
308    * @param column the column number
309    * @param row the row number
310    * @return the cell at the specified co-ordinates
311    */

312   public Cell getCell(int column, int row)
313   {
314     return getWritableCell(column, row);
315   }
316
317   /**
318    * Returns the cell specified at this row and at this column
319    *
320    * @param column the column number
321    * @param row the row number
322    * @return the cell at the specified co-ordinates
323    */

324   public WritableCell getWritableCell(int column, int row)
325   {
326     WritableCell c = null;
327
328     if (row < rows.length && rows[row] != null)
329     {
330       c = rows[row].getCell(column);
331     }
332
333     if (c == null)
334     {
335       c = new EmptyCell(column, row);
336     }
337     
338     return c;
339   }
340
341   /**
342    * Returns the number of rows in this sheet
343    *
344    * @return the number of rows in this sheet
345    */

346   public int getRows()
347   {
348     return numRows;
349   }
350
351   /**
352    * Returns the number of columns in this sheet
353    *
354    * @return the number of columns in this sheet
355    */

356   public int getColumns()
357   {
358     return numColumns;
359   }
360
361   /**
362    * Gets the cell whose contents match the string passed in.
363    * If no match is found, then null is returned. The search is performed
364    * on a row by row basis, so the lower the row number, the more
365    * efficiently the algorithm will perform
366    *
367    * @param contents the string to match
368    * @return the Cell whose contents match the parameter, null if not found
369    */

370   public Cell findCell(String JavaDoc contents)
371   {
372     Cell cell = null;
373     boolean found = false;
374     
375     for (int i = 0 ; i < getRows() && found == false; i++)
376     {
377       Cell[] row = getRow(i);
378       for (int j = 0 ; j < row.length && found == false; j++)
379       {
380         if (row[j].getContents().equals(contents))
381         {
382           cell = row[j];
383           found = true;
384         }
385       }
386     }
387
388     return cell;
389   }
390
391   /**
392    * Gets the cell whose contents match the string passed in.
393    * If no match is found, then null is returned. The search is performed
394    * on a row by row basis, so the lower the row number, the more
395    * efficiently the algorithm will perform. This method differs
396    * from the findCell methods in that only cells with labels are
397    * queried - all numerical cells are ignored. This should therefore
398    * improve performance.
399    *
400    * @param contents the string to match
401    * @return the Cell whose contents match the paramter, null if not found
402    */

403   public LabelCell findLabelCell(String JavaDoc contents)
404   {
405     LabelCell cell = null;
406     boolean found = false;
407     
408     for (int i = 0 ; i < getRows() && found == false; i++)
409     {
410       Cell[] row = getRow(i);
411       for (int j = 0 ; j < row.length && found == false; j++)
412       {
413         if ( ( row[j].getType() == CellType.LABEL ||
414                row[j].getType() == CellType.STRING_FORMULA) &&
415             row[j].getContents().equals(contents))
416         {
417           cell = (LabelCell) row[j];
418           found = true;
419         }
420       }
421     }
422
423     return cell;
424   }
425
426   /**
427    * Gets all the cells on the specified row
428    *
429    * @param row the rows whose cells are to be returned
430    * @return the cells on the given row
431    */

432   public Cell[] getRow(int row)
433   {
434     // Find the last non-null cell
435
boolean found = false;
436     int col = numColumns - 1;
437     while (col >= 0 && !found)
438     {
439       if (getCell(col, row).getType() != CellType.EMPTY)
440       {
441         found = true;
442       }
443       else
444       {
445         col--;
446       }
447     }
448
449     // Only create entries for non-empty cells
450
Cell[] cells = new Cell[col+1];
451
452     for (int i = 0; i <= col; i++)
453     {
454       cells[i] = getCell(i, row);
455     }
456     return cells;
457   }
458
459   /**
460    * Gets all the cells on the specified column
461    *
462    * @param col the column whose cells are to be returned
463    * @return the cells on the specified column
464    */

465   public Cell[] getColumn(int col)
466   {
467     // Find the last non-null cell
468
boolean found = false;
469     int row = numRows - 1;
470
471     while (row >= 0 && !found)
472     {
473       if (getCell(col, row).getType() != CellType.EMPTY)
474       {
475         found = true;
476       }
477       else
478       {
479         row--;
480       }
481     }
482
483     // Only create entries for non-empty cells
484
Cell[] cells = new Cell[row+1];
485
486     for (int i = 0; i <= row; i++)
487     {
488       cells[i] = getCell(col, i);
489     }
490     return cells;
491   }
492
493   /**
494    * Gets the name of this sheet
495    *
496    * @return the name of the sheet
497    */

498   public String JavaDoc getName()
499   {
500     return name;
501   }
502
503   /**
504    * Inserts a blank row into this spreadsheet. If the row is out of range
505    * of the rows in the sheet, then no action is taken
506    *
507    * @param row the row to insert
508    */

509   public void insertRow(int row)
510   {
511     if (row < 0 || row >= numRows)
512     {
513       return;
514     }
515
516     // Create a new array to hold the new rows. Grow it if need be
517
RowRecord[] oldRows = rows;
518
519     if (numRows == rows.length)
520     {
521       rows = new RowRecord[oldRows.length + rowGrowSize];
522     }
523     else
524     {
525       rows = new RowRecord[oldRows.length];
526     }
527
528     // Copy in everything up to the new row
529
System.arraycopy(oldRows, 0, rows, 0, row);
530     
531     // Copy in the remaining rows
532
System.arraycopy(oldRows, row, rows, row+1, numRows - row);
533
534     // Increment all the internal row number by one
535
for (int i = row+1; i <= numRows; i++)
536     {
537       if (rows[i] != null)
538       {
539         rows[i].incrementRow();
540       }
541     }
542
543     // Adjust any hyperlinks
544
HyperlinkRecord hr = null;
545     Iterator JavaDoc i = hyperlinks.iterator();
546     while (i.hasNext())
547     {
548       hr = (HyperlinkRecord) i.next();
549       hr.insertRow(row);
550     }
551
552     // Adjust any data validations
553
if (dataValidation != null)
554     {
555       dataValidation.insertRow(row);
556     }
557
558     // Adjust any merged cells
559
mergedCells.insertRow(row);
560
561     // Adjust any page breaks
562
ArrayList JavaDoc newRowBreaks = new ArrayList JavaDoc();
563     Iterator JavaDoc ri = rowBreaks.iterator();
564     while (ri.hasNext())
565     {
566       int val = ( (Integer JavaDoc) ri.next()).intValue();
567       if (val >= row)
568       {
569         val++;
570       }
571
572       newRowBreaks.add(new Integer JavaDoc(val));
573     }
574     rowBreaks = newRowBreaks;
575
576     // Handle interested cell references on the main workbook
577
if (workbookSettings.getFormulaAdjust())
578     {
579       workbook.rowInserted(this, row);
580     }
581
582     // Adjust the maximum row record
583
numRows++;
584   }
585
586   /**
587    * Inserts a blank column into this spreadsheet. If the column is out of
588    * range of the columns in the sheet, then no action is taken
589    *
590    * @param col the column to insert
591    */

592   public void insertColumn(int col)
593   {
594     if (col < 0 || col >= numColumns)
595     {
596       return;
597     }
598
599     // Iterate through all the row records adding in the column
600
for (int i = 0 ; i < numRows ; i++)
601     {
602       if (rows[i] != null)
603       {
604         rows[i].insertColumn(col);
605       }
606     }
607
608     // Adjust any hyperlinks
609
HyperlinkRecord hr = null;
610     Iterator JavaDoc i = hyperlinks.iterator();
611     while (i.hasNext())
612     {
613       hr = (HyperlinkRecord) i.next();
614       hr.insertColumn(col);
615     }
616
617     // Iterate through the column views, incrementing the column number
618
i = columnFormats.iterator();
619     while (i.hasNext())
620     {
621       ColumnInfoRecord cir = (ColumnInfoRecord) i.next();
622
623       if (cir.getColumn() >= col)
624       {
625         cir.incrementColumn();
626       }
627     }
628
629     // Handle any data validations
630
if (dataValidation != null)
631     {
632       dataValidation.insertColumn(col);
633     }
634
635     // Adjust any merged cells
636
mergedCells.insertColumn(col);
637
638     // Handle interested cell references on the main workbook
639
if (workbookSettings.getFormulaAdjust())
640     {
641       workbook.columnInserted(this, col);
642     }
643
644     numColumns++;
645   }
646
647   /**
648    * Removes a column from this spreadsheet. If the column is out of range
649    * of the columns in the sheet, then no action is taken
650    *
651    * @param col the column to remove
652    */

653   public void removeColumn(int col)
654   {
655     if (col < 0 || col >= numColumns)
656     {
657       return;
658     }
659
660     // Iterate through all the row records removing the column
661
for (int i = 0 ; i < numRows ; i++)
662     {
663       if (rows[i] != null)
664       {
665         rows[i].removeColumn(col);
666       }
667     }
668
669     // Adjust any hyperlinks
670
HyperlinkRecord hr = null;
671     Iterator JavaDoc i = hyperlinks.iterator();
672     while (i.hasNext())
673     {
674       hr = (HyperlinkRecord) i.next();
675
676       if (hr.getColumn() == col &&
677           hr.getLastColumn() == col)
678       {
679         // The row with the hyperlink on has been removed, so get
680
// rid of it from the list
681
hyperlinks.remove(hyperlinks.indexOf(hr));
682       }
683       else
684       {
685         hr.removeColumn(col);
686       }
687     }
688
689     // Adjust any data validations
690
if (dataValidation != null)
691     {
692       dataValidation.removeColumn(col);
693     }
694
695     // Adjust any merged cells
696
mergedCells.removeColumn(col);
697
698     // Iterate through the column views, decrementing the column number
699
i = columnFormats.iterator();
700     ColumnInfoRecord removeColumn = null;
701     while (i.hasNext())
702     {
703       ColumnInfoRecord cir = (ColumnInfoRecord) i.next();
704
705       if (cir.getColumn() == col)
706       {
707         removeColumn = cir;
708       }
709       else if (cir.getColumn() > col)
710       {
711         cir.decrementColumn();
712       }
713     }
714
715     if (removeColumn != null)
716     {
717       columnFormats.remove(removeColumn);
718     }
719
720     // Handle interested cell references on the main workbook
721
if (workbookSettings.getFormulaAdjust())
722     {
723       workbook.columnRemoved(this, col);
724     }
725
726     numColumns--;
727   }
728
729   /**
730    * Removes a row from this spreadsheet. If the row is out of
731    * range of the columns in the sheet, then no action is taken
732    *
733    * @param row the row to remove
734    */

735   public void removeRow(int row)
736   {
737     if (row < 0 || row >= numRows)
738     {
739       return;
740     }
741
742     // Create a new array to hold the new rows. Grow it if need be
743
RowRecord[] oldRows = rows;
744
745     rows = new RowRecord[oldRows.length];
746
747     // Copy in everything up to the row to be removed
748
System.arraycopy(oldRows, 0, rows, 0, row);
749     
750     // Copy in the remaining rows
751
System.arraycopy(oldRows, row + 1, rows, row, numRows - (row + 1));
752
753     // Decrement all the internal row numbers by one
754
for (int i = row; i < numRows; i++)
755     {
756       if (rows[i] != null)
757       {
758         rows[i].decrementRow();
759       }
760     }
761
762     // Adjust any hyperlinks
763
HyperlinkRecord hr = null;
764     Iterator JavaDoc i = hyperlinks.iterator();
765     while (i.hasNext())
766     {
767       hr = (HyperlinkRecord) i.next();
768
769       if (hr.getRow() == row &&
770           hr.getLastRow() == row)
771       {
772         // The row with the hyperlink on has been removed, so get
773
// rid of it from the list
774
i.remove();
775       }
776       else
777       {
778         hr.removeRow(row);
779       }
780     }
781
782     // Adjust any data validations
783
if (dataValidation != null)
784     {
785       dataValidation.removeRow(row);
786     }
787
788     // Adjust any merged cells
789
mergedCells.removeRow(row);
790
791     // Adjust any page breaks
792
ArrayList JavaDoc newRowBreaks = new ArrayList JavaDoc();
793     Iterator JavaDoc ri = rowBreaks.iterator();
794     while (ri.hasNext())
795     {
796       int val = ( (Integer JavaDoc) ri.next()).intValue();
797
798       if (val != row)
799       {
800         if (val > row)
801         {
802           val--;
803         }
804         
805         newRowBreaks.add(new Integer JavaDoc(val));
806       }
807     }
808
809     rowBreaks = newRowBreaks;
810
811     // Handle interested cell references on the main workbook
812
if (workbookSettings.getFormulaAdjust())
813     {
814       workbook.rowRemoved(this, row);
815     }
816
817     // Adjust the maximum row record
818
numRows--;
819   }
820
821   /**
822    * Adds the cell to this sheet. If the cell has already been added to
823    * this sheet or another sheet, a WriteException is thrown. If the
824    * position to be occupied by this cell is already taken, the incumbent
825    * cell is replaced.
826    * The cell is then marked as referenced, and its formatting information
827    * registered with the list of formatting records updated if necessary
828    * The RowsExceededException may be caught if client code wishes to
829    * explicitly trap the case where too many rows have been written
830    * to the current sheet. If this behaviour is not desired, it is
831    * sufficient simply to handle the WriteException, since this is a base
832    * class of RowsExceededException
833    *
834    * @exception WriteException
835    * @exception RowsExceededException
836    * @param cell the cell to add
837    */

838   public void addCell(WritableCell cell)
839     throws WriteException, RowsExceededException
840   {
841     if (cell.getType() == CellType.EMPTY)
842     {
843       if (cell != null && cell.getCellFormat() == null)
844       {
845         // return if it's a blank cell with no particular cell formatting
846
// information
847
return;
848       }
849     }
850     
851     CellValue cv = (CellValue) cell;
852
853     if (cv.isReferenced())
854     {
855       throw new JxlWriteException(JxlWriteException.cellReferenced);
856     }
857
858     int row = cell.getRow();
859     RowRecord rowrec = getRowRecord(row);
860     rowrec.addCell(cv);
861
862     // Adjust the max rows and max columns accordingly
863
numRows = Math.max(row+1, numRows);
864     numColumns = Math.max(numColumns, rowrec.getMaxColumn());
865
866     // Indicate this cell is now part of a worksheet, so that it can't be
867
// added anywhere else
868
cv.setCellDetails(formatRecords, sharedStrings, this);
869   }
870
871   /**
872    * Gets the row record at the specified row number, growing the
873    * array as needs dictate
874    *
875    * @param row the row number we are interested in
876    * @return the row record at the specified row
877    * @exception RowsExceededException
878    */

879   private RowRecord getRowRecord(int row) throws RowsExceededException
880   {
881     if (row >= numRowsPerSheet)
882     {
883       throw new RowsExceededException();
884     }
885
886     // Grow the array of rows if needs be
887
// Thanks to Brendan for spotting the flaw in merely adding on the
888
// grow size
889
if (row >= rows.length)
890     {
891       RowRecord[] oldRows = rows;
892       rows = new RowRecord[Math.max(oldRows.length + rowGrowSize, row+1)];
893       System.arraycopy(oldRows, 0, rows, 0, oldRows.length);
894       oldRows = null;
895     }
896
897     RowRecord rowrec = rows[row];
898
899     if (rowrec == null)
900     {
901       rowrec = new RowRecord(row);
902       rows[row] = rowrec;
903     }
904
905     return rowrec;
906   }
907
908   /**
909    * Gets the row record for the specified row
910    *
911    * @param r the row
912    * @return the row record
913    */

914   RowRecord getRowInfo(int r)
915   {
916     if (r < 0 || r > rows.length)
917     {
918       return null;
919     }
920
921     return rows[r];
922   }
923
924   /**
925    * Gets the column info record for the specified column
926    *
927    * @param c the column
928    * @return the column record
929    */

930   ColumnInfoRecord getColumnInfo(int c)
931   {
932     Iterator JavaDoc i = columnFormats.iterator();
933     ColumnInfoRecord cir = null;
934     boolean stop = false;
935
936     while (i.hasNext() && !stop)
937     {
938       cir = (ColumnInfoRecord) i.next();
939
940       if (cir.getColumn() >= c)
941       {
942         stop = true;
943       }
944     }
945
946     if (!stop)
947     {
948       return null;
949     }
950
951     return cir.getColumn() == c ? cir : null;
952   }
953
954   /**
955    * Sets the name of this worksheet
956    *
957    * @param n the name of this sheet
958    */

959   public void setName(String JavaDoc n)
960   {
961     name = n;
962   }
963
964   /**
965    * Sets the hidden status of this sheet
966    *
967    * @param h the hiden flag
968    * @deprecated use the settings bean instead
969    */

970   public void setHidden(boolean h)
971   {
972     settings.setHidden(h);
973   }
974
975   /**
976    * Indicates whether or not this sheet is protected
977    *
978    * @param prot protected flag
979    * @deprecated use the settings bean instead
980    */

981   public void setProtected(boolean prot)
982   {
983     settings.setProtected(prot);
984   }
985
986   /**
987    * Sets this sheet as selected
988    * @deprecated use the settings bean
989    */

990   public void setSelected()
991   {
992     settings.setSelected();
993   }
994   
995   /**
996    * Retrieves the hidden status of this sheet
997    *
998    * @return TRUE if hidden, FALSE otherwise
999    * @deprecated in favour of the getSettings() method
1000   */

1001  public boolean isHidden()
1002  {
1003    return settings.isHidden();
1004  }
1005
1006  /**
1007   * Sets the width (in characters) for a particular column in this sheet
1008   *
1009   * @param col the column whose width to set
1010   * @param width the width of the column in characters
1011   */

1012  public void setColumnView(int col, int width)
1013  {
1014    CellView cv = new CellView();
1015    cv.setSize(width * 256);
1016    setColumnView(col, cv);
1017  }
1018
1019  /**
1020   * Sets the width (in characters) and format options for a
1021   * particular column in this sheet
1022   *
1023   * @param col the column to set
1024   * @param width the width in characters
1025   * @param format the formt details for the column
1026   */

1027  public void setColumnView(int col, int width, CellFormat format)
1028  {
1029    CellView cv = new CellView();
1030    cv.setSize(width * 256);
1031    cv.setFormat(format);
1032    setColumnView(col, cv);
1033  }
1034
1035  /**
1036   * Sets the view for this column
1037   *
1038   * @param col the column on which to set the view
1039   * @param view the view to set
1040   */

1041  public void setColumnView(int col, CellView view)
1042  {
1043    XFRecord xfr = (XFRecord) view.getFormat();
1044    if (xfr == null)
1045    {
1046      Styles styles = getWorkbook().getStyles();
1047      xfr = (XFRecord) styles.getNormalStyle();
1048    }
1049
1050    try
1051    {
1052      if (!xfr.isInitialized())
1053      {
1054        formatRecords.addStyle(xfr);
1055      }
1056      
1057      int width = view.depUsed() ? view.getDimension() * 256 : view.getSize();
1058
1059      ColumnInfoRecord cir = new ColumnInfoRecord(col,
1060                                                  width,
1061                                                  xfr);
1062
1063      if (view.isHidden())
1064      {
1065        cir.setHidden(true);
1066      }
1067
1068      if (!columnFormats.contains(cir))
1069      {
1070        columnFormats.add(cir);
1071      }
1072      else
1073      {
1074        boolean removed = columnFormats.remove(cir);
1075        columnFormats.add(cir);
1076      }
1077    }
1078    catch (NumFormatRecordsException e)
1079    {
1080      logger.warn("Maximum number of format records exceeded. Using " +
1081                  "default format.");
1082
1083      ColumnInfoRecord cir = new ColumnInfoRecord
1084        (col, view.getDimension()*256,
1085         (XFRecord) WritableWorkbook.NORMAL_STYLE);
1086      if (!columnFormats.contains(cir))
1087      {
1088        columnFormats.add(cir);
1089      }
1090    }
1091  }
1092
1093
1094  /**
1095   * Sets the height of the specified row, as well as its collapse status
1096   *
1097   * @param row the row to be formatted
1098   * @param height the row height in points
1099   * @exception RowsExceededException
1100   */

1101  public void setRowView(int row, int height) throws RowsExceededException
1102  {
1103    setRowView(row, height, false);
1104  }
1105
1106  /**
1107   * Sets the height of the specified row, as well as its collapse status
1108   *
1109   * @param row the row to be formatted
1110   * @param collapsed indicates whether the row is collapsed
1111   * @exception jxl.write.biff.RowsExceededException
1112   */

1113  public void setRowView(int row, boolean collapsed)
1114    throws RowsExceededException
1115  {
1116    RowRecord rowrec = getRowRecord(row);
1117    rowrec.setCollapsed(collapsed);
1118  }
1119
1120  /**
1121   * Sets the height of the specified row, as well as its collapse status
1122   *
1123   * @param row the row to be formatted
1124   * @param height the row height in points
1125   * @param collapsed indicates whether the row is collapsed
1126   * @param zeroHeight indicates that the row has zero height
1127   * @exception RowsExceededException
1128   */

1129  public void setRowView(int row, int height,
1130                         boolean collapsed)
1131                         throws RowsExceededException
1132  {
1133    RowRecord rowrec = getRowRecord(row);
1134    rowrec.setRowHeight(height);
1135    rowrec.setCollapsed(collapsed);
1136  }
1137
1138  /**
1139   * Writes out this sheet. This functionality is delegated off to the
1140   * SheetWriter class in order to reduce the bloated nature of this source
1141   * file
1142   *
1143   * @exception IOException
1144   */

1145  public void write() throws IOException JavaDoc
1146  {
1147    boolean dmod = drawingsModified;
1148    if (workbook.getDrawingGroup() != null)
1149    {
1150      dmod |= workbook.getDrawingGroup().hasDrawingsOmitted();
1151    }
1152
1153    sheetWriter.setWriteData(rows,
1154                             rowBreaks,
1155                             hyperlinks,
1156                             mergedCells,
1157                             columnFormats );
1158    sheetWriter.setDimensions(getRows(), getColumns());
1159    sheetWriter.setSettings(settings);
1160    sheetWriter.setPLS(plsRecord);
1161    sheetWriter.setDrawings(drawings, dmod);
1162    sheetWriter.setButtonPropertySet(buttonPropertySet);
1163    sheetWriter.setDataValidation(dataValidation);
1164    
1165    sheetWriter.write();
1166  }
1167
1168  /**
1169   * Copies the cell contents from the specified sheet into this one
1170   *
1171   * @param s the sheet to copy
1172   */

1173  private void copyCells(Sheet s)
1174  {
1175    // Copy the cells
1176
int cells = s.getRows();
1177    Cell[] row = null;
1178    Cell cell = null;
1179    for (int i = 0; i < cells; i++)
1180    {
1181      row = s.getRow(i);
1182
1183      for (int j = 0; j < row.length; j++)
1184      {
1185        cell = row[j];
1186        CellType ct = cell.getType();
1187
1188        // Encase the calls to addCell in a try-catch block
1189
// These should not generate any errors, because we are
1190
// copying from an existing spreadsheet. In the event of
1191
// errors, catch the exception and then bomb out with an
1192
// assertion
1193
try
1194        {
1195          if (ct == CellType.LABEL)
1196          {
1197            Label l = new Label((LabelCell) cell);
1198            addCell(l);
1199          }
1200          else if (ct == CellType.NUMBER)
1201          {
1202            Number JavaDoc n = new Number JavaDoc((NumberCell) cell);
1203            addCell(n);
1204          }
1205          else if (ct == CellType.DATE)
1206          {
1207            DateTime dt = new DateTime((DateCell) cell);
1208            addCell(dt);
1209          }
1210          else if (ct == CellType.BOOLEAN)
1211          {
1212            Boolean JavaDoc b = new Boolean JavaDoc((BooleanCell) cell);
1213            addCell(b);
1214          }
1215          else if (ct == CellType.NUMBER_FORMULA)
1216          {
1217            ReadNumberFormulaRecord fr = new ReadNumberFormulaRecord
1218              ((FormulaData) cell);
1219            addCell(fr);
1220          }
1221          else if (ct == CellType.STRING_FORMULA)
1222          {
1223            ReadStringFormulaRecord fr = new ReadStringFormulaRecord
1224              ((FormulaData) cell);
1225            addCell(fr);
1226          }
1227          else if( ct == CellType.BOOLEAN_FORMULA)
1228          {
1229            ReadBooleanFormulaRecord fr = new ReadBooleanFormulaRecord
1230              ((FormulaData) cell);
1231            addCell(fr);
1232          }
1233          else if (ct == CellType.DATE_FORMULA)
1234          {
1235            ReadDateFormulaRecord fr = new ReadDateFormulaRecord
1236              ((FormulaData) cell);
1237            addCell(fr);
1238          }
1239          else if(ct == CellType.FORMULA_ERROR)
1240          {
1241            ReadErrorFormulaRecord fr = new ReadErrorFormulaRecord
1242              ((FormulaData) cell);
1243            addCell(fr);
1244          }
1245          else if (ct == CellType.EMPTY)
1246          {
1247            if (cell.getCellFormat() != null)
1248            {
1249              // It is a blank cell, rather than an empty cell, so
1250
// it may have formatting information, so
1251
// it must be copied
1252
Blank b = new Blank(cell);
1253              addCell(b);
1254            }
1255          }
1256        }
1257        catch (WriteException e)
1258        {
1259          Assert.verify(false);
1260        }
1261      }
1262    }
1263  }
1264
1265  /**
1266   * Copies the specified sheet, row by row and cell by cell
1267   *
1268   * @param s the sheet to copy
1269   */

1270  void copy(Sheet s)
1271  {
1272    // Copy the settings
1273
settings = new SheetSettings(s.getSettings());
1274
1275    copyCells(s);
1276
1277    // Copy the column info records
1278
jxl.read.biff.SheetImpl si = (jxl.read.biff.SheetImpl) s;
1279    jxl.read.biff.ColumnInfoRecord[] readCirs = si.getColumnInfos();
1280
1281    for (int i = 0 ; i < readCirs.length; i++)
1282    {
1283      jxl.read.biff.ColumnInfoRecord rcir = readCirs[i];
1284      for (int j = rcir.getStartColumn(); j <= rcir.getEndColumn() ; j++)
1285      {
1286        ColumnInfoRecord cir = new ColumnInfoRecord(rcir, j,
1287                                                    formatRecords);
1288        cir.setHidden(rcir.getHidden());
1289        columnFormats.add(cir);
1290      }
1291    }
1292
1293    // Copy the hyperlinks
1294
Hyperlink[] hls = s.getHyperlinks();
1295    for (int i = 0 ; i < hls.length; i++)
1296    {
1297      WritableHyperlink hr = new WritableHyperlink
1298        (hls[i], this);
1299      hyperlinks.add(hr);
1300    }
1301
1302    // Copy the merged cells
1303
Range[] merged = s.getMergedCells();
1304
1305    for (int i = 0; i < merged.length; i++)
1306    {
1307      mergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], this));
1308    }
1309
1310    // Copy the row properties
1311
try
1312    {
1313      jxl.read.biff.RowRecord[] rowprops = si.getRowProperties();
1314
1315      for (int i = 0; i < rowprops.length; i++)
1316      {
1317        RowRecord rr = getRowRecord(rowprops[i].getRowNumber());
1318        XFRecord format = rowprops[i].hasDefaultFormat() ?
1319          formatRecords.getXFRecord(rowprops[i].getXFIndex()) : null;
1320        rr.setRowDetails(rowprops[i].getRowHeight(),
1321                         rowprops[i].matchesDefaultFontHeight(),
1322                         rowprops[i].isCollapsed(),
1323                         format);
1324      }
1325    }
1326    catch (RowsExceededException e)
1327    {
1328      // Handle the rows exceeded exception - this cannot occur since
1329
// the sheet we are copying from will have a valid number of rows
1330
Assert.verify(false);
1331    }
1332
1333    // Copy the headers and footers
1334
// sheetWriter.setHeader(new HeaderRecord(si.getHeader()));
1335
// sheetWriter.setFooter(new FooterRecord(si.getFooter()));
1336

1337    // Copy the page breaks
1338
int[] rowbreaks = si.getRowPageBreaks();
1339
1340    if (rowbreaks != null)
1341    {
1342      for (int i = 0; i < rowbreaks.length; i++)
1343      {
1344        rowBreaks.add(new Integer JavaDoc(rowbreaks[i]));
1345      }
1346    }
1347
1348    // Copy the data validations
1349
jxl.read.biff.DataValidation rdv = si.getDataValidation();
1350    if (rdv != null)
1351    {
1352      dataValidation = new DataValidation(rdv, workbook, workbookSettings);
1353    }
1354
1355    // Copy the charts
1356
sheetWriter.setCharts(si.getCharts());
1357
1358    // Copy the drawings
1359
DrawingGroupObject[] dr = si.getDrawings();
1360    for (int i = 0 ; i < dr.length ; i++)
1361    {
1362      if (dr[i] instanceof jxl.biff.drawing.Drawing)
1363      {
1364        WritableImage wi = new WritableImage(dr[i],
1365                                             workbook.getDrawingGroup());
1366        drawings.add(wi);
1367        images.add(wi);
1368      }
1369      else if (dr[i] instanceof jxl.biff.drawing.Comment)
1370      {
1371        jxl.biff.drawing.Comment c =
1372          new jxl.biff.drawing.Comment(dr[i],
1373                                       workbook.getDrawingGroup(),
1374                                       workbookSettings);
1375        drawings.add(c);
1376        
1377        // Set up the reference on the cell value
1378
CellValue cv = (CellValue) getWritableCell(c.getColumn(), c.getRow());
1379        Assert.verify(cv.getCellFeatures() != null);
1380        cv.getWritableCellFeatures().setCommentDrawing(c);
1381      }
1382      else if (dr[i] instanceof jxl.biff.drawing.Button)
1383      {
1384        jxl.biff.drawing.Button b =
1385          new jxl.biff.drawing.Button(dr[i],
1386                                      workbook.getDrawingGroup(),
1387                                      workbookSettings);
1388        drawings.add(b);
1389      }
1390
1391    }
1392
1393    // Copy the workspace options
1394
sheetWriter.setWorkspaceOptions(si.getWorkspaceOptions());
1395
1396    // Set a flag to indicate if it contains a chart only
1397
if (si.getSheetBof().isChart())
1398    {
1399      chartOnly = true;
1400      sheetWriter.setChartOnly();
1401    }
1402
1403    // Copy the environment specific print record
1404
if (si.getPLS() != null)
1405    {
1406      if (si.getWorkbookBof().isBiff7())
1407      {
1408        logger.warn("Cannot copy Biff7 print settings record - ignoring");
1409      }
1410      else
1411      {
1412        plsRecord = new PLSRecord(si.getPLS());
1413      }
1414    }
1415
1416    // Copy the button property set
1417
if (si.getButtonPropertySet() != null)
1418    {
1419      buttonPropertySet = new ButtonPropertySetRecord
1420        (si.getButtonPropertySet());
1421    }
1422  }
1423
1424  /**
1425   * Copies the specified sheet, row by row and cell by cell
1426   *
1427   * @param s the sheet to copy
1428   */

1429  void copy(WritableSheet s)
1430  {
1431    settings = new SheetSettings(s.getSettings());
1432
1433    copyCells(s);
1434
1435    // Copy the column formats
1436
columnFormats = ( (WritableSheetImpl) s).columnFormats;
1437
1438    // Copy the merged cells
1439
Range[] merged = s.getMergedCells();
1440
1441    for (int i = 0; i < merged.length; i++)
1442    {
1443      mergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], this));
1444    }
1445
1446    // Copy the row properties
1447
try
1448    {
1449      RowRecord[] copyRows = ( (WritableSheetImpl) s).rows;
1450      RowRecord row = null;
1451      for (int i = 0; i < copyRows.length ; i++)
1452      {
1453        row = copyRows[i];
1454        
1455        if (row != null &&
1456            (!row.isDefaultHeight() ||
1457             row.isCollapsed()))
1458        {
1459          RowRecord rr = getRowRecord(i);
1460          rr.setRowDetails(row.getRowHeight(),
1461                           row.matchesDefaultFontHeight(),
1462                           row.isCollapsed(),
1463                           row.getStyle());
1464        }
1465      }
1466    }
1467    catch (RowsExceededException e)
1468    {
1469      // Handle the rows exceeded exception - this cannot occur since
1470
// the sheet we are copying from will have a valid number of rows
1471
Assert.verify(false);
1472    }
1473
1474    // Copy the headers and footers
1475
WritableSheetImpl si = (WritableSheetImpl) s;
1476
1477    // sheetWriter.setHeader(si.getHeader());
1478
// sheetWriter.setFooter(si.getFooter());
1479

1480    // Copy the horizontal page breaks
1481
rowBreaks = new ArrayList JavaDoc(si.rowBreaks);
1482
1483    // Copy the data validations
1484
DataValidation rdv = si.dataValidation;
1485    if (rdv != null)
1486    {
1487      dataValidation = new DataValidation(rdv,
1488                                          workbook,
1489                                          workbookSettings);
1490    }
1491
1492    // Copy the charts
1493
sheetWriter.setCharts(si.getCharts());
1494
1495    // Copy the drawings
1496
DrawingGroupObject[] dr = si.getDrawings();
1497    for (int i = 0 ; i < dr.length ; i++)
1498    {
1499      if (dr[i] instanceof jxl.biff.drawing.Drawing)
1500      {
1501        WritableImage wi = new WritableImage(dr[i],
1502                                             workbook.getDrawingGroup());
1503        drawings.add(wi);
1504        images.add(wi);
1505      }
1506      else if (dr[i] instanceof jxl.biff.drawing.Comment)
1507      {
1508        jxl.biff.drawing.Comment c = new jxl.biff.drawing.Comment
1509                                     (dr[i],
1510                                      workbook.getDrawingGroup(),
1511                                      workbookSettings);
1512        drawings.add(c);
1513      }
1514    }
1515
1516    // Copy the workspace options
1517
sheetWriter.setWorkspaceOptions(si.getWorkspaceOptions());
1518
1519    // Copy the environment specific print record
1520
if (si.plsRecord != null)
1521    {
1522      plsRecord = new PLSRecord(si.plsRecord);
1523    }
1524
1525    // Copy the button property set
1526
if (si.buttonPropertySet != null)
1527    {
1528      buttonPropertySet = new ButtonPropertySetRecord(si.buttonPropertySet);
1529    }
1530  }
1531
1532  /**
1533   * Gets the header. Called when copying sheets
1534   *
1535   * @return the page header
1536   */

1537  final HeaderRecord getHeader()
1538  {
1539    return sheetWriter.getHeader();
1540  }
1541
1542  /**
1543   * Gets the footer. Called when copying sheets
1544   *
1545   * @return the page footer
1546   */

1547  final FooterRecord getFooter()
1548  {
1549    return sheetWriter.getFooter();
1550  }
1551  /**
1552   * Determines whether the sheet is protected
1553   *
1554   * @return whether or not the sheet is protected
1555   * @deprecated in favour of getSettings() api
1556   */

1557  public boolean isProtected()
1558  {
1559    return settings.isProtected();
1560  }
1561
1562  /**
1563   * Gets the hyperlinks on this sheet
1564   *
1565   * @return an array of hyperlinks
1566   */

1567  public Hyperlink[] getHyperlinks()
1568  {
1569    Hyperlink[] hl = new Hyperlink[hyperlinks.size()];
1570
1571    for (int i = 0; i < hyperlinks.size(); i++)
1572    {
1573      hl[i] = (Hyperlink) hyperlinks.get(i);
1574    }
1575
1576    return hl;
1577  }
1578
1579  /**
1580   * Gets the cells which have been merged on this sheet
1581   *
1582   * @return an array of range objects
1583   */

1584  public Range[] getMergedCells()
1585  {
1586    return mergedCells.getMergedCells();
1587  }
1588
1589  /**
1590   * Gets the writable hyperlinks on this sheet
1591   *
1592   * @return an array of hyperlinks
1593   */

1594  public WritableHyperlink[] getWritableHyperlinks()
1595  {
1596    WritableHyperlink[] hl = new WritableHyperlink[hyperlinks.size()];
1597
1598    for (int i = 0; i < hyperlinks.size(); i++)
1599    {
1600      hl[i] = (WritableHyperlink) hyperlinks.get(i);
1601    }
1602
1603    return hl;
1604  }
1605
1606  /**
1607   * Removes the specified hyperlink. Note that if you merely set the
1608   * cell contents to be an Empty cell, then the cells containing the
1609   * hyperlink will still be active. The contents of the cell which
1610   * activate the hyperlink are removed.
1611   * The hyperlink passed in must be a hyperlink retrieved using the
1612   * getHyperlinks method
1613   *
1614   * @param h the hyperlink to remove.
1615   * @param preserveLabel if TRUE preserves the label contents, if FALSE
1616   * removes them
1617   */

1618  public void removeHyperlink(WritableHyperlink h)
1619  {
1620    removeHyperlink(h, false);
1621  }
1622
1623  /**
1624   * Removes the specified hyperlink. Note that if you merely set the
1625   * cell contents to be an Empty cell, then the cells containing the
1626   * hyperlink will still be active.
1627   * If the preserveLabel field is set, the cell contents of the
1628   * hyperlink are preserved, although the hyperlink is deactivated. If
1629   * this value is FALSE, the cell contents are removed
1630   * The hyperlink passed in must be a hyperlink retrieved using the
1631   * getHyperlinks method
1632   *
1633   * @param h the hyperlink to remove.
1634   * @param preserveLabel if TRUE preserves the label contents, if FALSE
1635   * removes them
1636   */

1637  public void removeHyperlink(WritableHyperlink h, boolean preserveLabel)
1638  {
1639    // Remove the hyperlink
1640
hyperlinks.remove(hyperlinks.indexOf(h));
1641
1642    if (!preserveLabel)
1643    {
1644      // Set the cell contents for the hyperlink - including any formatting
1645
// information - to be empty
1646
Assert.verify(rows.length > h.getRow() && rows[h.getRow()] != null);
1647      rows[h.getRow()].removeCell(h.getColumn());
1648    }
1649  }
1650
1651  /**
1652   * Adds the specified hyperlink
1653   *
1654   * @param the hyperlink
1655   * @exception WriteException
1656   * @exception RowsExceededException
1657   */

1658  public void addHyperlink(WritableHyperlink h)
1659    throws WriteException, RowsExceededException
1660  {
1661    // First set the label on the sheet
1662
Cell c = getCell(h.getColumn(), h.getRow());
1663
1664    String JavaDoc contents = null;
1665    if (h.isFile() || h.isUNC())
1666    {
1667      String JavaDoc cnts = ( (HyperlinkRecord) h).getContents();
1668      if (cnts == null)
1669      {
1670        contents = h.getFile().getPath();
1671      }
1672      else
1673      {
1674        contents = cnts;
1675      }
1676    }
1677    else if (h.isURL())
1678    {
1679      String JavaDoc cnts = ( (HyperlinkRecord) h).getContents();
1680      if (cnts == null)
1681      {
1682        contents = h.getURL().toString();
1683      }
1684      else
1685      {
1686        contents=cnts;
1687      }
1688    }
1689    else if (h.isLocation())
1690    {
1691      contents = ( (HyperlinkRecord) h).getContents();
1692    }
1693
1694    if (c.getType() == CellType.LABEL)
1695    {
1696      Label l = (Label) c;
1697      l.setString(contents);
1698      l.setCellFormat(WritableWorkbook.HYPERLINK_STYLE);
1699    }
1700    else
1701    {
1702      Label l = new Label(h.getColumn(), h.getRow(), contents,
1703                          WritableWorkbook.HYPERLINK_STYLE);
1704      addCell(l);
1705    }
1706    
1707    // Set all other cells within range to be empty
1708
for (int i = h.getRow(); i <= h.getLastRow(); i++)
1709    {
1710      for (int j = h.getColumn(); j <= h.getLastColumn(); j++)
1711      {
1712        if (i != h.getRow() && j != h.getColumn())
1713        {
1714          // Set the cell to be empty
1715
if (rows[i] != null)
1716          {
1717            rows[i].removeCell(j);
1718          }
1719        }
1720      }
1721    }
1722
1723    ((HyperlinkRecord) h).initialize(this);
1724    hyperlinks.add(h);
1725  }
1726
1727  /**
1728   * Merges the specified cells. Any clashes or intersections between
1729   * merged cells are resolved when the spreadsheet is written out
1730   *
1731   * @param col1 the column number of the top left cell
1732   * @param row1 the row number of the top left cell
1733   * @param col2 the column number of the bottom right cell
1734   * @param row2 the row number of the bottom right cell
1735   * @return the Range object representing the merged cells
1736   * @exception jxl.write..WriteException
1737   * @exception jxl.write.biff.RowsExceededException
1738   */

1739  public Range mergeCells(int col1, int row1, int col2, int row2)
1740    throws WriteException, RowsExceededException
1741  {
1742    // First check that the cells make sense
1743
if (col2 < col1 || row2 < row1)
1744    {
1745      logger.warn("Cannot merge cells - top left and bottom right "+
1746                  "incorrectly specified");
1747    }
1748
1749    // Make sure the spreadsheet is up to size
1750
if (col2 >= numColumns || row2 >= numRows)
1751    {
1752      addCell(new Blank(col2, row2));
1753    }
1754
1755    SheetRangeImpl range = new SheetRangeImpl(this, col1, row1, col2, row2);
1756    mergedCells.add(range);
1757
1758    return range;
1759  }
1760
1761  /**
1762   * Unmerges the specified cells. The Range passed in should be one that
1763   * has been previously returned as a result of the getMergedCells method
1764   *
1765   * @param r the range of cells to unmerge
1766   */

1767  public void unmergeCells(Range r)
1768  {
1769    mergedCells.unmergeCells(r);
1770  }
1771
1772  /**
1773   * Sets the header for this page
1774   *
1775   * @param l the print header to print on the left side
1776   * @param c the print header to print in the centre
1777   * @param r the print header to print on the right hand side
1778   * @deprecated use the sheet settings beant
1779   */

1780  public void setHeader(String JavaDoc l, String JavaDoc c, String JavaDoc r)
1781  {
1782    HeaderFooter header = new HeaderFooter();
1783    header.getLeft().append(l);
1784    header.getCentre().append(c);
1785    header.getRight().append(r);
1786    settings.setHeader(header);
1787  }
1788
1789  /**
1790   * Sets the footer for this page
1791   *
1792   * @param l the print header to print on the left side
1793   * @param c the print header to print in the centre
1794   * @param r the print header to print on the right hand side
1795   * @deprecated use the sheet settings bean
1796   */

1797  public void setFooter(String JavaDoc l, String JavaDoc c, String JavaDoc r)
1798  {
1799    HeaderFooter footer = new HeaderFooter();
1800    footer.getLeft().append(l);
1801    footer.getCentre().append(c);
1802    footer.getRight().append(r);
1803    settings.setFooter(footer);
1804  }
1805
1806  /**
1807   * Sets the page setup details
1808   *
1809   * @param p the page orientation
1810   * @deprecated use the SheetSettings bean
1811   */

1812  public void setPageSetup(PageOrientation p)
1813  {
1814    settings.setOrientation(p);
1815  }
1816
1817  /**
1818   * Sets the page setup details
1819   *
1820   * @param p the page orientation
1821   * @param hm the header margin, in inches
1822   * @param fm the footer margin, in inches
1823   * @deprecated use the SheetSettings bean
1824   */

1825  public void setPageSetup(PageOrientation p, double hm, double fm)
1826  {
1827    settings.setOrientation(p);
1828    settings.setHeaderMargin(hm);
1829    settings.setFooterMargin(fm);
1830  }
1831
1832  /**
1833   * Sets the page setup details
1834   *
1835   * @param p the page orientation
1836   * @param ps the paper size
1837   * @param hm the header margin, in inches
1838   * @param fm the footer margin, in inches
1839   * @deprecated use the SheetSettings bean
1840   */

1841  public void setPageSetup(PageOrientation p, PaperSize ps,
1842                           double hm, double fm)
1843  {
1844    settings.setPaperSize(ps);
1845    settings.setOrientation(p);
1846    settings.setHeaderMargin(hm);
1847    settings.setFooterMargin(fm);
1848  }
1849
1850  /**
1851   * Gets the settings for this sheet
1852   *
1853   * @return the page settings bean
1854   */

1855  public SheetSettings getSettings()
1856  {
1857    return settings;
1858  }
1859
1860  /**
1861   * Gets the workbook settings
1862   */

1863  WorkbookSettings getWorkbookSettings()
1864  {
1865    return workbookSettings;
1866  }
1867
1868  /**
1869   * Forces a page break at the specified row
1870   *
1871   * @param row the row to break at
1872   */

1873  public void addRowPageBreak(int row)
1874  {
1875    // First check that the row is not already present
1876
Iterator JavaDoc i = rowBreaks.iterator();
1877    boolean found = false;
1878
1879    while (i.hasNext() && !found)
1880    {
1881      if (( (Integer JavaDoc) i.next()).intValue() == row)
1882      {
1883        found = true;
1884      }
1885    }
1886
1887    if (!found)
1888    {
1889      rowBreaks.add(new Integer JavaDoc(row));
1890    }
1891  }
1892
1893  /**
1894   * Accessor for the charts. Used when copying
1895   *
1896   * @return the charts on this sheet
1897   */

1898  private Chart[] getCharts()
1899  {
1900    return sheetWriter.getCharts();
1901  }
1902
1903  /**
1904   * Accessor for the drawings. Used when copying
1905   *
1906   * @return the drawings on this sheet
1907   */

1908  private DrawingGroupObject[] getDrawings()
1909  {
1910    DrawingGroupObject[] dr = new DrawingGroupObject[drawings.size()];
1911    dr = (DrawingGroupObject[]) drawings.toArray(dr);
1912    return dr;
1913  }
1914
1915  /**
1916   * Check all the merged cells for borders. Although in an OO sense the
1917   * logic should belong in this class, in order to reduce the bloated
1918   * nature of the source code for this object this logic has been delegated
1919   * to the SheetWriter
1920   */

1921  void checkMergedBorders()
1922  {
1923    sheetWriter.setWriteData(rows,
1924                             rowBreaks,
1925                             hyperlinks,
1926                             mergedCells,
1927                             columnFormats );
1928    sheetWriter.setDimensions(getRows(), getColumns());
1929    sheetWriter.checkMergedBorders();
1930  }
1931
1932  /**
1933   * Accessor for the workspace options
1934   *
1935   * @return the workspace options
1936   */

1937  private WorkspaceInformationRecord getWorkspaceOptions()
1938  {
1939    return sheetWriter.getWorkspaceOptions();
1940  }
1941
1942  /**
1943   * Rationalizes the sheets xf index mapping
1944   * @param xfMapping the index mapping for XFRecords
1945   * @param fontMapping the index mapping for fonts
1946   * @param formatMapping the index mapping for formats
1947   */

1948  void rationalize(IndexMapping xfMapping,
1949                   IndexMapping fontMapping,
1950                   IndexMapping formatMapping)
1951  {
1952    // Rationalize the column formats
1953
for (Iterator JavaDoc i = columnFormats.iterator() ; i.hasNext() ;)
1954    {
1955      ColumnInfoRecord cir = (ColumnInfoRecord) i.next();
1956      cir.rationalize(xfMapping);
1957    }
1958
1959    // Rationalize the row formats
1960
for (int i = 0; i < rows.length ; i++)
1961    {
1962      if (rows[i] != null)
1963      {
1964        rows[i].rationalize(xfMapping);
1965      }
1966    }
1967
1968    // Rationalize any data that appears on the charts
1969
Chart[] charts = getCharts();
1970    for (int c = 0; c < charts.length; c++)
1971    {
1972      charts[c].rationalize(xfMapping, fontMapping, formatMapping);
1973    }
1974  }
1975
1976  /**
1977   * Accessor for the workbook
1978   * @return the workbook
1979   */

1980  WritableWorkbookImpl getWorkbook()
1981  {
1982    return workbook;
1983  }
1984
1985  /**
1986   * Gets the column format for the specified column
1987   *
1988   * @param col the column number
1989   * @return the column format, or NULL if the column has no specific format
1990   * @deprecated use getColumnView instead
1991   */

1992  public CellFormat getColumnFormat(int col)
1993  {
1994    return getColumnView(col).getFormat();
1995  }
1996
1997  /**
1998   * Gets the column width for the specified column
1999   *
2000   * @param col the column number
2001   * @return the column width, or the default width if the column has no
2002   * specified format
2003   * @deprecated use getColumnView instead
2004   */

2005  public int getColumnWidth(int col)
2006  {
2007    return getColumnView(col).getDimension();
2008  }
2009
2010  /**
2011   * Gets the column width for the specified column
2012   *
2013   * @param row the column number
2014   * @return the row height, or the default height if the column has no
2015   * specified format
2016   * @deprecated use getRowView instead
2017   */

2018  public int getRowHeight(int row)
2019  {
2020    return getRowView(row).getDimension();
2021  }
2022
2023  /**
2024   * Accessor for the chart only method
2025   *
2026   * @return TRUE if this is a chart only, FALSE otherwise
2027   */

2028  boolean isChartOnly()
2029  {
2030    return chartOnly;
2031  }
2032
2033  /**
2034   * Gets the row view for the specified row
2035   *
2036   * @param col the row number
2037   * @return the row format, or the default format if no override is
2038             specified
2039   */

2040  public CellView getRowView(int row)
2041  {
2042    CellView cv = new CellView();
2043
2044    try
2045    {
2046      RowRecord rr = getRowRecord(row);
2047
2048      if (rr == null || rr.isDefaultHeight())
2049      {
2050        cv.setDimension(settings.getDefaultRowHeight());
2051        cv.setSize(settings.getDefaultRowHeight());
2052      }
2053      else if (rr.isCollapsed())
2054      {
2055        cv.setHidden(true);
2056      }
2057      else
2058      {
2059        cv.setDimension(rr.getRowHeight());
2060        cv.setSize(rr.getRowHeight());
2061      }
2062      return cv;
2063    }
2064    catch (RowsExceededException e)
2065    {
2066      // Simple return the default
2067
cv.setDimension(settings.getDefaultRowHeight());
2068      cv.setSize(settings.getDefaultRowHeight());
2069      return cv;
2070    }
2071  }
2072
2073  /**
2074   * Gets the column width for the specified column
2075   *
2076   * @param col the column number
2077   * @return the column format, or the default format if no override is
2078             specified
2079   */

2080  public CellView getColumnView(int col)
2081  {
2082    ColumnInfoRecord cir = getColumnInfo(col);
2083    CellView cv = new CellView();
2084
2085    if (cir != null)
2086    {
2087      cv.setDimension(cir.getWidth()/256);
2088      cv.setSize(cir.getWidth());
2089      cv.setHidden(cir.getHidden());
2090      cv.setFormat(cir.getCellFormat());
2091    }
2092    else
2093    {
2094      cv.setDimension(settings.getDefaultColumnWidth()/256);
2095      cv.setSize(settings.getDefaultColumnWidth());
2096    }
2097
2098    return cv;
2099  }
2100
2101  /**
2102   * Adds an image to this sheet
2103   *
2104   * @param image the image to add
2105   */

2106  public void addImage(WritableImage image)
2107  {
2108    boolean supported = false;
2109    java.io.File JavaDoc imageFile = image.getImageFile();
2110    String JavaDoc fileType = "?";
2111
2112    if (imageFile != null)
2113    {
2114    
2115      String JavaDoc fileName = imageFile.getName();
2116      int fileTypeIndex = fileName.lastIndexOf('.');
2117      fileType = fileTypeIndex != -1 ?
2118        fileName.substring(fileTypeIndex+1) : "";
2119      
2120      for (int i = 0 ; i < imageTypes.length && !supported ; i++)
2121      {
2122        if (fileType.equalsIgnoreCase(imageTypes[i]))
2123        {
2124          supported = true;
2125        }
2126      }
2127    }
2128    else
2129    {
2130      supported = true;
2131    }
2132
2133    if (supported)
2134    {
2135      workbook.addDrawing(image);
2136      drawings.add(image);
2137      images.add(image);
2138    }
2139    else
2140    {
2141      StringBuffer JavaDoc message = new StringBuffer JavaDoc("Image type ");
2142      message.append(fileType);
2143      message.append(" not supported. Supported types are ");
2144      message.append(imageTypes[0]);
2145      for (int i = 1 ; i < imageTypes.length ; i++)
2146      {
2147        message.append(", ");
2148        message.append(imageTypes[i]);
2149      }
2150      logger.warn(message.toString());
2151    }
2152  }
2153
2154  /**
2155   * Gets the number of images on this sheet
2156   *
2157   * @return the number of images on this sheet
2158   */

2159  public int getNumberOfImages()
2160  {
2161    return images.size();
2162  }
2163
2164  /**
2165   * Accessor for a particular image on this sheet
2166   *
2167   * @param i the 0-based image index number
2168   * @return the image with the specified index number
2169   */

2170  public WritableImage getImage(int i)
2171  {
2172    return (WritableImage) images.get(i);
2173  }
2174
2175  /**
2176   * Accessor for a particular image on this sheet
2177   *
2178   * @param i the 0-based image index number
2179   * @return the image with the specified index number
2180   */

2181  public Image getDrawing(int i)
2182  {
2183    return (Image) images.get(i);
2184  }
2185
2186  /**
2187   * Removes the specified image from this sheet. The image passed in
2188   * must be the same instance as that retrieved from a getImage call
2189   *
2190   * @param wi the image to remove
2191   */

2192  public void removeImage(WritableImage wi)
2193  {
2194    boolean removed = drawings.remove(wi);
2195    images.remove(wi);
2196    drawingsModified = true;
2197    workbook.removeDrawing(wi);
2198  }
2199
2200  /**
2201   * Validates the sheet name
2202   */

2203  private String JavaDoc validateName(String JavaDoc n)
2204  {
2205    if (n.length() > maxSheetNameLength)
2206    {
2207      logger.warn("Sheet name " + n + " too long - truncating");
2208      n = n.substring(0, maxSheetNameLength);
2209    }
2210
2211    if (n.charAt(0) == '\'')
2212    {
2213      logger.warn("Sheet naming cannot start with \' - removing");
2214      n = n.substring(1);
2215    }
2216
2217    for (int i = 0 ; i < illegalSheetNameCharacters.length ; i++)
2218    {
2219      String JavaDoc newname = n.replace(illegalSheetNameCharacters[i], '@');
2220      if (n != newname)
2221      {
2222        logger.warn(illegalSheetNameCharacters[i] +
2223        " is not a valid character within a sheet name - replacing");
2224      }
2225      n = newname;
2226    }
2227
2228    return n;
2229  }
2230
2231  /**
2232   * Adds a drawing to the list - typically used for comments
2233   *
2234   * @param the drawing to add
2235   */

2236  void addDrawing(DrawingGroupObject o)
2237  {
2238    drawings.add(o);
2239    Assert.verify(!(o instanceof Drawing));
2240  }
2241}
2242
Popular Tags