diff --git a/core/src/processing/data/IntDict.java b/core/src/processing/data/IntDict.java index af4486169..749a2562a 100644 --- a/core/src/processing/data/IntDict.java +++ b/core/src/processing/data/IntDict.java @@ -26,29 +26,6 @@ public class IntDict { private HashMap indices = new HashMap(); -// /** -// * Create a new object by counting the number of times each unique entry -// * shows up in the specified String array. -// */ -// static public IntHash fromTally(String[] list) { -// IntHash outgoing = new IntHash(); -// for (String s : list) { -// outgoing.inc(s); -// } -// outgoing.crop(); -// return outgoing; -// } -// -// -// static public IntHash fromOrder(String[] list) { -// IntHash outgoing = new IntHash(); -// for (int i = 0; i < list.length; i++) { -// outgoing.set(list[i], i); -// } -// return outgoing; -// } - - public IntDict() { count = 0; keys = new String[10]; diff --git a/core/src/processing/data/StringList.java b/core/src/processing/data/StringList.java index 67e58a9ee..c7e13769e 100644 --- a/core/src/processing/data/StringList.java +++ b/core/src/processing/data/StringList.java @@ -683,14 +683,6 @@ public class StringList implements Iterable { } -// public void println() { -// for (int i = 0; i < count; i++) { -// System.out.println("[" + i + "] " + data[i]); -// } -// System.out.flush(); -// } - - public String join(String separator) { if (count == 0) { return ""; diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java index 837c7475c..e9b221a55 100644 --- a/core/src/processing/data/Table.java +++ b/core/src/processing/data/Table.java @@ -25,6 +25,7 @@ package processing.data; import java.io.*; import java.lang.reflect.*; +import java.nio.charset.Charset; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -34,6 +35,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; import javax.xml.parsers.ParserConfigurationException; @@ -292,7 +294,7 @@ public class Table { static final String[] loadExtensions = { "csv", "tsv", "ods", "bin" }; - static final String[] saveExtensions = { "csv", "tsv", "html", "bin" }; + static final String[] saveExtensions = { "csv", "tsv", "ods", "bin", "html" }; static public String extensionOptions(boolean loading, String filename, String options) { String extension = PApplet.checkExtension(filename); @@ -971,6 +973,13 @@ public class Table { writeCSV(writer); } else if (extension.equals("tsv")) { writeTSV(writer); + } else if (extension.equals("ods")) { + try { + saveODS(output); + } catch (IOException e) { + e.printStackTrace(); + return false; + } } else if (extension.equals("html")) { writeHTML(writer); } else if (extension.equals("bin")) { @@ -1140,6 +1149,205 @@ public class Table { } + protected void saveODS(OutputStream os) throws IOException { + ZipOutputStream zos = new ZipOutputStream(os); + + final String xmlHeader = ""; + + ZipEntry entry = new ZipEntry("META-INF/manifest.xml"); + String[] lines = new String[] { + xmlHeader, + "", + " ", + " ", + " ", + " ", + " ", + "" + }; + zos.putNextEntry(entry); + zos.write(PApplet.join(lines, "\n").getBytes()); + zos.closeEntry(); + + /* + entry = new ZipEntry("meta.xml"); + lines = new String[] { + xmlHeader, + "" + }; + zos.putNextEntry(entry); + zos.write(PApplet.join(lines, "\n").getBytes()); + zos.closeEntry(); + + entry = new ZipEntry("meta.xml"); + lines = new String[] { + xmlHeader, + "" + }; + zos.putNextEntry(entry); + zos.write(PApplet.join(lines, "\n").getBytes()); + zos.closeEntry(); + + entry = new ZipEntry("settings.xml"); + lines = new String[] { + xmlHeader, + "" + }; + zos.putNextEntry(entry); + zos.write(PApplet.join(lines, "\n").getBytes()); + zos.closeEntry(); + + entry = new ZipEntry("styles.xml"); + lines = new String[] { + xmlHeader, + "" + }; + zos.putNextEntry(entry); + zos.write(PApplet.join(lines, "\n").getBytes()); + zos.closeEntry(); + */ + + final String[] dummyFiles = new String[] { + "meta.xml", "settings.xml", "styles.xml" + }; + lines = new String[] { + xmlHeader, + "" + }; + byte[] dummyBytes = PApplet.join(lines, "\n").getBytes(); + for (String filename : dummyFiles) { + entry = new ZipEntry(filename); + zos.putNextEntry(entry); + zos.write(dummyBytes); + zos.closeEntry(); + } + + // + + entry = new ZipEntry("mimetype"); + zos.putNextEntry(entry); + zos.write("application/vnd.oasis.opendocument.spreadsheet".getBytes()); + zos.closeEntry(); + + // + + entry = new ZipEntry("content.xml"); + zos.putNextEntry(entry); + //lines = new String[] { + writeUTF(zos, new String[] { + xmlHeader, + "", + " ", + " ", + " " + }); + //zos.write(PApplet.join(lines, "\n").getBytes()); + + byte[] rowStart = " \n".getBytes(); + byte[] rowStop = " \n".getBytes(); + + if (hasColumnTitles()) { + zos.write(rowStart); + for (int i = 0; i < getColumnCount(); i++) { + saveStringODS(zos, columnTitles[i]); + } + zos.write(rowStop); + } + + for (TableRow row : rows()) { + zos.write(rowStart); + for (int i = 0; i < getColumnCount(); i++) { + if (columnTypes[i] == STRING || columnTypes[i] == CATEGORY) { + saveStringODS(zos, row.getString(i)); + } else { + saveNumberODS(zos, row.getString(i)); + } + } + zos.write(rowStop); + } + + //lines = new String[] { + writeUTF(zos, new String[] { + " ", + " ", + " ", + "" + }); + //zos.write(PApplet.join(lines, "\n").getBytes()); + zos.closeEntry(); + + zos.flush(); + zos.close(); + } + + + void saveStringODS(OutputStream output, String text) throws IOException { + // At this point, I should have just used the XML library. But this does + // save us from having to create the entire document in memory again before + // writing to the file. So while it's dorky, the outcome is still useful. + StringBuilder sanitized = new StringBuilder(); + char[] array = text.toCharArray(); + for (char c : array) { + if (c == '&') { + sanitized.append("&"); + } else if (c == '\'') { + sanitized.append("'"); + } else if (c == '"') { + sanitized.append("""); + } else if (c == '<') { + sanitized.append("<"); + } else if (c == '>') { + sanitized.append("&rt;"); + } else if (c < 32 || c > 127) { + sanitized.append("&#" + ((int) c) + ";"); + } else { + sanitized.append(c); + } + } + + writeUTF(output, + " ", + " " + sanitized + "", + " "); + } + + + void saveNumberODS(OutputStream output, String text) throws IOException { + writeUTF(output, + " ", + " " + text + "", + " "); + } + + + static Charset utf8; + + static void writeUTF(OutputStream output, String... lines) throws IOException { + if (utf8 == null) { + utf8 = Charset.forName("UTF-8"); + } + for (String str : lines) { + output.write(str.getBytes(utf8)); + output.write('\n'); + } + } + + protected void saveBinary(OutputStream os) throws IOException { DataOutputStream output = new DataOutputStream(new BufferedOutputStream(os)); output.writeInt(0x9007AB1E); // version