com.hfg.xml.msofficexml.xlsx.spreadsheetml.style.StylesPart Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.xml.msofficexml.xlsx.spreadsheetml.style;
import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.hfg.datetime.DateUtil;
import com.hfg.util.StringUtil;
import com.hfg.util.collection.OrderedMap;
import com.hfg.util.collection.OrderedSet;
import com.hfg.xml.XMLDoc;
import com.hfg.util.StackTraceUtil;
import com.hfg.xml.XMLTag;
import com.hfg.xml.msofficexml.OfficeOpenXMLContentType;
import com.hfg.xml.msofficexml.OfficeOpenXmlException;
import com.hfg.xml.msofficexml.OfficeXML;
import com.hfg.xml.msofficexml.docx.part.WmlContentType;
import com.hfg.xml.msofficexml.xlsx.SsmlRelationshipType;
import com.hfg.xml.msofficexml.xlsx.Xlsx;
import com.hfg.xml.msofficexml.xlsx.part.SsmlContentType;
import com.hfg.xml.msofficexml.xlsx.part.XlsxPart;
import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlCell;
import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlRow;
import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlSheetData;
import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlWorksheet;
import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlXML;
import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlXMLTag;
//------------------------------------------------------------------------------
/**
Styles part specific to OfficeOpenXML SpreadsheetML.
@author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
public class StylesPart extends XlsxPart
{
private XMLTag mStylesheetTag;
private XMLTag mNumberFormatsTag;
private XMLTag mFontsTag;
private XMLTag mFillsTag;
private XMLTag mBordersTag;
private XMLTag mCellFormatsTag;
private XMLTag mStyleFormatsTag;
private XMLTag mCellStylesTag;
private XMLTag mDifferentialFormatsTag;
private SsmlFont mDefaultFont;
private SsmlFill mDefaultFill;
private SsmlBorder mDefaultBorder;
private SsmlStyleFormat mDefaultStyleFormat;
private SsmlCellFormat mDefaultCellFormat;
private SsmlCellStyle mNormalStyle;
private SsmlCellFormat mDefaultDateCellFormat;
private List mNumberFormats = new ArrayList<>();
private List mFonts = new ArrayList<>();
private List mFills = new ArrayList<>();
private List mBorders = new ArrayList<>();
private List mCellFormats = new ArrayList<>();
private List mCellStyleFormats = new ArrayList<>();
private List mCellStyles = new ArrayList<>();
private List mDifferentialFormats = new ArrayList<>();
private final static Logger LOGGER = OfficeXML.getLogger();
//##########################################################################
// CONSTRUCTORS
//##########################################################################
//---------------------------------------------------------------------------
/**
This constructor should only be used by the Xlsx object.
* @param inXlsx the parent Excel document
*/
public StylesPart(Xlsx inXlsx)
{
super(inXlsx);
setFile(SsmlXML.STYLES_FILE);
if (! (StackTraceUtil.getCallingClassName().equals(getClass().getName())
|| StackTraceUtil.getCallingClassName().equals(Xlsx.class.getName())))
{
throw new OfficeOpenXmlException("An Xlsx object can have only one styles part! Use xlsx.getStylesPart() to access it.");
}
// Register the content type
inXlsx.getContentTypesPart().addOverride(this);
// Register the relationship
inXlsx.getWorkbookRelationshipPart().addRelationship(SsmlRelationshipType.STYLES, this);
mStylesheetTag = new SsmlStylesheet(inXlsx);
setRootNode(mStylesheetTag);
// setDefaults();
}
//---------------------------------------------------------------------------
public StylesPart(Xlsx inXlsx, BufferedInputStream inStream)
{
this(inXlsx);
XMLDoc doc = new XMLDoc(inStream);
mStylesheetTag = (XMLTag) doc.getRootNode();
setRootNode(doc.getRootNode());
mNumberFormatsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.NUM_FORMATS);
if (mNumberFormatsTag != null)
{
for (XMLTag subtag : (List) (Object) mNumberFormatsTag.getSubtagsByName(SsmlXML.NUM_FORMAT))
{
mNumberFormats.add(new SsmlNumberFormat(inXlsx, subtag));
}
}
mFontsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.FONTS);
if (mFontsTag != null)
{
for (XMLTag subtag : (List) (Object) mFontsTag.getSubtagsByName(SsmlXML.FONT))
{
SsmlFont font = new SsmlFont(inXlsx, subtag);
mFonts.add(font);
font.setIndex(mFonts.size() - 1);
}
}
// TODO
mFillsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.FILLS);
// TODO
mBordersTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.BORDERS);
mCellFormatsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.CELL_FORMATS);
if (mCellFormatsTag != null)
{
for (XMLTag subtag : (List) (Object) mCellFormatsTag.getSubtagsByName(SsmlXML.CELL_FORMAT))
{
mCellFormats.add(new SsmlCellFormat(inXlsx, subtag));
}
}
mStyleFormatsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.CELL_STYLE_FORMATS);
mCellStylesTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.CELL_STYLES);
mDifferentialFormatsTag = mStylesheetTag.getOptionalSubtagByName(SsmlXML.DIFFERENTIAL_FORMATS);
}
//---------------------------------------------------------------------------
public void setDefaults()
{
// mDefaultFont = new SsmlFont(this).setName("Verdana").setSize(10);
mDefaultFont = new SsmlFont(this).setName("Calibri").setSize(11).lock();
mDefaultFill = new SsmlFill(this).setPatternType(SsmlPatternType.none);
// Add a second default fill value that sometimes seems to be required
new SsmlFill(this).setPatternType(SsmlPatternType.gray125);
mDefaultBorder = new SsmlBorder(this);
mDefaultStyleFormat = new SsmlStyleFormat(this)
.setFont(mDefaultFont)
.setFill(mDefaultFill)
.setBorder(mDefaultBorder);
mDefaultCellFormat = new SsmlCellFormat(this)
.setFont(mDefaultFont)
.setFill(mDefaultFill)
.setBorder(mDefaultBorder)
.setStyleFormat(mDefaultStyleFormat);
mNormalStyle = new SsmlCellStyle(this)
.setName("Normal")
.setBuiltinId(1)
.setStyleFormat(mDefaultStyleFormat);
}
//##########################################################################
// PUBLIC METHODS
//##########################################################################
//---------------------------------------------------------------------------
@Override
public OfficeOpenXMLContentType getContentType()
{
return SsmlContentType.SPREADSHEET_STYLES;
}
//---------------------------------------------------------------------------
public SsmlFont getDefaultFont()
{
return mDefaultFont;
}
//---------------------------------------------------------------------------
// The default font is special - it must occupy the 0 index position.
public StylesPart setDefaultFont(SsmlFont inValue)
{
mFonts.remove(mDefaultFont);
mFontsTag.removeSubtag(mDefaultFont);
// Create a locked copy so the default font isn't inadvertently changed.
mDefaultFont = inValue.clone();
// It has to go in the 0 position so we have to move it.
// First pull it out using the index.
mFonts.remove(mDefaultFont.getIndex());
mFonts.add(0, mDefaultFont);
// Now do the same for the list of font tags.
mFontsTag.removeSubtag(mDefaultFont.getIndex());
mFontsTag.addSubtag(0, mDefaultFont);
mDefaultFont.setIndex(0); // We shouldn't have to adjust any other index values
mDefaultFont.lock();
return this;
}
//---------------------------------------------------------------------------
public SsmlFill getDefaultFill()
{
return mDefaultFill;
}
//---------------------------------------------------------------------------
public SsmlBorder getDefaultBorder()
{
return mDefaultBorder;
}
//---------------------------------------------------------------------------
public SsmlStyleFormat getDefaultStyleFormat()
{
return mDefaultStyleFormat;
}
//---------------------------------------------------------------------------
public SsmlCellFormat getDefaultCellFormat()
{
return mDefaultCellFormat;
}
//---------------------------------------------------------------------------
public SsmlCellFormat getDefaultDateCellFormat()
{
if (null == mDefaultDateCellFormat)
{
mDefaultDateCellFormat = new SsmlCellFormat(this)
.setNumberFormat(SsmlNumberFormat.DATE_MM_DD_YY)
.setFont(mDefaultFont)
.setFill(mDefaultFill)
.setBorder(mDefaultBorder);
}
return mDefaultDateCellFormat;
}
//---------------------------------------------------------------------------
public List getNumberFormats()
{
return mNumberFormats;
}
//---------------------------------------------------------------------------
public int defineNumberFormat(SsmlNumberFormat inValue)
{
int index = mNumberFormats.size();
mNumberFormats.add(inValue);
if (null == mNumberFormatsTag)
{
mNumberFormatsTag = new XMLTag(SsmlXML.NUM_FORMATS);
mStylesheetTag.addSubtag(mNumberFormatsTag);
}
// Add it to a number formats tag
mNumberFormatsTag.addSubtag(inValue);
mNumberFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mNumberFormats.size());
return index;
}
//---------------------------------------------------------------------------
public int defineFont(SsmlFont inValue)
{
int index = mFonts.size();
mFonts.add(inValue);
if (null == mFontsTag)
{
mFontsTag = new XMLTag(SsmlXML.FONTS);
mStylesheetTag.addSubtag(mFontsTag);
}
// Add it to a fonts tag
mFontsTag.addSubtag(inValue);
mFontsTag.setAttribute(SsmlXML.COUNT_ATT, mFonts.size());
return index;
}
//---------------------------------------------------------------------------
public List getFonts()
{
return mFonts;
}
//---------------------------------------------------------------------------
public List getFills()
{
return mFills;
}
//---------------------------------------------------------------------------
public int defineFill(SsmlFill inValue)
{
int index = mFills.size();
mFills.add(inValue);
if (null == mFillsTag)
{
mFillsTag = new XMLTag(SsmlXML.FILLS);
mStylesheetTag.addSubtag(mFillsTag);
}
// Add it to a fills tag
mFillsTag.addSubtag(inValue);
mFillsTag.setAttribute(SsmlXML.COUNT_ATT, mFills.size());
return index;
}
//---------------------------------------------------------------------------
public List getBorders()
{
return mBorders;
}
//---------------------------------------------------------------------------
public int defineBorder(SsmlBorder inValue)
{
int index = mBorders.size();
mBorders.add(inValue);
if (null == mBordersTag)
{
mBordersTag = new XMLTag(SsmlXML.BORDERS);
mStylesheetTag.addSubtag(mBordersTag);
}
// Add it to a borders tag
mBordersTag.addSubtag(inValue);
mBordersTag.setAttribute(SsmlXML.COUNT_ATT, mBorders.size());
return index;
}
//---------------------------------------------------------------------------
public int defineCellFormat(SsmlCellFormat inValue)
{
int index = mCellFormats.size();
mCellFormats.add(inValue);
if (null == mCellFormatsTag)
{
mCellFormatsTag = new XMLTag(SsmlXML.CELL_FORMATS);
mStylesheetTag.addSubtag(mCellFormatsTag);
}
// Add it to a cell formats (cellXfs) tag
mCellFormatsTag.addSubtag(inValue);
mCellFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mCellFormats.size());
return index;
}
//---------------------------------------------------------------------------
public List getCellFormats()
{
return mCellFormats;
}
//---------------------------------------------------------------------------
public int defineDifferentialFormat(SsmlDifferentialFormat inValue)
{
int index = mDifferentialFormats.size();
mDifferentialFormats.add(inValue);
if (null == mDifferentialFormatsTag)
{
mDifferentialFormatsTag = new XMLTag(SsmlXML.DIFFERENTIAL_FORMATS);
mStylesheetTag.addSubtag(mDifferentialFormatsTag);
}
// Add it to a differential formats (dxfs) tag
mDifferentialFormatsTag.addSubtag(inValue);
mDifferentialFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mDifferentialFormats.size());
return index;
}
//---------------------------------------------------------------------------
public List getDifferentialFormat()
{
return mDifferentialFormats;
}
//---------------------------------------------------------------------------
public int defineStyleFormat(SsmlStyleFormat inValue)
{
int index = mCellStyleFormats.size();
mCellStyleFormats.add(inValue);
if (null == mCellFormatsTag)
{
mStyleFormatsTag = new XMLTag(SsmlXML.CELL_STYLE_FORMATS);
mStylesheetTag.addSubtag(mStyleFormatsTag);
}
// Add it to a cell style formats (cellStyleXfs) tag
mStyleFormatsTag.addSubtag(inValue);
mStyleFormatsTag.setAttribute(SsmlXML.COUNT_ATT, mCellStyleFormats.size());
return index;
}
//---------------------------------------------------------------------------
public List getCellStyleFormats()
{
return Collections.unmodifiableList(mCellStyleFormats);
}
//---------------------------------------------------------------------------
public SsmlStyleFormat getStyleFormat(int inIndex)
{
SsmlStyleFormat requestedStyleFormat = null;
if (inIndex >=0
&& inIndex < mCellStyleFormats.size())
{
requestedStyleFormat = mCellStyleFormats.get(inIndex);
}
return requestedStyleFormat;
}
//---------------------------------------------------------------------------
public int defineCellStyle(SsmlCellStyle inValue)
{
int index = mCellStyles.size();
mCellStyles.add(inValue);
if (null == mCellStylesTag)
{
mCellStylesTag = new XMLTag(SsmlXML.CELL_STYLES);
mStylesheetTag.addSubtag(mCellStylesTag);
}
// Add it to a cell styles (cellStyles) tag
mCellStylesTag.addSubtag(inValue);
mCellStylesTag.setAttribute(SsmlXML.COUNT_ATT, mCellStyles.size());
return index;
}
//---------------------------------------------------------------------------
public List getCellStyles()
{
return Collections.unmodifiableList(mCellStyles);
}
//---------------------------------------------------------------------------
public void finalizeStyles()
{
condenseFonts();
// TODO: condenseFills();
condenseBorders();
// TODO: condenseCellStyleFormats();
condenseCellFormats();
}
//---------------------------------------------------------------------------
private void condenseFonts()
{
long startTime = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.FINE))
{
LOGGER.log(Level.FINE, getFonts().size() + " fonts before condensation.");
}
OrderedMap condensedFontMap = new OrderedMap<>(25);
for (SsmlFont font : getFonts())
{
if (0 == condensedFontMap.size())
{
condensedFontMap.put(font.getIndex(), font);
}
else
{
boolean isDuplicate = false;
for (SsmlFont condensedFont : condensedFontMap.values())
{
if (font.equals(condensedFont))
{
condensedFontMap.put(font.getIndex(), condensedFont);
isDuplicate = true;
break;
}
}
if (! isDuplicate)
{
condensedFontMap.put(font.getIndex(), font);
}
}
}
OrderedSet condensedFonts = new OrderedSet<>(condensedFontMap.values());
mFontsTag.clearSubtags();
mFontsTag.setAttribute(SsmlXML.COUNT_ATT, condensedFonts.size());
int index = 0;
for (SsmlFont condensedFont : condensedFonts)
{
condensedFont.setIndex(index++);
mFontsTag.addSubtag(condensedFont);
}
// Now update font ids
for (SsmlStyleFormat styleFormat : getCellStyleFormats())
{
SsmlFont existingFont = styleFormat.getFont();
if (existingFont != null)
{
styleFormat.setFont(condensedFontMap.get(existingFont.getIndex()));
}
}
for (SsmlCellFormat cellFormat : getCellFormats())
{
cellFormat.setFont(condensedFontMap.get(cellFormat.getFontId()));
}
if (LOGGER.isLoggable(Level.INFO))
{
if (LOGGER.isLoggable(Level.FINE))
{
LOGGER.log(Level.FINE, condensedFonts.size() + " fonts after condensation.");
}
LOGGER.log(Level.INFO, "condenseFonts() timing: " + DateUtil.generateElapsedTimeString(startTime));
}
}
//---------------------------------------------------------------------------
private void condenseBorders()
{
long startTime = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.FINE))
{
LOGGER.log(Level.FINE, getBorders().size() + " borders before condensation.");
}
OrderedMap condensedBorderMap = new OrderedMap<>(25);
for (SsmlBorder border : getBorders())
{
if (0 == condensedBorderMap.size())
{
condensedBorderMap.put(border.getIndex(), border);
}
else
{
boolean isDuplicate = false;
for (SsmlBorder condensedBorder : condensedBorderMap.values())
{
if (border.equals(condensedBorder))
{
condensedBorderMap.put(border.getIndex(), condensedBorder);
isDuplicate = true;
break;
}
}
if (! isDuplicate)
{
condensedBorderMap.put(border.getIndex(), border);
}
}
}
OrderedSet condensedBorders = new OrderedSet<>(condensedBorderMap.values());
mBordersTag.clearSubtags();
mBordersTag.setAttribute(SsmlXML.COUNT_ATT, condensedBorders.size());
int index = 0;
for (SsmlBorder condensedBorder : condensedBorders)
{
condensedBorder.setIndex(index++);
mBordersTag.addSubtag(condensedBorder);
}
// Now update font ids
for (SsmlStyleFormat styleFormat : getCellStyleFormats())
{
SsmlBorder existingBorder = styleFormat.getBorder();
if (existingBorder != null)
{
styleFormat.setBorder(condensedBorderMap.get(existingBorder.getIndex()));
}
}
for (SsmlCellFormat cellFormat : getCellFormats())
{
cellFormat.setBorder(condensedBorderMap.get(cellFormat.getBorderId()));
}
if (LOGGER.isLoggable(Level.INFO))
{
if (LOGGER.isLoggable(Level.FINE))
{
LOGGER.log(Level.FINE, condensedBorders.size() + " borders after condensation.");
}
LOGGER.log(Level.INFO, "condenseBorders() timing: " + DateUtil.generateElapsedTimeString(startTime));
}
}
//---------------------------------------------------------------------------
private void condenseCellFormats()
{
long startTime = System.currentTimeMillis();
if (LOGGER.isLoggable(Level.FINE))
{
LOGGER.log(Level.FINE, getCellFormats().size() + " cell formats before condensation.");
}
// First, build a map of the existing indices to the condensed list of formats
OrderedMap condensedCellFormatMap = new OrderedMap<>(25);
for (SsmlCellFormat cellFormat : getCellFormats())
{
if (0 == condensedCellFormatMap.size())
{
condensedCellFormatMap.put(cellFormat.getIndex(), cellFormat);
}
else
{
boolean isDuplicate = false;
for (SsmlCellFormat condensedCellFormat : condensedCellFormatMap.values())
{
if (cellFormat.equals(condensedCellFormat))
{
condensedCellFormatMap.put(cellFormat.getIndex(), condensedCellFormat);
isDuplicate = true;
break;
}
}
if (! isDuplicate)
{
condensedCellFormatMap.put(cellFormat.getIndex(), cellFormat);
}
}
}
// Second, clear the cell format tags from the styles part
mCellFormatsTag.clearSubtags();
// Third, add back the condensed list of cell formats
OrderedSet condensedCellFormats = new OrderedSet<>(condensedCellFormatMap.values());
mCellFormatsTag.setAttribute(SsmlXML.COUNT_ATT, condensedCellFormats.size());
int index = 0;
for (SsmlCellFormat condensedCellFormat : condensedCellFormats)
{
condensedCellFormat.setIndex(index++);
mCellFormatsTag.addSubtag(condensedCellFormat);
}
// Fourth, update references to cell formats
for (SsmlWorksheet worksheet : getParentDoc().getWorkbook().getWorksheets())
{
SsmlSheetData sheetData = worksheet.getSheetData();
for (SsmlRow row : sheetData.getRows())
{
for (SsmlCell cell : row.getCells())
{
String style = cell.getAttributeValue(SsmlXML.STYLE_IDX_ATT);
if (StringUtil.isSet(style))
{
SsmlCellFormat newCellFormat = condensedCellFormatMap.get(Integer.parseInt(style));
if (newCellFormat != null)
{
cell.setAttribute(SsmlXML.STYLE_IDX_ATT, newCellFormat.getIndex());
}
else
{
// This should never be the case. It means that something was referencing a non-existent cell format!?
System.err.printf("Dangling cell format reference! Sheet:%s, Cell:%s, StyleIdx:%s%n", worksheet.getName(), cell.getRef(), style);
}
}
}
}
}
if (LOGGER.isLoggable(Level.INFO))
{
if (LOGGER.isLoggable(Level.FINE))
{
LOGGER.log(Level.FINE, condensedCellFormats.size() + " cell formats after condensation.");
}
LOGGER.log(Level.INFO, "condenseCellFormats() timing: " + DateUtil.generateElapsedTimeString(startTime));
}
}
private class SsmlStylesheet extends SsmlXMLTag
{
//---------------------------------------------------------------------------
public SsmlStylesheet(Xlsx inXlsx)
{
super(SsmlXML.STYLESHEET, inXlsx);
}
}
}