KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jxl > biff > FormattingRecords


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.biff;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.text.DateFormat JavaDoc;
26 import java.text.NumberFormat JavaDoc;
27 import java.io.IOException JavaDoc;
28
29 import common.Assert;
30 import common.Logger;
31
32 import jxl.write.biff.File;
33 import jxl.format.Colour;
34 import jxl.format.RGB;
35
36 /**
37  * The list of XF records and formatting records for the workbook
38  */

39 public class FormattingRecords
40 {
41   /**
42    * The logger
43    */

44   private static Logger logger = Logger.getLogger(FormattingRecords.class);
45
46   /**
47    * A hash map of FormatRecords, for random access retrieval when reading
48    * in a spreadsheet
49    */

50   private HashMap JavaDoc formats;
51
52   /**
53    * A list of formats, used when writing out a spreadsheet
54    */

55   private ArrayList JavaDoc formatsList;
56
57   /**
58    * The list of extended format records
59    */

60   private ArrayList JavaDoc xfRecords;
61
62   /**
63    * The next available index number for custom format records
64    */

65   private int nextCustomIndexNumber;
66
67   /**
68    * A handle to the available fonts
69    */

70   private Fonts fonts;
71
72   /**
73    * The colour palette
74    */

75   private PaletteRecord palette;
76
77   /**
78    * The start index number for custom format records
79    */

80   private static final int customFormatStartIndex = 0xa4;
81
82   /**
83    * The maximum number of format records. This is some weird internal
84    * Excel constraint
85    */

86   private static final int maxFormatRecordsIndex = 0x1b9;
87
88   /**
89    * The minimum number of XF records for a sheet. The rationalization
90    * processes commences immediately after this number
91    */

92   private static final int minXFRecords = 21;
93
94   /**
95    * Constructor
96    *
97    * @param f the container for the fonts
98    */

99   public FormattingRecords(Fonts f)
100   {
101     xfRecords = new ArrayList JavaDoc(10);
102     formats = new HashMap JavaDoc(10);
103     formatsList = new ArrayList JavaDoc(10);
104     fonts = f;
105     nextCustomIndexNumber = customFormatStartIndex;
106   }
107
108   /**
109    * Adds an extended formatting record to the list. If the XF record passed
110    * in has not been initialized, its index is determined based on the
111    * xfRecords list, and
112    * this position is passed to the XF records initialize method
113    *
114    * @param xf the xf record to add
115    * @exception NumFormatRecordsException
116    */

117   public final void addStyle(XFRecord xf)
118     throws NumFormatRecordsException
119   {
120     if (!xf.isInitialized())
121     {
122       int pos = xfRecords.size();
123       xf.initialize(pos, this, fonts);
124       xfRecords.add(xf);
125     }
126     else
127     {
128       // The XF record has probably been read in. If the index is greater
129
// Than the size of the list, then it is not a preset format,
130
// so add it
131
if (xf.getXFIndex() >= xfRecords.size())
132       {
133         xfRecords.add(xf);
134       }
135     }
136   }
137
138   /**
139    * Adds a cell format to the hash map, keyed on its index. If the format
140    * record is not initialized, then its index number is determined and its
141    * initialize method called. If the font is not a built in format, then it
142    * is added to the list of formats for writing out
143    *
144    * @param fr the format record
145    */

146   public final void addFormat(DisplayFormat fr)
147     throws NumFormatRecordsException
148   {
149     // Handle the case the where the index number in the read Excel
150
// file exhibits some major weirdness
151
if (fr.isInitialized() &&
152         fr.getFormatIndex() >= maxFormatRecordsIndex)
153     {
154       logger.warn("Format index exceeds Excel maximum - assigning custom " +
155                   "number");
156       fr.initialize(nextCustomIndexNumber);
157       nextCustomIndexNumber++;
158     }
159
160     // Initialize the format record with a custom index number
161
if (!fr.isInitialized())
162     {
163       fr.initialize(nextCustomIndexNumber);
164       nextCustomIndexNumber++;
165     }
166
167     if (nextCustomIndexNumber > maxFormatRecordsIndex)
168     {
169       nextCustomIndexNumber = maxFormatRecordsIndex;
170       throw new NumFormatRecordsException();
171     }
172
173
174     if (fr.getFormatIndex() >= nextCustomIndexNumber)
175     {
176       nextCustomIndexNumber = fr.getFormatIndex() + 1;
177     }
178
179     if (!fr.isBuiltIn())
180     {
181       formatsList.add(fr);
182       formats.put(new Integer JavaDoc(fr.getFormatIndex()), fr);
183     }
184   }
185
186   /**
187    * Sees if the extended formatting record at the specified position
188    * represents a date. First checks against the built in formats, and
189    * then checks against the hash map of FormatRecords
190    *
191    * @param pos the xf format index
192    * @return TRUE if this format index is formatted as a Date
193    */

194   public final boolean isDate(int pos)
195   {
196     XFRecord xfr = (XFRecord) xfRecords.get(pos);
197
198     if (xfr.isDate())
199     {
200       return true;
201     }
202
203     FormatRecord fr = (FormatRecord)
204       formats.get(new Integer JavaDoc(xfr.getFormatRecord()));
205
206     return fr == null ? false : fr.isDate();
207   }
208
209   /**
210    * Gets the DateFormat used to format the cell.
211    *
212    * @param pos the xf format index
213    * @return the DateFormat object used to format the date in the original
214    * excel cell
215    */

216   public final DateFormat JavaDoc getDateFormat(int pos)
217   {
218     XFRecord xfr = (XFRecord) xfRecords.get(pos);
219
220     if (xfr.isDate())
221     {
222       return xfr.getDateFormat();
223     }
224
225     FormatRecord fr = (FormatRecord)
226       formats.get(new Integer JavaDoc(xfr.getFormatRecord()));
227
228     if (fr == null)
229     {
230       return null;
231     }
232
233     return fr.isDate() ? fr.getDateFormat() : null;
234   }
235
236   /**
237    * Gets the NumberFormat used to format the cell.
238    *
239    * @param pos the xf format index
240    * @return the DateFormat object used to format the date in the original
241    * excel cell
242    */

243   public final NumberFormat JavaDoc getNumberFormat(int pos)
244   {
245     XFRecord xfr = (XFRecord) xfRecords.get(pos);
246     
247     if (xfr.isNumber())
248     {
249       return xfr.getNumberFormat();
250     }
251
252     FormatRecord fr = (FormatRecord)
253       formats.get(new Integer JavaDoc(xfr.getFormatRecord()));
254
255     if (fr == null)
256     {
257       return null;
258     }
259
260     return fr.isNumber() ? fr.getNumberFormat() : null;
261   }
262
263   /**
264    * Gets the format record
265    *
266    * @param index the formatting record index to retrieve
267    * @return the format record at the specified index
268    */

269   FormatRecord getFormatRecord(int index)
270   {
271     return (FormatRecord)
272       formats.get(new Integer JavaDoc(index));
273   }
274   /**
275    * Writes out all the format records and the XF records
276    *
277    * @param outputFile the file to write to
278    * @exception IOException
279    */

280   public void write(File outputFile) throws IOException JavaDoc
281   {
282     // Write out all the formats
283
Iterator JavaDoc i = formatsList.iterator();
284     FormatRecord fr = null;
285     while (i.hasNext())
286     {
287       fr = (FormatRecord) i.next();
288       outputFile.write(fr);
289     }
290
291     // Write out the styles
292
i = xfRecords.iterator();
293     XFRecord xfr = null;
294     while (i.hasNext())
295     {
296       xfr = (XFRecord) i.next();
297       outputFile.write(xfr);
298     }
299
300     // Write out the style records
301
BuiltInStyle style = new BuiltInStyle(0x10, 3);
302     outputFile.write(style);
303
304     style = new BuiltInStyle(0x11, 6);
305     outputFile.write(style);
306
307     style = new BuiltInStyle(0x12, 4);
308     outputFile.write(style);
309
310     style = new BuiltInStyle(0x13, 7);
311     outputFile.write(style);
312
313     style = new BuiltInStyle(0x0, 0);
314     outputFile.write(style);
315
316     style = new BuiltInStyle(0x14, 5);
317     outputFile.write(style);
318   }
319
320   /**
321    * Accessor for the fonts used by this workbook
322    *
323    * @return the fonts container
324    */

325   protected final Fonts getFonts()
326   {
327     return fonts;
328   }
329
330   /**
331    * Gets the XFRecord for the specified index. Used when copying individual
332    * cells
333    *
334    * @param index the XF record to retrieve
335    * @return the XF record at the specified index
336    */

337   public final XFRecord getXFRecord(int index)
338   {
339     return (XFRecord) xfRecords.get(index);
340   }
341
342   /**
343    * Gets the number of formatting records on the list. This is used by the
344    * writable subclass because there is an upper limit on the amount of
345    * format records that are allowed to be present in an excel sheet
346    *
347    * @return the number of format records present
348    */

349   protected final int getNumberOfFormatRecords()
350   {
351     return formatsList.size();
352   }
353
354   /**
355    * Rationalizes all the fonts, removing duplicate entries
356    *
357    * @return the list of new font index number
358    */

359   public IndexMapping rationalizeFonts()
360   {
361     return fonts.rationalize();
362   }
363
364   /**
365    * Rationalizes the cell formats. Duplicate
366    * formats are removed and the format indexed of the cells
367    * adjusted accordingly
368    *
369    * @param fontMapping the font mapping index numbers
370    * @param formatMapping the format mapping index numbers
371    * @return the list of new font index number
372    */

373   public IndexMapping rationalize(IndexMapping fontMapping,
374                                   IndexMapping formatMapping)
375   {
376     // Update the index codes for the XF records using the format
377
// mapping and the font mapping
378
// at the same time
379
XFRecord xfr = null;
380     for (Iterator JavaDoc it = xfRecords.iterator(); it.hasNext();)
381     {
382       xfr = (XFRecord) it.next();
383
384       if (xfr.getFormatRecord() >= customFormatStartIndex)
385       {
386         xfr.setFormatIndex(formatMapping.getNewIndex(xfr.getFormatRecord()));
387       }
388
389       xfr.setFontIndex(fontMapping.getNewIndex(xfr.getFontIndex()));
390     }
391
392     ArrayList JavaDoc newrecords = new ArrayList JavaDoc(minXFRecords);
393     IndexMapping mapping = new IndexMapping(xfRecords.size());
394     int numremoved = 0;
395
396     // Copy across the fundamental styles
397
for (int i = 0; i < minXFRecords; i++)
398     {
399       newrecords.add(xfRecords.get(i));
400       mapping.setMapping(i, i);
401     }
402
403     // Iterate through the old list
404
for (int i = minXFRecords; i < xfRecords.size(); i++)
405     {
406       XFRecord xf = (XFRecord) xfRecords.get(i);
407
408       // Compare against formats already on the list
409
boolean duplicate = false;
410       for (Iterator JavaDoc it = newrecords.iterator();
411            it.hasNext() && !duplicate;)
412       {
413         XFRecord xf2 = (XFRecord) it.next();
414         if (xf2.equals(xf))
415         {
416           duplicate = true;
417           mapping.setMapping(i, mapping.getNewIndex(xf2.getXFIndex()));
418           numremoved++;
419         }
420       }
421
422       // If this format is not a duplicate then add it to the new list
423
if (!duplicate)
424       {
425         newrecords.add(xf);
426         mapping.setMapping(i, i - numremoved);
427       }
428     }
429
430     // It is sufficient to merely change the xf index field on all XFRecords
431
// In this case, CellValues which refer to defunct format records
432
// will nevertheless be written out with the correct index number
433
for (Iterator JavaDoc i = xfRecords.iterator(); i.hasNext();)
434     {
435       XFRecord xf = (XFRecord) i.next();
436       xf.rationalize(mapping);
437     }
438
439     // Set the new list
440
xfRecords = newrecords;
441
442     return mapping;
443   }
444
445   /**
446    * Rationalizes the display formats. Duplicate
447    * formats are removed and the format indices of the cells
448    * adjusted accordingly. It is invoked immediately prior to writing
449    * writing out the sheet
450    * @return the index mapping between the old display formats and the
451    * rationalized ones
452    */

453   public IndexMapping rationalizeDisplayFormats()
454   {
455     ArrayList JavaDoc newformats = new ArrayList JavaDoc();
456     int numremoved = 0;
457     IndexMapping mapping = new IndexMapping(nextCustomIndexNumber);
458
459     // Iterate through the old list
460
Iterator JavaDoc i = formatsList.iterator();
461     DisplayFormat df = null;
462     DisplayFormat df2 = null;
463     boolean duplicate = false;
464     while (i.hasNext())
465     {
466       df = (DisplayFormat) i.next();
467
468       Assert.verify(!df.isBuiltIn());
469
470       // Compare against formats already on the list
471
Iterator JavaDoc i2 = newformats.iterator();
472       duplicate = false;
473       while (i2.hasNext() && !duplicate)
474       {
475         df2 = (DisplayFormat) i2.next();
476         if (df2.equals(df))
477         {
478           duplicate = true;
479           mapping.setMapping(df.getFormatIndex(),
480                              mapping.getNewIndex(df2.getFormatIndex()));
481           numremoved++;
482         }
483       }
484
485       // If this format is not a duplicate then add it to the new list
486
if (!duplicate)
487       {
488         newformats.add(df);
489         int indexnum = df.getFormatIndex() - numremoved;
490         if (indexnum > maxFormatRecordsIndex)
491         {
492           logger.warn("Too many number formats - using default format.");
493           indexnum = 0; // the default number format index
494
}
495         mapping.setMapping(df.getFormatIndex(),
496                            df.getFormatIndex() - numremoved);
497       }
498     }
499
500     // Set the new list
501
formatsList = newformats;
502
503     // Update the index codes for the remaining formats
504
i = formatsList.iterator();
505
506     while (i.hasNext())
507     {
508       df = (DisplayFormat) i.next();
509       df.initialize(mapping.getNewIndex(df.getFormatIndex()));
510     }
511
512     return mapping;
513   }
514
515   /**
516    * Accessor for the colour palette
517    *
518    * @return the palette
519    */

520   public PaletteRecord getPalette()
521   {
522     return palette;
523   }
524
525   /**
526    * Called from the WorkbookParser to set the colour palette
527    *
528    * @param pr the palette
529    */

530   public void setPalette(PaletteRecord pr)
531   {
532     palette = pr;
533   }
534
535   /**
536    * Sets the RGB value for the specified colour for this workbook
537    *
538    * @param c the colour whose RGB value is to be overwritten
539    * @param r the red portion to set (0-255)
540    * @param g the green portion to set (0-255)
541    * @param b the blue portion to set (0-255)
542    */

543   public void setColourRGB(Colour c, int r, int g, int b)
544   {
545     if (palette == null)
546     {
547       palette = new PaletteRecord();
548     }
549     palette.setColourRGB(c, r, g, b);
550   }
551
552   /**
553    * Accessor for the RGB value for the specified colour
554    *
555    * @return the RGB for the specified colour
556    */

557   public RGB getColourRGB(Colour c)
558   {
559     if (palette == null)
560     {
561       return c.getDefaultRGB();
562     }
563
564     return palette.getColourRGB(c);
565   }
566 }
567
Popular Tags