package com.yoursway.commons.excelexport;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import com.yoursway.commons.excelexport.internal.RuntimeIOException;
import com.yoursway.commons.excelexport.internal.Pool;
import com.yoursway.commons.excelexport.internal.Theme;
import com.yoursway.utils.XmlWriter;
import com.yoursway.utils.YsFileUtils;
public class Workbook {
private Pool<String> stringPool = Pool.create();
private Pool<Fill> fillPool = Pool.create();
private Pool<BorderSet> borderPool = Pool.create();
private Pool<CellFormat> formatPool = Pool.create();
private List<Sheet> sheets = new ArrayList<Sheet>();
public Sheet addSheet(String name) {
Sheet sheet = new Sheet(this, sheets.size() + 1, name);
sheets.add(sheet);
return sheet;
}
public void build(OutputStream output) throws IOException {
try {
doBuild(output);
} catch (RuntimeIOException e) {
throw e.getCause();
}
}
private void doBuild(OutputStream output) throws IOException {
ZipOutputStream zip = new ZipOutputStream(output);
stringPool.add("Foo");
zip.putNextEntry(new ZipEntry("[Content_Types].xml"));
writeContentTypesXml(zip);
zip.putNextEntry(new ZipEntry("_rels/.rels"));
writeRelsXml(zip);
zip.putNextEntry(new ZipEntry("docProps/app.xml"));
writeDocPropsAppXml(zip);
zip.putNextEntry(new ZipEntry("docProps/core.xml"));
writeDocPropsCoreXml(zip);
zip.putNextEntry(new ZipEntry("xl/_rels/workbook.xml.rels"));
writeWorkbookRelsXml(zip);
zip.putNextEntry(new ZipEntry("xl/theme/theme1.xml"));
YsFileUtils.transfer(Theme.getThemeFileAsStream(), zip);
zip.putNextEntry(new ZipEntry("xl/styles.xml"));
writeStylesXml(zip);
zip.putNextEntry(new ZipEntry("xl/workbook.xml"));
writeWorkbookXml(zip);
for (Sheet sheet : sheets) {
zip.putNextEntry(new ZipEntry("xl/worksheets/sheet" + sheet.ordinal() + ".xml"));
writeSheetXml(zip, sheet);
}
zip.putNextEntry(new ZipEntry("xl/sharedStrings.xml"));
writeSharedStringsXml(zip);
zip.finish();
}
private void writeContentTypesXml(OutputStream out) throws IOException {
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("Types").xmlns("http://schemas.openxmlformats.org/package/2006/content-types");
xml.tag("Override", "PartName", "/xl/theme/theme1.xml", "ContentType",
"application/vnd.openxmlformats-officedocument.theme+xml");
xml.tag("Override", "PartName", "/xl/styles.xml", "ContentType",
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
xml.tag("Default", "Extension", "rels", "ContentType",
"application/vnd.openxmlformats-package.relationships+xml");
xml.tag("Default", "Extension", "xml", "ContentType", "application/xml");
xml.tag("Override", "PartName", "/xl/workbook.xml", "ContentType",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
xml.tag("Override", "PartName", "/docProps/app.xml", "ContentType",
"application/vnd.openxmlformats-officedocument.extended-properties+xml");
for (Sheet sheet : sheets)
xml.tag("Override", "PartName", "/xl/worksheets/sheet" + sheet.ordinal() + ".xml", "ContentType",
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
xml.tag("Override", "PartName", "/xl/sharedStrings.xml", "ContentType",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
xml.tag("Override", "PartName", "/docProps/core.xml", "ContentType",
"application/vnd.openxmlformats-package.core-properties+xml");
xml.end().finish();
}
private void writeRelsXml(OutputStream out) throws IOException {
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("Relationships").xmlns("http://schemas.openxmlformats.org/package/2006/relationships");
xml.tag("Relationship", "Id", "rId3", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
"Target", "docProps/app.xml");
xml.tag("Relationship", "Id", "rId2", "Type",
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
"Target", "docProps/core.xml");
xml.tag("Relationship", "Id", "rId1", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", "Target",
"xl/workbook.xml");
xml.end().finish();
}
private void writeWorkbookRelsXml(OutputStream out) throws IOException {
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("Relationships").xmlns("http://schemas.openxmlformats.org/package/2006/relationships");
for (Sheet sheet : sheets)
xml.tag("Relationship", "Id", "sheetId" + sheet.ordinal(), "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", "Target",
"worksheets/sheet" + sheet.ordinal() + ".xml");
xml.tag("Relationship", "Id", "rId6", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings", "Target",
"sharedStrings.xml");
xml.tag("Relationship", "Id", "rId5", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "Target",
"styles.xml");
xml.tag("Relationship", "Id", "rId4", "Type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme", "Target",
"theme/theme1.xml");
xml.end().finish();
}
private void writeDocPropsAppXml(OutputStream out) throws IOException {
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("Properties").xmlns(
"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties").xmlns("vt",
"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
xml.tag("Application", "Microsoft Excel");
xml.tag("DocSecurity", "0");
xml.tag("ScaleCrop", "false");
xml.start("HeadingPairs").start("vt:vector", "size", "2", "baseType", "variant");
xml.start("vt:variant").tag("vt:lpstr", "Worksheets").end();
xml.start("vt:variant").tag("vt:i4", ("" + sheets.size())).end();
xml.end().end();
xml.start("TitlesOfParts").start("vt:vector", "size", "" + sheets.size(), "baseType", "lpstr");
for (Sheet sheet : sheets)
xml.tag("vt:lpstr", sheet.name());
xml.end().end();
xml.tag("Company");
xml.tag("LinksUpToDate", "false");
xml.tag("SharedDoc", "false");
xml.tag("HyperlinksChanged", "false");
xml.tag("AppVersion", "12.0000");
xml.end().finish();
}
private void writeDocPropsCoreXml(OutputStream out) throws IOException {
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("cp:coreProperties").xmlns("cp",
"http://schemas.openxmlformats.org/package/2006/metadata/core-properties").xmlns("dc",
"http://purl.org/dc/elements/1.1/").xmlns("dcterms", "http://purl.org/dc/terms/").xmlns(
"dcmitype", "http://purl.org/dc/dcmitype/").xmlns("xsi",
"http://www.w3.org/2001/XMLSchema-instance");
xml.tag("dc:creator", "Tour Guide Tool");
xml.tag("cp:lastModifiedBy", "Tour Guide Tool");
xml.tag("dcterms:created", "xsi:type", "dcterms:W3CDTF", "2009-02-01T22:02:10Z");
xml.tag("dcterms:modified", "xsi:type", "dcterms:W3CDTF", "2009-02-01T22:37:30Z");
xml.end().finish();
}
private void writeSharedStringsXml(OutputStream out) throws IOException {
List<String> strings = stringPool.entireSequence();
String size = Integer.toString(strings.size());
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("sst").xmlns("http://schemas.openxmlformats.org/spreadsheetml/2006/main").attr("count",
size).attr("uniqueCount", size);
for (String string : strings)
xml.start("si").tag("t", string).end();
xml.end().finish();
}
private void writeStylesXml(OutputStream out) throws IOException {
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("styleSheet").xmlns("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
xml.start("fonts", "count", "1");
xml.start("font").tag("sz", "val", "11").tag("color", "theme", "1").tag("name", "val", "Calibri")
.tag("family", "val", "2").tag("scheme", "val", "minor").end();
xml.end();
formatPool.add(CellFormat.DEFAULT);
for (Sheet sheet : sheets)
for (Row row : sheet.rows())
for (Cell cell : row.cells())
formatPool.add(cell.format());
// first two indexes seem to be special-cased in Excel
fillPool.add(Fill.NONE);
fillPool.add(Fill.GRAY125);
for (CellFormat format : formatPool.entireSequence())
fillPool.add(format.fill());
borderPool.add(BorderSet.DEFAULT);
for (CellFormat format : formatPool.entireSequence())
borderPool.add(format.borderSet());
List<Fill> fills = fillPool.entireSequence();
xml.start("fills", "count", "" + fills.size());
for (Fill fill : fills) {
xml.start("fill");
fill.encode(xml);
xml.end();
}
xml.end();
List<BorderSet> borders = borderPool.entireSequence();
xml.start("borders", "count", "" + borders.size());
for (BorderSet borderSet : borders) {
xml.start("border");
borderSet.encode(xml);
xml.end();
}
xml.end();
xml.start("cellStyleXfs", "count", "1");
xml.tag("xf", "numFmtId", "0", "fontId", "0", "fillId", "0", "borderId", "0");
xml.end();
xml.start("cellXfs", "count", "4");
for (CellFormat format : formatPool.entireSequence()) {
xml.start("xf", "numFmtId", "0", "fontId", "0", "fillId", "" + fillPool.retrieve(format.fill()),
"borderId", "" + borderPool.retrieve(format.borderSet())).attr("xfId", "0");
format.encode(xml);
xml.end();
}
xml.end();
xml.start("cellStyles", "count", "1");
xml.tag("cellStyle", "name", "Normal", "xfId", "0", "builtinId", "0");
xml.end();
xml.start("dxfs", "count", "0");
xml.end();
xml.start("tableStyles", "count", "0", "defaultTableStyle", "TableStyleMedium9", "defaultPivotStyle",
"PivotStyleLight16");
xml.end();
xml.end().finish();
}
private void writeWorkbookXml(OutputStream out) throws IOException {
XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("workbook").xmlns("http://schemas.openxmlformats.org/spreadsheetml/2006/main").xmlns("r",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships");
xml.tag("fileVersion", "appName", "xl", "lastEdited", "4", "lowestEdited", "4", "rupBuild", "4505");
xml.tag("workbookPr", "defaultThemeVersion", "124226");
xml.start("bookViews").tag("workbookView", "xWindow", "480", "yWindow", "30", "windowWidth", "16095",
"windowHeight", "5580").end();
xml.start("sheets");
for (Sheet sheet : sheets)
xml.tag("sheet", "name", sheet.name(), "sheetId", "" + sheet.ordinal(), "r:id", "sheetId"
+ sheet.ordinal());
xml.end();
xml.tag("calcPr", "calcId", "124519");
xml.end().finish();
}
private void writeSheetXml(OutputStream out, Sheet sheet) throws IOException {
final XmlWriter xml = new XmlWriter(out);
xml.xmlHeader("standalone", "yes");
xml.start("worksheet").xmlns("http://schemas.openxmlformats.org/spreadsheetml/2006/main").xmlns("r",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships");
xml.tag("dimension", "ref", sheet.range().name());
xml.start("sheetViews").start("sheetView", "workbookViewId", "0").end().end();
// .tag("selection", "activeCell", "B8", "sqref", "B8")
xml.tag("sheetFormatPr", "defaultRowHeight", "15");
boolean shouldEncodeCols = false;
for (Column column : sheet.columns())
shouldEncodeCols |= column.shouldEncode();
if (shouldEncodeCols) {
xml.start("cols");
for (Column column : sheet.columns())
if (column.shouldEncode())
column.encode(xml);
xml.end();
}
xml.start("sheetData");
Collection<Cell> mergedCells = new ArrayList<Cell>();
for (Row row : sheet.rows()) {
List<Cell> cells = row.cells();
xml.start("row", "r", "" + row.ordinal(), "spans", "1:" + cells.size());
for (Cell cell : cells) {
xml.start("c", "r", cell.name());
int formatId = formatPool.retrieve(cell.format());
if (formatId != 0)
xml.attr("s", "" + formatId);
cell.acceptContentVisitor(new ContentVisitor() {
public void visitNullContent() {
}
public void visitStringContent(String data) {
try {
xml.attr("t", "s").tag("v", "" + stringPool.add(data));
} catch (IOException e) {
throw new RuntimeIOException(e);
}
}
});
xml.end();
if (cell.horizonalSpan() > 1)
mergedCells.add(cell);
}
xml.end();
}
xml.end();
if (!mergedCells.isEmpty()) {
xml.start("mergeCells");
for (Cell cell : mergedCells)
xml.tag("mergeCell", "ref", cell.span().name());
xml.end();
}
xml.start("pageMargins", "left", "0.7", "right", "0.7", "top", "0.75", "bottom", "0.75").attr(
"header", "0.3").attr("footer", "0.3").end();
xml.end().finish();
}
}