package Xlsx;
import Utils.*;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Locale;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
public class SpreadsheetWriter {
public static class Sheet {
public String name = "";
public RefVector<String> formattedRows = new RefVector<>(String.class);
public RefVector<Object[]> valueRows = new RefVector<>(Object[].class);
public int colsInWidestRow = 0;
}
public DateFormat dateFormatter;
public DateFormat timeFormatter;
public final File parentFolder;
public final String fileName;
HashMap<String, Integer> stringsMap;
RefVector<String> stringsList;
int totalStringCount;
public RefVector<Sheet> sheets;
public int currentSheetIdx = 0;
public boolean shouldSaveAfterEveryRow = true;
public SpreadsheetWriter(File absPath) {
this(absPath.getParentFile(), absPath.getName());
}
public SpreadsheetWriter(File parentFolder, String name) {
this.parentFolder = parentFolder;
this.fileName = name;
this.dateFormatter = DateFormat.getDateInstance(2, Locale.US);
this.timeFormatter = DateFormat.getTimeInstance(2, Locale.US);
this.stringsMap = new HashMap<>();
this.stringsList = new RefVector<>(String.class);
this.totalStringCount = 0;
this.sheets = new RefVector<>(Sheet.class);
this.currentSheetIdx = 0;
}
public static SpreadsheetWriter fromExistingXlsx(String path) throws IOException {
ArrayList<XlsxReader.SheetCells> existingSheets = XlsxReader.loadXlsxFromFile(path);
SpreadsheetWriter writer = new SpreadsheetWriter(new File(path));
writer.addSheets(existingSheets);
return writer;
}
public void addSheets(ArrayList<XlsxReader.SheetCells> existingSheets) throws IOException {
if (existingSheets == null || existingSheets.isEmpty())
return;
boolean wasSaving = this.shouldSaveAfterEveryRow;
this.shouldSaveAfterEveryRow = false;
for (XlsxReader.SheetCells s : existingSheets) {
if (s.rows <= 0 || s.cols <= 0)
continue;
if (s.cells != null) {
for (int i = 0; i < s.rows; i++)
writeRowFromObjectArray(s.cells, i * s.cols, s.cols);
}
else if (s.valueRows != null) {
for (int i = 0; i < s.rows; i++) {
Object[] row = s.valueRows.buf[i];
writeRowFromObjectArray(row, 0, row.length);
}
}
currentSheetIdx++;
}
this.shouldSaveAfterEveryRow = wasSaving;
}
public static String makeColumnNumber(int n) {
if (n <= 1)
return "A";
byte[] buf = new byte[16];
int idx = buf.length;
while (n > 0) {
n--;
buf[--idx] = (byte)(0x41 + (n % 26));
n /= 26;
}
return new String(buf, idx, buf.length - idx);
}
private int lookupOrAddCellString(String str) {
Integer idx = stringsMap.get(str);
if (idx == null) {
int len = stringsList.size;
stringsList.add(str);
stringsMap.put(str, len);
return len;
}
return idx;
}
public void writeRow(Object... values) throws IOException {
Object[] valuesList = values;
writeRowFromObjectArray(valuesList, 0, valuesList.length);
}
public void writeRowFromObjectArray(Object[] values, int off, int len) throws IOException {
int sheetIdx = currentSheetIdx;
while (sheetIdx >= sheets.size)
sheets.add(new Sheet());
Sheet s = sheets.buf[sheetIdx];
int rowNumber = s.formattedRows.size + 1;
ByteVectorOutputStream sb = new ByteVectorOutputStream();
sb.add("<row r=\"");
sb.add("" + rowNumber);
sb.add("\">");
int colNumber = 0;
for (int i = 0; i < len; i++) {
colNumber++;
Object value = values[off+i];
if (value == null)
continue;
sb.add("<c r=\"");
sb.add(makeColumnNumber(colNumber));
sb.add("" + rowNumber);
sb.add("\"");
// set style index here with s=""
if (value instanceof Number) {
sb.add("><v>");
sb.add(value.toString());
sb.add("</v></c>");
}
else {
int idx = lookupOrAddCellString(value.toString());
totalStringCount++;
sb.add(" t=\"s\"><v>");
sb.add("" + idx);
sb.add("</v></c>");
}
}
sb.add("</row>");
if (colNumber > s.colsInWidestRow)
s.colsInWidestRow = colNumber;
Object[] valueRow = off == 0 && len == values.length ?
values :
Arrays.copyOfRange(values, off, off+len);
s.valueRows.add(valueRow);
s.formattedRows.add(sb.toString());
if (shouldSaveAfterEveryRow)
save();
}
public void save() throws IOException {
ByteVectorOutputStream vector = new ByteVectorOutputStream();
ZipOutputStream zip = new ZipOutputStream(vector);
String dateStr = LocalDateTime.now().atZone(ZoneId.of("GMT")).format(DateTimeFormatter.ISO_INSTANT);
int sheetCount = sheets.size;
for (int i = 0; i < sheetCount; i++) {
Sheet s = sheets.buf[i];
if (s.name == null || s.name.isEmpty())
s.name = "Sheet" + (i+1);
}
String[] folders = new String[] {
"_rels/",
"docProps/",
"xl/",
"xl/_rels/",
"xl/worksheets/"
};
for (String folder : folders) {
zip.putNextEntry(new ZipEntry(folder));
zip.closeEntry();
}
writeStringBuilderToZip(zip, "[Content_Types].xml", generateContentTypesXml(sheetCount));
writeStringBuilderToZip(zip, "_rels/.rels", generateTopRelsXml());
writeStringBuilderToZip(zip, "docProps/app.xml", generateAppXml(sheets));
writeStringBuilderToZip(zip, "docProps/core.xml", generateCoreXml("AngioTool-Batch", dateStr));
writeStringBuilderToZip(zip, "xl/_rels/workbook.xml.rels", generateWorkbookRels(sheetCount));
writeStringBuilderToZip(zip, "xl/styles.xml", generateStylesXml());
writeStringBuilderToZip(zip, "xl/workbook.xml", generateWorkbookXml(sheets, dateStr));
writeStringBuilderToZip(zip, "xl/sharedStrings.xml", generateSharedStringsXml(stringsList, totalStringCount));
for (int i = 0; i < sheetCount; i++)
writeStringBuilderToZip(zip, "xl/worksheets/sheet" + (i+1) + ".xml", generateSheetXml(sheets.buf[i], dateStr));
zip.close();
File outPath = new File(parentFolder, fileName);
FileOutputStream fileStream = new FileOutputStream(outPath);
fileStream.write(vector.buf, 0, vector.size);
fileStream.close();
}
static String escapeXmlString(String input) {
ByteVectorOutputStream sb = new ByteVectorOutputStream();
byte[] bytes = input.getBytes();
int start = 0;
String esc = null;
for (int i = 0; i < bytes.length; i++) {
byte c = bytes[i];
switch (c) {
case '&':
esc = "&";
break;
case '<':
esc = "<";
break;
case '>':
esc = ">";
break;
case '\'':
esc = "'";
break;
case '"':
esc = """;
break;
default:
esc = null;
}
if (esc != null) {
if (start < i)
sb.add(bytes, start, i - start);
sb.add(esc);
start = i + 1;
}
}
if (start < bytes.length)
sb.add(bytes, start, bytes.length - start);
return sb.toString();
}
static String generateExcelUuid(String seed) {
byte[] seedBytes = seed.getBytes();
long hash1 = murmurHash64a(seedBytes, 0, seedBytes.length, 1337);
long hash2 = murmurHash64a(seedBytes, 0, seedBytes.length, 420);
byte[] uuid = new byte[36];
int pos = 0;
for (int i = 0; i < 32; i++) {
long h = i < 16 ? hash1 : hash2;
int d = i == 12 ? 4 : (int)((h >>> ((i&15L)*4L)) & 15L);
uuid[pos++] = (byte)(d + (d < 10 ? 0x30 : 0x57));
if (i == 7 || i == 11 || i == 15 || i == 19)
uuid[pos++] = '-';
}
return new String(uuid, 0, 36);
}
static void writeStringBuilderToZip(ZipOutputStream zip, String path, StringBuilder sb) throws IOException {
zip.putNextEntry(new ZipEntry(path));
zip.write(sb.toString().getBytes());
}
static StringBuilder generateContentTypesXml(int sheetCount) {
StringBuilder sb = new StringBuilder();
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">" +
"<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>" +
"<Default Extension=\"xml\" ContentType=\"application/xml\"/>" +
"<Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>"
);
for (int i = 0; i < sheetCount; i++) {
sb.append("<Override PartName=\"/xl/worksheets/sheet");
sb.append(i + 1);
sb.append(".xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>");
}
sb.append(
"<Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/>" +
"<Override PartName=\"/xl/sharedStrings.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\"/>" +
"<Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/>" +
"<Override PartName=\"/docProps/app.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/>" +
"</Types>"
);
return sb;
}
static StringBuilder generateTopRelsXml() {
StringBuilder sb = new StringBuilder();
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" +
"<Relationship Id=\"rId3\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\"" +
" Target=\"docProps/app.xml\"/>" +
"<Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\"" +
" Target=\"docProps/core.xml\"/>" +
"<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\"" +
" Target=\"xl/workbook.xml\"/>" +
"</Relationships>"
);
return sb;
}
static StringBuilder generateAppXml(RefVector<Sheet> sheets) {
StringBuilder sb = new StringBuilder();
int sheetCount = sheets.size;
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\"" +
" xmlns:vt=\"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\">" +
"<!-- AngioTool__Batch -->" +
"<Application>Microsoft Excel</Application>" +
"<DocSecurity>0</DocSecurity>" +
"<ScaleCrop>false</ScaleCrop>" +
"<HeadingPairs><vt:vector size=\"2\" baseType=\"variant\">" +
"<vt:variant><vt:lpstr>Worksheets</vt:lpstr></vt:variant>" +
"<vt:variant><vt:i4>"
);
sb.append(sheetCount);
sb.append("</vt:i4>" +
"</vt:variant></vt:vector>" +
"</HeadingPairs><TitlesOfParts><vt:vector size=\""
);
sb.append(sheetCount);
sb.append("\" baseType=\"lpstr\">");
for (int i = 0; i < sheetCount; i++) {
sb.append("<vt:lpstr>");
sb.append(escapeXmlString(sheets.buf[i].name));
sb.append("</vt:lpstr>");
}
sb.append(
"</vt:vector></TitlesOfParts><Company></Company>" +
"<LinksUpToDate>false</LinksUpToDate>" +
"<SharedDoc>false</SharedDoc>" +
"<HyperlinksChanged>false</HyperlinksChanged>" +
"<AppVersion>16.0300</AppVersion></Properties>"
);
return sb;
}
static StringBuilder generateCoreXml(String creator, String dateStr) {
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<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\">" +
"<dc:creator>"
);
sb.append(creator);
sb.append("</dc:creator><cp:lastModifiedBy>");
sb.append(creator);
sb.append("</cp:lastModifiedBy><dcterms:created xsi:type=\"dcterms:W3CDTF\">");
sb.append(dateStr);
sb.append("</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">");
sb.append(dateStr);
sb.append("</dcterms:modified></cp:coreProperties>");
return sb;
}
static StringBuilder generateWorkbookRels(int sheetCount) {
StringBuilder sb = new StringBuilder();
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">"
);
for (int i = 0; i < sheetCount; i++) {
sb.append("<Relationship Id=\"rId");
sb.append(i + 1);
sb.append("\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet");
sb.append(i + 1);
sb.append(".xml\"/>");
}
sb.append("<Relationship Id=\"rId");
sb.append(sheetCount + 2);
sb.append(
"\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings\"" +
" Target=\"sharedStrings.xml\"/>"
);
sb.append("<Relationship Id=\"rId");
sb.append(sheetCount + 1);
sb.append(
"\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\"" +
" Target=\"styles.xml\"/>"
);
sb.append("</Relationships>");
return sb;
}
static StringBuilder generateStylesXml() {
StringBuilder sb = new StringBuilder();
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<styleSheet" +
" xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"" +
" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x14ac x16r2\"" +
" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\"" +
" xmlns:x16r2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/02/main\">" +
"<numFmts count=\"3\">" +
"<numFmt numFmtId=\"167\" formatCode=\""$"#,##0.00_);[Red]\\("$"#,##0.00\\)\"/>" +
"<numFmt numFmtId=\"173\" formatCode=\"#,##0.00;[Red]\\-#,##0.00\"/>" +
"<numFmt numFmtId=\"174\" formatCode=\""$"#,##0.00;[Red]\\("$"#,##0.00\\)\"/>" +
"</numFmts>" +
"<fonts count=\"3\" x14ac:knownFonts=\"1\">" +
"<font><sz val=\"11\"/><color theme=\"1\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/></font>" +
"<font><b/><sz val=\"11\"/><color theme=\"1\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/></font>" +
"<font><b/><sz val=\"16\"/><color theme=\"1\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/></font>" +
"</fonts>" +
"<fills count=\"2\">" +
"<fill><patternFill patternType=\"none\"/></fill>" +
"<fill><patternFill patternType=\"gray125\"/></fill>" +
"</fills>" +
"<borders count=\"1\">" +
"<border><left/><right/><top/><bottom/><diagonal/></border>" +
"</borders>" +
"<cellStyleXfs count=\"1\">" +
"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>" +
"</cellStyleXfs>" +
"<cellXfs count=\"7\">" +
"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>" +
"<xf numFmtId=\"173\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/>" +
"<xf numFmtId=\"174\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/>" +
"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyAlignment=\"1\">" +
"<alignment horizontal=\"right\"/>" +
"</xf>" +
"<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyAlignment=\"1\">" +
"<alignment horizontal=\"center\"/>" +
"</xf>" +
"<xf numFmtId=\"0\" fontId=\"1\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\" applyAlignment=\"1\">" +
"<alignment horizontal=\"center\"/>" +
"</xf>" +
"<xf numFmtId=\"0\" fontId=\"2\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\" applyAlignment=\"1\">" +
"<alignment horizontal=\"center\"/>" +
"</xf>" +
"</cellXfs>" +
"<cellStyles count=\"1\">" +
"<cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>" +
"</cellStyles>" +
"<dxfs count=\"0\"/>" +
"<tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium2\" defaultPivotStyle=\"PivotStyleLight16\"/>" +
"<extLst>" +
"<ext uri=\"{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}\" xmlns:x14=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\">" +
"<x14:slicerStyles defaultSlicerStyle=\"SlicerStyleLight1\"/>" +
"</ext>" +
"<ext uri=\"{9260A510-F301-46a8-8635-F512D64BE5F5}\" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\">" +
"<x15:timelineStyles defaultTimelineStyle=\"TimeSlicerStyleLight1\"/>" +
"</ext>" +
"</extLst>" +
"</styleSheet>"
);
return sb;
}
static StringBuilder generateWorkbookXml(RefVector<Sheet> sheets, String dateStr) {
StringBuilder sb = new StringBuilder();
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<workbook" +
" xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"" +
" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"" +
" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"" +
" mc:Ignorable=\"x15 xr xr6 xr10 xr2\"" +
" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\"" +
" xmlns:xr=\"http://schemas.microsoft.com/office/spreadsheetml/2014/revision\"" +
" xmlns:xr6=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision6\"" +
" xmlns:xr10=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision10\"" +
" xmlns:xr2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/revision2\">" +
"<fileVersion appName=\"xl\" lastEdited=\"7\" lowestEdited=\"7\" rupBuild=\"27029\"/>" +
"<workbookPr defaultThemeVersion=\"166925\"/>" +
"<xr:revisionPtr revIDLastSave=\"0\" documentId=\"8_{"
);
sb.append(generateExcelUuid(dateStr + "_workbook_xml_1"));
sb.append(
"}\" xr6:coauthVersionLast=\"47\" xr6:coauthVersionMax=\"47\"" +
" xr10:uidLastSave=\"{00000000-0000-0000-0000-000000000000}\"/>" +
"<bookViews>" +
"<workbookView xWindow=\"2652\" yWindow=\"2652\" windowWidth=\"17280\" windowHeight=\"8964\" xr2:uid=\"{"
);
sb.append(generateExcelUuid(dateStr + "_workbook_xml_2"));
sb.append("}\"/></bookViews><sheets>");
int sheetCount = sheets.size;
for (int i = 0; i < sheetCount; i++) {
sb.append("<sheet name=\"");
sb.append(escapeXmlString(sheets.buf[i].name));
sb.append("\" sheetId=\"");
sb.append(i + 1);
sb.append("\" r:id=\"rId");
sb.append(i + 1);
sb.append("\"/>");
}
sb.append(
"</sheets>" +
"<calcPr calcId=\"191029\"/>" +
"<extLst>" +
"<ext uri=\"{140A7094-0E35-4892-8432-C4D2E57EDEB5}\" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\">" +
"<x15:workbookPr chartTrackingRefBase=\"1\"/>" +
"</ext>" +
"<ext uri=\"{B58B0392-4F1F-4190-BB64-5DF3571DCE5F}\" xmlns:xcalcf=\"http://schemas.microsoft.com/office/spreadsheetml/2018/calcfeatures\">" +
"<xcalcf:calcFeatures>" +
"<xcalcf:feature name=\"microsoft.com:RD\"/>" +
"<xcalcf:feature name=\"microsoft.com:Single\"/>" +
"<xcalcf:feature name=\"microsoft.com:FV\"/>" +
"<xcalcf:feature name=\"microsoft.com:CNMTM\"/>" +
"<xcalcf:feature name=\"microsoft.com:LET_WF\"/>" +
"<xcalcf:feature name=\"microsoft.com:LAMBDA_WF\"/>" +
"<xcalcf:feature name=\"microsoft.com:ARRAYTEXT_WF\"/>" +
"</xcalcf:calcFeatures>" +
"</ext>" +
"</extLst>" +
"</workbook>"
);
return sb;
}
static StringBuilder generateSharedStringsXml(RefVector<String> uniqueStrings, int nTotalStrings) {
StringBuilder sb = new StringBuilder();
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\""
);
sb.append(nTotalStrings);
sb.append("\" uniqueCount=\"");
sb.append(uniqueStrings.size);
sb.append("\">");
for (String str : uniqueStrings) {
sb.append("<si><t>");
sb.append(escapeXmlString(str));
sb.append("</t></si>");
}
sb.append("</sst>");
return sb;
}
static StringBuilder generateSheetXml(Sheet sheet, String dateStr) {
StringBuilder sb = new StringBuilder();
sb.append(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<worksheet" +
" xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"" +
" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"" +
" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"" +
" mc:Ignorable=\"x14ac xr xr2 xr3\"" +
" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\"" +
" xmlns:xr=\"http://schemas.microsoft.com/office/spreadsheetml/2014/revision\"" +
" xmlns:xr2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/revision2\"" +
" xmlns:xr3=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision3\"" +
" xr:uid=\"{"
);
sb.append(generateExcelUuid(dateStr + "_sheet_" + sheet.name));
sb.append(
"}\">" +
"<dimension ref=\"A1:"
);
sb.append(makeColumnNumber(sheet.colsInWidestRow));
sb.append(sheet.formattedRows.size + 1);
sb.append(
"\"/>" +
"<sheetViews>" +
"<sheetView workbookViewId=\"0\">" +
"<selection activeCell=\"A1\" sqref=\"A1\"/>" +
"</sheetView>" +
"</sheetViews>" +
"<sheetFormatPr defaultRowHeight=\"14.4\" x14ac:dyDescent=\"0.3\"/>"
);
// maybe add <cols>...</cols> to specify column widths
sb.append("<sheetData>");
for (String row : sheet.formattedRows)
sb.append(row);
sb.append("</sheetData>");
// <mergeCells>...</mergeCells> goes here
sb.append(
"<pageMargins left=\"0.7\" right=\"0.7\" top=\"0.75\" bottom=\"0.75\" header=\"0.3\" footer=\"0.3\"/>" +
"</worksheet>"
);
return sb;
}
static long murmurHash64a(byte[] key, int off, int len, long seed) {
if (off < 0 || len <= 0 || off+len > key.length)
return 0L;
final long m = 0xc6a4a7935bd1e995L;
final int r = 47;
long h = seed ^ ((long)len * m);
int blocks = len >> 3;
for (int i = 0; i < len - 7; i += 8) {
long k =
(key[off+i] & 0xffL) |
((key[off+i+1] & 0xffL) << 8L) |
((key[off+i+2] & 0xffL) << 16L) |
((key[off+i+3] & 0xffL) << 24L) |
((key[off+i+4] & 0xffL) << 32L) |
((key[off+i+5] & 0xffL) << 40L) |
((key[off+i+6] & 0xffL) << 48L) |
((key[off+i+7] & 0xffL) << 56L);
k *= m;
k ^= k >>> r;
k *= m;
h ^= k;
h *= m;
}
int tail = off + (blocks << 3);
switch (len & 7) {
case 7: h ^= (key[tail+6] & 0xffL) << 48L;
case 6: h ^= (key[tail+5] & 0xffL) << 40L;
case 5: h ^= (key[tail+4] & 0xffL) << 32L;
case 4: h ^= (key[tail+3] & 0xffL) << 24L;
case 3: h ^= (key[tail+2] & 0xffL) << 16L;
case 2: h ^= (key[tail+1] & 0xffL) << 8L;
case 1: h ^= (key[tail] & 0xffL);
h *= m;
};
h ^= h >>> r;
h *= m;
h ^= h >>> r;
return h;
}
}