diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java
index fb9a5e3e3..58b1b5d2a 100644
--- a/core/src/processing/core/PApplet.java
+++ b/core/src/processing/core/PApplet.java
@@ -5761,7 +5761,8 @@ public class PApplet extends Applet
public Table createTable() {
- return new Table(this);
+// return new Table(this);
+ return new Table();
}
@@ -5779,7 +5780,8 @@ public class PApplet extends Applet
public Table loadTable(String filename, String options) {
try {
- return new Table(this, filename, options);
+// return new Table(this, filename, options);
+ return new Table(createInput(filename), options);
} catch (IOException e) {
e.printStackTrace();
return null;
@@ -5787,11 +5789,30 @@ public class PApplet extends Applet
}
+ public void saveTable(Table table, String filename) {
+ saveTable(table, filename, null);
+ }
+
+
+ public void saveTable(Table table, String filename, String options) {
+ try {
+ table.save(saveFile(filename), options);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
// static public Table loadTable(File file) {
// return new Table(this, file);
// }
+ static public String[] fixOptions(String options) {
+ return options == null ? new String[0] : options.split("\\s*,\\s*");
+ }
+
+
//////////////////////////////////////////////////////////////
diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java
index 7d09f4747..08fb7f57b 100644
--- a/core/src/processing/data/Table.java
+++ b/core/src/processing/data/Table.java
@@ -73,7 +73,7 @@ public class Table {
// protected boolean skipCommentLines = true;
protected String extension = null;
// protected boolean commaSeparatedValues = false;
- protected boolean awfulCSV = false;
+// protected boolean awfulCSV = false;
protected String missingString = null;
protected int missingInt = 0;
@@ -113,20 +113,21 @@ public class Table {
// double[][] doubleData;
// Object[][] objectData;
- PApplet sketch;
+// PApplet sketch;
/**
* Creates a new, empty table. Use addRow() to add additional rows.
*/
public Table() {
- init(null);
+// init(null);
+ init();
}
- public Table(PApplet sketch) {
- init(sketch);
- }
+// public Table(PApplet sketch) {
+// init(sketch);
+// }
// public Table(File file) {
@@ -134,95 +135,43 @@ public class Table {
// }
- public Table(PApplet parent, String filename) throws IOException {
- this(parent, filename, null);
+// public Table(PApplet parent, String filename) throws IOException {
+// this(parent, filename, null);
+// }
+
+
+ public Table(File file) throws IOException {
+ this(file, null);
+ }
+
+
+ // version that uses a File object; future releases (or data types)
+ // may include additional optimizations here
+ public Table(File file, String options) throws IOException {
+ parse(new FileInputStream(file), checkOptions(file, options));
}
/**
- * Can handle TSV or CSV files.
- * @param parent
- * @param filename
+ * Read the table from a stream. Possible options include:
+ *
+ * - csv - parse the table as comma-separated values
+ *
- tsv - parse the table as tab-separated values
+ *
- newlines - this CSV file contains newlines inside individual cells
+ *
- header - this table has a header (title) row
+ *
+ * @param input
+ * @param options
* @throws IOException
*/
- public Table(PApplet parent, String filename, String options) throws IOException {
- init(parent);
- //String[] opts = PApplet.split(options, ',');
- //PApplet.trim(opts);
-
- // try to determine file type from extension
- String extension = null;
- int dotIndex = filename.lastIndexOf('.');
- if (dotIndex != -1) {
- extension = filename.substring(dotIndex + 1).toLowerCase();
- if (!extension.equals("csv") &&
- !extension.equals("tsv")) {
- // ignore extension
- extension = null;
- }
- }
-
- String[] opts = null;
- if (options != null) {
- opts = options.split("\\s*,\\s*");
-// PApplet.println("options:");
-// PApplet.println(opts);
-
- for (String opt : opts) {
- if (opt.equals("tsv")) {
- extension = "tsv";
- } else if (opt.equals("csv")) {
- extension = "csv";
- } else if (opt.equals("newlines")) {
- awfulCSV = true;
- } else {
- System.err.println(opt + " is not a valid option for loading a Table");
- }
- }
- }
-
- BufferedReader reader = parent.createReader(filename);
- if (awfulCSV) {
- parseAwfulCSV(reader);
- } else if ("tsv".equals(extension)) {
- parseBasic(reader, true);
- } else if ("csv".equals(extension)) {
- parseBasic(reader, false);
- }
- //read();
+ public Table(InputStream input, String options) throws IOException {
+ parse(input, options);
}
-// public Table(BufferedReader reader) {
-// read(reader);
-// }
-
-
- protected void init(PApplet sketch) {
- this.sketch = sketch;
- columns = new Object[0];
- columnTypes = new int[0];
- columnCategories = new HashMapBlows[0];
- }
-
-
-// protected void read(BufferedReader reader) {
-// init();
-// try {
-// boolean csv = peekCSV(reader);
-// if (csv) {
-// parseCSV(reader);
-// } else {
-// parseTSV(reader);
-// }
-// } catch (IOException e) {
-// e.printStackTrace();
-// }
-// }
-
-
public Table(ResultSet rs) {
- init(null);
+// init(null);
+ init();
try {
ResultSetMetaData rsmd = rs.getMetaData();
@@ -279,86 +228,80 @@ public class Table {
}
- /**
- * Guess whether this file is tab separated or comma separated by checking
- * whether there are more tabs or commas in the first 100 characters.
- */
- /*
- protected boolean peekCSV(BufferedReader reader) throws IOException {
- char[] buffer = new char[100];
- int remaining = buffer.length;
- reader.mark(remaining);
-// int count = 0;
- int commas = 0;
- int tabs = 0;
- for (int i = 0; i < remaining; i++) {
- int c = reader.read();
- if (c == -1) break;
- if (c == ',') commas++;
- if (c == '\t') tabs++;
- }
- reader.reset();
- return (commas > tabs);
+ protected void init() {
+ columns = new Object[0];
+ columnTypes = new int[0];
+ columnCategories = new HashMapBlows[0];
}
- public void parse(BufferedReader reader) throws IOException {
- if (commaSeparatedValues) {
- if (awfulCSV) {
- parseAwfulCSV(reader);
- } else {
- parseCSV(reader);
+ protected String checkOptions(File file, String options) throws IOException {
+ String extension = null;
+ String filename = file.getName();
+ int dotIndex = filename.lastIndexOf('.');
+ if (dotIndex != -1) {
+ extension = filename.substring(dotIndex + 1).toLowerCase();
+ if (!extension.equals("csv") &&
+ !extension.equals("tsv")) {
+ // ignore extension
+ extension = null;
}
- } else {
- parseTSV(reader);
+ }
+ if (extension == null) {
+ if (options == null) {
+ throw new IOException("This table filename has no extension, and no options are set.");
+ }
+ } else { // extension is not null
+ if (options == null) {
+ options = extension;
+ } else {
+ // prepend the extension, it will be overridden if there's an option for it.
+ options = extension + "," + options;
+ }
+ }
+ return options;
+ }
+
+
+ protected void parse(InputStream input, String options) throws IOException {
+ init();
+
+ String[] opts = PApplet.fixOptions(options);
+// if (options != null) {
+// opts = options.split("\\s*,\\s*");
+//// PApplet.println("options:");
+//// PApplet.println(opts);
+
+ boolean awfulCSV = false;
+ boolean header = false;
+ for (String opt : opts) {
+ if (opt.equals("tsv")) {
+ extension = "tsv";
+ } else if (opt.equals("csv")) {
+ extension = "csv";
+ } else if (opt.equals("newlines")) {
+ awfulCSV = true;
+ } else if (opt.equals("header")) {
+ header = true;
+ } else {
+ throw new IllegalArgumentException("'" + opt + "' is not a valid option for loading a Table");
+ }
+ }
+// }
+
+ BufferedReader reader = PApplet.createReader(input);
+ if (awfulCSV) {
+ parseAwfulCSV(reader, header);
+ } else if ("tsv".equals(extension)) {
+ parseBasic(reader, header, true);
+ } else if ("csv".equals(extension)) {
+ parseBasic(reader, header, false);
}
}
- */
- public void parseTSV(BufferedReader reader) throws IOException {
- parseBasic(reader, true);
-// String line = null;
-// int row = 0;
-// if (rowCount == 0) {
-// setRowCount(10);
-// }
-// while ((line = reader.readLine()) != null) {
-// if (row == getRowCount()) {
-// setRowCount(row << 1);
-// }
-// setRow(row, PApplet.split(line, '\t'));
-// row++;
-// }
-// // shorten or lengthen based on what's left
-// if (row != getRowCount()) {
-// setRowCount(row);
-// }
- }
-
-
- public void parseCSV(BufferedReader reader) throws IOException {
- parseBasic(reader, false);
-// String line = null;
-// int row = 0;
-// if (rowCount == 0) {
-// setRowCount(10);
-// }
-// while ((line = reader.readLine()) != null) {
-// if (row == getRowCount()) {
-// setRowCount(row << 1);
-// }
-// setRow(row, splitLineCSV(line));
-// row++;
-// }
-// // shorten or lengthen based on what's left
-// if (row != getRowCount()) {
-// setRowCount(row);
-// }
- }
-
-
- protected void parseBasic(BufferedReader reader, boolean tsv) throws IOException {
+ protected void parseBasic(BufferedReader reader,
+ boolean header, boolean tsv) throws IOException {
String line = null;
int row = 0;
if (rowCount == 0) {
@@ -394,86 +337,13 @@ public class Table {
}
- public void convertTSV(BufferedReader reader, File outputFile) throws IOException {
- convertBasic(reader, true, outputFile);
- }
+// public void convertTSV(BufferedReader reader, File outputFile) throws IOException {
+// convertBasic(reader, true, outputFile);
+// }
- protected void convertBasic(BufferedReader reader, boolean tsv,
- File outputFile) throws IOException {
- FileOutputStream fos = new FileOutputStream(outputFile);
- BufferedOutputStream bos = new BufferedOutputStream(fos, 16384);
- DataOutputStream output = new DataOutputStream(bos);
- output.writeInt(0); // come back for row count
- output.writeInt(getColumnCount());
- if (columnTitles != null) {
- output.writeBoolean(true);
- for (String title : columnTitles) {
- output.writeUTF(title);
- }
- } else {
- output.writeBoolean(false);
- }
- for (int type : columnTypes) {
- output.writeInt(type);
- }
-
- String line = null;
- //setRowCount(1);
- int prev = -1;
- int row = 0;
- while ((line = reader.readLine()) != null) {
- convertRow(output, tsv ? PApplet.split(line, '\t') : splitLineCSV(line));
- row++;
-
- if (row % 10000 == 0) {
- if (row < rowCount) {
- int pct = (100 * row) / rowCount;
- if (pct != prev) {
- System.out.println(pct + "%");
- prev = pct;
- }
- }
-// try {
-// Thread.sleep(5);
-// } catch (InterruptedException e) {
-// e.printStackTrace();
-// }
- }
- }
- // shorten or lengthen based on what's left
-// if (row != getRowCount()) {
-// setRowCount(row);
-// }
-
- // has to come afterwards, since these tables get built out during the conversion
- int col = 0;
- for (HashMapBlows hmb : columnCategories) {
- if (hmb == null) {
- output.writeInt(0);
- } else {
- hmb.write(output);
- hmb.writeln(PApplet.createWriter(new File(columnTitles[col] + ".categories")));
-// output.writeInt(hmb.size());
-// for (Map.Entry e : hmb.entrySet()) {
-// output.writeUTF(e.getKey());
-// output.writeInt(e.getValue());
-// }
- }
- col++;
- }
-
- output.flush();
- output.close();
-
- // come back and write the row count
- RandomAccessFile raf = new RandomAccessFile(outputFile, "rw");
- raf.writeInt(rowCount);
- raf.close();
- }
-
-
- protected void parseAwfulCSV(BufferedReader reader) throws IOException {
+ protected void parseAwfulCSV(BufferedReader reader,
+ boolean header) throws IOException {
char[] c = new char[100];
int count = 0;
boolean insideQuote = false;
@@ -512,20 +382,23 @@ public class Table {
if (ch == '\"') {
insideQuote = true;
- } else if (ch == '\r') {
- // check to see if next is a '\n'
- reader.mark(1);
- if (reader.read() != '\n') {
- reader.reset();
+ } else if (ch == '\r' || ch == '\n') {
+ if (ch == '\r') {
+ // check to see if next is a '\n'
+ reader.mark(1);
+ if (reader.read() != '\n') {
+ reader.reset();
+ }
}
setString(row, col, new String(c, 0, count));
count = 0;
- row++;
- col = 0;
-
- } else if (ch == '\n') {
- setString(row, col, new String(c, 0, count));
- count = 0;
+ if (row == 0 && header) {
+ // Use internal row removal (efficient because only one row).
+ removeTitleRow();
+ // Un-set the header variable so that next time around, we don't
+ // just get stuck into a loop, removing the 0th row repeatedly.
+ header = false;
+ }
row++;
col = 0;
@@ -563,7 +436,7 @@ public class Table {
* @param line line of text to be parsed
* @return an array of the individual values formerly separated by commas
*/
- static public String[] splitLineCSV(String line) {
+ static protected String[] splitLineCSV(String line) {
char[] c = line.toCharArray();
int rough = 1; // at least one
boolean quote = false;
@@ -609,7 +482,7 @@ public class Table {
}
- static int nextComma(char[] c, int index) {
+ static protected int nextComma(char[] c, int index) {
boolean quote = false;
for (int i = index; i < c.length; i++) {
if (!quote && (c[i] == ',')) {
@@ -640,16 +513,18 @@ public class Table {
// compiler) of an inner class by the runtime.
/** incomplete, do not use */
- public void parseInto(String fieldName) {
+// public void parseInto(PApplet sketch, String fieldName) {
+ public void parseInto(Object enclosingObject, String fieldName) {
Class> target = null;
Object outgoing = null;
Field targetField = null;
try {
// Object targetObject,
// Class target -> get this from the type of fieldName
- Class sketchClass = sketch.getClass();
+// Class sketchClass = sketch.getClass();
+ Class sketchClass = enclosingObject.getClass();
targetField = sketchClass.getDeclaredField(fieldName);
- PApplet.println("found " + targetField);
+// PApplet.println("found " + targetField);
Class targetArray = targetField.getType();
if (!targetArray.isArray()) {
// fieldName is not an array
@@ -663,22 +538,21 @@ public class Table {
e.printStackTrace();
}
- Object enclosingObject = sketch;
- PApplet.println("enclosing obj is " + enclosingObject);
+// Object enclosingObject = sketch;
+// PApplet.println("enclosing obj is " + enclosingObject);
Class enclosingClass = target.getEnclosingClass();
Constructor con = null;
try {
if (enclosingClass == null) {
con = target.getDeclaredConstructor(); //new Class[] { });
- PApplet.println("no enclosing class");
+// PApplet.println("no enclosing class");
} else {
con = target.getDeclaredConstructor(new Class[] { enclosingClass });
-// con = target.getConstructor(enclosingClass);
- PApplet.println("enclosed by " + enclosingClass.getName());
+// PApplet.println("enclosed by " + enclosingClass.getName());
}
if (!con.isAccessible()) {
- System.out.println("setting constructor to public");
+// System.out.println("setting constructor to public");
con.setAccessible(true);
}
} catch (SecurityException e) {
@@ -692,32 +566,18 @@ public class Table {
for (Field field : fields) {
String name = field.getName();
if (getColumnIndex(name, false) != -1) {
- System.out.println("found field " + name);
+// System.out.println("found field " + name);
if (!field.isAccessible()) {
- PApplet.println(" changing field access");
+// PApplet.println(" changing field access");
field.setAccessible(true);
}
inuse.add(field);
} else {
- System.out.println("skipping field " + name);
+// System.out.println("skipping field " + name);
}
}
-// Constructor[] cons = target.getDeclaredConstructors();
-// //for (Method m : methods) {
-// Constructor defaultCons = null;
-// for (Constructor c : cons) {
-// //System.out.println("found " + c.getParameterTypes());
-// if (c.getParameterTypes().length == 0) {
-// System.out.println("found default");
-// defaultCons = c;
-// c.setAccessible(true);
-// } else {
-// PApplet.println(c.getParameterTypes());
-// }
-// }
int index = 0;
-// ArrayList