001package com.hfg.xml.msofficexml.xlsx.spreadsheetml.style;
002
003import java.io.BufferedInputStream;
004import java.util.ArrayList;
005import java.util.Collections;
006import java.util.List;
007import java.util.logging.Level;
008import java.util.logging.Logger;
009
010import com.hfg.datetime.DateUtil;
011import com.hfg.util.StringUtil;
012import com.hfg.util.collection.OrderedMap;
013import com.hfg.util.collection.OrderedSet;
014import com.hfg.xml.XMLDoc;
015import com.hfg.util.StackTraceUtil;
016import com.hfg.xml.XMLTag;
017import com.hfg.xml.msofficexml.OfficeOpenXmlException;
018import com.hfg.xml.msofficexml.OfficeXML;
019import com.hfg.xml.msofficexml.xlsx.SsmlRelationshipType;
020import com.hfg.xml.msofficexml.xlsx.Xlsx;
021import com.hfg.xml.msofficexml.xlsx.part.SsmlContentType;
022import com.hfg.xml.msofficexml.xlsx.part.XlsxPart;
023import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlCell;
024import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlRow;
025import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlSheetData;
026import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlWorksheet;
027import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlXML;
028import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlXMLTag;
029
030//------------------------------------------------------------------------------
031/**
032 Styles part specific to OfficeOpenXML SpreadsheetML.
033 <div>
034 @author J. Alex Taylor, hairyfatguy.com
035 </div>
036 */
037//------------------------------------------------------------------------------
038// com.hfg XML/HTML Coding Library
039//
040// This library is free software; you can redistribute it and/or
041// modify it under the terms of the GNU Lesser General Public
042// License as published by the Free Software Foundation; either
043// version 2.1 of the License, or (at your option) any later version.
044//
045// This library is distributed in the hope that it will be useful,
046// but WITHOUT ANY WARRANTY; without even the implied warranty of
047// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
048// Lesser General Public License for more details.
049//
050// You should have received a copy of the GNU Lesser General Public
051// License along with this library; if not, write to the Free Software
052// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
053//
054// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
055// jataylor@hairyfatguy.com
056//------------------------------------------------------------------------------
057
058public class StylesPart extends XlsxPart
059{
060   private XMLTag mStylesheetTag;
061   private XMLTag mNumberFormatsTag;
062   private XMLTag mFontsTag;
063   private XMLTag mFillsTag;
064   private XMLTag mBordersTag;
065   private XMLTag mCellFormatsTag;
066   private XMLTag mStyleFormatsTag;
067   private XMLTag mCellStylesTag;
068   private XMLTag mDifferentialFormatsTag;
069   private SsmlFont        mDefaultFont;
070   private SsmlFill        mDefaultFill;
071   private SsmlBorder      mDefaultBorder;
072   private SsmlStyleFormat mDefaultStyleFormat;
073   private SsmlCellFormat  mDefaultCellFormat;
074   private SsmlCellStyle   mNormalStyle;
075   private SsmlCellFormat  mDefaultDateCellFormat;
076
077   private List<SsmlNumberFormat>   mNumberFormats = new ArrayList<>();
078   private List<SsmlFont>   mFonts   = new ArrayList<>();
079   private List<SsmlFill>   mFills   = new ArrayList<>();
080   private List<SsmlBorder> mBorders = new ArrayList<>();
081   private List<SsmlCellFormat> mCellFormats = new ArrayList<>();
082   private List<SsmlStyleFormat> mCellStyleFormats = new ArrayList<>();
083   private List<SsmlCellStyle> mCellStyles = new ArrayList<>();
084   private List<SsmlDifferentialFormat> mDifferentialFormats = new ArrayList<>();
085
086   private final static Logger LOGGER = OfficeXML.getLogger();
087
088   //##########################################################################
089   // CONSTRUCTORS
090   //##########################################################################
091
092   //---------------------------------------------------------------------------
093   /**
094    This constructor should only be used by the Xlsx object.
095    * @param inXlsx the parent Excel document
096    */
097   public StylesPart(Xlsx inXlsx)
098   {
099      super(inXlsx);
100      setFile(SsmlXML.STYLES_FILE);
101
102      if (! (StackTraceUtil.getCallingClassName().equals(getClass().getName())
103             || StackTraceUtil.getCallingClassName().equals(Xlsx.class.getName())))
104      {
105         throw new OfficeOpenXmlException("An Xlsx object can have only one styles part! Use xlsx.getStylesPart() to access it.");
106      }
107
108      // Register the content type
109      inXlsx.getContentTypesPart().addOverride(this, SsmlContentType.SPREADSHEET_STYLES);
110
111      // Register the relationship
112      inXlsx.getWorkbookRelationshipPart().addRelationship(SsmlRelationshipType.STYLES, this);
113
114      mStylesheetTag = new SsmlStylesheet(inXlsx);
115      setRootNode(mStylesheetTag);
116
117//      setDefaults();
118   }
119
120   //---------------------------------------------------------------------------
121   public StylesPart(Xlsx inXlsx, BufferedInputStream inStream)
122   {
123      this(inXlsx);
124
125      XMLDoc doc = new XMLDoc(inStream);
126      mStylesheetTag = (XMLTag) doc.getRootNode();
127
128      mNumberFormatsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.NUM_FORMATS);
129      if (mNumberFormatsTag != null)
130      {
131         for (XMLTag subtag : (List<XMLTag>) (Object) mNumberFormatsTag.getSubtagsByName(SsmlXML.NUM_FORMAT))
132         {
133            mNumberFormats.add(new SsmlNumberFormat(inXlsx, subtag));
134         }
135      }
136
137      mFontsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.FONTS);
138      if (mFontsTag != null)
139      {
140         for (XMLTag subtag : (List<XMLTag>) (Object) mFontsTag.getSubtagsByName(SsmlXML.FONT))
141         {
142            mFonts.add(new SsmlFont(inXlsx, subtag));
143         }
144      }
145
146      // TODO
147      mFillsTag         = mStylesheetTag.getOptionalSubtagByName(SsmlXML.FILLS);
148      // TODO
149      mBordersTag       = mStylesheetTag.getOptionalSubtagByName(SsmlXML.BORDERS);
150
151      mCellFormatsTag   = mStylesheetTag.getOptionalSubtagByName(SsmlXML.CELL_FORMATS);
152      if (mCellFormatsTag != null)
153      {
154         for (XMLTag subtag : (List<XMLTag>) (Object) mCellFormatsTag.getSubtagsByName(SsmlXML.CELL_FORMAT))
155         {
156            mCellFormats.add(new SsmlCellFormat(inXlsx, subtag));
157         }
158      }
159
160
161      mStyleFormatsTag  = mStylesheetTag.getOptionalSubtagByName(SsmlXML.CELL_STYLE_FORMATS);
162      mCellStylesTag    = mStylesheetTag.getOptionalSubtagByName(SsmlXML.CELL_STYLES);
163      mDifferentialFormatsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.DIFFERENTIAL_FORMATS);
164   }
165
166   //---------------------------------------------------------------------------
167   public void setDefaults()
168   {
169//      mDefaultFont   = new SsmlFont(this).setName("Verdana").setSize(10);
170      mDefaultFont   = new SsmlFont(this).setName("Calibri").setSize(11).lock();
171
172      mDefaultFill   = new SsmlFill(this).setPatternType(SsmlPatternType.none);
173      // Add a second default fill value that sometimes seems to be required
174      new SsmlFill(this).setPatternType(SsmlPatternType.gray125);
175
176      mDefaultBorder = new SsmlBorder(this);
177
178
179      mDefaultStyleFormat = new SsmlStyleFormat(this)
180            .setFont(mDefaultFont)
181            .setFill(mDefaultFill)
182            .setBorder(mDefaultBorder);
183
184      mDefaultCellFormat = new SsmlCellFormat(this)
185            .setFont(mDefaultFont)
186            .setFill(mDefaultFill)
187            .setBorder(mDefaultBorder)
188            .setStyleFormat(mDefaultStyleFormat);
189
190      mNormalStyle = new SsmlCellStyle(this)
191            .setName("Normal")
192            .setBuiltinId(1)
193            .setStyleFormat(mDefaultStyleFormat);
194   }
195
196   //##########################################################################
197   // PUBLIC METHODS
198   //##########################################################################
199
200   //---------------------------------------------------------------------------
201   public SsmlFont getDefaultFont()
202   {
203      return mDefaultFont;
204   }
205
206   //---------------------------------------------------------------------------
207   public StylesPart setDefaultFont(SsmlFont inValue)
208   {
209      mFonts.remove(mDefaultFont);
210      mFontsTag.removeSubtag(mDefaultFont);
211
212      mDefaultFont = inValue.clone();
213      mFonts.remove(mDefaultFont);
214      mFonts.add(0, mDefaultFont);
215      mFontsTag.removeSubtag(mDefaultFont);
216      mFontsTag.addSubtag(0, mDefaultFont);
217      mDefaultFont.setIndex(0);
218      mDefaultFont.lock();
219
220      return this;
221   }
222
223   //---------------------------------------------------------------------------
224   public SsmlFill getDefaultFill()
225   {
226      return mDefaultFill;
227   }
228
229   //---------------------------------------------------------------------------
230   public SsmlBorder getDefaultBorder()
231   {
232      return mDefaultBorder;
233   }
234
235   //---------------------------------------------------------------------------
236   public SsmlStyleFormat getDefaultStyleFormat()
237   {
238      return mDefaultStyleFormat;
239   }
240
241   //---------------------------------------------------------------------------
242   public SsmlCellFormat getDefaultCellFormat()
243   {
244      return mDefaultCellFormat;
245   }
246
247   //---------------------------------------------------------------------------
248   public SsmlCellFormat getDefaultDateCellFormat()
249   {
250      if (null == mDefaultDateCellFormat)
251      {
252         mDefaultDateCellFormat = new SsmlCellFormat(this)
253               .setNumberFormat(SsmlNumberFormat.DATE_MM_DD_YY)
254               .setFont(mDefaultFont)
255               .setFill(mDefaultFill)
256               .setBorder(mDefaultBorder);
257      }
258
259      return mDefaultDateCellFormat;
260   }
261
262   //---------------------------------------------------------------------------
263   public List<SsmlNumberFormat> getNumberFormats()
264   {
265      return mNumberFormats;
266   }
267
268   //---------------------------------------------------------------------------
269   public int defineNumberFormat(SsmlNumberFormat inValue)
270   {
271      int index = mNumberFormats.size();
272      mNumberFormats.add(inValue);
273
274      if (null == mNumberFormatsTag)
275      {
276         mNumberFormatsTag = new XMLTag(SsmlXML.NUM_FORMATS);
277         mStylesheetTag.addSubtag(mNumberFormatsTag);
278      }
279
280      // Add it to a number formats tag
281      mNumberFormatsTag.addSubtag(inValue);
282      mNumberFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mNumberFormats.size());
283
284      return index;
285   }
286
287   //---------------------------------------------------------------------------
288   public int defineFont(SsmlFont inValue)
289   {
290      int index = mFonts.size();
291      mFonts.add(inValue);
292
293      if (null == mFontsTag)
294      {
295         mFontsTag = new XMLTag(SsmlXML.FONTS);
296         mStylesheetTag.addSubtag(mFontsTag);
297      }
298
299      // Add it to a fonts tag
300      mFontsTag.addSubtag(inValue);
301      mFontsTag.setAttribute(SsmlXML.COUNT_ATT, mFonts.size());
302
303      return index;
304   }
305
306   //---------------------------------------------------------------------------
307   public List<SsmlFont> getFonts()
308   {
309      return mFonts;
310   }
311
312   //---------------------------------------------------------------------------
313   public List<SsmlFill> getFills()
314   {
315      return mFills;
316   }
317
318   //---------------------------------------------------------------------------
319   public int defineFill(SsmlFill inValue)
320   {
321      int index = mFills.size();
322      mFills.add(inValue);
323
324      if (null == mFillsTag)
325      {
326         mFillsTag = new XMLTag(SsmlXML.FILLS);
327         mStylesheetTag.addSubtag(mFillsTag);
328      }
329
330      // Add it to a fills tag
331      mFillsTag.addSubtag(inValue);
332      mFillsTag.setAttribute(SsmlXML.COUNT_ATT, mFills.size());
333
334      return index;
335   }
336
337   //---------------------------------------------------------------------------
338   public List<SsmlBorder> getBorders()
339   {
340      return mBorders;
341   }
342
343   //---------------------------------------------------------------------------
344   public int defineBorder(SsmlBorder inValue)
345   {
346      int index = mBorders.size();
347      mBorders.add(inValue);
348
349      if (null == mBordersTag)
350      {
351         mBordersTag = new XMLTag(SsmlXML.BORDERS);
352         mStylesheetTag.addSubtag(mBordersTag);
353      }
354
355      // Add it to a borders tag
356      mBordersTag.addSubtag(inValue);
357      mBordersTag.setAttribute(SsmlXML.COUNT_ATT, mBorders.size());
358
359      return index;
360   }
361
362   //---------------------------------------------------------------------------
363   public int defineCellFormat(SsmlCellFormat inValue)
364   {
365      int index = mCellFormats.size();
366      mCellFormats.add(inValue);
367
368      if (null == mCellFormatsTag)
369      {
370         mCellFormatsTag = new XMLTag(SsmlXML.CELL_FORMATS);
371         mStylesheetTag.addSubtag(mCellFormatsTag);
372      }
373
374      // Add it to a cell formats (cellXfs) tag
375      mCellFormatsTag.addSubtag(inValue);
376      mCellFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mCellFormats.size());
377
378      return index;
379   }
380
381   //---------------------------------------------------------------------------
382   public List<SsmlCellFormat> getCellFormats()
383   {
384      return mCellFormats;
385   }
386
387   //---------------------------------------------------------------------------
388   public int defineDifferentialFormat(SsmlDifferentialFormat inValue)
389   {
390      int index = mDifferentialFormats.size();
391      mDifferentialFormats.add(inValue);
392
393      if (null == mDifferentialFormatsTag)
394      {
395         mDifferentialFormatsTag = new XMLTag(SsmlXML.DIFFERENTIAL_FORMATS);
396         mStylesheetTag.addSubtag(mDifferentialFormatsTag);
397      }
398
399      // Add it to a differential formats (dxfs) tag
400      mDifferentialFormatsTag.addSubtag(inValue);
401      mDifferentialFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mDifferentialFormats.size());
402
403      return index;
404   }
405
406   //---------------------------------------------------------------------------
407   public List<SsmlDifferentialFormat> getDifferentialFormat()
408   {
409      return mDifferentialFormats;
410   }
411
412   //---------------------------------------------------------------------------
413   public int defineStyleFormat(SsmlStyleFormat inValue)
414   {
415      int index = mCellStyleFormats.size();
416      mCellStyleFormats.add(inValue);
417
418      if (null == mCellFormatsTag)
419      {
420         mStyleFormatsTag = new XMLTag(SsmlXML.CELL_STYLE_FORMATS);
421         mStylesheetTag.addSubtag(mStyleFormatsTag);
422      }
423
424      // Add it to a cell style formats (cellStyleXfs) tag
425      mStyleFormatsTag.addSubtag(inValue);
426      mStyleFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mCellStyleFormats.size());
427
428      return index;
429   }
430
431   //---------------------------------------------------------------------------
432   public List<SsmlStyleFormat> getCellStyleFormats()
433   {
434      return Collections.unmodifiableList(mCellStyleFormats);
435   }
436
437   //---------------------------------------------------------------------------
438   public SsmlStyleFormat getStyleFormat(int inIndex)
439   {
440      SsmlStyleFormat requestedStyleFormat = null;
441      if (inIndex >=0
442          && inIndex < mCellStyleFormats.size())
443      {
444         requestedStyleFormat = mCellStyleFormats.get(inIndex);
445      }
446
447      return requestedStyleFormat;
448   }
449
450
451   //---------------------------------------------------------------------------
452   public int defineCellStyle(SsmlCellStyle inValue)
453   {
454      int index = mCellStyles.size();
455      mCellStyles.add(inValue);
456
457      if (null == mCellStylesTag)
458      {
459         mCellStylesTag = new XMLTag(SsmlXML.CELL_STYLES);
460         mStylesheetTag.addSubtag(mCellStylesTag);
461      }
462
463      // Add it to a cell styles (cellStyles) tag
464      mCellStylesTag.addSubtag(inValue);
465      mCellStylesTag.setAttribute(SsmlXML.COUNT_ATT, mCellStyles.size());
466
467      return index;
468   }
469
470   //---------------------------------------------------------------------------
471   public List<SsmlCellStyle> getCellStyles()
472   {
473      return Collections.unmodifiableList(mCellStyles);
474   }
475
476
477   //---------------------------------------------------------------------------
478   public void finalizeStyles()
479   {
480      condenseFonts();
481
482      // TODO: condenseFills();
483
484      condenseBorders();
485
486      // TODO: condenseCellStyleFormats();
487
488      condenseCellFormats();
489   }
490
491   //---------------------------------------------------------------------------
492   private void condenseFonts()
493   {
494      long startTime = System.currentTimeMillis();
495
496      if (LOGGER.isLoggable(Level.FINE))
497      {
498         LOGGER.log(Level.FINE, getFonts().size() + " fonts before condensation.");
499      }
500
501      OrderedMap<Integer, SsmlFont> condensedFontMap = new OrderedMap<>(25);
502      for (SsmlFont font : getFonts())
503      {
504         if (0 == condensedFontMap.size())
505         {
506            condensedFontMap.put(font.getIndex(), font);
507         }
508         else
509         {
510            boolean isDuplicate = false;
511            for (SsmlFont condensedFont : condensedFontMap.values())
512            {
513               if (font.equals(condensedFont))
514               {
515                  condensedFontMap.put(font.getIndex(), condensedFont);
516                  isDuplicate = true;
517                  break;
518               }
519            }
520
521            if (! isDuplicate)
522            {
523               condensedFontMap.put(font.getIndex(), font);
524            }
525         }
526      }
527
528      OrderedSet<SsmlFont> condensedFonts = new OrderedSet<>(condensedFontMap.values());
529
530      mFontsTag.clearSubtags();
531      mFontsTag.setAttribute(SsmlXML.COUNT_ATT, condensedFonts.size());
532
533      int index = 0;
534      for (SsmlFont condensedFont : condensedFonts)
535      {
536         condensedFont.setIndex(index++);
537         mFontsTag.addSubtag(condensedFont);
538      }
539
540      // Now update font ids
541      for (SsmlStyleFormat styleFormat : getCellStyleFormats())
542      {
543         SsmlFont existingFont = styleFormat.getFont();
544         if (existingFont != null)
545         {
546            styleFormat.setFont(condensedFontMap.get(existingFont.getIndex()));
547         }
548      }
549
550      for (SsmlCellFormat cellFormat : getCellFormats())
551      {
552         cellFormat.setFont(condensedFontMap.get(cellFormat.getFontId()));
553      }
554
555      if (LOGGER.isLoggable(Level.INFO))
556      {
557         if (LOGGER.isLoggable(Level.FINE))
558         {
559            LOGGER.log(Level.FINE, condensedFonts.size() + " fonts after condensation.");
560         }
561
562         LOGGER.log(Level.INFO, "condenseFonts() timing: " + DateUtil.generateElapsedTimeString(startTime));
563      }
564   }
565
566   //---------------------------------------------------------------------------
567   private void condenseBorders()
568   {
569      long startTime = System.currentTimeMillis();
570
571      if (LOGGER.isLoggable(Level.FINE))
572      {
573         LOGGER.log(Level.FINE, getBorders().size() + " borders before condensation.");
574      }
575
576      OrderedMap<Integer, SsmlBorder> condensedBorderMap = new OrderedMap<>(25);
577      for (SsmlBorder border : getBorders())
578      {
579         if (0 == condensedBorderMap.size())
580         {
581            condensedBorderMap.put(border.getIndex(), border);
582         }
583         else
584         {
585            boolean isDuplicate = false;
586            for (SsmlBorder condensedBorder : condensedBorderMap.values())
587            {
588               if (border.equals(condensedBorder))
589               {
590                  condensedBorderMap.put(border.getIndex(), condensedBorder);
591                  isDuplicate = true;
592                  break;
593               }
594            }
595
596            if (! isDuplicate)
597            {
598               condensedBorderMap.put(border.getIndex(), border);
599            }
600         }
601      }
602
603      OrderedSet<SsmlBorder> condensedBorders = new OrderedSet<>(condensedBorderMap.values());
604
605      mBordersTag.clearSubtags();
606      mBordersTag.setAttribute(SsmlXML.COUNT_ATT, condensedBorders.size());
607
608      int index = 0;
609      for (SsmlBorder condensedBorder : condensedBorders)
610      {
611         condensedBorder.setIndex(index++);
612         mBordersTag.addSubtag(condensedBorder);
613      }
614
615      // Now update font ids
616      for (SsmlStyleFormat styleFormat : getCellStyleFormats())
617      {
618         SsmlBorder existingBorder = styleFormat.getBorder();
619         if (existingBorder != null)
620         {
621            styleFormat.setBorder(condensedBorderMap.get(existingBorder.getIndex()));
622         }
623      }
624
625      for (SsmlCellFormat cellFormat : getCellFormats())
626      {
627         cellFormat.setBorder(condensedBorderMap.get(cellFormat.getBorderId()));
628      }
629
630      if (LOGGER.isLoggable(Level.INFO))
631      {
632         if (LOGGER.isLoggable(Level.FINE))
633         {
634            LOGGER.log(Level.FINE, condensedBorders.size() + " borders after condensation.");
635         }
636
637         LOGGER.log(Level.INFO, "condenseBorders() timing: " + DateUtil.generateElapsedTimeString(startTime));
638      }
639   }
640
641
642   //---------------------------------------------------------------------------
643   private void condenseCellFormats()
644   {
645      long startTime = System.currentTimeMillis();
646
647      if (LOGGER.isLoggable(Level.FINE))
648      {
649         LOGGER.log(Level.FINE, getCellFormats().size() + " cell formats before condensation.");
650      }
651
652      // First, build a map of the existing indices to the condensed list of formats
653      OrderedMap<Integer, SsmlCellFormat> condensedCellFormatMap = new OrderedMap<>(25);
654      for (SsmlCellFormat cellFormat : getCellFormats())
655      {
656         if (0 == condensedCellFormatMap.size())
657         {
658            condensedCellFormatMap.put(cellFormat.getIndex(), cellFormat);
659         }
660         else
661         {
662            boolean isDuplicate = false;
663            for (SsmlCellFormat condensedCellFormat : condensedCellFormatMap.values())
664            {
665               if (cellFormat.equals(condensedCellFormat))
666               {
667                  condensedCellFormatMap.put(cellFormat.getIndex(), condensedCellFormat);
668                  isDuplicate = true;
669                  break;
670               }
671            }
672
673            if (! isDuplicate)
674            {
675               condensedCellFormatMap.put(cellFormat.getIndex(), cellFormat);
676            }
677         }
678      }
679
680
681      // Second, clear the cell format tags from the styles part
682      mCellFormatsTag.clearSubtags();
683
684      // Third, add back the condensed list of cell formats
685      OrderedSet<SsmlCellFormat> condensedCellFormats = new OrderedSet<>(condensedCellFormatMap.values());
686      mCellFormatsTag.setAttribute(SsmlXML.COUNT_ATT, condensedCellFormats.size());
687
688      int index = 0;
689      for (SsmlCellFormat condensedCellFormat : condensedCellFormats)
690      {
691         condensedCellFormat.setIndex(index++);
692         mCellFormatsTag.addSubtag(condensedCellFormat);
693      }
694
695      // Fourth, update references to cell formats
696      for (SsmlWorksheet worksheet : getParentDoc().getWorkbook().getWorksheets())
697      {
698         SsmlSheetData sheetData = worksheet.getSheetData();
699
700         for (SsmlRow row : sheetData.getRows())
701         {
702            for (SsmlCell cell : row.getCells())
703            {
704               String style = cell.getAttributeValue(SsmlXML.STYLE_IDX_ATT);
705               if (StringUtil.isSet(style))
706               {
707                  SsmlCellFormat newCellFormat = condensedCellFormatMap.get(Integer.parseInt(style));
708                  if (newCellFormat != null)
709                  {
710                     cell.setAttribute(SsmlXML.STYLE_IDX_ATT, newCellFormat.getIndex());
711                  }
712                  else
713                  {
714                     // This should never be the case. It means that something was referencing a non-existent cell format!?
715                     System.err.printf("Dangling cell format reference! Sheet:%s, Cell:%s, StyleIdx:%s%n", worksheet.getName(), cell.getRef(), style);
716                  }
717               }
718            }
719         }
720      }
721
722      if (LOGGER.isLoggable(Level.INFO))
723      {
724         if (LOGGER.isLoggable(Level.FINE))
725         {
726            LOGGER.log(Level.FINE, condensedCellFormats.size() + " cell formats after condensation.");
727         }
728
729         LOGGER.log(Level.INFO, "condenseCellFormats() timing: " + DateUtil.generateElapsedTimeString(startTime));
730      }
731   }
732
733
734   private class SsmlStylesheet extends SsmlXMLTag
735   {
736      //---------------------------------------------------------------------------
737      public SsmlStylesheet(Xlsx inXlsx)
738      {
739         super(SsmlXML.STYLESHEET, inXlsx);
740      }
741   }
742}