propane 3.6.0-java → 3.10.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.mvn/extensions.xml +1 -1
- data/.travis.yml +1 -1
- data/CHANGELOG.md +5 -1
- data/README.md +6 -13
- data/Rakefile +7 -6
- data/lib/java/japplemenubar/JAppleMenuBar.java +88 -0
- data/lib/java/japplemenubar/libjAppleMenuBar.jnilib +0 -0
- data/lib/java/monkstone/ColorUtil.java +127 -0
- data/lib/java/monkstone/MathToolModule.java +287 -0
- data/lib/java/monkstone/PropaneLibrary.java +46 -0
- data/lib/java/monkstone/core/LibraryProxy.java +136 -0
- data/lib/java/monkstone/fastmath/DegLutTables.java +111 -0
- data/lib/java/monkstone/fastmath/Deglut.java +71 -0
- data/lib/java/monkstone/fastmath/package-info.java +6 -0
- data/lib/java/monkstone/filechooser/Chooser.java +39 -0
- data/lib/java/monkstone/noise/FastTerrain.java +874 -0
- data/lib/java/monkstone/noise/Noise.java +90 -0
- data/lib/java/monkstone/noise/NoiseGenerator.java +75 -0
- data/lib/java/monkstone/noise/NoiseMode.java +28 -0
- data/lib/java/monkstone/noise/OpenSimplex2F.java +881 -0
- data/lib/java/monkstone/noise/OpenSimplex2S.java +1106 -0
- data/lib/java/monkstone/noise/SmoothTerrain.java +1099 -0
- data/lib/java/monkstone/slider/CustomHorizontalSlider.java +164 -0
- data/lib/java/monkstone/slider/CustomVerticalSlider.java +178 -0
- data/lib/java/monkstone/slider/SimpleHorizontalSlider.java +145 -0
- data/lib/java/monkstone/slider/SimpleSlider.java +166 -0
- data/lib/java/monkstone/slider/SimpleVerticalSlider.java +157 -0
- data/lib/java/monkstone/slider/Slider.java +61 -0
- data/lib/java/monkstone/slider/SliderBar.java +245 -0
- data/lib/java/monkstone/slider/SliderGroup.java +56 -0
- data/lib/java/monkstone/slider/WheelHandler.java +35 -0
- data/lib/java/monkstone/vecmath/GfxRender.java +86 -0
- data/lib/java/monkstone/vecmath/JRender.java +56 -0
- data/lib/java/monkstone/vecmath/ShapeRender.java +87 -0
- data/lib/java/monkstone/vecmath/package-info.java +20 -0
- data/lib/java/monkstone/vecmath/vec2/Vec2.java +802 -0
- data/lib/java/monkstone/vecmath/vec2/package-info.java +6 -0
- data/lib/java/monkstone/vecmath/vec3/Vec3.java +727 -0
- data/lib/java/monkstone/vecmath/vec3/package-info.java +6 -0
- data/lib/java/monkstone/videoevent/CaptureEvent.java +27 -0
- data/lib/java/monkstone/videoevent/MovieEvent.java +32 -0
- data/lib/java/monkstone/videoevent/package-info.java +20 -0
- data/lib/java/processing/awt/PGraphicsJava2D.java +3040 -0
- data/lib/java/processing/awt/PImageAWT.java +377 -0
- data/lib/java/processing/awt/PShapeJava2D.java +387 -0
- data/lib/java/processing/awt/PSurfaceAWT.java +1581 -0
- data/lib/java/processing/awt/ShimAWT.java +581 -0
- data/lib/java/processing/core/PApplet.java +15156 -0
- data/lib/java/processing/core/PConstants.java +523 -0
- data/lib/java/processing/core/PFont.java +1126 -0
- data/lib/java/processing/core/PGraphics.java +8600 -0
- data/lib/java/processing/core/PImage.java +3377 -0
- data/lib/java/processing/core/PMatrix.java +208 -0
- data/lib/java/processing/core/PMatrix2D.java +562 -0
- data/lib/java/processing/core/PMatrix3D.java +890 -0
- data/lib/java/processing/core/PShape.java +3561 -0
- data/lib/java/processing/core/PShapeOBJ.java +483 -0
- data/lib/java/processing/core/PShapeSVG.java +2016 -0
- data/lib/java/processing/core/PStyle.java +63 -0
- data/lib/java/processing/core/PSurface.java +198 -0
- data/lib/java/processing/core/PSurfaceNone.java +431 -0
- data/lib/java/processing/core/PVector.java +1066 -0
- data/lib/java/processing/core/ThinkDifferent.java +115 -0
- data/lib/java/processing/data/DoubleDict.java +850 -0
- data/lib/java/processing/data/DoubleList.java +928 -0
- data/lib/java/processing/data/FloatDict.java +847 -0
- data/lib/java/processing/data/FloatList.java +936 -0
- data/lib/java/processing/data/IntDict.java +807 -0
- data/lib/java/processing/data/IntList.java +936 -0
- data/lib/java/processing/data/JSONArray.java +1260 -0
- data/lib/java/processing/data/JSONObject.java +2282 -0
- data/lib/java/processing/data/JSONTokener.java +435 -0
- data/lib/java/processing/data/LongDict.java +802 -0
- data/lib/java/processing/data/LongList.java +937 -0
- data/lib/java/processing/data/Sort.java +46 -0
- data/lib/java/processing/data/StringDict.java +613 -0
- data/lib/java/processing/data/StringList.java +800 -0
- data/lib/java/processing/data/Table.java +4936 -0
- data/lib/java/processing/data/TableRow.java +198 -0
- data/lib/java/processing/data/XML.java +1156 -0
- data/lib/java/processing/dxf/RawDXF.java +404 -0
- data/lib/java/processing/event/Event.java +125 -0
- data/lib/java/processing/event/KeyEvent.java +70 -0
- data/lib/java/processing/event/MouseEvent.java +114 -0
- data/lib/java/processing/event/TouchEvent.java +57 -0
- data/lib/java/processing/javafx/PGraphicsFX2D.java +32 -0
- data/lib/java/processing/javafx/PSurfaceFX.java +173 -0
- data/lib/java/processing/net/Client.java +744 -0
- data/lib/java/processing/net/Server.java +388 -0
- data/lib/java/processing/opengl/FontTexture.java +378 -0
- data/lib/java/processing/opengl/FrameBuffer.java +513 -0
- data/lib/java/processing/opengl/LinePath.java +627 -0
- data/lib/java/processing/opengl/LineStroker.java +681 -0
- data/lib/java/processing/opengl/PGL.java +3483 -0
- data/lib/java/processing/opengl/PGraphics2D.java +615 -0
- data/lib/java/processing/opengl/PGraphics3D.java +281 -0
- data/lib/java/processing/opengl/PGraphicsOpenGL.java +13753 -0
- data/lib/java/processing/opengl/PJOGL.java +2008 -0
- data/lib/java/processing/opengl/PShader.java +1484 -0
- data/lib/java/processing/opengl/PShapeOpenGL.java +5269 -0
- data/lib/java/processing/opengl/PSurfaceJOGL.java +1385 -0
- data/lib/java/processing/opengl/Texture.java +1696 -0
- data/lib/java/processing/opengl/VertexBuffer.java +88 -0
- data/lib/java/processing/opengl/cursors/arrow.png +0 -0
- data/lib/java/processing/opengl/cursors/cross.png +0 -0
- data/lib/java/processing/opengl/cursors/hand.png +0 -0
- data/lib/java/processing/opengl/cursors/license.txt +27 -0
- data/lib/java/processing/opengl/cursors/move.png +0 -0
- data/lib/java/processing/opengl/cursors/text.png +0 -0
- data/lib/java/processing/opengl/cursors/wait.png +0 -0
- data/lib/java/processing/opengl/shaders/ColorFrag.glsl +32 -0
- data/lib/java/processing/opengl/shaders/ColorVert.glsl +34 -0
- data/lib/java/processing/opengl/shaders/LightFrag.glsl +33 -0
- data/lib/java/processing/opengl/shaders/LightVert.glsl +151 -0
- data/lib/java/processing/opengl/shaders/LineFrag.glsl +32 -0
- data/lib/java/processing/opengl/shaders/LineVert.glsl +100 -0
- data/lib/java/processing/opengl/shaders/MaskFrag.glsl +40 -0
- data/lib/java/processing/opengl/shaders/PointFrag.glsl +32 -0
- data/lib/java/processing/opengl/shaders/PointVert.glsl +56 -0
- data/lib/java/processing/opengl/shaders/TexFrag.glsl +37 -0
- data/lib/java/processing/opengl/shaders/TexLightFrag.glsl +37 -0
- data/lib/java/processing/opengl/shaders/TexLightVert.glsl +157 -0
- data/lib/java/processing/opengl/shaders/TexVert.glsl +38 -0
- data/lib/java/processing/pdf/PGraphicsPDF.java +581 -0
- data/lib/java/processing/svg/PGraphicsSVG.java +378 -0
- data/lib/propane/app.rb +9 -10
- data/lib/propane/runner.rb +10 -12
- data/lib/propane/version.rb +1 -1
- data/library/pdf/pdf.rb +7 -0
- data/library/svg/svg.rb +7 -0
- data/mvnw +3 -3
- data/mvnw.cmd +2 -2
- data/pom.rb +30 -3
- data/pom.xml +54 -3
- data/propane.gemspec +7 -3
- data/src/main/java/monkstone/FastNoiseModuleJava.java +127 -0
- data/src/main/java/monkstone/MathToolModule.java +30 -30
- data/src/main/java/monkstone/PropaneLibrary.java +2 -0
- data/src/main/java/monkstone/SmoothNoiseModuleJava.java +127 -0
- data/src/main/java/monkstone/fastmath/DegLutTables.java +111 -0
- data/src/main/java/monkstone/fastmath/Deglut.java +6 -56
- data/src/main/java/monkstone/filechooser/Chooser.java +1 -1
- data/src/main/java/monkstone/noise/OpenSimplex2F.java +813 -0
- data/src/main/java/monkstone/noise/OpenSimplex2S.java +1138 -0
- data/src/main/java/monkstone/slider/WheelHandler.java +1 -1
- data/src/main/java/monkstone/vecmath/JRender.java +6 -6
- data/src/main/java/monkstone/vecmath/vec2/Vec2.java +20 -19
- data/src/main/java/monkstone/vecmath/vec3/Vec3.java +12 -12
- data/src/main/java/processing/awt/PGraphicsJava2D.java +11 -3
- data/src/main/java/processing/core/PApplet.java +13242 -13374
- data/src/main/java/processing/core/PConstants.java +155 -163
- data/src/main/java/processing/core/PGraphics.java +118 -111
- data/src/main/java/processing/opengl/PJOGL.java +6 -5
- data/src/main/java/processing/pdf/PGraphicsPDF.java +581 -0
- data/src/main/java/processing/svg/PGraphicsSVG.java +378 -0
- data/test/deglut_spec_test.rb +2 -2
- data/vendors/Rakefile +1 -1
- metadata +146 -17
- data/library/simplex_noise/simplex_noise.rb +0 -5
- data/src/main/java/monkstone/noise/SimplexNoise.java +0 -436
@@ -0,0 +1,4936 @@
|
|
1
|
+
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
2
|
+
|
3
|
+
/*
|
4
|
+
Part of the Processing project - http://processing.org
|
5
|
+
|
6
|
+
Copyright (c) 2011-13 Ben Fry and Casey Reas
|
7
|
+
Copyright (c) 2006-11 Ben Fry
|
8
|
+
|
9
|
+
This library is free software; you can redistribute it and/or
|
10
|
+
modify it under the terms of the GNU Lesser General Public
|
11
|
+
License version 2.1 as published by the Free Software Foundation.
|
12
|
+
|
13
|
+
This library is distributed in the hope that it will be useful,
|
14
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
16
|
+
Lesser General Public License for more details.
|
17
|
+
|
18
|
+
You should have received a copy of the GNU Lesser General
|
19
|
+
Public License along with this library; if not, write to the
|
20
|
+
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
21
|
+
Boston, MA 02111-1307 USA
|
22
|
+
*/
|
23
|
+
|
24
|
+
package processing.data;
|
25
|
+
|
26
|
+
import java.io.*;
|
27
|
+
import java.lang.reflect.*;
|
28
|
+
import java.nio.charset.Charset;
|
29
|
+
import java.sql.ResultSet;
|
30
|
+
import java.sql.ResultSetMetaData;
|
31
|
+
import java.sql.SQLException;
|
32
|
+
import java.sql.Types;
|
33
|
+
import java.util.*;
|
34
|
+
import java.util.concurrent.ExecutorService;
|
35
|
+
import java.util.concurrent.Executors;
|
36
|
+
import java.util.zip.ZipEntry;
|
37
|
+
import java.util.zip.ZipInputStream;
|
38
|
+
import java.util.zip.ZipOutputStream;
|
39
|
+
|
40
|
+
import javax.xml.parsers.ParserConfigurationException;
|
41
|
+
|
42
|
+
import org.xml.sax.SAXException;
|
43
|
+
|
44
|
+
import processing.core.PApplet;
|
45
|
+
import processing.core.PConstants;
|
46
|
+
|
47
|
+
|
48
|
+
/**
|
49
|
+
* <p>Generic class for handling tabular data, typically from a CSV, TSV, or
|
50
|
+
* other sort of spreadsheet file.</p>
|
51
|
+
* <p>CSV files are
|
52
|
+
* <a href="http://en.wikipedia.org/wiki/Comma-separated_values">comma separated values</a>,
|
53
|
+
* often with the data in quotes. TSV files use tabs as separators, and usually
|
54
|
+
* don't bother with the quotes.</p>
|
55
|
+
* <p>File names should end with .csv if they're comma separated.</p>
|
56
|
+
* <p>A rough "spec" for CSV can be found <a href="http://tools.ietf.org/html/rfc4180">here</a>.</p>
|
57
|
+
*
|
58
|
+
* @webref data:composite
|
59
|
+
* @see PApplet#loadTable(String)
|
60
|
+
* @see PApplet#saveTable(Table, String)
|
61
|
+
* @see TableRow
|
62
|
+
*/
|
63
|
+
public class Table {
|
64
|
+
protected int rowCount;
|
65
|
+
protected int allocCount;
|
66
|
+
|
67
|
+
// protected boolean skipEmptyRows = true;
|
68
|
+
// protected boolean skipCommentLines = true;
|
69
|
+
// protected String extension = null;
|
70
|
+
// protected boolean commaSeparatedValues = false;
|
71
|
+
// protected boolean awfulCSV = false;
|
72
|
+
|
73
|
+
protected String missingString = null;
|
74
|
+
protected int missingInt = 0;
|
75
|
+
protected long missingLong = 0;
|
76
|
+
protected float missingFloat = Float.NaN;
|
77
|
+
protected double missingDouble = Double.NaN;
|
78
|
+
protected int missingCategory = -1;
|
79
|
+
|
80
|
+
String[] columnTitles;
|
81
|
+
HashMapBlows[] columnCategories;
|
82
|
+
HashMap<String, Integer> columnIndices;
|
83
|
+
|
84
|
+
protected Object[] columns; // [column]
|
85
|
+
|
86
|
+
// accessible for advanced users
|
87
|
+
static public final int STRING = 0;
|
88
|
+
static public final int INT = 1;
|
89
|
+
static public final int LONG = 2;
|
90
|
+
static public final int FLOAT = 3;
|
91
|
+
static public final int DOUBLE = 4;
|
92
|
+
static public final int CATEGORY = 5;
|
93
|
+
int[] columnTypes;
|
94
|
+
|
95
|
+
protected RowIterator rowIterator;
|
96
|
+
|
97
|
+
// 0 for doubling each time, otherwise the number of rows to increment on
|
98
|
+
// each expansion.
|
99
|
+
protected int expandIncrement;
|
100
|
+
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Creates a new, empty table. Use addRow() to add additional rows.
|
104
|
+
*/
|
105
|
+
public Table() {
|
106
|
+
init();
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* @nowebref
|
111
|
+
*/
|
112
|
+
public Table(File file) throws IOException {
|
113
|
+
this(file, null);
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
/**
|
118
|
+
* version that uses a File object; future releases (or data types)
|
119
|
+
* may include additional optimizations here
|
120
|
+
*
|
121
|
+
* @nowebref
|
122
|
+
*/
|
123
|
+
public Table(File file, String options) throws IOException {
|
124
|
+
// uses createInput() to handle .gz (and eventually .bz2) files
|
125
|
+
init();
|
126
|
+
parse(PApplet.createInput(file),
|
127
|
+
extensionOptions(true, file.getName(), options));
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* @nowebref
|
132
|
+
*/
|
133
|
+
public Table(InputStream input) throws IOException {
|
134
|
+
this(input, null);
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
/**
|
139
|
+
* Read the table from a stream. Possible options include:
|
140
|
+
* <ul>
|
141
|
+
* <li>csv - parse the table as comma-separated values
|
142
|
+
* <li>tsv - parse the table as tab-separated values
|
143
|
+
* <li>newlines - this CSV file contains newlines inside individual cells
|
144
|
+
* <li>header - this table has a header (title) row
|
145
|
+
* </ul>
|
146
|
+
*
|
147
|
+
* @nowebref
|
148
|
+
* @param input
|
149
|
+
* @param options
|
150
|
+
* @throws IOException
|
151
|
+
*/
|
152
|
+
public Table(InputStream input, String options) throws IOException {
|
153
|
+
init();
|
154
|
+
parse(input, options);
|
155
|
+
}
|
156
|
+
|
157
|
+
|
158
|
+
public Table(Iterable<TableRow> rows) {
|
159
|
+
init();
|
160
|
+
|
161
|
+
int row = 0;
|
162
|
+
int alloc = 10;
|
163
|
+
|
164
|
+
for (TableRow incoming : rows) {
|
165
|
+
if (row == 0) {
|
166
|
+
setColumnTypes(incoming.getColumnTypes());
|
167
|
+
setColumnTitles(incoming.getColumnTitles());
|
168
|
+
// Do this after setting types, otherwise it'll attempt to parse the
|
169
|
+
// allocated but empty rows, and drive CATEGORY columns nutso.
|
170
|
+
setRowCount(alloc);
|
171
|
+
// sometimes more columns than titles (and types?)
|
172
|
+
setColumnCount(incoming.getColumnCount());
|
173
|
+
|
174
|
+
} else if (row == alloc) {
|
175
|
+
// Far more efficient than re-allocating all columns and doing a copy
|
176
|
+
alloc *= 2;
|
177
|
+
setRowCount(alloc);
|
178
|
+
}
|
179
|
+
|
180
|
+
//addRow(row);
|
181
|
+
// try {
|
182
|
+
setRow(row++, incoming);
|
183
|
+
// } catch (ArrayIndexOutOfBoundsException aioobe) {
|
184
|
+
// for (int i = 0; i < incoming.getColumnCount(); i++) {
|
185
|
+
// System.out.format("[%d] %s%n", i, incoming.getString(i));
|
186
|
+
// }
|
187
|
+
// throw aioobe;
|
188
|
+
// }
|
189
|
+
}
|
190
|
+
// Shrink the table to only the rows that were used
|
191
|
+
if (row != alloc) {
|
192
|
+
setRowCount(row);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
|
197
|
+
/**
|
198
|
+
* @nowebref
|
199
|
+
*/
|
200
|
+
public Table(ResultSet rs) {
|
201
|
+
init();
|
202
|
+
try {
|
203
|
+
ResultSetMetaData rsmd = rs.getMetaData();
|
204
|
+
|
205
|
+
int columnCount = rsmd.getColumnCount();
|
206
|
+
setColumnCount(columnCount);
|
207
|
+
|
208
|
+
for (int col = 0; col < columnCount; col++) {
|
209
|
+
setColumnTitle(col, rsmd.getColumnName(col + 1));
|
210
|
+
|
211
|
+
int type = rsmd.getColumnType(col + 1);
|
212
|
+
switch (type) { // TODO these aren't tested. nor are they complete.
|
213
|
+
case Types.INTEGER:
|
214
|
+
case Types.TINYINT:
|
215
|
+
case Types.SMALLINT:
|
216
|
+
setColumnType(col, INT);
|
217
|
+
break;
|
218
|
+
case Types.BIGINT:
|
219
|
+
setColumnType(col, LONG);
|
220
|
+
break;
|
221
|
+
case Types.FLOAT:
|
222
|
+
setColumnType(col, FLOAT);
|
223
|
+
break;
|
224
|
+
case Types.DECIMAL:
|
225
|
+
case Types.DOUBLE:
|
226
|
+
case Types.REAL:
|
227
|
+
setColumnType(col, DOUBLE);
|
228
|
+
break;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
int row = 0;
|
233
|
+
while (rs.next()) {
|
234
|
+
for (int col = 0; col < columnCount; col++) {
|
235
|
+
switch (columnTypes[col]) {
|
236
|
+
case STRING: setString(row, col, rs.getString(col+1)); break;
|
237
|
+
case INT: setInt(row, col, rs.getInt(col+1)); break;
|
238
|
+
case LONG: setLong(row, col, rs.getLong(col+1)); break;
|
239
|
+
case FLOAT: setFloat(row, col, rs.getFloat(col+1)); break;
|
240
|
+
case DOUBLE: setDouble(row, col, rs.getDouble(col+1)); break;
|
241
|
+
default: throw new IllegalArgumentException("column type " + columnTypes[col] + " not supported.");
|
242
|
+
}
|
243
|
+
}
|
244
|
+
row++;
|
245
|
+
// String[] row = new String[columnCount];
|
246
|
+
// for (int col = 0; col < columnCount; col++) {
|
247
|
+
// row[col] = rs.get(col + 1);
|
248
|
+
// }
|
249
|
+
// addRow(row);
|
250
|
+
}
|
251
|
+
|
252
|
+
} catch (SQLException s) {
|
253
|
+
throw new RuntimeException(s);
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
|
258
|
+
public Table typedParse(InputStream input, String options) throws IOException {
|
259
|
+
Table table = new Table();
|
260
|
+
table.setColumnTypes(this);
|
261
|
+
table.parse(input, options);
|
262
|
+
return table;
|
263
|
+
}
|
264
|
+
|
265
|
+
|
266
|
+
protected void init() {
|
267
|
+
columns = new Object[0];
|
268
|
+
columnTypes = new int[0];
|
269
|
+
columnCategories = new HashMapBlows[0];
|
270
|
+
}
|
271
|
+
|
272
|
+
|
273
|
+
/*
|
274
|
+
protected String checkOptions(File file, String options) throws IOException {
|
275
|
+
String extension = null;
|
276
|
+
String filename = file.getName();
|
277
|
+
int dotIndex = filename.lastIndexOf('.');
|
278
|
+
if (dotIndex != -1) {
|
279
|
+
extension = filename.substring(dotIndex + 1).toLowerCase();
|
280
|
+
if (!extension.equals("csv") &&
|
281
|
+
!extension.equals("tsv") &&
|
282
|
+
!extension.equals("html") &&
|
283
|
+
!extension.equals("bin")) {
|
284
|
+
// ignore extension
|
285
|
+
extension = null;
|
286
|
+
}
|
287
|
+
}
|
288
|
+
if (extension == null) {
|
289
|
+
if (options == null) {
|
290
|
+
throw new IOException("This table filename has no extension, and no options are set.");
|
291
|
+
}
|
292
|
+
} else { // extension is not null
|
293
|
+
if (options == null) {
|
294
|
+
options = extension;
|
295
|
+
} else {
|
296
|
+
// prepend the extension, it will be overridden if there's an option for it.
|
297
|
+
options = extension + "," + options;
|
298
|
+
}
|
299
|
+
}
|
300
|
+
return options;
|
301
|
+
}
|
302
|
+
*/
|
303
|
+
|
304
|
+
|
305
|
+
static final String[] loadExtensions = { "csv", "tsv", "ods", "bin" };
|
306
|
+
static final String[] saveExtensions = { "csv", "tsv", "ods", "bin", "html" };
|
307
|
+
|
308
|
+
static public String extensionOptions(boolean loading, String filename, String options) {
|
309
|
+
String extension = PApplet.checkExtension(filename);
|
310
|
+
if (extension != null) {
|
311
|
+
for (String possible : loading ? loadExtensions : saveExtensions) {
|
312
|
+
if (extension.equals(possible)) {
|
313
|
+
if (options == null) {
|
314
|
+
return extension;
|
315
|
+
} else {
|
316
|
+
// prepend the extension to the options (will be replaced by other
|
317
|
+
// options that override it later in the load loop)
|
318
|
+
return extension + "," + options;
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
return options;
|
324
|
+
}
|
325
|
+
|
326
|
+
|
327
|
+
protected void parse(InputStream input, String options) throws IOException {
|
328
|
+
// boolean awfulCSV = false;
|
329
|
+
boolean header = false;
|
330
|
+
String extension = null;
|
331
|
+
boolean binary = false;
|
332
|
+
String encoding = "UTF-8";
|
333
|
+
|
334
|
+
String worksheet = null;
|
335
|
+
final String sheetParam = "worksheet=";
|
336
|
+
|
337
|
+
String[] opts = null;
|
338
|
+
if (options != null) {
|
339
|
+
opts = PApplet.trim(PApplet.split(options, ','));
|
340
|
+
for (String opt : opts) {
|
341
|
+
if (opt.equals("tsv")) {
|
342
|
+
extension = "tsv";
|
343
|
+
} else if (opt.equals("csv")) {
|
344
|
+
extension = "csv";
|
345
|
+
} else if (opt.equals("ods")) {
|
346
|
+
extension = "ods";
|
347
|
+
} else if (opt.equals("newlines")) {
|
348
|
+
//awfulCSV = true;
|
349
|
+
//extension = "csv";
|
350
|
+
throw new IllegalArgumentException("The 'newlines' option is no longer necessary.");
|
351
|
+
} else if (opt.equals("bin")) {
|
352
|
+
binary = true;
|
353
|
+
extension = "bin";
|
354
|
+
} else if (opt.equals("header")) {
|
355
|
+
header = true;
|
356
|
+
} else if (opt.startsWith(sheetParam)) {
|
357
|
+
worksheet = opt.substring(sheetParam.length());
|
358
|
+
} else if (opt.startsWith("dictionary=")) {
|
359
|
+
// ignore option, this is only handled by PApplet
|
360
|
+
} else if (opt.startsWith("encoding=")) {
|
361
|
+
encoding = opt.substring(9);
|
362
|
+
} else {
|
363
|
+
throw new IllegalArgumentException("'" + opt + "' is not a valid option for loading a Table");
|
364
|
+
}
|
365
|
+
}
|
366
|
+
}
|
367
|
+
|
368
|
+
if (extension == null) {
|
369
|
+
throw new IllegalArgumentException("No extension specified for this Table");
|
370
|
+
}
|
371
|
+
|
372
|
+
if (binary) {
|
373
|
+
loadBinary(input);
|
374
|
+
|
375
|
+
} else if (extension.equals("ods")) {
|
376
|
+
odsParse(input, worksheet, header);
|
377
|
+
|
378
|
+
} else {
|
379
|
+
InputStreamReader isr = new InputStreamReader(input, encoding);
|
380
|
+
BufferedReader reader = new BufferedReader(isr);
|
381
|
+
|
382
|
+
// strip out the Unicode BOM, if present
|
383
|
+
reader.mark(1);
|
384
|
+
int c = reader.read();
|
385
|
+
// if not the BOM, back up to the beginning again
|
386
|
+
if (c != '\uFEFF') {
|
387
|
+
reader.reset();
|
388
|
+
}
|
389
|
+
|
390
|
+
/*
|
391
|
+
if (awfulCSV) {
|
392
|
+
parseAwfulCSV(reader, header);
|
393
|
+
} else if ("tsv".equals(extension)) {
|
394
|
+
parseBasic(reader, header, true);
|
395
|
+
} else if ("csv".equals(extension)) {
|
396
|
+
parseBasic(reader, header, false);
|
397
|
+
}
|
398
|
+
*/
|
399
|
+
parseBasic(reader, header, "tsv".equals(extension));
|
400
|
+
}
|
401
|
+
}
|
402
|
+
|
403
|
+
|
404
|
+
protected void parseBasic(BufferedReader reader,
|
405
|
+
boolean header, boolean tsv) throws IOException {
|
406
|
+
String line = null;
|
407
|
+
int row = 0;
|
408
|
+
if (rowCount == 0) {
|
409
|
+
setRowCount(10);
|
410
|
+
}
|
411
|
+
//int prev = 0; //-1;
|
412
|
+
try {
|
413
|
+
while ((line = reader.readLine()) != null) {
|
414
|
+
if (row == getRowCount()) {
|
415
|
+
setRowCount(row << 1);
|
416
|
+
}
|
417
|
+
if (row == 0 && header) {
|
418
|
+
setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader));
|
419
|
+
header = false;
|
420
|
+
} else {
|
421
|
+
setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader));
|
422
|
+
row++;
|
423
|
+
}
|
424
|
+
|
425
|
+
if (row % 10000 == 0) {
|
426
|
+
/*
|
427
|
+
// this is problematic unless we're going to calculate rowCount first
|
428
|
+
if (row < rowCount) {
|
429
|
+
int pct = (100 * row) / rowCount;
|
430
|
+
if (pct != prev) { // also prevents "0%" from showing up
|
431
|
+
System.out.println(pct + "%");
|
432
|
+
prev = pct;
|
433
|
+
}
|
434
|
+
}
|
435
|
+
*/
|
436
|
+
try {
|
437
|
+
// Sleep this thread so that the GC can catch up
|
438
|
+
Thread.sleep(10);
|
439
|
+
} catch (InterruptedException e) {
|
440
|
+
e.printStackTrace();
|
441
|
+
}
|
442
|
+
}
|
443
|
+
}
|
444
|
+
} catch (Exception e) {
|
445
|
+
throw new RuntimeException("Error reading table on line " + row, e);
|
446
|
+
}
|
447
|
+
// shorten or lengthen based on what's left
|
448
|
+
if (row != getRowCount()) {
|
449
|
+
setRowCount(row);
|
450
|
+
}
|
451
|
+
}
|
452
|
+
|
453
|
+
|
454
|
+
// public void convertTSV(BufferedReader reader, File outputFile) throws IOException {
|
455
|
+
// convertBasic(reader, true, outputFile);
|
456
|
+
// }
|
457
|
+
|
458
|
+
|
459
|
+
/*
|
460
|
+
protected void parseAwfulCSV(BufferedReader reader,
|
461
|
+
boolean header) throws IOException {
|
462
|
+
char[] c = new char[100];
|
463
|
+
int count = 0;
|
464
|
+
boolean insideQuote = false;
|
465
|
+
|
466
|
+
int alloc = 100;
|
467
|
+
setRowCount(100);
|
468
|
+
|
469
|
+
int row = 0;
|
470
|
+
int col = 0;
|
471
|
+
int ch;
|
472
|
+
while ((ch = reader.read()) != -1) {
|
473
|
+
if (insideQuote) {
|
474
|
+
if (ch == '\"') {
|
475
|
+
// this is either the end of a quoted entry, or a quote character
|
476
|
+
reader.mark(1);
|
477
|
+
if (reader.read() == '\"') {
|
478
|
+
// it's "", which means a quote character
|
479
|
+
if (count == c.length) {
|
480
|
+
c = PApplet.expand(c);
|
481
|
+
}
|
482
|
+
c[count++] = '\"';
|
483
|
+
} else {
|
484
|
+
// nope, just the end of a quoted csv entry
|
485
|
+
reader.reset();
|
486
|
+
insideQuote = false;
|
487
|
+
// TODO nothing here that prevents bad csv data from showing up
|
488
|
+
// after the quote and before the comma...
|
489
|
+
// set(row, col, new String(c, 0, count));
|
490
|
+
// count = 0;
|
491
|
+
// col++;
|
492
|
+
// insideQuote = false;
|
493
|
+
}
|
494
|
+
} else { // inside a quote, but the character isn't a quote
|
495
|
+
if (count == c.length) {
|
496
|
+
c = PApplet.expand(c);
|
497
|
+
}
|
498
|
+
c[count++] = (char) ch;
|
499
|
+
}
|
500
|
+
} else { // not inside a quote
|
501
|
+
if (ch == '\"') {
|
502
|
+
insideQuote = true;
|
503
|
+
|
504
|
+
} else if (ch == '\r' || ch == '\n') {
|
505
|
+
if (ch == '\r') {
|
506
|
+
// check to see if next is a '\n'
|
507
|
+
reader.mark(1);
|
508
|
+
if (reader.read() != '\n') {
|
509
|
+
reader.reset();
|
510
|
+
}
|
511
|
+
}
|
512
|
+
setString(row, col, new String(c, 0, count));
|
513
|
+
count = 0;
|
514
|
+
row++;
|
515
|
+
if (row == 1 && header) {
|
516
|
+
// Use internal row removal (efficient because only one row).
|
517
|
+
removeTitleRow();
|
518
|
+
// Un-set the header variable so that next time around, we don't
|
519
|
+
// just get stuck into a loop, removing the 0th row repeatedly.
|
520
|
+
header = false;
|
521
|
+
// Reset the number of rows (removeTitleRow() won't reset our local 'row' counter)
|
522
|
+
row = 0;
|
523
|
+
}
|
524
|
+
// if (row % 1000 == 0) {
|
525
|
+
// PApplet.println(PApplet.nfc(row));
|
526
|
+
// }
|
527
|
+
if (row == alloc) {
|
528
|
+
alloc *= 2;
|
529
|
+
setRowCount(alloc);
|
530
|
+
}
|
531
|
+
col = 0;
|
532
|
+
|
533
|
+
} else if (ch == ',') {
|
534
|
+
setString(row, col, new String(c, 0, count));
|
535
|
+
count = 0;
|
536
|
+
// starting a new column, make sure we have room
|
537
|
+
col++;
|
538
|
+
ensureColumn(col);
|
539
|
+
|
540
|
+
} else { // just a regular character, add it
|
541
|
+
if (count == c.length) {
|
542
|
+
c = PApplet.expand(c);
|
543
|
+
}
|
544
|
+
c[count++] = (char) ch;
|
545
|
+
}
|
546
|
+
}
|
547
|
+
}
|
548
|
+
// catch any leftovers
|
549
|
+
if (count > 0) {
|
550
|
+
setString(row, col, new String(c, 0, count));
|
551
|
+
}
|
552
|
+
row++; // set row to row count (the current row index + 1)
|
553
|
+
if (alloc != row) {
|
554
|
+
setRowCount(row); // shrink to the actual size
|
555
|
+
}
|
556
|
+
}
|
557
|
+
*/
|
558
|
+
|
559
|
+
|
560
|
+
static class CommaSeparatedLine {
|
561
|
+
char[] c;
|
562
|
+
String[] pieces;
|
563
|
+
int pieceCount;
|
564
|
+
|
565
|
+
// int offset;
|
566
|
+
int start; //, stop;
|
567
|
+
|
568
|
+
String[] handle(String line, BufferedReader reader) throws IOException {
|
569
|
+
// PApplet.println("handle() called for: " + line);
|
570
|
+
start = 0;
|
571
|
+
pieceCount = 0;
|
572
|
+
c = line.toCharArray();
|
573
|
+
|
574
|
+
// get tally of number of columns and allocate the array
|
575
|
+
int cols = 1; // the first comma indicates the second column
|
576
|
+
boolean quote = false;
|
577
|
+
for (int i = 0; i < c.length; i++) {
|
578
|
+
if (!quote && (c[i] == ',')) {
|
579
|
+
cols++;
|
580
|
+
} else if (c[i] == '\"') {
|
581
|
+
// double double quotes (escaped quotes like "") will simply toggle
|
582
|
+
// this back and forth, so it should remain accurate
|
583
|
+
quote = !quote;
|
584
|
+
}
|
585
|
+
}
|
586
|
+
pieces = new String[cols];
|
587
|
+
|
588
|
+
// while (offset < c.length) {
|
589
|
+
// start = offset;
|
590
|
+
while (start < c.length) {
|
591
|
+
boolean enough = ingest();
|
592
|
+
while (!enough) {
|
593
|
+
// found a newline inside the quote, grab another line
|
594
|
+
String nextLine = reader.readLine();
|
595
|
+
// System.out.println("extending to " + nextLine);
|
596
|
+
if (nextLine == null) {
|
597
|
+
// System.err.println(line);
|
598
|
+
throw new IOException("Found a quoted line that wasn't terminated properly.");
|
599
|
+
}
|
600
|
+
// for simplicity, not bothering to skip what's already been read
|
601
|
+
// from c (and reset the offset to 0), opting to make a bigger array
|
602
|
+
// with both lines.
|
603
|
+
char[] temp = new char[c.length + 1 + nextLine.length()];
|
604
|
+
PApplet.arrayCopy(c, temp, c.length);
|
605
|
+
// NOTE: we're converting to \n here, which isn't perfect
|
606
|
+
temp[c.length] = '\n';
|
607
|
+
nextLine.getChars(0, nextLine.length(), temp, c.length + 1);
|
608
|
+
// c = temp;
|
609
|
+
return handle(new String(temp), reader);
|
610
|
+
//System.out.println(" full line is now " + new String(c));
|
611
|
+
//stop = nextComma(c, offset);
|
612
|
+
//System.out.println("stop is now " + stop);
|
613
|
+
//enough = ingest();
|
614
|
+
}
|
615
|
+
}
|
616
|
+
|
617
|
+
// Make any remaining entries blanks instead of nulls. Empty columns from
|
618
|
+
// CSV are always "" not null, so this handles successive commas in a line
|
619
|
+
for (int i = pieceCount; i < pieces.length; i++) {
|
620
|
+
pieces[i] = "";
|
621
|
+
}
|
622
|
+
// PApplet.printArray(pieces);
|
623
|
+
return pieces;
|
624
|
+
}
|
625
|
+
|
626
|
+
protected void addPiece(int start, int stop, boolean quotes) {
|
627
|
+
if (quotes) {
|
628
|
+
int dest = start;
|
629
|
+
for (int i = start; i < stop; i++) {
|
630
|
+
if (c[i] == '\"') {
|
631
|
+
++i; // step over the quote
|
632
|
+
}
|
633
|
+
if (i != dest) {
|
634
|
+
c[dest] = c[i];
|
635
|
+
}
|
636
|
+
dest++;
|
637
|
+
}
|
638
|
+
pieces[pieceCount++] = new String(c, start, dest - start);
|
639
|
+
|
640
|
+
} else {
|
641
|
+
pieces[pieceCount++] = new String(c, start, stop - start);
|
642
|
+
}
|
643
|
+
}
|
644
|
+
|
645
|
+
/**
|
646
|
+
* Returns the next comma (not inside a quote) in the specified array.
|
647
|
+
* @param c array to search
|
648
|
+
* @param index offset at which to start looking
|
649
|
+
* @return index of the comma, or -1 if line ended inside an unclosed quote
|
650
|
+
*/
|
651
|
+
protected boolean ingest() {
|
652
|
+
boolean hasEscapedQuotes = false;
|
653
|
+
// not possible
|
654
|
+
// if (index == c.length) { // we're already at the end
|
655
|
+
// return c.length;
|
656
|
+
// }
|
657
|
+
boolean quoted = c[start] == '\"';
|
658
|
+
if (quoted) {
|
659
|
+
start++; // step over the quote
|
660
|
+
}
|
661
|
+
int i = start;
|
662
|
+
while (i < c.length) {
|
663
|
+
// PApplet.println(c[i] + " i=" + i);
|
664
|
+
if (c[i] == '\"') {
|
665
|
+
// if this fella started with a quote
|
666
|
+
if (quoted) {
|
667
|
+
if (i == c.length-1) {
|
668
|
+
// closing quote for field; last field on the line
|
669
|
+
addPiece(start, i, hasEscapedQuotes);
|
670
|
+
start = c.length;
|
671
|
+
return true;
|
672
|
+
|
673
|
+
} else if (c[i+1] == '\"') {
|
674
|
+
// an escaped quote inside a quoted field, step over it
|
675
|
+
hasEscapedQuotes = true;
|
676
|
+
i += 2;
|
677
|
+
|
678
|
+
} else if (c[i+1] == ',') {
|
679
|
+
// that was our closing quote, get outta here
|
680
|
+
addPiece(start, i, hasEscapedQuotes);
|
681
|
+
start = i+2;
|
682
|
+
return true;
|
683
|
+
|
684
|
+
} else {
|
685
|
+
// This is a lone-wolf quote, occasionally seen in exports.
|
686
|
+
// It's a single quote in the middle of some other text,
|
687
|
+
// and not escaped properly. Pray for the best!
|
688
|
+
i++;
|
689
|
+
}
|
690
|
+
|
691
|
+
} else { // not a quoted line
|
692
|
+
if (i == c.length-1) {
|
693
|
+
// we're at the end of the line, can't have an unescaped quote
|
694
|
+
throw new RuntimeException("Unterminated quote at end of line");
|
695
|
+
|
696
|
+
} else if (c[i+1] == '\"') {
|
697
|
+
// step over this crummy quote escape
|
698
|
+
hasEscapedQuotes = true;
|
699
|
+
i += 2;
|
700
|
+
|
701
|
+
} else {
|
702
|
+
throw new RuntimeException("Unterminated quoted field mid-line");
|
703
|
+
}
|
704
|
+
}
|
705
|
+
} else if (!quoted && c[i] == ',') {
|
706
|
+
addPiece(start, i, hasEscapedQuotes);
|
707
|
+
start = i+1;
|
708
|
+
return true;
|
709
|
+
|
710
|
+
} else if (!quoted && i == c.length-1) {
|
711
|
+
addPiece(start, c.length, hasEscapedQuotes);
|
712
|
+
start = c.length;
|
713
|
+
return true;
|
714
|
+
|
715
|
+
} else { // nothing all that interesting
|
716
|
+
i++;
|
717
|
+
}
|
718
|
+
}
|
719
|
+
// if (!quote && (c[i] == ',')) {
|
720
|
+
// // found a comma, return this location
|
721
|
+
// return i;
|
722
|
+
// } else if (c[i] == '\"') {
|
723
|
+
// // if it's a quote, then either the next char is another quote,
|
724
|
+
// // or if this is a quoted entry, it better be a comma
|
725
|
+
// quote = !quote;
|
726
|
+
// }
|
727
|
+
// }
|
728
|
+
|
729
|
+
// if still inside a quote, indicate that another line should be read
|
730
|
+
if (quoted) {
|
731
|
+
return false;
|
732
|
+
}
|
733
|
+
|
734
|
+
// // made it to the end of the array with no new comma
|
735
|
+
// return c.length;
|
736
|
+
|
737
|
+
throw new RuntimeException("not sure how...");
|
738
|
+
}
|
739
|
+
}
|
740
|
+
|
741
|
+
|
742
|
+
CommaSeparatedLine csl;
|
743
|
+
|
744
|
+
/**
|
745
|
+
* Parse a line of text as comma-separated values, returning each value as
|
746
|
+
* one entry in an array of String objects. Remove quotes from entries that
|
747
|
+
* begin and end with them, and convert 'escaped' quotes to actual quotes.
|
748
|
+
* @param line line of text to be parsed
|
749
|
+
* @return an array of the individual values formerly separated by commas
|
750
|
+
*/
|
751
|
+
protected String[] splitLineCSV(String line, BufferedReader reader) throws IOException {
|
752
|
+
if (csl == null) {
|
753
|
+
csl = new CommaSeparatedLine();
|
754
|
+
}
|
755
|
+
return csl.handle(line, reader);
|
756
|
+
}
|
757
|
+
|
758
|
+
|
759
|
+
/**
|
760
|
+
* Returns the next comma (not inside a quote) in the specified array.
|
761
|
+
* @param c array to search
|
762
|
+
* @param index offset at which to start looking
|
763
|
+
* @return index of the comma, or -1 if line ended inside an unclosed quote
|
764
|
+
*/
|
765
|
+
/*
|
766
|
+
static protected int nextComma(char[] c, int index) {
|
767
|
+
if (index == c.length) { // we're already at the end
|
768
|
+
return c.length;
|
769
|
+
}
|
770
|
+
boolean quoted = c[index] == '\"';
|
771
|
+
if (quoted) {
|
772
|
+
index++; // step over the quote
|
773
|
+
}
|
774
|
+
for (int i = index; i < c.length; i++) {
|
775
|
+
if (c[i] == '\"') {
|
776
|
+
// if this fella started with a quote
|
777
|
+
if (quoted) {
|
778
|
+
if (i == c.length-1) {
|
779
|
+
//return -1; // ran out of chars
|
780
|
+
// closing quote for field; last field on the line
|
781
|
+
return c.length;
|
782
|
+
} else if (c[i+1] == '\"') {
|
783
|
+
// an escaped quote inside a quoted field, step over it
|
784
|
+
i++;
|
785
|
+
} else if (c[i+1] == ',') {
|
786
|
+
// that's our closing quote, get outta here
|
787
|
+
return i+1;
|
788
|
+
}
|
789
|
+
|
790
|
+
} else { // not a quoted line
|
791
|
+
if (i == c.length-1) {
|
792
|
+
// we're at the end of the line, can't have an unescaped quote
|
793
|
+
//return -1; // ran out of chars
|
794
|
+
throw new RuntimeException("Unterminated quoted field at end of line");
|
795
|
+
} else if (c[i+1] == '\"') {
|
796
|
+
// step over this crummy quote escape
|
797
|
+
++i;
|
798
|
+
} else {
|
799
|
+
throw new RuntimeException("Unterminated quoted field mid-line");
|
800
|
+
}
|
801
|
+
}
|
802
|
+
} else if (!quoted && c[i] == ',') {
|
803
|
+
return i;
|
804
|
+
}
|
805
|
+
if (!quote && (c[i] == ',')) {
|
806
|
+
// found a comma, return this location
|
807
|
+
return i;
|
808
|
+
} else if (c[i] == '\"') {
|
809
|
+
// if it's a quote, then either the next char is another quote,
|
810
|
+
// or if this is a quoted entry, it better be a comma
|
811
|
+
quote = !quote;
|
812
|
+
}
|
813
|
+
}
|
814
|
+
// if still inside a quote, indicate that another line should be read
|
815
|
+
if (quote) {
|
816
|
+
return -1;
|
817
|
+
}
|
818
|
+
// made it to the end of the array with no new comma
|
819
|
+
return c.length;
|
820
|
+
}
|
821
|
+
*/
|
822
|
+
|
823
|
+
|
824
|
+
/**
|
825
|
+
* Read a .ods (OpenDoc spreadsheet) zip file from an InputStream, and
|
826
|
+
* return the InputStream for content.xml contained inside.
|
827
|
+
*/
|
828
|
+
private InputStream odsFindContentXML(InputStream input) {
|
829
|
+
ZipInputStream zis = new ZipInputStream(input);
|
830
|
+
ZipEntry entry = null;
|
831
|
+
try {
|
832
|
+
while ((entry = zis.getNextEntry()) != null) {
|
833
|
+
if (entry.getName().equals("content.xml")) {
|
834
|
+
return zis;
|
835
|
+
}
|
836
|
+
}
|
837
|
+
} catch (IOException e) {
|
838
|
+
e.printStackTrace();
|
839
|
+
}
|
840
|
+
return null;
|
841
|
+
}
|
842
|
+
|
843
|
+
|
844
|
+
protected void odsParse(InputStream input, String worksheet, boolean header) {
|
845
|
+
try {
|
846
|
+
InputStream contentStream = odsFindContentXML(input);
|
847
|
+
XML xml = new XML(contentStream);
|
848
|
+
|
849
|
+
// table files will have multiple sheets..
|
850
|
+
// <table:table table:name="Sheet1" table:style-name="ta1" table:print="false">
|
851
|
+
// <table:table table:name="Sheet2" table:style-name="ta1" table:print="false">
|
852
|
+
// <table:table table:name="Sheet3" table:style-name="ta1" table:print="false">
|
853
|
+
XML[] sheets =
|
854
|
+
xml.getChildren("office:body/office:spreadsheet/table:table");
|
855
|
+
|
856
|
+
boolean found = false;
|
857
|
+
for (XML sheet : sheets) {
|
858
|
+
// System.out.println(sheet.getAttribute("table:name"));
|
859
|
+
if (worksheet == null || worksheet.equals(sheet.getString("table:name"))) {
|
860
|
+
odsParseSheet(sheet, header);
|
861
|
+
found = true;
|
862
|
+
if (worksheet == null) {
|
863
|
+
break; // only read the first sheet
|
864
|
+
}
|
865
|
+
}
|
866
|
+
}
|
867
|
+
if (!found) {
|
868
|
+
if (worksheet == null) {
|
869
|
+
throw new RuntimeException("No worksheets found in the ODS file.");
|
870
|
+
} else {
|
871
|
+
throw new RuntimeException("No worksheet named " + worksheet +
|
872
|
+
" found in the ODS file.");
|
873
|
+
}
|
874
|
+
}
|
875
|
+
} catch (UnsupportedEncodingException e) {
|
876
|
+
e.printStackTrace();
|
877
|
+
} catch (IOException e) {
|
878
|
+
e.printStackTrace();
|
879
|
+
} catch (ParserConfigurationException e) {
|
880
|
+
e.printStackTrace();
|
881
|
+
} catch (SAXException e) {
|
882
|
+
e.printStackTrace();
|
883
|
+
}
|
884
|
+
}
|
885
|
+
|
886
|
+
|
887
|
+
/**
|
888
|
+
* Parses a single sheet of XML from this file.
|
889
|
+
* @param The XML object for a single worksheet from the ODS file
|
890
|
+
*/
|
891
|
+
private void odsParseSheet(XML sheet, boolean header) {
|
892
|
+
// Extra <p> or <a> tags inside the text tag for the cell will be stripped.
|
893
|
+
// Different from showing formulas, and not quite the same as 'save as
|
894
|
+
// displayed' option when saving from inside OpenOffice. Only time we
|
895
|
+
// wouldn't want this would be so that we could parse hyperlinks and
|
896
|
+
// styling information intact, but that's out of scope for the p5 version.
|
897
|
+
final boolean ignoreTags = true;
|
898
|
+
|
899
|
+
XML[] rows = sheet.getChildren("table:table-row");
|
900
|
+
//xml.getChildren("office:body/office:spreadsheet/table:table/table:table-row");
|
901
|
+
|
902
|
+
int rowIndex = 0;
|
903
|
+
for (XML row : rows) {
|
904
|
+
int rowRepeat = row.getInt("table:number-rows-repeated", 1);
|
905
|
+
// if (rowRepeat != 1) {
|
906
|
+
// System.out.println(rowRepeat + " " + rowCount + " " + (rowCount + rowRepeat));
|
907
|
+
// }
|
908
|
+
boolean rowNotNull = false;
|
909
|
+
XML[] cells = row.getChildren();
|
910
|
+
int columnIndex = 0;
|
911
|
+
|
912
|
+
for (XML cell : cells) {
|
913
|
+
int cellRepeat = cell.getInt("table:number-columns-repeated", 1);
|
914
|
+
|
915
|
+
// <table:table-cell table:formula="of:=SUM([.E7:.E8])" office:value-type="float" office:value="4150">
|
916
|
+
// <text:p>4150.00</text:p>
|
917
|
+
// </table:table-cell>
|
918
|
+
|
919
|
+
String cellData = ignoreTags ? cell.getString("office:value") : null;
|
920
|
+
|
921
|
+
// if there's an office:value in the cell, just roll with that
|
922
|
+
if (cellData == null) {
|
923
|
+
int cellKids = cell.getChildCount();
|
924
|
+
if (cellKids != 0) {
|
925
|
+
XML[] paragraphElements = cell.getChildren("text:p");
|
926
|
+
if (paragraphElements.length != 1) {
|
927
|
+
for (XML el : paragraphElements) {
|
928
|
+
System.err.println(el.toString());
|
929
|
+
}
|
930
|
+
throw new RuntimeException("found more than one text:p element");
|
931
|
+
}
|
932
|
+
XML textp = paragraphElements[0];
|
933
|
+
String textpContent = textp.getContent();
|
934
|
+
// if there are sub-elements, the content shows up as a child element
|
935
|
+
// (for which getName() returns null.. which seems wrong)
|
936
|
+
if (textpContent != null) {
|
937
|
+
cellData = textpContent; // nothing fancy, the text is in the text:p element
|
938
|
+
} else {
|
939
|
+
XML[] textpKids = textp.getChildren();
|
940
|
+
StringBuilder cellBuffer = new StringBuilder();
|
941
|
+
for (XML kid : textpKids) {
|
942
|
+
String kidName = kid.getName();
|
943
|
+
if (kidName == null) {
|
944
|
+
odsAppendNotNull(kid, cellBuffer);
|
945
|
+
|
946
|
+
} else if (kidName.equals("text:s")) {
|
947
|
+
int spaceCount = kid.getInt("text:c", 1);
|
948
|
+
for (int space = 0; space < spaceCount; space++) {
|
949
|
+
cellBuffer.append(' ');
|
950
|
+
}
|
951
|
+
} else if (kidName.equals("text:span")) {
|
952
|
+
odsAppendNotNull(kid, cellBuffer);
|
953
|
+
|
954
|
+
} else if (kidName.equals("text:a")) {
|
955
|
+
// <text:a xlink:href="http://blah.com/">blah.com</text:a>
|
956
|
+
if (ignoreTags) {
|
957
|
+
cellBuffer.append(kid.getString("xlink:href"));
|
958
|
+
} else {
|
959
|
+
odsAppendNotNull(kid, cellBuffer);
|
960
|
+
}
|
961
|
+
|
962
|
+
} else {
|
963
|
+
odsAppendNotNull(kid, cellBuffer);
|
964
|
+
System.err.println(getClass().getName() + ": don't understand: " + kid);
|
965
|
+
//throw new RuntimeException("I'm not used to this.");
|
966
|
+
}
|
967
|
+
}
|
968
|
+
cellData = cellBuffer.toString();
|
969
|
+
}
|
970
|
+
//setString(rowIndex, columnIndex, c); //text[0].getContent());
|
971
|
+
//columnIndex++;
|
972
|
+
}
|
973
|
+
}
|
974
|
+
for (int r = 0; r < cellRepeat; r++) {
|
975
|
+
if (cellData != null) {
|
976
|
+
//System.out.println("setting " + rowIndex + "," + columnIndex + " to " + cellData);
|
977
|
+
setString(rowIndex, columnIndex, cellData);
|
978
|
+
}
|
979
|
+
columnIndex++;
|
980
|
+
if (cellData != null) {
|
981
|
+
// if (columnIndex > columnMax) {
|
982
|
+
// columnMax = columnIndex;
|
983
|
+
// }
|
984
|
+
rowNotNull = true;
|
985
|
+
}
|
986
|
+
}
|
987
|
+
}
|
988
|
+
if (header) {
|
989
|
+
removeTitleRow(); // efficient enough on the first row
|
990
|
+
header = false; // avoid infinite loop
|
991
|
+
|
992
|
+
} else {
|
993
|
+
if (rowNotNull && rowRepeat > 1) {
|
994
|
+
String[] rowStrings = getStringRow(rowIndex);
|
995
|
+
for (int r = 1; r < rowRepeat; r++) {
|
996
|
+
addRow(rowStrings);
|
997
|
+
}
|
998
|
+
}
|
999
|
+
rowIndex += rowRepeat;
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
|
1005
|
+
private void odsAppendNotNull(XML kid, StringBuilder buffer) {
|
1006
|
+
String content = kid.getContent();
|
1007
|
+
if (content != null) {
|
1008
|
+
buffer.append(content);
|
1009
|
+
}
|
1010
|
+
}
|
1011
|
+
|
1012
|
+
|
1013
|
+
// A 'Class' object is used here, so the syntax for this function is:
|
1014
|
+
// Table t = loadTable("cars3.tsv", "header");
|
1015
|
+
// Record[] records = (Record[]) t.parse(Record.class);
|
1016
|
+
// While t.parse("Record") might be nicer, the class is likely to be an
|
1017
|
+
// inner class (another tab in a PDE sketch) or even inside a package,
|
1018
|
+
// so additional information would be needed to locate it. The name of the
|
1019
|
+
// inner class would be "SketchName$Record" which isn't acceptable syntax
|
1020
|
+
// to make people use. Better to just introduce the '.class' syntax.
|
1021
|
+
|
1022
|
+
// Unlike the Table class itself, this accepts char and boolean fields in
|
1023
|
+
// the target class, since they're much more prevalent, and don't require
|
1024
|
+
// a zillion extra methods and special cases in the rest of the class here.
|
1025
|
+
|
1026
|
+
// since this is likely an inner class, needs a reference to its parent,
|
1027
|
+
// because that's passed to the constructor parameter (inserted by the
|
1028
|
+
// compiler) of an inner class by the runtime.
|
1029
|
+
|
1030
|
+
/** incomplete, do not use */
|
1031
|
+
public void parseInto(Object enclosingObject, String fieldName) {
|
1032
|
+
Class<?> target = null;
|
1033
|
+
Object outgoing = null;
|
1034
|
+
Field targetField = null;
|
1035
|
+
try {
|
1036
|
+
// Object targetObject,
|
1037
|
+
// Class target -> get this from the type of fieldName
|
1038
|
+
// Class sketchClass = sketch.getClass();
|
1039
|
+
Class<?> sketchClass = enclosingObject.getClass();
|
1040
|
+
targetField = sketchClass.getDeclaredField(fieldName);
|
1041
|
+
// PApplet.println("found " + targetField);
|
1042
|
+
Class<?> targetArray = targetField.getType();
|
1043
|
+
if (!targetArray.isArray()) {
|
1044
|
+
// fieldName is not an array
|
1045
|
+
} else {
|
1046
|
+
target = targetArray.getComponentType();
|
1047
|
+
outgoing = Array.newInstance(target, getRowCount());
|
1048
|
+
}
|
1049
|
+
} catch (NoSuchFieldException e) {
|
1050
|
+
e.printStackTrace();
|
1051
|
+
} catch (SecurityException e) {
|
1052
|
+
e.printStackTrace();
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
// Object enclosingObject = sketch;
|
1056
|
+
// PApplet.println("enclosing obj is " + enclosingObject);
|
1057
|
+
Class<?> enclosingClass = target.getEnclosingClass();
|
1058
|
+
Constructor<?> con = null;
|
1059
|
+
|
1060
|
+
try {
|
1061
|
+
if (enclosingClass == null) {
|
1062
|
+
con = target.getDeclaredConstructor(); //new Class[] { });
|
1063
|
+
// PApplet.println("no enclosing class");
|
1064
|
+
} else {
|
1065
|
+
con = target.getDeclaredConstructor(enclosingClass);
|
1066
|
+
// PApplet.println("enclosed by " + enclosingClass.getName());
|
1067
|
+
}
|
1068
|
+
if (!con.canAccess(null)) {
|
1069
|
+
// System.out.println("setting constructor to public");
|
1070
|
+
con.setAccessible(true);
|
1071
|
+
}
|
1072
|
+
} catch (SecurityException e) {
|
1073
|
+
e.printStackTrace();
|
1074
|
+
} catch (NoSuchMethodException e) {
|
1075
|
+
e.printStackTrace();
|
1076
|
+
}
|
1077
|
+
|
1078
|
+
Field[] fields = target.getDeclaredFields();
|
1079
|
+
ArrayList<Field> inuse = new ArrayList<>();
|
1080
|
+
for (Field field : fields) {
|
1081
|
+
String name = field.getName();
|
1082
|
+
if (getColumnIndex(name, false) != -1) {
|
1083
|
+
inuse.add(field);
|
1084
|
+
} else {
|
1085
|
+
// System.out.println("skipping field " + name);
|
1086
|
+
}
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
int index = 0;
|
1090
|
+
try {
|
1091
|
+
for (TableRow row : rows()) {
|
1092
|
+
Object item = null;
|
1093
|
+
if (enclosingClass == null) {
|
1094
|
+
//item = target.newInstance();
|
1095
|
+
item = con.newInstance();
|
1096
|
+
} else {
|
1097
|
+
item = con.newInstance(enclosingObject);
|
1098
|
+
}
|
1099
|
+
|
1100
|
+
// Only needed once
|
1101
|
+
if (index == 0) {
|
1102
|
+
for (Field field : inuse) {
|
1103
|
+
if (!field.canAccess(item)) {
|
1104
|
+
// PApplet.println(" changing field access");
|
1105
|
+
field.setAccessible(true);
|
1106
|
+
}
|
1107
|
+
}
|
1108
|
+
}
|
1109
|
+
|
1110
|
+
//Object item = defaultCons.newInstance(new Object[] { });
|
1111
|
+
for (Field field : inuse) {
|
1112
|
+
String name = field.getName();
|
1113
|
+
// PApplet.println("gonna set field " + name);
|
1114
|
+
|
1115
|
+
if (field.getType() == String.class) {
|
1116
|
+
field.set(item, row.getString(name));
|
1117
|
+
|
1118
|
+
} else if (field.getType() == Integer.TYPE) {
|
1119
|
+
field.setInt(item, row.getInt(name));
|
1120
|
+
|
1121
|
+
} else if (field.getType() == Long.TYPE) {
|
1122
|
+
field.setLong(item, row.getLong(name));
|
1123
|
+
|
1124
|
+
} else if (field.getType() == Float.TYPE) {
|
1125
|
+
field.setFloat(item, row.getFloat(name));
|
1126
|
+
|
1127
|
+
} else if (field.getType() == Double.TYPE) {
|
1128
|
+
field.setDouble(item, row.getDouble(name));
|
1129
|
+
|
1130
|
+
} else if (field.getType() == Boolean.TYPE) {
|
1131
|
+
String content = row.getString(name);
|
1132
|
+
if (content != null) {
|
1133
|
+
// Only bother setting if it's true,
|
1134
|
+
// otherwise false by default anyway.
|
1135
|
+
if (content.toLowerCase().equals("true") ||
|
1136
|
+
content.equals("1")) {
|
1137
|
+
field.setBoolean(item, true);
|
1138
|
+
}
|
1139
|
+
}
|
1140
|
+
// if (content == null) {
|
1141
|
+
// field.setBoolean(item, false); // necessary?
|
1142
|
+
// } else if (content.toLowerCase().equals("true")) {
|
1143
|
+
// field.setBoolean(item, true);
|
1144
|
+
// } else if (content.equals("1")) {
|
1145
|
+
// field.setBoolean(item, true);
|
1146
|
+
// } else {
|
1147
|
+
// field.setBoolean(item, false); // necessary?
|
1148
|
+
// }
|
1149
|
+
} else if (field.getType() == Character.TYPE) {
|
1150
|
+
String content = row.getString(name);
|
1151
|
+
if (content != null && content.length() > 0) {
|
1152
|
+
// Otherwise set to \0 anyway
|
1153
|
+
field.setChar(item, content.charAt(0));
|
1154
|
+
}
|
1155
|
+
}
|
1156
|
+
}
|
1157
|
+
// list.add(item);
|
1158
|
+
Array.set(outgoing, index++, item);
|
1159
|
+
}
|
1160
|
+
if (!targetField.canAccess(enclosingObject)) {
|
1161
|
+
// PApplet.println("setting target field to public");
|
1162
|
+
targetField.setAccessible(true);
|
1163
|
+
}
|
1164
|
+
// Set the array in the sketch
|
1165
|
+
// targetField.set(sketch, outgoing);
|
1166
|
+
targetField.set(enclosingObject, outgoing);
|
1167
|
+
|
1168
|
+
} catch (InstantiationException e) {
|
1169
|
+
e.printStackTrace();
|
1170
|
+
} catch (IllegalAccessException e) {
|
1171
|
+
e.printStackTrace();
|
1172
|
+
} catch (IllegalArgumentException e) {
|
1173
|
+
e.printStackTrace();
|
1174
|
+
} catch (InvocationTargetException e) {
|
1175
|
+
e.printStackTrace();
|
1176
|
+
}
|
1177
|
+
}
|
1178
|
+
|
1179
|
+
|
1180
|
+
public boolean save(File file, String options) throws IOException {
|
1181
|
+
return save(PApplet.createOutput(file),
|
1182
|
+
Table.extensionOptions(false, file.getName(), options));
|
1183
|
+
}
|
1184
|
+
|
1185
|
+
|
1186
|
+
public boolean save(OutputStream output, String options) {
|
1187
|
+
PrintWriter writer = PApplet.createWriter(output);
|
1188
|
+
String extension = null;
|
1189
|
+
if (options == null) {
|
1190
|
+
throw new IllegalArgumentException("No extension specified for saving this Table");
|
1191
|
+
}
|
1192
|
+
|
1193
|
+
String[] opts = PApplet.trim(PApplet.split(options, ','));
|
1194
|
+
// Only option for save is the extension, so we can safely grab the last
|
1195
|
+
extension = opts[opts.length - 1];
|
1196
|
+
boolean found = false;
|
1197
|
+
for (String ext : saveExtensions) {
|
1198
|
+
if (extension.equals(ext)) {
|
1199
|
+
found = true;
|
1200
|
+
break;
|
1201
|
+
}
|
1202
|
+
}
|
1203
|
+
// Not providing a fallback; let's make users specify an extension
|
1204
|
+
if (!found) {
|
1205
|
+
throw new IllegalArgumentException("'" + extension + "' not available for Table");
|
1206
|
+
}
|
1207
|
+
|
1208
|
+
if (extension.equals("csv")) {
|
1209
|
+
writeCSV(writer);
|
1210
|
+
} else if (extension.equals("tsv")) {
|
1211
|
+
writeTSV(writer);
|
1212
|
+
} else if (extension.equals("ods")) {
|
1213
|
+
try {
|
1214
|
+
saveODS(output);
|
1215
|
+
} catch (IOException e) {
|
1216
|
+
e.printStackTrace();
|
1217
|
+
return false;
|
1218
|
+
}
|
1219
|
+
} else if (extension.equals("html")) {
|
1220
|
+
writeHTML(writer);
|
1221
|
+
} else if (extension.equals("bin")) {
|
1222
|
+
try {
|
1223
|
+
saveBinary(output);
|
1224
|
+
} catch (IOException e) {
|
1225
|
+
e.printStackTrace();
|
1226
|
+
return false;
|
1227
|
+
}
|
1228
|
+
}
|
1229
|
+
writer.flush();
|
1230
|
+
writer.close();
|
1231
|
+
return true;
|
1232
|
+
}
|
1233
|
+
|
1234
|
+
|
1235
|
+
protected void writeTSV(PrintWriter writer) {
|
1236
|
+
if (columnTitles != null) {
|
1237
|
+
for (int col = 0; col < columns.length; col++) {
|
1238
|
+
if (col != 0) {
|
1239
|
+
writer.print('\t');
|
1240
|
+
}
|
1241
|
+
if (columnTitles[col] != null) {
|
1242
|
+
writer.print(columnTitles[col]);
|
1243
|
+
}
|
1244
|
+
}
|
1245
|
+
writer.println();
|
1246
|
+
}
|
1247
|
+
for (int row = 0; row < rowCount; row++) {
|
1248
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
1249
|
+
if (col != 0) {
|
1250
|
+
writer.print('\t');
|
1251
|
+
}
|
1252
|
+
String entry = getString(row, col);
|
1253
|
+
// just write null entries as blanks, rather than spewing 'null'
|
1254
|
+
// all over the spreadsheet file.
|
1255
|
+
if (entry != null) {
|
1256
|
+
writer.print(entry);
|
1257
|
+
}
|
1258
|
+
}
|
1259
|
+
writer.println();
|
1260
|
+
}
|
1261
|
+
writer.flush();
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
|
1265
|
+
protected void writeCSV(PrintWriter writer) {
|
1266
|
+
if (columnTitles != null) {
|
1267
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
1268
|
+
if (col != 0) {
|
1269
|
+
writer.print(',');
|
1270
|
+
}
|
1271
|
+
try {
|
1272
|
+
if (columnTitles[col] != null) { // col < columnTitles.length &&
|
1273
|
+
writeEntryCSV(writer, columnTitles[col]);
|
1274
|
+
}
|
1275
|
+
} catch (ArrayIndexOutOfBoundsException e) {
|
1276
|
+
PApplet.printArray(columnTitles);
|
1277
|
+
PApplet.printArray(columns);
|
1278
|
+
throw e;
|
1279
|
+
}
|
1280
|
+
}
|
1281
|
+
writer.println();
|
1282
|
+
}
|
1283
|
+
for (int row = 0; row < rowCount; row++) {
|
1284
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
1285
|
+
if (col != 0) {
|
1286
|
+
writer.print(',');
|
1287
|
+
}
|
1288
|
+
String entry = getString(row, col);
|
1289
|
+
// just write null entries as blanks, rather than spewing 'null'
|
1290
|
+
// all over the spreadsheet file.
|
1291
|
+
if (entry != null) {
|
1292
|
+
writeEntryCSV(writer, entry);
|
1293
|
+
}
|
1294
|
+
}
|
1295
|
+
// Prints the newline for the row, even if it's missing
|
1296
|
+
writer.println();
|
1297
|
+
}
|
1298
|
+
writer.flush();
|
1299
|
+
}
|
1300
|
+
|
1301
|
+
|
1302
|
+
protected void writeEntryCSV(PrintWriter writer, String entry) {
|
1303
|
+
if (entry != null) {
|
1304
|
+
if (entry.indexOf('\"') != -1) { // convert quotes to double quotes
|
1305
|
+
char[] c = entry.toCharArray();
|
1306
|
+
writer.print('\"');
|
1307
|
+
for (int i = 0; i < c.length; i++) {
|
1308
|
+
if (c[i] == '\"') {
|
1309
|
+
writer.print("\"\"");
|
1310
|
+
} else {
|
1311
|
+
writer.print(c[i]);
|
1312
|
+
}
|
1313
|
+
}
|
1314
|
+
writer.print('\"');
|
1315
|
+
|
1316
|
+
// add quotes if commas or CR/LF are in the entry
|
1317
|
+
} else if (entry.indexOf(',') != -1 ||
|
1318
|
+
entry.indexOf('\n') != -1 ||
|
1319
|
+
entry.indexOf('\r') != -1) {
|
1320
|
+
writer.print('\"');
|
1321
|
+
writer.print(entry);
|
1322
|
+
writer.print('\"');
|
1323
|
+
|
1324
|
+
|
1325
|
+
// add quotes if leading or trailing space
|
1326
|
+
} else if ((entry.length() > 0) &&
|
1327
|
+
(entry.charAt(0) == ' ' ||
|
1328
|
+
entry.charAt(entry.length() - 1) == ' ')) {
|
1329
|
+
writer.print('\"');
|
1330
|
+
writer.print(entry);
|
1331
|
+
writer.print('\"');
|
1332
|
+
|
1333
|
+
} else {
|
1334
|
+
writer.print(entry);
|
1335
|
+
}
|
1336
|
+
}
|
1337
|
+
}
|
1338
|
+
|
1339
|
+
|
1340
|
+
protected void writeHTML(PrintWriter writer) {
|
1341
|
+
writer.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2//EN\">");
|
1342
|
+
// writer.println("<!DOCTYPE html>");
|
1343
|
+
// writer.println("<meta charset=\"utf-8\">");
|
1344
|
+
|
1345
|
+
writer.println("<html>");
|
1346
|
+
writer.println("<head>");
|
1347
|
+
writer.println(" <meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" />");
|
1348
|
+
writer.println("</head>");
|
1349
|
+
|
1350
|
+
writer.println("<body>");
|
1351
|
+
writer.println(" <table>");
|
1352
|
+
|
1353
|
+
if (hasColumnTitles()) {
|
1354
|
+
writer.println(" <tr>");
|
1355
|
+
for (String entry : getColumnTitles()) {
|
1356
|
+
writer.print(" <th>");
|
1357
|
+
if (entry != null) {
|
1358
|
+
writeEntryHTML(writer, entry);
|
1359
|
+
}
|
1360
|
+
writer.println("</th>");
|
1361
|
+
}
|
1362
|
+
writer.println(" </tr>");
|
1363
|
+
}
|
1364
|
+
|
1365
|
+
for (int row = 0; row < getRowCount(); row++) {
|
1366
|
+
writer.println(" <tr>");
|
1367
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
1368
|
+
String entry = getString(row, col);
|
1369
|
+
writer.print(" <td>");
|
1370
|
+
if (entry != null) {
|
1371
|
+
// probably not a great idea to mess w/ the export
|
1372
|
+
// if (entry.startsWith("<") && entry.endsWith(">")) {
|
1373
|
+
// writer.print(entry);
|
1374
|
+
// } else {
|
1375
|
+
writeEntryHTML(writer, entry);
|
1376
|
+
// }
|
1377
|
+
}
|
1378
|
+
writer.println("</td>");
|
1379
|
+
}
|
1380
|
+
writer.println(" </tr>");
|
1381
|
+
}
|
1382
|
+
writer.println(" </table>");
|
1383
|
+
writer.println("</body>");
|
1384
|
+
|
1385
|
+
writer.println("</html>");
|
1386
|
+
writer.flush();
|
1387
|
+
}
|
1388
|
+
|
1389
|
+
|
1390
|
+
protected void writeEntryHTML(PrintWriter writer, String entry) {
|
1391
|
+
//char[] chars = entry.toCharArray();
|
1392
|
+
for (char c : entry.toCharArray()) { //chars) {
|
1393
|
+
if (c == '<') {
|
1394
|
+
writer.print("<");
|
1395
|
+
} else if (c == '>') {
|
1396
|
+
writer.print(">");
|
1397
|
+
} else if (c == '&') {
|
1398
|
+
writer.print("&");
|
1399
|
+
// } else if (c == '\'') { // only in XML
|
1400
|
+
// writer.print("'");
|
1401
|
+
} else if (c == '"') {
|
1402
|
+
writer.print(""");
|
1403
|
+
|
1404
|
+
} else if (c < 32 || c > 127) { // keep in ASCII or Tidy complains
|
1405
|
+
writer.print("&#");
|
1406
|
+
writer.print((int) c);
|
1407
|
+
writer.print(';');
|
1408
|
+
|
1409
|
+
} else {
|
1410
|
+
writer.print(c);
|
1411
|
+
}
|
1412
|
+
}
|
1413
|
+
}
|
1414
|
+
|
1415
|
+
|
1416
|
+
protected void saveODS(OutputStream os) throws IOException {
|
1417
|
+
ZipOutputStream zos = new ZipOutputStream(os);
|
1418
|
+
|
1419
|
+
final String xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
1420
|
+
|
1421
|
+
ZipEntry entry = new ZipEntry("META-INF/manifest.xml");
|
1422
|
+
String[] lines = new String[] {
|
1423
|
+
xmlHeader,
|
1424
|
+
"<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">",
|
1425
|
+
" <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.spreadsheet\" manifest:version=\"1.2\" manifest:full-path=\"/\"/>",
|
1426
|
+
" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>",
|
1427
|
+
" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"styles.xml\"/>",
|
1428
|
+
" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>",
|
1429
|
+
" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"settings.xml\"/>",
|
1430
|
+
"</manifest:manifest>"
|
1431
|
+
};
|
1432
|
+
zos.putNextEntry(entry);
|
1433
|
+
zos.write(PApplet.join(lines, "\n").getBytes());
|
1434
|
+
zos.closeEntry();
|
1435
|
+
|
1436
|
+
/*
|
1437
|
+
entry = new ZipEntry("meta.xml");
|
1438
|
+
lines = new String[] {
|
1439
|
+
xmlHeader,
|
1440
|
+
"<office:document-meta office:version=\"1.0\"" +
|
1441
|
+
" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" />"
|
1442
|
+
};
|
1443
|
+
zos.putNextEntry(entry);
|
1444
|
+
zos.write(PApplet.join(lines, "\n").getBytes());
|
1445
|
+
zos.closeEntry();
|
1446
|
+
|
1447
|
+
entry = new ZipEntry("meta.xml");
|
1448
|
+
lines = new String[] {
|
1449
|
+
xmlHeader,
|
1450
|
+
"<office:document-settings office:version=\"1.0\"" +
|
1451
|
+
" xmlns:config=\"urn:oasis:names:tc:opendocument:xmlns:config:1.0\"" +
|
1452
|
+
" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"" +
|
1453
|
+
" xmlns:ooo=\"http://openoffice.org/2004/office\"" +
|
1454
|
+
" xmlns:xlink=\"http://www.w3.org/1999/xlink\" />"
|
1455
|
+
};
|
1456
|
+
zos.putNextEntry(entry);
|
1457
|
+
zos.write(PApplet.join(lines, "\n").getBytes());
|
1458
|
+
zos.closeEntry();
|
1459
|
+
|
1460
|
+
entry = new ZipEntry("settings.xml");
|
1461
|
+
lines = new String[] {
|
1462
|
+
xmlHeader,
|
1463
|
+
"<office:document-settings office:version=\"1.0\"" +
|
1464
|
+
" xmlns:config=\"urn:oasis:names:tc:opendocument:xmlns:config:1.0\"" +
|
1465
|
+
" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"" +
|
1466
|
+
" xmlns:ooo=\"http://openoffice.org/2004/office\"" +
|
1467
|
+
" xmlns:xlink=\"http://www.w3.org/1999/xlink\" />"
|
1468
|
+
};
|
1469
|
+
zos.putNextEntry(entry);
|
1470
|
+
zos.write(PApplet.join(lines, "\n").getBytes());
|
1471
|
+
zos.closeEntry();
|
1472
|
+
|
1473
|
+
entry = new ZipEntry("styles.xml");
|
1474
|
+
lines = new String[] {
|
1475
|
+
xmlHeader,
|
1476
|
+
"<office:document-styles office:version=\"1.0\"" +
|
1477
|
+
" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" />"
|
1478
|
+
};
|
1479
|
+
zos.putNextEntry(entry);
|
1480
|
+
zos.write(PApplet.join(lines, "\n").getBytes());
|
1481
|
+
zos.closeEntry();
|
1482
|
+
*/
|
1483
|
+
|
1484
|
+
final String[] dummyFiles = new String[] {
|
1485
|
+
"meta.xml", "settings.xml", "styles.xml"
|
1486
|
+
};
|
1487
|
+
lines = new String[] {
|
1488
|
+
xmlHeader,
|
1489
|
+
"<office:document-meta office:version=\"1.0\"" +
|
1490
|
+
" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" />"
|
1491
|
+
};
|
1492
|
+
byte[] dummyBytes = PApplet.join(lines, "\n").getBytes();
|
1493
|
+
for (String filename : dummyFiles) {
|
1494
|
+
entry = new ZipEntry(filename);
|
1495
|
+
zos.putNextEntry(entry);
|
1496
|
+
zos.write(dummyBytes);
|
1497
|
+
zos.closeEntry();
|
1498
|
+
}
|
1499
|
+
|
1500
|
+
//
|
1501
|
+
|
1502
|
+
entry = new ZipEntry("mimetype");
|
1503
|
+
zos.putNextEntry(entry);
|
1504
|
+
zos.write("application/vnd.oasis.opendocument.spreadsheet".getBytes());
|
1505
|
+
zos.closeEntry();
|
1506
|
+
|
1507
|
+
//
|
1508
|
+
|
1509
|
+
entry = new ZipEntry("content.xml");
|
1510
|
+
zos.putNextEntry(entry);
|
1511
|
+
//lines = new String[] {
|
1512
|
+
writeUTF(zos, xmlHeader,
|
1513
|
+
"<office:document-content" +
|
1514
|
+
" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"" +
|
1515
|
+
" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"" +
|
1516
|
+
" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"" +
|
1517
|
+
" office:version=\"1.2\">",
|
1518
|
+
" <office:body>",
|
1519
|
+
" <office:spreadsheet>",
|
1520
|
+
" <table:table table:name=\"Sheet1\" table:print=\"false\">");
|
1521
|
+
//zos.write(PApplet.join(lines, "\n").getBytes());
|
1522
|
+
|
1523
|
+
byte[] rowStart = " <table:table-row>\n".getBytes();
|
1524
|
+
byte[] rowStop = " </table:table-row>\n".getBytes();
|
1525
|
+
|
1526
|
+
if (hasColumnTitles()) {
|
1527
|
+
zos.write(rowStart);
|
1528
|
+
for (int i = 0; i < getColumnCount(); i++) {
|
1529
|
+
saveStringODS(zos, columnTitles[i]);
|
1530
|
+
}
|
1531
|
+
zos.write(rowStop);
|
1532
|
+
}
|
1533
|
+
|
1534
|
+
for (TableRow row : rows()) {
|
1535
|
+
zos.write(rowStart);
|
1536
|
+
for (int i = 0; i < getColumnCount(); i++) {
|
1537
|
+
if (columnTypes[i] == STRING || columnTypes[i] == CATEGORY) {
|
1538
|
+
saveStringODS(zos, row.getString(i));
|
1539
|
+
} else {
|
1540
|
+
saveNumberODS(zos, row.getString(i));
|
1541
|
+
}
|
1542
|
+
}
|
1543
|
+
zos.write(rowStop);
|
1544
|
+
}
|
1545
|
+
|
1546
|
+
//lines = new String[] {
|
1547
|
+
writeUTF(zos, " </table:table>",
|
1548
|
+
" </office:spreadsheet>",
|
1549
|
+
" </office:body>",
|
1550
|
+
"</office:document-content>");
|
1551
|
+
//zos.write(PApplet.join(lines, "\n").getBytes());
|
1552
|
+
zos.closeEntry();
|
1553
|
+
|
1554
|
+
zos.flush();
|
1555
|
+
zos.close();
|
1556
|
+
}
|
1557
|
+
|
1558
|
+
|
1559
|
+
void saveStringODS(OutputStream output, String text) throws IOException {
|
1560
|
+
// At this point, I should have just used the XML library. But this does
|
1561
|
+
// save us from having to create the entire document in memory again before
|
1562
|
+
// writing to the file. So while it's dorky, the outcome is still useful.
|
1563
|
+
StringBuilder sanitized = new StringBuilder();
|
1564
|
+
if (text != null) {
|
1565
|
+
char[] array = text.toCharArray();
|
1566
|
+
for (char c : array) {
|
1567
|
+
if (c == '&') {
|
1568
|
+
sanitized.append("&");
|
1569
|
+
} else if (c == '\'') {
|
1570
|
+
sanitized.append("'");
|
1571
|
+
} else if (c == '"') {
|
1572
|
+
sanitized.append(""");
|
1573
|
+
} else if (c == '<') {
|
1574
|
+
sanitized.append("<");
|
1575
|
+
} else if (c == '>') {
|
1576
|
+
sanitized.append("&rt;");
|
1577
|
+
} else if (c < 32 || c > 127) {
|
1578
|
+
sanitized.append("&#" + ((int) c) + ";");
|
1579
|
+
} else {
|
1580
|
+
sanitized.append(c);
|
1581
|
+
}
|
1582
|
+
}
|
1583
|
+
}
|
1584
|
+
|
1585
|
+
writeUTF(output,
|
1586
|
+
" <table:table-cell office:value-type=\"string\">",
|
1587
|
+
" <text:p>" + sanitized + "</text:p>",
|
1588
|
+
" </table:table-cell>");
|
1589
|
+
}
|
1590
|
+
|
1591
|
+
|
1592
|
+
void saveNumberODS(OutputStream output, String text) throws IOException {
|
1593
|
+
writeUTF(output,
|
1594
|
+
" <table:table-cell office:value-type=\"float\" office:value=\"" + text + "\">",
|
1595
|
+
" <text:p>" + text + "</text:p>",
|
1596
|
+
" </table:table-cell>");
|
1597
|
+
}
|
1598
|
+
|
1599
|
+
|
1600
|
+
static Charset utf8;
|
1601
|
+
|
1602
|
+
static void writeUTF(OutputStream output, String... lines) throws IOException {
|
1603
|
+
if (utf8 == null) {
|
1604
|
+
utf8 = Charset.forName("UTF-8");
|
1605
|
+
}
|
1606
|
+
for (String str : lines) {
|
1607
|
+
output.write(str.getBytes(utf8));
|
1608
|
+
output.write('\n');
|
1609
|
+
}
|
1610
|
+
}
|
1611
|
+
|
1612
|
+
|
1613
|
+
protected void saveBinary(OutputStream os) throws IOException {
|
1614
|
+
DataOutputStream output = new DataOutputStream(new BufferedOutputStream(os));
|
1615
|
+
output.writeInt(0x9007AB1E); // version
|
1616
|
+
output.writeInt(getRowCount());
|
1617
|
+
output.writeInt(getColumnCount());
|
1618
|
+
if (columnTitles != null) {
|
1619
|
+
output.writeBoolean(true);
|
1620
|
+
for (String title : columnTitles) {
|
1621
|
+
output.writeUTF(title);
|
1622
|
+
}
|
1623
|
+
} else {
|
1624
|
+
output.writeBoolean(false);
|
1625
|
+
}
|
1626
|
+
for (int i = 0; i < getColumnCount(); i++) {
|
1627
|
+
//System.out.println(i + " is " + columnTypes[i]);
|
1628
|
+
output.writeInt(columnTypes[i]);
|
1629
|
+
}
|
1630
|
+
|
1631
|
+
for (int i = 0; i < getColumnCount(); i++) {
|
1632
|
+
if (columnTypes[i] == CATEGORY) {
|
1633
|
+
columnCategories[i].write(output);
|
1634
|
+
}
|
1635
|
+
}
|
1636
|
+
if (missingString == null) {
|
1637
|
+
output.writeBoolean(false);
|
1638
|
+
} else {
|
1639
|
+
output.writeBoolean(true);
|
1640
|
+
output.writeUTF(missingString);
|
1641
|
+
}
|
1642
|
+
output.writeInt(missingInt);
|
1643
|
+
output.writeLong(missingLong);
|
1644
|
+
output.writeFloat(missingFloat);
|
1645
|
+
output.writeDouble(missingDouble);
|
1646
|
+
output.writeInt(missingCategory);
|
1647
|
+
|
1648
|
+
for (TableRow row : rows()) {
|
1649
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
1650
|
+
switch (columnTypes[col]) {
|
1651
|
+
case STRING:
|
1652
|
+
String str = row.getString(col);
|
1653
|
+
if (str == null) {
|
1654
|
+
output.writeBoolean(false);
|
1655
|
+
} else {
|
1656
|
+
output.writeBoolean(true);
|
1657
|
+
output.writeUTF(str);
|
1658
|
+
}
|
1659
|
+
break;
|
1660
|
+
case INT:
|
1661
|
+
output.writeInt(row.getInt(col));
|
1662
|
+
break;
|
1663
|
+
case LONG:
|
1664
|
+
output.writeLong(row.getLong(col));
|
1665
|
+
break;
|
1666
|
+
case FLOAT:
|
1667
|
+
output.writeFloat(row.getFloat(col));
|
1668
|
+
break;
|
1669
|
+
case DOUBLE:
|
1670
|
+
output.writeDouble(row.getDouble(col));
|
1671
|
+
break;
|
1672
|
+
case CATEGORY:
|
1673
|
+
String peace = row.getString(col);
|
1674
|
+
if (peace.equals(missingString)) {
|
1675
|
+
output.writeInt(missingCategory);
|
1676
|
+
} else {
|
1677
|
+
output.writeInt(columnCategories[col].index(peace));
|
1678
|
+
}
|
1679
|
+
break;
|
1680
|
+
}
|
1681
|
+
}
|
1682
|
+
}
|
1683
|
+
|
1684
|
+
output.flush();
|
1685
|
+
output.close();
|
1686
|
+
}
|
1687
|
+
|
1688
|
+
|
1689
|
+
protected void loadBinary(InputStream is) throws IOException {
|
1690
|
+
DataInputStream input = new DataInputStream(new BufferedInputStream(is));
|
1691
|
+
|
1692
|
+
int magic = input.readInt();
|
1693
|
+
if (magic != 0x9007AB1E) {
|
1694
|
+
throw new IOException("Not a compatible binary table (magic was " + PApplet.hex(magic) + ")");
|
1695
|
+
}
|
1696
|
+
int rowCount = input.readInt();
|
1697
|
+
setRowCount(rowCount);
|
1698
|
+
int columnCount = input.readInt();
|
1699
|
+
setColumnCount(columnCount);
|
1700
|
+
|
1701
|
+
boolean hasTitles = input.readBoolean();
|
1702
|
+
if (hasTitles) {
|
1703
|
+
columnTitles = new String[getColumnCount()];
|
1704
|
+
for (int i = 0; i < columnCount; i++) {
|
1705
|
+
//columnTitles[i] = input.readUTF();
|
1706
|
+
setColumnTitle(i, input.readUTF());
|
1707
|
+
}
|
1708
|
+
}
|
1709
|
+
for (int column = 0; column < columnCount; column++) {
|
1710
|
+
int newType = input.readInt();
|
1711
|
+
columnTypes[column] = newType;
|
1712
|
+
switch (newType) {
|
1713
|
+
case INT:
|
1714
|
+
columns[column] = new int[rowCount];
|
1715
|
+
break;
|
1716
|
+
case LONG:
|
1717
|
+
columns[column] = new long[rowCount];
|
1718
|
+
break;
|
1719
|
+
case FLOAT:
|
1720
|
+
columns[column] = new float[rowCount];
|
1721
|
+
break;
|
1722
|
+
case DOUBLE:
|
1723
|
+
columns[column] = new double[rowCount];
|
1724
|
+
break;
|
1725
|
+
case STRING:
|
1726
|
+
columns[column] = new String[rowCount];
|
1727
|
+
break;
|
1728
|
+
case CATEGORY:
|
1729
|
+
columns[column] = new int[rowCount];
|
1730
|
+
break;
|
1731
|
+
default:
|
1732
|
+
throw new IllegalArgumentException(newType + " is not a valid column type.");
|
1733
|
+
}
|
1734
|
+
}
|
1735
|
+
|
1736
|
+
for (int i = 0; i < columnCount; i++) {
|
1737
|
+
if (columnTypes[i] == CATEGORY) {
|
1738
|
+
columnCategories[i] = new HashMapBlows(input);
|
1739
|
+
}
|
1740
|
+
}
|
1741
|
+
|
1742
|
+
if (input.readBoolean()) {
|
1743
|
+
missingString = input.readUTF();
|
1744
|
+
} else {
|
1745
|
+
missingString = null;
|
1746
|
+
}
|
1747
|
+
missingInt = input.readInt();
|
1748
|
+
missingLong = input.readLong();
|
1749
|
+
missingFloat = input.readFloat();
|
1750
|
+
missingDouble = input.readDouble();
|
1751
|
+
missingCategory = input.readInt();
|
1752
|
+
|
1753
|
+
for (int row = 0; row < rowCount; row++) {
|
1754
|
+
for (int col = 0; col < columnCount; col++) {
|
1755
|
+
switch (columnTypes[col]) {
|
1756
|
+
case STRING:
|
1757
|
+
String str = null;
|
1758
|
+
if (input.readBoolean()) {
|
1759
|
+
str = input.readUTF();
|
1760
|
+
}
|
1761
|
+
setString(row, col, str);
|
1762
|
+
break;
|
1763
|
+
case INT:
|
1764
|
+
setInt(row, col, input.readInt());
|
1765
|
+
break;
|
1766
|
+
case LONG:
|
1767
|
+
setLong(row, col, input.readLong());
|
1768
|
+
break;
|
1769
|
+
case FLOAT:
|
1770
|
+
setFloat(row, col, input.readFloat());
|
1771
|
+
break;
|
1772
|
+
case DOUBLE:
|
1773
|
+
setDouble(row, col, input.readDouble());
|
1774
|
+
break;
|
1775
|
+
case CATEGORY:
|
1776
|
+
int index = input.readInt();
|
1777
|
+
//String name = columnCategories[col].key(index);
|
1778
|
+
setInt(row, col, index);
|
1779
|
+
break;
|
1780
|
+
}
|
1781
|
+
}
|
1782
|
+
}
|
1783
|
+
|
1784
|
+
input.close();
|
1785
|
+
}
|
1786
|
+
|
1787
|
+
|
1788
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
1789
|
+
|
1790
|
+
|
1791
|
+
/**
|
1792
|
+
* @webref table:method
|
1793
|
+
* @brief Adds a new column to a table
|
1794
|
+
* @see Table#removeColumn(String)
|
1795
|
+
*/
|
1796
|
+
public void addColumn() {
|
1797
|
+
addColumn(null, STRING);
|
1798
|
+
}
|
1799
|
+
|
1800
|
+
|
1801
|
+
/**
|
1802
|
+
* @param title the title to be used for the new column
|
1803
|
+
*/
|
1804
|
+
public void addColumn(String title) {
|
1805
|
+
addColumn(title, STRING);
|
1806
|
+
}
|
1807
|
+
|
1808
|
+
|
1809
|
+
/**
|
1810
|
+
* @param type the type to be used for the new column: INT, LONG, FLOAT, DOUBLE, or STRING
|
1811
|
+
*/
|
1812
|
+
public void addColumn(String title, int type) {
|
1813
|
+
insertColumn(columns.length, title, type);
|
1814
|
+
}
|
1815
|
+
|
1816
|
+
|
1817
|
+
public void insertColumn(int index) {
|
1818
|
+
insertColumn(index, null, STRING);
|
1819
|
+
}
|
1820
|
+
|
1821
|
+
|
1822
|
+
public void insertColumn(int index, String title) {
|
1823
|
+
insertColumn(index, title, STRING);
|
1824
|
+
}
|
1825
|
+
|
1826
|
+
|
1827
|
+
public void insertColumn(int index, String title, int type) {
|
1828
|
+
if (title != null && columnTitles == null) {
|
1829
|
+
columnTitles = new String[columns.length];
|
1830
|
+
}
|
1831
|
+
if (columnTitles != null) {
|
1832
|
+
columnTitles = PApplet.splice(columnTitles, title, index);
|
1833
|
+
columnIndices = null;
|
1834
|
+
}
|
1835
|
+
columnTypes = PApplet.splice(columnTypes, type, index);
|
1836
|
+
|
1837
|
+
// columnCategories = (HashMapBlows[])
|
1838
|
+
// PApplet.splice(columnCategories, new HashMapBlows(), index);
|
1839
|
+
HashMapBlows[] catTemp = new HashMapBlows[columns.length + 1];
|
1840
|
+
// Faster than arrayCopy for a dozen or so entries
|
1841
|
+
for (int i = 0; i < index; i++) {
|
1842
|
+
catTemp[i] = columnCategories[i];
|
1843
|
+
}
|
1844
|
+
catTemp[index] = new HashMapBlows();
|
1845
|
+
for (int i = index; i < columns.length; i++) {
|
1846
|
+
catTemp[i+1] = columnCategories[i];
|
1847
|
+
}
|
1848
|
+
columnCategories = catTemp;
|
1849
|
+
|
1850
|
+
Object[] temp = new Object[columns.length + 1];
|
1851
|
+
System.arraycopy(columns, 0, temp, 0, index);
|
1852
|
+
System.arraycopy(columns, index, temp, index+1, columns.length - index);
|
1853
|
+
columns = temp;
|
1854
|
+
|
1855
|
+
switch (type) {
|
1856
|
+
case INT: columns[index] = new int[rowCount]; break;
|
1857
|
+
case LONG: columns[index] = new long[rowCount]; break;
|
1858
|
+
case FLOAT: columns[index] = new float[rowCount]; break;
|
1859
|
+
case DOUBLE: columns[index] = new double[rowCount]; break;
|
1860
|
+
case STRING: columns[index] = new String[rowCount]; break;
|
1861
|
+
case CATEGORY: columns[index] = new int[rowCount]; break;
|
1862
|
+
}
|
1863
|
+
}
|
1864
|
+
|
1865
|
+
/**
|
1866
|
+
* @webref table:method
|
1867
|
+
* @brief Removes a column from a table
|
1868
|
+
* @param columnName the title of the column to be removed
|
1869
|
+
* @see Table#addColumn()
|
1870
|
+
*/
|
1871
|
+
public void removeColumn(String columnName) {
|
1872
|
+
removeColumn(getColumnIndex(columnName));
|
1873
|
+
}
|
1874
|
+
|
1875
|
+
/**
|
1876
|
+
* @param column the index number of the column to be removed
|
1877
|
+
*/
|
1878
|
+
public void removeColumn(int column) {
|
1879
|
+
int newCount = columns.length - 1;
|
1880
|
+
|
1881
|
+
Object[] columnsTemp = new Object[newCount];
|
1882
|
+
HashMapBlows[] catTemp = new HashMapBlows[newCount];
|
1883
|
+
|
1884
|
+
for (int i = 0; i < column; i++) {
|
1885
|
+
columnsTemp[i] = columns[i];
|
1886
|
+
catTemp[i] = columnCategories[i];
|
1887
|
+
}
|
1888
|
+
for (int i = column; i < newCount; i++) {
|
1889
|
+
columnsTemp[i] = columns[i+1];
|
1890
|
+
catTemp[i] = columnCategories[i+1];
|
1891
|
+
}
|
1892
|
+
|
1893
|
+
columns = columnsTemp;
|
1894
|
+
columnCategories = catTemp;
|
1895
|
+
|
1896
|
+
if (columnTitles != null) {
|
1897
|
+
String[] titlesTemp = new String[newCount];
|
1898
|
+
for (int i = 0; i < column; i++) {
|
1899
|
+
titlesTemp[i] = columnTitles[i];
|
1900
|
+
}
|
1901
|
+
for (int i = column; i < newCount; i++) {
|
1902
|
+
titlesTemp[i] = columnTitles[i+1];
|
1903
|
+
}
|
1904
|
+
columnTitles = titlesTemp;
|
1905
|
+
columnIndices = null;
|
1906
|
+
}
|
1907
|
+
}
|
1908
|
+
|
1909
|
+
|
1910
|
+
/**
|
1911
|
+
* @webref table:method
|
1912
|
+
* @brief Gets the number of columns in a table
|
1913
|
+
* @see Table#getRowCount()
|
1914
|
+
*/
|
1915
|
+
public int getColumnCount() {
|
1916
|
+
return columns.length;
|
1917
|
+
}
|
1918
|
+
|
1919
|
+
|
1920
|
+
/**
|
1921
|
+
* Change the number of columns in this table. Resizes all rows to ensure
|
1922
|
+
* the same number of columns in each row. Entries in the additional (empty)
|
1923
|
+
* columns will be set to null.
|
1924
|
+
* @param newCount
|
1925
|
+
*/
|
1926
|
+
public void setColumnCount(int newCount) {
|
1927
|
+
int oldCount = columns.length;
|
1928
|
+
if (oldCount != newCount) {
|
1929
|
+
columns = (Object[]) PApplet.expand(columns, newCount);
|
1930
|
+
// create new columns, default to String as the data type
|
1931
|
+
for (int c = oldCount; c < newCount; c++) {
|
1932
|
+
columns[c] = new String[rowCount];
|
1933
|
+
}
|
1934
|
+
|
1935
|
+
if (columnTitles != null) {
|
1936
|
+
columnTitles = PApplet.expand(columnTitles, newCount);
|
1937
|
+
}
|
1938
|
+
columnTypes = PApplet.expand(columnTypes, newCount);
|
1939
|
+
columnCategories = (HashMapBlows[])
|
1940
|
+
PApplet.expand(columnCategories, newCount);
|
1941
|
+
}
|
1942
|
+
}
|
1943
|
+
|
1944
|
+
|
1945
|
+
public void setColumnType(String columnName, String columnType) {
|
1946
|
+
setColumnType(checkColumnIndex(columnName), columnType);
|
1947
|
+
}
|
1948
|
+
|
1949
|
+
|
1950
|
+
static int parseColumnType(String columnType) {
|
1951
|
+
columnType = columnType.toLowerCase();
|
1952
|
+
int type = -1;
|
1953
|
+
if (columnType.equals("string")) {
|
1954
|
+
type = STRING;
|
1955
|
+
} else if (columnType.equals("int")) {
|
1956
|
+
type = INT;
|
1957
|
+
} else if (columnType.equals("long")) {
|
1958
|
+
type = LONG;
|
1959
|
+
} else if (columnType.equals("float")) {
|
1960
|
+
type = FLOAT;
|
1961
|
+
} else if (columnType.equals("double")) {
|
1962
|
+
type = DOUBLE;
|
1963
|
+
} else if (columnType.equals("category")) {
|
1964
|
+
type = CATEGORY;
|
1965
|
+
} else {
|
1966
|
+
throw new IllegalArgumentException("'" + columnType + "' is not a valid column type.");
|
1967
|
+
}
|
1968
|
+
return type;
|
1969
|
+
}
|
1970
|
+
|
1971
|
+
|
1972
|
+
/**
|
1973
|
+
* Set the data type for a column so that using it is more efficient.
|
1974
|
+
* @param column the column to change
|
1975
|
+
* @param columnType One of int, long, float, double, string, or category.
|
1976
|
+
*/
|
1977
|
+
public void setColumnType(int column, String columnType) {
|
1978
|
+
setColumnType(column, parseColumnType(columnType));
|
1979
|
+
}
|
1980
|
+
|
1981
|
+
|
1982
|
+
public void setColumnType(String columnName, int newType) {
|
1983
|
+
setColumnType(checkColumnIndex(columnName), newType);
|
1984
|
+
}
|
1985
|
+
|
1986
|
+
|
1987
|
+
/**
|
1988
|
+
* Sets the column type. If data already exists, then it'll be converted to
|
1989
|
+
* the new type.
|
1990
|
+
* @param column the column whose type should be changed
|
1991
|
+
* @param newType something fresh, maybe try an int or a float for size?
|
1992
|
+
*/
|
1993
|
+
public void setColumnType(int column, int newType) {
|
1994
|
+
switch (newType) {
|
1995
|
+
case INT: {
|
1996
|
+
int[] intData = new int[rowCount];
|
1997
|
+
for (int row = 0; row < rowCount; row++) {
|
1998
|
+
String s = getString(row, column);
|
1999
|
+
intData[row] = (s == null) ? missingInt : PApplet.parseInt(s, missingInt);
|
2000
|
+
}
|
2001
|
+
columns[column] = intData;
|
2002
|
+
break;
|
2003
|
+
}
|
2004
|
+
case LONG: {
|
2005
|
+
long[] longData = new long[rowCount];
|
2006
|
+
for (int row = 0; row < rowCount; row++) {
|
2007
|
+
String s = getString(row, column);
|
2008
|
+
try {
|
2009
|
+
longData[row] = (s == null) ? missingLong : Long.parseLong(s);
|
2010
|
+
} catch (NumberFormatException nfe) {
|
2011
|
+
longData[row] = missingLong;
|
2012
|
+
}
|
2013
|
+
}
|
2014
|
+
columns[column] = longData;
|
2015
|
+
break;
|
2016
|
+
}
|
2017
|
+
case FLOAT: {
|
2018
|
+
float[] floatData = new float[rowCount];
|
2019
|
+
for (int row = 0; row < rowCount; row++) {
|
2020
|
+
String s = getString(row, column);
|
2021
|
+
floatData[row] = (s == null) ? missingFloat : PApplet.parseFloat(s, missingFloat);
|
2022
|
+
}
|
2023
|
+
columns[column] = floatData;
|
2024
|
+
break;
|
2025
|
+
}
|
2026
|
+
case DOUBLE: {
|
2027
|
+
double[] doubleData = new double[rowCount];
|
2028
|
+
for (int row = 0; row < rowCount; row++) {
|
2029
|
+
String s = getString(row, column);
|
2030
|
+
try {
|
2031
|
+
doubleData[row] = (s == null) ? missingDouble : Double.parseDouble(s);
|
2032
|
+
} catch (NumberFormatException nfe) {
|
2033
|
+
doubleData[row] = missingDouble;
|
2034
|
+
}
|
2035
|
+
}
|
2036
|
+
columns[column] = doubleData;
|
2037
|
+
break;
|
2038
|
+
}
|
2039
|
+
case STRING: {
|
2040
|
+
if (columnTypes[column] != STRING) {
|
2041
|
+
String[] stringData = new String[rowCount];
|
2042
|
+
for (int row = 0; row < rowCount; row++) {
|
2043
|
+
stringData[row] = getString(row, column);
|
2044
|
+
}
|
2045
|
+
columns[column] = stringData;
|
2046
|
+
}
|
2047
|
+
break;
|
2048
|
+
}
|
2049
|
+
case CATEGORY: {
|
2050
|
+
int[] indexData = new int[rowCount];
|
2051
|
+
HashMapBlows categories = new HashMapBlows();
|
2052
|
+
for (int row = 0; row < rowCount; row++) {
|
2053
|
+
String s = getString(row, column);
|
2054
|
+
indexData[row] = categories.index(s);
|
2055
|
+
}
|
2056
|
+
columnCategories[column] = categories;
|
2057
|
+
columns[column] = indexData;
|
2058
|
+
break;
|
2059
|
+
}
|
2060
|
+
default: {
|
2061
|
+
throw new IllegalArgumentException("That's not a valid column type.");
|
2062
|
+
}
|
2063
|
+
}
|
2064
|
+
// System.out.println("new type is " + newType);
|
2065
|
+
columnTypes[column] = newType;
|
2066
|
+
}
|
2067
|
+
|
2068
|
+
|
2069
|
+
/**
|
2070
|
+
* Set the entire table to a specific data type.
|
2071
|
+
*/
|
2072
|
+
public void setTableType(String type) {
|
2073
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
2074
|
+
setColumnType(col, type);
|
2075
|
+
}
|
2076
|
+
}
|
2077
|
+
|
2078
|
+
|
2079
|
+
public void setColumnTypes(int[] types) {
|
2080
|
+
ensureColumn(types.length - 1);
|
2081
|
+
for (int col = 0; col < types.length; col++) {
|
2082
|
+
setColumnType(col, types[col]);
|
2083
|
+
}
|
2084
|
+
}
|
2085
|
+
|
2086
|
+
|
2087
|
+
/**
|
2088
|
+
* Set the titles (and if a second column is present) the data types for
|
2089
|
+
* this table based on a file loaded separately. This will look for the
|
2090
|
+
* title in column 0, and the type in column 1. Better yet, specify a
|
2091
|
+
* column named "title" and another named "type" in the dictionary table
|
2092
|
+
* to future-proof the code.
|
2093
|
+
* @param dictionary
|
2094
|
+
*/
|
2095
|
+
public void setColumnTypes(final Table dictionary) {
|
2096
|
+
ensureColumn(dictionary.getRowCount() - 1);
|
2097
|
+
int titleCol = 0;
|
2098
|
+
int typeCol = 1;
|
2099
|
+
if (dictionary.hasColumnTitles()) {
|
2100
|
+
titleCol = dictionary.getColumnIndex("title", true);
|
2101
|
+
typeCol = dictionary.getColumnIndex("type", true);
|
2102
|
+
}
|
2103
|
+
setColumnTitles(dictionary.getStringColumn(titleCol));
|
2104
|
+
final String[] typeNames = dictionary.getStringColumn(typeCol);
|
2105
|
+
|
2106
|
+
if (dictionary.getColumnCount() > 1) {
|
2107
|
+
if (getRowCount() > 1000) {
|
2108
|
+
int proc = Runtime.getRuntime().availableProcessors();
|
2109
|
+
ExecutorService pool = Executors.newFixedThreadPool(proc/2);
|
2110
|
+
for (int i = 0; i < dictionary.getRowCount(); i++) {
|
2111
|
+
final int col = i;
|
2112
|
+
pool.execute(new Runnable() {
|
2113
|
+
public void run() {
|
2114
|
+
setColumnType(col, typeNames[col]);
|
2115
|
+
}
|
2116
|
+
});
|
2117
|
+
}
|
2118
|
+
pool.shutdown();
|
2119
|
+
while (!pool.isTerminated()) {
|
2120
|
+
Thread.yield();
|
2121
|
+
}
|
2122
|
+
|
2123
|
+
} else {
|
2124
|
+
for (int col = 0; col < dictionary.getRowCount(); col++) {
|
2125
|
+
// setColumnType(i, dictionary.getString(i, typeCol));
|
2126
|
+
setColumnType(col, typeNames[col]);
|
2127
|
+
}
|
2128
|
+
}
|
2129
|
+
}
|
2130
|
+
}
|
2131
|
+
|
2132
|
+
|
2133
|
+
public int getColumnType(String columnName) {
|
2134
|
+
return getColumnType(getColumnIndex(columnName));
|
2135
|
+
}
|
2136
|
+
|
2137
|
+
|
2138
|
+
/** Returns one of Table.STRING, Table.INT, etc... */
|
2139
|
+
public int getColumnType(int column) {
|
2140
|
+
return columnTypes[column];
|
2141
|
+
}
|
2142
|
+
|
2143
|
+
|
2144
|
+
public int[] getColumnTypes() {
|
2145
|
+
return columnTypes;
|
2146
|
+
}
|
2147
|
+
|
2148
|
+
|
2149
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
2150
|
+
|
2151
|
+
|
2152
|
+
/**
|
2153
|
+
* Remove the first row from the data set, and use it as the column titles.
|
2154
|
+
* Use loadTable("table.csv", "header") instead.
|
2155
|
+
*/
|
2156
|
+
@Deprecated
|
2157
|
+
public String[] removeTitleRow() {
|
2158
|
+
String[] titles = getStringRow(0);
|
2159
|
+
removeRow(0);
|
2160
|
+
setColumnTitles(titles);
|
2161
|
+
return titles;
|
2162
|
+
}
|
2163
|
+
|
2164
|
+
|
2165
|
+
public void setColumnTitles(String[] titles) {
|
2166
|
+
if (titles != null) {
|
2167
|
+
ensureColumn(titles.length - 1);
|
2168
|
+
}
|
2169
|
+
columnTitles = titles;
|
2170
|
+
columnIndices = null; // remove the cache
|
2171
|
+
}
|
2172
|
+
|
2173
|
+
|
2174
|
+
public void setColumnTitle(int column, String title) {
|
2175
|
+
ensureColumn(column);
|
2176
|
+
if (columnTitles == null) {
|
2177
|
+
columnTitles = new String[getColumnCount()];
|
2178
|
+
}
|
2179
|
+
columnTitles[column] = title;
|
2180
|
+
columnIndices = null; // reset these fellas
|
2181
|
+
}
|
2182
|
+
|
2183
|
+
|
2184
|
+
public boolean hasColumnTitles() {
|
2185
|
+
return columnTitles != null;
|
2186
|
+
}
|
2187
|
+
|
2188
|
+
|
2189
|
+
public String[] getColumnTitles() {
|
2190
|
+
return columnTitles;
|
2191
|
+
}
|
2192
|
+
|
2193
|
+
|
2194
|
+
public String getColumnTitle(int col) {
|
2195
|
+
return (columnTitles == null) ? null : columnTitles[col];
|
2196
|
+
}
|
2197
|
+
|
2198
|
+
|
2199
|
+
public int getColumnIndex(String columnName) {
|
2200
|
+
return getColumnIndex(columnName, true);
|
2201
|
+
}
|
2202
|
+
|
2203
|
+
|
2204
|
+
/**
|
2205
|
+
* Get the index of a column.
|
2206
|
+
* @param name Name of the column.
|
2207
|
+
* @param report Whether to throw an exception if the column wasn't found.
|
2208
|
+
* @return index of the found column, or -1 if not found.
|
2209
|
+
*/
|
2210
|
+
protected int getColumnIndex(String name, boolean report) {
|
2211
|
+
if (columnTitles == null) {
|
2212
|
+
if (report) {
|
2213
|
+
throw new IllegalArgumentException("This table has no header, so no column titles are set.");
|
2214
|
+
}
|
2215
|
+
return -1;
|
2216
|
+
}
|
2217
|
+
// only create this on first get(). subsequent calls to set the title will
|
2218
|
+
// also update this array, but only if it exists.
|
2219
|
+
if (columnIndices == null) {
|
2220
|
+
columnIndices = new HashMap<>();
|
2221
|
+
for (int col = 0; col < columns.length; col++) {
|
2222
|
+
columnIndices.put(columnTitles[col], col);
|
2223
|
+
}
|
2224
|
+
}
|
2225
|
+
Integer index = columnIndices.get(name);
|
2226
|
+
if (index == null) {
|
2227
|
+
if (report) {
|
2228
|
+
// Throws an exception here because the name is known and therefore most useful.
|
2229
|
+
// (Rather than waiting for it to fail inside, say, getInt())
|
2230
|
+
throw new IllegalArgumentException("This table has no column named '" + name + "'");
|
2231
|
+
}
|
2232
|
+
return -1;
|
2233
|
+
}
|
2234
|
+
return index.intValue();
|
2235
|
+
}
|
2236
|
+
|
2237
|
+
|
2238
|
+
/**
|
2239
|
+
* Same as getColumnIndex(), but creates the column if it doesn't exist.
|
2240
|
+
* Named this way to not conflict with checkColumn(), an internal function
|
2241
|
+
* used to ensure that a columns exists, and also to denote that it returns
|
2242
|
+
* an int for the column index.
|
2243
|
+
* @param title column title
|
2244
|
+
* @return index of a new or previously existing column
|
2245
|
+
*/
|
2246
|
+
public int checkColumnIndex(String title) {
|
2247
|
+
int index = getColumnIndex(title, false);
|
2248
|
+
if (index != -1) {
|
2249
|
+
return index;
|
2250
|
+
}
|
2251
|
+
addColumn(title);
|
2252
|
+
return getColumnCount() - 1;
|
2253
|
+
}
|
2254
|
+
|
2255
|
+
|
2256
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
2257
|
+
|
2258
|
+
/**
|
2259
|
+
* @webref table:method
|
2260
|
+
* @brief Gets the number of rows in a table
|
2261
|
+
* @see Table#getColumnCount()
|
2262
|
+
*/
|
2263
|
+
public int getRowCount() {
|
2264
|
+
return rowCount;
|
2265
|
+
}
|
2266
|
+
|
2267
|
+
|
2268
|
+
public int lastRowIndex() {
|
2269
|
+
return getRowCount() - 1;
|
2270
|
+
}
|
2271
|
+
|
2272
|
+
|
2273
|
+
/**
|
2274
|
+
* @webref table:method
|
2275
|
+
* @brief Removes all rows from a table
|
2276
|
+
* @see Table#addRow()
|
2277
|
+
* @see Table#removeRow(int)
|
2278
|
+
*/
|
2279
|
+
public void clearRows() {
|
2280
|
+
setRowCount(0);
|
2281
|
+
}
|
2282
|
+
|
2283
|
+
|
2284
|
+
public void setRowCount(int newCount) {
|
2285
|
+
if (newCount != rowCount) {
|
2286
|
+
if (newCount > 1000000) {
|
2287
|
+
System.out.print("Note: setting maximum row count to " + PApplet.nfc(newCount));
|
2288
|
+
}
|
2289
|
+
long t = System.currentTimeMillis();
|
2290
|
+
for (int col = 0; col < columns.length; col++) {
|
2291
|
+
switch (columnTypes[col]) {
|
2292
|
+
case INT: columns[col] = PApplet.expand((int[]) columns[col], newCount); break;
|
2293
|
+
case LONG: columns[col] = PApplet.expand((long[]) columns[col], newCount); break;
|
2294
|
+
case FLOAT: columns[col] = PApplet.expand((float[]) columns[col], newCount); break;
|
2295
|
+
case DOUBLE: columns[col] = PApplet.expand((double[]) columns[col], newCount); break;
|
2296
|
+
case STRING: columns[col] = PApplet.expand((String[]) columns[col], newCount); break;
|
2297
|
+
case CATEGORY: columns[col] = PApplet.expand((int[]) columns[col], newCount); break;
|
2298
|
+
}
|
2299
|
+
if (newCount > 1000000) {
|
2300
|
+
try {
|
2301
|
+
Thread.sleep(10); // gc time!
|
2302
|
+
} catch (InterruptedException e) {
|
2303
|
+
e.printStackTrace();
|
2304
|
+
}
|
2305
|
+
}
|
2306
|
+
}
|
2307
|
+
if (newCount > 1000000) {
|
2308
|
+
int ms = (int) (System.currentTimeMillis() - t);
|
2309
|
+
System.out.println(" (resize took " + PApplet.nfc(ms) + " ms)");
|
2310
|
+
}
|
2311
|
+
}
|
2312
|
+
rowCount = newCount;
|
2313
|
+
}
|
2314
|
+
|
2315
|
+
|
2316
|
+
/**
|
2317
|
+
* @webref table:method
|
2318
|
+
* @brief Adds a row to a table
|
2319
|
+
* @see Table#removeRow(int)
|
2320
|
+
* @see Table#clearRows()
|
2321
|
+
*/
|
2322
|
+
public TableRow addRow() {
|
2323
|
+
//if (rowIncrement == 0) {
|
2324
|
+
setRowCount(rowCount + 1);
|
2325
|
+
return new RowPointer(this, rowCount - 1);
|
2326
|
+
}
|
2327
|
+
|
2328
|
+
|
2329
|
+
/**
|
2330
|
+
* @param source a reference to the original row to be duplicated
|
2331
|
+
*/
|
2332
|
+
public TableRow addRow(TableRow source) {
|
2333
|
+
return setRow(rowCount, source);
|
2334
|
+
}
|
2335
|
+
|
2336
|
+
|
2337
|
+
public TableRow setRow(int row, TableRow source) {
|
2338
|
+
// Make sure there are enough columns to add this data
|
2339
|
+
ensureBounds(row, source.getColumnCount() - 1);
|
2340
|
+
|
2341
|
+
for (int col = 0; col < Math.min(source.getColumnCount(), columns.length); col++) {
|
2342
|
+
switch (columnTypes[col]) {
|
2343
|
+
case INT:
|
2344
|
+
setInt(row, col, source.getInt(col));
|
2345
|
+
break;
|
2346
|
+
case LONG:
|
2347
|
+
setLong(row, col, source.getLong(col));
|
2348
|
+
break;
|
2349
|
+
case FLOAT:
|
2350
|
+
setFloat(row, col, source.getFloat(col));
|
2351
|
+
break;
|
2352
|
+
case DOUBLE:
|
2353
|
+
setDouble(row, col, source.getDouble(col));
|
2354
|
+
break;
|
2355
|
+
case STRING:
|
2356
|
+
setString(row, col, source.getString(col));
|
2357
|
+
break;
|
2358
|
+
case CATEGORY:
|
2359
|
+
int index = source.getInt(col);
|
2360
|
+
setInt(row, col, index);
|
2361
|
+
if (!columnCategories[col].hasCategory(index)) {
|
2362
|
+
columnCategories[col].setCategory(index, source.getString(col));
|
2363
|
+
}
|
2364
|
+
break;
|
2365
|
+
|
2366
|
+
default:
|
2367
|
+
throw new RuntimeException("no types");
|
2368
|
+
}
|
2369
|
+
}
|
2370
|
+
return new RowPointer(this, row);
|
2371
|
+
}
|
2372
|
+
|
2373
|
+
|
2374
|
+
/**
|
2375
|
+
* @nowebref
|
2376
|
+
*/
|
2377
|
+
public TableRow addRow(Object[] columnData) {
|
2378
|
+
setRow(getRowCount(), columnData);
|
2379
|
+
return new RowPointer(this, rowCount - 1);
|
2380
|
+
}
|
2381
|
+
|
2382
|
+
|
2383
|
+
public void addRows(Table source) {
|
2384
|
+
int index = getRowCount();
|
2385
|
+
setRowCount(index + source.getRowCount());
|
2386
|
+
for (TableRow row : source.rows()) {
|
2387
|
+
setRow(index++, row);
|
2388
|
+
}
|
2389
|
+
}
|
2390
|
+
|
2391
|
+
|
2392
|
+
public void insertRow(int insert, Object[] columnData) {
|
2393
|
+
for (int col = 0; col < columns.length; col++) {
|
2394
|
+
switch (columnTypes[col]) {
|
2395
|
+
case CATEGORY:
|
2396
|
+
case INT: {
|
2397
|
+
int[] intTemp = new int[rowCount+1];
|
2398
|
+
System.arraycopy(columns[col], 0, intTemp, 0, insert);
|
2399
|
+
System.arraycopy(columns[col], insert, intTemp, insert+1, rowCount - insert);
|
2400
|
+
columns[col] = intTemp;
|
2401
|
+
break;
|
2402
|
+
}
|
2403
|
+
case LONG: {
|
2404
|
+
long[] longTemp = new long[rowCount+1];
|
2405
|
+
System.arraycopy(columns[col], 0, longTemp, 0, insert);
|
2406
|
+
System.arraycopy(columns[col], insert, longTemp, insert+1, rowCount - insert);
|
2407
|
+
columns[col] = longTemp;
|
2408
|
+
break;
|
2409
|
+
}
|
2410
|
+
case FLOAT: {
|
2411
|
+
float[] floatTemp = new float[rowCount+1];
|
2412
|
+
System.arraycopy(columns[col], 0, floatTemp, 0, insert);
|
2413
|
+
System.arraycopy(columns[col], insert, floatTemp, insert+1, rowCount - insert);
|
2414
|
+
columns[col] = floatTemp;
|
2415
|
+
break;
|
2416
|
+
}
|
2417
|
+
case DOUBLE: {
|
2418
|
+
double[] doubleTemp = new double[rowCount+1];
|
2419
|
+
System.arraycopy(columns[col], 0, doubleTemp, 0, insert);
|
2420
|
+
System.arraycopy(columns[col], insert, doubleTemp, insert+1, rowCount - insert);
|
2421
|
+
columns[col] = doubleTemp;
|
2422
|
+
break;
|
2423
|
+
}
|
2424
|
+
case STRING: {
|
2425
|
+
String[] stringTemp = new String[rowCount+1];
|
2426
|
+
System.arraycopy(columns[col], 0, stringTemp, 0, insert);
|
2427
|
+
System.arraycopy(columns[col], insert, stringTemp, insert+1, rowCount - insert);
|
2428
|
+
columns[col] = stringTemp;
|
2429
|
+
break;
|
2430
|
+
}
|
2431
|
+
}
|
2432
|
+
}
|
2433
|
+
// Need to increment before setRow(), because it calls ensureBounds()
|
2434
|
+
// https://github.com/processing/processing/issues/5406
|
2435
|
+
++rowCount;
|
2436
|
+
setRow(insert, columnData);
|
2437
|
+
}
|
2438
|
+
|
2439
|
+
|
2440
|
+
/**
|
2441
|
+
* @webref table:method
|
2442
|
+
* @brief Removes a row from a table
|
2443
|
+
* @param row ID number of the row to remove
|
2444
|
+
* @see Table#addRow()
|
2445
|
+
* @see Table#clearRows()
|
2446
|
+
*/
|
2447
|
+
public void removeRow(int row) {
|
2448
|
+
for (int col = 0; col < columns.length; col++) {
|
2449
|
+
switch (columnTypes[col]) {
|
2450
|
+
case CATEGORY:
|
2451
|
+
case INT: {
|
2452
|
+
int[] intTemp = new int[rowCount-1];
|
2453
|
+
// int[] intData = (int[]) columns[col];
|
2454
|
+
// System.arraycopy(intData, 0, intTemp, 0, dead);
|
2455
|
+
// System.arraycopy(intData, dead+1, intTemp, dead, (rowCount - dead) + 1);
|
2456
|
+
System.arraycopy(columns[col], 0, intTemp, 0, row);
|
2457
|
+
System.arraycopy(columns[col], row+1, intTemp, row, (rowCount - row) - 1);
|
2458
|
+
columns[col] = intTemp;
|
2459
|
+
break;
|
2460
|
+
}
|
2461
|
+
case LONG: {
|
2462
|
+
long[] longTemp = new long[rowCount-1];
|
2463
|
+
// long[] longData = (long[]) columns[col];
|
2464
|
+
// System.arraycopy(longData, 0, longTemp, 0, dead);
|
2465
|
+
// System.arraycopy(longData, dead+1, longTemp, dead, (rowCount - dead) + 1);
|
2466
|
+
System.arraycopy(columns[col], 0, longTemp, 0, row);
|
2467
|
+
System.arraycopy(columns[col], row+1, longTemp, row, (rowCount - row) - 1);
|
2468
|
+
columns[col] = longTemp;
|
2469
|
+
break;
|
2470
|
+
}
|
2471
|
+
case FLOAT: {
|
2472
|
+
float[] floatTemp = new float[rowCount-1];
|
2473
|
+
// float[] floatData = (float[]) columns[col];
|
2474
|
+
// System.arraycopy(floatData, 0, floatTemp, 0, dead);
|
2475
|
+
// System.arraycopy(floatData, dead+1, floatTemp, dead, (rowCount - dead) + 1);
|
2476
|
+
System.arraycopy(columns[col], 0, floatTemp, 0, row);
|
2477
|
+
System.arraycopy(columns[col], row+1, floatTemp, row, (rowCount - row) - 1);
|
2478
|
+
columns[col] = floatTemp;
|
2479
|
+
break;
|
2480
|
+
}
|
2481
|
+
case DOUBLE: {
|
2482
|
+
double[] doubleTemp = new double[rowCount-1];
|
2483
|
+
// double[] doubleData = (double[]) columns[col];
|
2484
|
+
// System.arraycopy(doubleData, 0, doubleTemp, 0, dead);
|
2485
|
+
// System.arraycopy(doubleData, dead+1, doubleTemp, dead, (rowCount - dead) + 1);
|
2486
|
+
System.arraycopy(columns[col], 0, doubleTemp, 0, row);
|
2487
|
+
System.arraycopy(columns[col], row+1, doubleTemp, row, (rowCount - row) - 1);
|
2488
|
+
columns[col] = doubleTemp;
|
2489
|
+
break;
|
2490
|
+
}
|
2491
|
+
case STRING: {
|
2492
|
+
String[] stringTemp = new String[rowCount-1];
|
2493
|
+
System.arraycopy(columns[col], 0, stringTemp, 0, row);
|
2494
|
+
System.arraycopy(columns[col], row+1, stringTemp, row, (rowCount - row) - 1);
|
2495
|
+
columns[col] = stringTemp;
|
2496
|
+
}
|
2497
|
+
}
|
2498
|
+
}
|
2499
|
+
rowCount--;
|
2500
|
+
}
|
2501
|
+
|
2502
|
+
|
2503
|
+
/*
|
2504
|
+
public void setRow(int row, String[] pieces) {
|
2505
|
+
checkSize(row, pieces.length - 1);
|
2506
|
+
// pieces.length may be less than columns.length, so loop over pieces
|
2507
|
+
for (int col = 0; col < pieces.length; col++) {
|
2508
|
+
setRowCol(row, col, pieces[col]);
|
2509
|
+
}
|
2510
|
+
}
|
2511
|
+
|
2512
|
+
|
2513
|
+
protected void setRowCol(int row, int col, String piece) {
|
2514
|
+
switch (columnTypes[col]) {
|
2515
|
+
case STRING:
|
2516
|
+
String[] stringData = (String[]) columns[col];
|
2517
|
+
stringData[row] = piece;
|
2518
|
+
break;
|
2519
|
+
case INT:
|
2520
|
+
int[] intData = (int[]) columns[col];
|
2521
|
+
intData[row] = PApplet.parseInt(piece, missingInt);
|
2522
|
+
break;
|
2523
|
+
case LONG:
|
2524
|
+
long[] longData = (long[]) columns[col];
|
2525
|
+
try {
|
2526
|
+
longData[row] = Long.parseLong(piece);
|
2527
|
+
} catch (NumberFormatException nfe) {
|
2528
|
+
longData[row] = missingLong;
|
2529
|
+
}
|
2530
|
+
break;
|
2531
|
+
case FLOAT:
|
2532
|
+
float[] floatData = (float[]) columns[col];
|
2533
|
+
floatData[row] = PApplet.parseFloat(piece, missingFloat);
|
2534
|
+
break;
|
2535
|
+
case DOUBLE:
|
2536
|
+
double[] doubleData = (double[]) columns[col];
|
2537
|
+
try {
|
2538
|
+
doubleData[row] = Double.parseDouble(piece);
|
2539
|
+
} catch (NumberFormatException nfe) {
|
2540
|
+
doubleData[row] = missingDouble;
|
2541
|
+
}
|
2542
|
+
break;
|
2543
|
+
case CATEGORY:
|
2544
|
+
int[] indexData = (int[]) columns[col];
|
2545
|
+
indexData[row] = columnCategories[col].index(piece);
|
2546
|
+
break;
|
2547
|
+
default:
|
2548
|
+
throw new IllegalArgumentException("That's not a valid column type.");
|
2549
|
+
}
|
2550
|
+
}
|
2551
|
+
*/
|
2552
|
+
|
2553
|
+
|
2554
|
+
public void setRow(int row, Object[] pieces) {
|
2555
|
+
ensureBounds(row, pieces.length - 1);
|
2556
|
+
// pieces.length may be less than columns.length, so loop over pieces
|
2557
|
+
for (int col = 0; col < pieces.length; col++) {
|
2558
|
+
setRowCol(row, col, pieces[col]);
|
2559
|
+
}
|
2560
|
+
}
|
2561
|
+
|
2562
|
+
|
2563
|
+
protected void setRowCol(int row, int col, Object piece) {
|
2564
|
+
switch (columnTypes[col]) {
|
2565
|
+
case STRING:
|
2566
|
+
String[] stringData = (String[]) columns[col];
|
2567
|
+
if (piece == null) {
|
2568
|
+
stringData[row] = null;
|
2569
|
+
// } else if (piece instanceof String) {
|
2570
|
+
// stringData[row] = (String) piece;
|
2571
|
+
} else {
|
2572
|
+
// Calls toString() on the object, which is 'return this' for String
|
2573
|
+
stringData[row] = String.valueOf(piece);
|
2574
|
+
}
|
2575
|
+
break;
|
2576
|
+
case INT:
|
2577
|
+
int[] intData = (int[]) columns[col];
|
2578
|
+
//intData[row] = PApplet.parseInt(piece, missingInt);
|
2579
|
+
if (piece == null) {
|
2580
|
+
intData[row] = missingInt;
|
2581
|
+
} else if (piece instanceof Integer) {
|
2582
|
+
intData[row] = (Integer) piece;
|
2583
|
+
} else {
|
2584
|
+
intData[row] = PApplet.parseInt(String.valueOf(piece), missingInt);
|
2585
|
+
}
|
2586
|
+
break;
|
2587
|
+
case LONG:
|
2588
|
+
long[] longData = (long[]) columns[col];
|
2589
|
+
if (piece == null) {
|
2590
|
+
longData[row] = missingLong;
|
2591
|
+
} else if (piece instanceof Long) {
|
2592
|
+
longData[row] = (Long) piece;
|
2593
|
+
} else {
|
2594
|
+
try {
|
2595
|
+
longData[row] = Long.parseLong(String.valueOf(piece));
|
2596
|
+
} catch (NumberFormatException nfe) {
|
2597
|
+
longData[row] = missingLong;
|
2598
|
+
}
|
2599
|
+
}
|
2600
|
+
break;
|
2601
|
+
case FLOAT:
|
2602
|
+
float[] floatData = (float[]) columns[col];
|
2603
|
+
if (piece == null) {
|
2604
|
+
floatData[row] = missingFloat;
|
2605
|
+
} else if (piece instanceof Float) {
|
2606
|
+
floatData[row] = (Float) piece;
|
2607
|
+
} else {
|
2608
|
+
floatData[row] = PApplet.parseFloat(String.valueOf(piece), missingFloat);
|
2609
|
+
}
|
2610
|
+
break;
|
2611
|
+
case DOUBLE:
|
2612
|
+
double[] doubleData = (double[]) columns[col];
|
2613
|
+
if (piece == null) {
|
2614
|
+
doubleData[row] = missingDouble;
|
2615
|
+
} else if (piece instanceof Double) {
|
2616
|
+
doubleData[row] = (Double) piece;
|
2617
|
+
} else {
|
2618
|
+
try {
|
2619
|
+
doubleData[row] = Double.parseDouble(String.valueOf(piece));
|
2620
|
+
} catch (NumberFormatException nfe) {
|
2621
|
+
doubleData[row] = missingDouble;
|
2622
|
+
}
|
2623
|
+
}
|
2624
|
+
break;
|
2625
|
+
case CATEGORY:
|
2626
|
+
int[] indexData = (int[]) columns[col];
|
2627
|
+
if (piece == null) {
|
2628
|
+
indexData[row] = missingCategory;
|
2629
|
+
} else {
|
2630
|
+
String peace = String.valueOf(piece);
|
2631
|
+
if (peace.equals(missingString)) { // missingString might be null
|
2632
|
+
indexData[row] = missingCategory;
|
2633
|
+
} else {
|
2634
|
+
indexData[row] = columnCategories[col].index(peace);
|
2635
|
+
}
|
2636
|
+
}
|
2637
|
+
break;
|
2638
|
+
default:
|
2639
|
+
throw new IllegalArgumentException("That's not a valid column type.");
|
2640
|
+
}
|
2641
|
+
}
|
2642
|
+
|
2643
|
+
|
2644
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
2645
|
+
|
2646
|
+
/**
|
2647
|
+
* @webref table:method
|
2648
|
+
* @brief Gets a row from a table
|
2649
|
+
* @param row ID number of the row to get
|
2650
|
+
* @see Table#rows()
|
2651
|
+
* @see Table#findRow(String, int)
|
2652
|
+
* @see Table#findRows(String, int)
|
2653
|
+
* @see Table#matchRow(String, int)
|
2654
|
+
* @see Table#matchRows(String, int)
|
2655
|
+
*/
|
2656
|
+
public TableRow getRow(int row) {
|
2657
|
+
return new RowPointer(this, row);
|
2658
|
+
}
|
2659
|
+
|
2660
|
+
|
2661
|
+
/**
|
2662
|
+
* Note that this one iterator instance is shared by any calls to iterate
|
2663
|
+
* the rows of this table. This is very efficient, but not thread-safe.
|
2664
|
+
* If you want to iterate in a multi-threaded manner, don't use the iterator.
|
2665
|
+
*
|
2666
|
+
* @webref table:method
|
2667
|
+
* @brief Gets multiple rows from a table
|
2668
|
+
* @see Table#getRow(int)
|
2669
|
+
* @see Table#findRow(String, int)
|
2670
|
+
* @see Table#findRows(String, int)
|
2671
|
+
* @see Table#matchRow(String, int)
|
2672
|
+
* @see Table#matchRows(String, int)
|
2673
|
+
*/
|
2674
|
+
public Iterable<TableRow> rows() {
|
2675
|
+
return new Iterable<TableRow>() {
|
2676
|
+
public Iterator<TableRow> iterator() {
|
2677
|
+
if (rowIterator == null) {
|
2678
|
+
rowIterator = new RowIterator(Table.this);
|
2679
|
+
} else {
|
2680
|
+
rowIterator.reset();
|
2681
|
+
}
|
2682
|
+
return rowIterator;
|
2683
|
+
}
|
2684
|
+
};
|
2685
|
+
}
|
2686
|
+
|
2687
|
+
/**
|
2688
|
+
* @nowebref
|
2689
|
+
*/
|
2690
|
+
public Iterable<TableRow> rows(final int[] indices) {
|
2691
|
+
return new Iterable<TableRow>() {
|
2692
|
+
public Iterator<TableRow> iterator() {
|
2693
|
+
return new RowIndexIterator(Table.this, indices);
|
2694
|
+
}
|
2695
|
+
};
|
2696
|
+
}
|
2697
|
+
|
2698
|
+
|
2699
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
2700
|
+
|
2701
|
+
|
2702
|
+
static class RowPointer implements TableRow {
|
2703
|
+
Table table;
|
2704
|
+
int row;
|
2705
|
+
|
2706
|
+
public RowPointer(Table table, int row) {
|
2707
|
+
this.table = table;
|
2708
|
+
this.row = row;
|
2709
|
+
}
|
2710
|
+
|
2711
|
+
public void setRow(int row) {
|
2712
|
+
this.row = row;
|
2713
|
+
}
|
2714
|
+
|
2715
|
+
public String getString(int column) {
|
2716
|
+
return table.getString(row, column);
|
2717
|
+
}
|
2718
|
+
|
2719
|
+
public String getString(String columnName) {
|
2720
|
+
return table.getString(row, columnName);
|
2721
|
+
}
|
2722
|
+
|
2723
|
+
public int getInt(int column) {
|
2724
|
+
return table.getInt(row, column);
|
2725
|
+
}
|
2726
|
+
|
2727
|
+
public int getInt(String columnName) {
|
2728
|
+
return table.getInt(row, columnName);
|
2729
|
+
}
|
2730
|
+
|
2731
|
+
public long getLong(int column) {
|
2732
|
+
return table.getLong(row, column);
|
2733
|
+
}
|
2734
|
+
|
2735
|
+
public long getLong(String columnName) {
|
2736
|
+
return table.getLong(row, columnName);
|
2737
|
+
}
|
2738
|
+
|
2739
|
+
public float getFloat(int column) {
|
2740
|
+
return table.getFloat(row, column);
|
2741
|
+
}
|
2742
|
+
|
2743
|
+
public float getFloat(String columnName) {
|
2744
|
+
return table.getFloat(row, columnName);
|
2745
|
+
}
|
2746
|
+
|
2747
|
+
public double getDouble(int column) {
|
2748
|
+
return table.getDouble(row, column);
|
2749
|
+
}
|
2750
|
+
|
2751
|
+
public double getDouble(String columnName) {
|
2752
|
+
return table.getDouble(row, columnName);
|
2753
|
+
}
|
2754
|
+
|
2755
|
+
public void setString(int column, String value) {
|
2756
|
+
table.setString(row, column, value);
|
2757
|
+
}
|
2758
|
+
|
2759
|
+
public void setString(String columnName, String value) {
|
2760
|
+
table.setString(row, columnName, value);
|
2761
|
+
}
|
2762
|
+
|
2763
|
+
public void setInt(int column, int value) {
|
2764
|
+
table.setInt(row, column, value);
|
2765
|
+
}
|
2766
|
+
|
2767
|
+
public void setInt(String columnName, int value) {
|
2768
|
+
table.setInt(row, columnName, value);
|
2769
|
+
}
|
2770
|
+
|
2771
|
+
public void setLong(int column, long value) {
|
2772
|
+
table.setLong(row, column, value);
|
2773
|
+
}
|
2774
|
+
|
2775
|
+
public void setLong(String columnName, long value) {
|
2776
|
+
table.setLong(row, columnName, value);
|
2777
|
+
}
|
2778
|
+
|
2779
|
+
public void setFloat(int column, float value) {
|
2780
|
+
table.setFloat(row, column, value);
|
2781
|
+
}
|
2782
|
+
|
2783
|
+
public void setFloat(String columnName, float value) {
|
2784
|
+
table.setFloat(row, columnName, value);
|
2785
|
+
}
|
2786
|
+
|
2787
|
+
public void setDouble(int column, double value) {
|
2788
|
+
table.setDouble(row, column, value);
|
2789
|
+
}
|
2790
|
+
|
2791
|
+
public void setDouble(String columnName, double value) {
|
2792
|
+
table.setDouble(row, columnName, value);
|
2793
|
+
}
|
2794
|
+
|
2795
|
+
public int getColumnCount() {
|
2796
|
+
return table.getColumnCount();
|
2797
|
+
}
|
2798
|
+
|
2799
|
+
public int getColumnType(String columnName) {
|
2800
|
+
return table.getColumnType(columnName);
|
2801
|
+
}
|
2802
|
+
|
2803
|
+
public int getColumnType(int column) {
|
2804
|
+
return table.getColumnType(column);
|
2805
|
+
}
|
2806
|
+
|
2807
|
+
public int[] getColumnTypes() {
|
2808
|
+
return table.getColumnTypes();
|
2809
|
+
}
|
2810
|
+
|
2811
|
+
public String getColumnTitle(int column) {
|
2812
|
+
return table.getColumnTitle(column);
|
2813
|
+
}
|
2814
|
+
|
2815
|
+
public String[] getColumnTitles() {
|
2816
|
+
return table.getColumnTitles();
|
2817
|
+
}
|
2818
|
+
|
2819
|
+
public void print() {
|
2820
|
+
write(new PrintWriter(System.out));
|
2821
|
+
}
|
2822
|
+
|
2823
|
+
public void write(PrintWriter writer) {
|
2824
|
+
for (int i = 0 ; i < getColumnCount(); i++) {
|
2825
|
+
if (i != 0) {
|
2826
|
+
writer.print('\t');
|
2827
|
+
}
|
2828
|
+
writer.print(getString(i));
|
2829
|
+
}
|
2830
|
+
}
|
2831
|
+
}
|
2832
|
+
|
2833
|
+
|
2834
|
+
static class RowIterator implements Iterator<TableRow> {
|
2835
|
+
Table table;
|
2836
|
+
RowPointer rp;
|
2837
|
+
int row;
|
2838
|
+
|
2839
|
+
public RowIterator(Table table) {
|
2840
|
+
this.table = table;
|
2841
|
+
row = -1;
|
2842
|
+
rp = new RowPointer(table, row);
|
2843
|
+
}
|
2844
|
+
|
2845
|
+
public void remove() {
|
2846
|
+
table.removeRow(row);
|
2847
|
+
}
|
2848
|
+
|
2849
|
+
public TableRow next() {
|
2850
|
+
rp.setRow(++row);
|
2851
|
+
return rp;
|
2852
|
+
}
|
2853
|
+
|
2854
|
+
public boolean hasNext() {
|
2855
|
+
return row+1 < table.getRowCount();
|
2856
|
+
}
|
2857
|
+
|
2858
|
+
public void reset() {
|
2859
|
+
row = -1;
|
2860
|
+
}
|
2861
|
+
}
|
2862
|
+
|
2863
|
+
|
2864
|
+
static class RowIndexIterator implements Iterator<TableRow> {
|
2865
|
+
Table table;
|
2866
|
+
RowPointer rp;
|
2867
|
+
int[] indices;
|
2868
|
+
int index;
|
2869
|
+
|
2870
|
+
public RowIndexIterator(Table table, int[] indices) {
|
2871
|
+
this.table = table;
|
2872
|
+
this.indices = indices;
|
2873
|
+
index = -1;
|
2874
|
+
// just set to something arbitrary
|
2875
|
+
rp = new RowPointer(table, -1);
|
2876
|
+
}
|
2877
|
+
|
2878
|
+
public void remove() {
|
2879
|
+
table.removeRow(indices[index]);
|
2880
|
+
}
|
2881
|
+
|
2882
|
+
public TableRow next() {
|
2883
|
+
rp.setRow(indices[++index]);
|
2884
|
+
return rp;
|
2885
|
+
}
|
2886
|
+
|
2887
|
+
public boolean hasNext() {
|
2888
|
+
//return row+1 < table.getRowCount();
|
2889
|
+
return index + 1 < indices.length;
|
2890
|
+
}
|
2891
|
+
|
2892
|
+
public void reset() {
|
2893
|
+
index = -1;
|
2894
|
+
}
|
2895
|
+
}
|
2896
|
+
|
2897
|
+
|
2898
|
+
/*
|
2899
|
+
static public Iterator<TableRow> createIterator(final ResultSet rs) {
|
2900
|
+
return new Iterator<TableRow>() {
|
2901
|
+
boolean already;
|
2902
|
+
|
2903
|
+
public boolean hasNext() {
|
2904
|
+
already = true;
|
2905
|
+
try {
|
2906
|
+
return rs.next();
|
2907
|
+
} catch (SQLException e) {
|
2908
|
+
throw new RuntimeException(e);
|
2909
|
+
}
|
2910
|
+
}
|
2911
|
+
|
2912
|
+
|
2913
|
+
public TableRow next() {
|
2914
|
+
if (!already) {
|
2915
|
+
try {
|
2916
|
+
rs.next();
|
2917
|
+
} catch (SQLException e) {
|
2918
|
+
throw new RuntimeException(e);
|
2919
|
+
}
|
2920
|
+
} else {
|
2921
|
+
already = false;
|
2922
|
+
}
|
2923
|
+
|
2924
|
+
return new TableRow() {
|
2925
|
+
public double getDouble(int column) {
|
2926
|
+
try {
|
2927
|
+
return rs.getDouble(column);
|
2928
|
+
} catch (SQLException e) {
|
2929
|
+
throw new RuntimeException(e);
|
2930
|
+
}
|
2931
|
+
}
|
2932
|
+
|
2933
|
+
public double getDouble(String columnName) {
|
2934
|
+
try {
|
2935
|
+
return rs.getDouble(columnName);
|
2936
|
+
} catch (SQLException e) {
|
2937
|
+
throw new RuntimeException(e);
|
2938
|
+
}
|
2939
|
+
}
|
2940
|
+
|
2941
|
+
public float getFloat(int column) {
|
2942
|
+
try {
|
2943
|
+
return rs.getFloat(column);
|
2944
|
+
} catch (SQLException e) {
|
2945
|
+
throw new RuntimeException(e);
|
2946
|
+
}
|
2947
|
+
}
|
2948
|
+
|
2949
|
+
public float getFloat(String columnName) {
|
2950
|
+
try {
|
2951
|
+
return rs.getFloat(columnName);
|
2952
|
+
} catch (SQLException e) {
|
2953
|
+
throw new RuntimeException(e);
|
2954
|
+
}
|
2955
|
+
}
|
2956
|
+
|
2957
|
+
public int getInt(int column) {
|
2958
|
+
try {
|
2959
|
+
return rs.getInt(column);
|
2960
|
+
} catch (SQLException e) {
|
2961
|
+
throw new RuntimeException(e);
|
2962
|
+
}
|
2963
|
+
}
|
2964
|
+
|
2965
|
+
public int getInt(String columnName) {
|
2966
|
+
try {
|
2967
|
+
return rs.getInt(columnName);
|
2968
|
+
} catch (SQLException e) {
|
2969
|
+
throw new RuntimeException(e);
|
2970
|
+
}
|
2971
|
+
}
|
2972
|
+
|
2973
|
+
public long getLong(int column) {
|
2974
|
+
try {
|
2975
|
+
return rs.getLong(column);
|
2976
|
+
} catch (SQLException e) {
|
2977
|
+
throw new RuntimeException(e);
|
2978
|
+
}
|
2979
|
+
}
|
2980
|
+
|
2981
|
+
public long getLong(String columnName) {
|
2982
|
+
try {
|
2983
|
+
return rs.getLong(columnName);
|
2984
|
+
} catch (SQLException e) {
|
2985
|
+
throw new RuntimeException(e);
|
2986
|
+
}
|
2987
|
+
}
|
2988
|
+
|
2989
|
+
public String getString(int column) {
|
2990
|
+
try {
|
2991
|
+
return rs.getString(column);
|
2992
|
+
} catch (SQLException e) {
|
2993
|
+
throw new RuntimeException(e);
|
2994
|
+
}
|
2995
|
+
}
|
2996
|
+
|
2997
|
+
public String getString(String columnName) {
|
2998
|
+
try {
|
2999
|
+
return rs.getString(columnName);
|
3000
|
+
} catch (SQLException e) {
|
3001
|
+
throw new RuntimeException(e);
|
3002
|
+
}
|
3003
|
+
}
|
3004
|
+
|
3005
|
+
public void setString(int column, String value) { immutable(); }
|
3006
|
+
public void setString(String columnName, String value) { immutable(); }
|
3007
|
+
public void setInt(int column, int value) { immutable(); }
|
3008
|
+
public void setInt(String columnName, int value) { immutable(); }
|
3009
|
+
public void setLong(int column, long value) { immutable(); }
|
3010
|
+
public void setLong(String columnName, long value) { immutable(); }
|
3011
|
+
public void setFloat(int column, float value) { immutable(); }
|
3012
|
+
public void setFloat(String columnName, float value) { immutable(); }
|
3013
|
+
public void setDouble(int column, double value) { immutable(); }
|
3014
|
+
public void setDouble(String columnName, double value) { immutable(); }
|
3015
|
+
|
3016
|
+
private void immutable() {
|
3017
|
+
throw new IllegalArgumentException("This TableRow cannot be modified.");
|
3018
|
+
}
|
3019
|
+
|
3020
|
+
public int getColumnCount() {
|
3021
|
+
try {
|
3022
|
+
return rs.getMetaData().getColumnCount();
|
3023
|
+
} catch (SQLException e) {
|
3024
|
+
e.printStackTrace();
|
3025
|
+
return -1;
|
3026
|
+
}
|
3027
|
+
}
|
3028
|
+
|
3029
|
+
|
3030
|
+
public int getColumnType(String columnName) {
|
3031
|
+
// unimplemented
|
3032
|
+
}
|
3033
|
+
|
3034
|
+
|
3035
|
+
public int getColumnType(int column) {
|
3036
|
+
// unimplemented
|
3037
|
+
}
|
3038
|
+
|
3039
|
+
};
|
3040
|
+
}
|
3041
|
+
|
3042
|
+
public void remove() {
|
3043
|
+
throw new IllegalArgumentException("remove() not supported");
|
3044
|
+
}
|
3045
|
+
};
|
3046
|
+
}
|
3047
|
+
*/
|
3048
|
+
|
3049
|
+
|
3050
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3051
|
+
|
3052
|
+
|
3053
|
+
/**
|
3054
|
+
* @webref table:method
|
3055
|
+
* @brief Get an integer value from the specified row and column
|
3056
|
+
* @param row ID number of the row to reference
|
3057
|
+
* @param column ID number of the column to reference
|
3058
|
+
* @see Table#getFloat(int, int)
|
3059
|
+
* @see Table#getString(int, int)
|
3060
|
+
* @see Table#getStringColumn(String)
|
3061
|
+
* @see Table#setInt(int, int, int)
|
3062
|
+
* @see Table#setFloat(int, int, float)
|
3063
|
+
* @see Table#setString(int, int, String)
|
3064
|
+
*/
|
3065
|
+
public int getInt(int row, int column) {
|
3066
|
+
checkBounds(row, column);
|
3067
|
+
if (columnTypes[column] == INT ||
|
3068
|
+
columnTypes[column] == CATEGORY) {
|
3069
|
+
int[] intData = (int[]) columns[column];
|
3070
|
+
return intData[row];
|
3071
|
+
}
|
3072
|
+
String str = getString(row, column);
|
3073
|
+
return (str == null || str.equals(missingString)) ?
|
3074
|
+
missingInt : PApplet.parseInt(str, missingInt);
|
3075
|
+
}
|
3076
|
+
|
3077
|
+
/**
|
3078
|
+
* @param columnName title of the column to reference
|
3079
|
+
*/
|
3080
|
+
public int getInt(int row, String columnName) {
|
3081
|
+
return getInt(row, getColumnIndex(columnName));
|
3082
|
+
}
|
3083
|
+
|
3084
|
+
|
3085
|
+
public void setMissingInt(int value) {
|
3086
|
+
missingInt = value;
|
3087
|
+
}
|
3088
|
+
|
3089
|
+
|
3090
|
+
/**
|
3091
|
+
* @webref table:method
|
3092
|
+
* @brief Store an integer value in the specified row and column
|
3093
|
+
* @param row ID number of the target row
|
3094
|
+
* @param column ID number of the target column
|
3095
|
+
* @param value value to assign
|
3096
|
+
* @see Table#setFloat(int, int, float)
|
3097
|
+
* @see Table#setString(int, int, String)
|
3098
|
+
* @see Table#getInt(int, int)
|
3099
|
+
* @see Table#getFloat(int, int)
|
3100
|
+
* @see Table#getString(int, int)
|
3101
|
+
* @see Table#getStringColumn(String)
|
3102
|
+
*/
|
3103
|
+
public void setInt(int row, int column, int value) {
|
3104
|
+
if (columnTypes[column] == STRING) {
|
3105
|
+
setString(row, column, String.valueOf(value));
|
3106
|
+
|
3107
|
+
} else {
|
3108
|
+
ensureBounds(row, column);
|
3109
|
+
if (columnTypes[column] != INT &&
|
3110
|
+
columnTypes[column] != CATEGORY) {
|
3111
|
+
throw new IllegalArgumentException("Column " + column + " is not an int column.");
|
3112
|
+
}
|
3113
|
+
int[] intData = (int[]) columns[column];
|
3114
|
+
intData[row] = value;
|
3115
|
+
}
|
3116
|
+
}
|
3117
|
+
|
3118
|
+
/**
|
3119
|
+
* @param columnName title of the target column
|
3120
|
+
*/
|
3121
|
+
public void setInt(int row, String columnName, int value) {
|
3122
|
+
setInt(row, getColumnIndex(columnName), value);
|
3123
|
+
}
|
3124
|
+
|
3125
|
+
|
3126
|
+
|
3127
|
+
public int[] getIntColumn(String name) {
|
3128
|
+
int col = getColumnIndex(name);
|
3129
|
+
return (col == -1) ? null : getIntColumn(col);
|
3130
|
+
}
|
3131
|
+
|
3132
|
+
|
3133
|
+
public int[] getIntColumn(int col) {
|
3134
|
+
int[] outgoing = new int[rowCount];
|
3135
|
+
for (int row = 0; row < rowCount; row++) {
|
3136
|
+
outgoing[row] = getInt(row, col);
|
3137
|
+
}
|
3138
|
+
return outgoing;
|
3139
|
+
}
|
3140
|
+
|
3141
|
+
|
3142
|
+
public int[] getIntRow(int row) {
|
3143
|
+
int[] outgoing = new int[columns.length];
|
3144
|
+
for (int col = 0; col < columns.length; col++) {
|
3145
|
+
outgoing[col] = getInt(row, col);
|
3146
|
+
}
|
3147
|
+
return outgoing;
|
3148
|
+
}
|
3149
|
+
|
3150
|
+
|
3151
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3152
|
+
|
3153
|
+
|
3154
|
+
public long getLong(int row, int column) {
|
3155
|
+
checkBounds(row, column);
|
3156
|
+
if (columnTypes[column] == LONG) {
|
3157
|
+
long[] longData = (long[]) columns[column];
|
3158
|
+
return longData[row];
|
3159
|
+
}
|
3160
|
+
String str = getString(row, column);
|
3161
|
+
if (str == null || str.equals(missingString)) {
|
3162
|
+
return missingLong;
|
3163
|
+
}
|
3164
|
+
try {
|
3165
|
+
return Long.parseLong(str);
|
3166
|
+
} catch (NumberFormatException nfe) {
|
3167
|
+
return missingLong;
|
3168
|
+
}
|
3169
|
+
}
|
3170
|
+
|
3171
|
+
|
3172
|
+
public long getLong(int row, String columnName) {
|
3173
|
+
return getLong(row, getColumnIndex(columnName));
|
3174
|
+
}
|
3175
|
+
|
3176
|
+
|
3177
|
+
public void setMissingLong(long value) {
|
3178
|
+
missingLong = value;
|
3179
|
+
}
|
3180
|
+
|
3181
|
+
|
3182
|
+
public void setLong(int row, int column, long value) {
|
3183
|
+
if (columnTypes[column] == STRING) {
|
3184
|
+
setString(row, column, String.valueOf(value));
|
3185
|
+
|
3186
|
+
} else {
|
3187
|
+
ensureBounds(row, column);
|
3188
|
+
if (columnTypes[column] != LONG) {
|
3189
|
+
throw new IllegalArgumentException("Column " + column + " is not a 'long' column.");
|
3190
|
+
}
|
3191
|
+
long[] longData = (long[]) columns[column];
|
3192
|
+
longData[row] = value;
|
3193
|
+
}
|
3194
|
+
}
|
3195
|
+
|
3196
|
+
|
3197
|
+
public void setLong(int row, String columnName, long value) {
|
3198
|
+
setLong(row, getColumnIndex(columnName), value);
|
3199
|
+
}
|
3200
|
+
|
3201
|
+
|
3202
|
+
public long[] getLongColumn(String name) {
|
3203
|
+
int col = getColumnIndex(name);
|
3204
|
+
return (col == -1) ? null : getLongColumn(col);
|
3205
|
+
}
|
3206
|
+
|
3207
|
+
|
3208
|
+
public long[] getLongColumn(int col) {
|
3209
|
+
long[] outgoing = new long[rowCount];
|
3210
|
+
for (int row = 0; row < rowCount; row++) {
|
3211
|
+
outgoing[row] = getLong(row, col);
|
3212
|
+
}
|
3213
|
+
return outgoing;
|
3214
|
+
}
|
3215
|
+
|
3216
|
+
|
3217
|
+
public long[] getLongRow(int row) {
|
3218
|
+
long[] outgoing = new long[columns.length];
|
3219
|
+
for (int col = 0; col < columns.length; col++) {
|
3220
|
+
outgoing[col] = getLong(row, col);
|
3221
|
+
}
|
3222
|
+
return outgoing;
|
3223
|
+
}
|
3224
|
+
|
3225
|
+
|
3226
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3227
|
+
|
3228
|
+
|
3229
|
+
/**
|
3230
|
+
* Get a float value from the specified row and column. If the value is null
|
3231
|
+
* or not parseable as a float, the "missing" value is returned. By default,
|
3232
|
+
* this is Float.NaN, but can be controlled with setMissingFloat().
|
3233
|
+
*
|
3234
|
+
* @webref table:method
|
3235
|
+
* @brief Get a float value from the specified row and column
|
3236
|
+
* @param row ID number of the row to reference
|
3237
|
+
* @param column ID number of the column to reference
|
3238
|
+
* @see Table#getInt(int, int)
|
3239
|
+
* @see Table#getString(int, int)
|
3240
|
+
* @see Table#getStringColumn(String)
|
3241
|
+
* @see Table#setInt(int, int, int)
|
3242
|
+
* @see Table#setFloat(int, int, float)
|
3243
|
+
* @see Table#setString(int, int, String)
|
3244
|
+
*/
|
3245
|
+
public float getFloat(int row, int column) {
|
3246
|
+
checkBounds(row, column);
|
3247
|
+
if (columnTypes[column] == FLOAT) {
|
3248
|
+
float[] floatData = (float[]) columns[column];
|
3249
|
+
return floatData[row];
|
3250
|
+
}
|
3251
|
+
String str = getString(row, column);
|
3252
|
+
if (str == null || str.equals(missingString)) {
|
3253
|
+
return missingFloat;
|
3254
|
+
}
|
3255
|
+
return PApplet.parseFloat(str, missingFloat);
|
3256
|
+
}
|
3257
|
+
|
3258
|
+
/**
|
3259
|
+
* @param columnName title of the column to reference
|
3260
|
+
*/
|
3261
|
+
public float getFloat(int row, String columnName) {
|
3262
|
+
return getFloat(row, getColumnIndex(columnName));
|
3263
|
+
}
|
3264
|
+
|
3265
|
+
|
3266
|
+
public void setMissingFloat(float value) {
|
3267
|
+
missingFloat = value;
|
3268
|
+
}
|
3269
|
+
|
3270
|
+
|
3271
|
+
/**
|
3272
|
+
* @webref table:method
|
3273
|
+
* @brief Store a float value in the specified row and column
|
3274
|
+
* @param row ID number of the target row
|
3275
|
+
* @param column ID number of the target column
|
3276
|
+
* @param value value to assign
|
3277
|
+
* @see Table#setInt(int, int, int)
|
3278
|
+
* @see Table#setString(int, int, String)
|
3279
|
+
* @see Table#getInt(int, int)
|
3280
|
+
* @see Table#getFloat(int, int)
|
3281
|
+
* @see Table#getString(int, int)
|
3282
|
+
* @see Table#getStringColumn(String)
|
3283
|
+
*/
|
3284
|
+
public void setFloat(int row, int column, float value) {
|
3285
|
+
if (columnTypes[column] == STRING) {
|
3286
|
+
setString(row, column, String.valueOf(value));
|
3287
|
+
|
3288
|
+
} else {
|
3289
|
+
ensureBounds(row, column);
|
3290
|
+
if (columnTypes[column] != FLOAT) {
|
3291
|
+
throw new IllegalArgumentException("Column " + column + " is not a float column.");
|
3292
|
+
}
|
3293
|
+
float[] longData = (float[]) columns[column];
|
3294
|
+
longData[row] = value;
|
3295
|
+
}
|
3296
|
+
}
|
3297
|
+
|
3298
|
+
/**
|
3299
|
+
* @param columnName title of the target column
|
3300
|
+
*/
|
3301
|
+
public void setFloat(int row, String columnName, float value) {
|
3302
|
+
setFloat(row, getColumnIndex(columnName), value);
|
3303
|
+
}
|
3304
|
+
|
3305
|
+
|
3306
|
+
public float[] getFloatColumn(String name) {
|
3307
|
+
int col = getColumnIndex(name);
|
3308
|
+
return (col == -1) ? null : getFloatColumn(col);
|
3309
|
+
}
|
3310
|
+
|
3311
|
+
|
3312
|
+
public float[] getFloatColumn(int col) {
|
3313
|
+
float[] outgoing = new float[rowCount];
|
3314
|
+
for (int row = 0; row < rowCount; row++) {
|
3315
|
+
outgoing[row] = getFloat(row, col);
|
3316
|
+
}
|
3317
|
+
return outgoing;
|
3318
|
+
}
|
3319
|
+
|
3320
|
+
|
3321
|
+
public float[] getFloatRow(int row) {
|
3322
|
+
float[] outgoing = new float[columns.length];
|
3323
|
+
for (int col = 0; col < columns.length; col++) {
|
3324
|
+
outgoing[col] = getFloat(row, col);
|
3325
|
+
}
|
3326
|
+
return outgoing;
|
3327
|
+
}
|
3328
|
+
|
3329
|
+
|
3330
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3331
|
+
|
3332
|
+
|
3333
|
+
public double getDouble(int row, int column) {
|
3334
|
+
checkBounds(row, column);
|
3335
|
+
if (columnTypes[column] == DOUBLE) {
|
3336
|
+
double[] doubleData = (double[]) columns[column];
|
3337
|
+
return doubleData[row];
|
3338
|
+
}
|
3339
|
+
String str = getString(row, column);
|
3340
|
+
if (str == null || str.equals(missingString)) {
|
3341
|
+
return missingDouble;
|
3342
|
+
}
|
3343
|
+
try {
|
3344
|
+
return Double.parseDouble(str);
|
3345
|
+
} catch (NumberFormatException nfe) {
|
3346
|
+
return missingDouble;
|
3347
|
+
}
|
3348
|
+
}
|
3349
|
+
|
3350
|
+
|
3351
|
+
public double getDouble(int row, String columnName) {
|
3352
|
+
return getDouble(row, getColumnIndex(columnName));
|
3353
|
+
}
|
3354
|
+
|
3355
|
+
|
3356
|
+
public void setMissingDouble(double value) {
|
3357
|
+
missingDouble = value;
|
3358
|
+
}
|
3359
|
+
|
3360
|
+
|
3361
|
+
public void setDouble(int row, int column, double value) {
|
3362
|
+
if (columnTypes[column] == STRING) {
|
3363
|
+
setString(row, column, String.valueOf(value));
|
3364
|
+
|
3365
|
+
} else {
|
3366
|
+
ensureBounds(row, column);
|
3367
|
+
if (columnTypes[column] != DOUBLE) {
|
3368
|
+
throw new IllegalArgumentException("Column " + column + " is not a 'double' column.");
|
3369
|
+
}
|
3370
|
+
double[] doubleData = (double[]) columns[column];
|
3371
|
+
doubleData[row] = value;
|
3372
|
+
}
|
3373
|
+
}
|
3374
|
+
|
3375
|
+
|
3376
|
+
public void setDouble(int row, String columnName, double value) {
|
3377
|
+
setDouble(row, getColumnIndex(columnName), value);
|
3378
|
+
}
|
3379
|
+
|
3380
|
+
|
3381
|
+
public double[] getDoubleColumn(String name) {
|
3382
|
+
int col = getColumnIndex(name);
|
3383
|
+
return (col == -1) ? null : getDoubleColumn(col);
|
3384
|
+
}
|
3385
|
+
|
3386
|
+
|
3387
|
+
public double[] getDoubleColumn(int col) {
|
3388
|
+
double[] outgoing = new double[rowCount];
|
3389
|
+
for (int row = 0; row < rowCount; row++) {
|
3390
|
+
outgoing[row] = getDouble(row, col);
|
3391
|
+
}
|
3392
|
+
return outgoing;
|
3393
|
+
}
|
3394
|
+
|
3395
|
+
|
3396
|
+
public double[] getDoubleRow(int row) {
|
3397
|
+
double[] outgoing = new double[columns.length];
|
3398
|
+
for (int col = 0; col < columns.length; col++) {
|
3399
|
+
outgoing[col] = getDouble(row, col);
|
3400
|
+
}
|
3401
|
+
return outgoing;
|
3402
|
+
}
|
3403
|
+
|
3404
|
+
|
3405
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3406
|
+
|
3407
|
+
|
3408
|
+
//public long getTimestamp(String rowName, int column) {
|
3409
|
+
//return getTimestamp(getRowIndex(rowName), column);
|
3410
|
+
//}
|
3411
|
+
|
3412
|
+
|
3413
|
+
/**
|
3414
|
+
* Returns the time in milliseconds by parsing a SQL Timestamp at this cell.
|
3415
|
+
*/
|
3416
|
+
// public long getTimestamp(int row, int column) {
|
3417
|
+
// String str = get(row, column);
|
3418
|
+
// java.sql.Timestamp timestamp = java.sql.Timestamp.valueOf(str);
|
3419
|
+
// return timestamp.getTime();
|
3420
|
+
// }
|
3421
|
+
|
3422
|
+
|
3423
|
+
// public long getExcelTimestamp(int row, int column) {
|
3424
|
+
// return parseExcelTimestamp(get(row, column));
|
3425
|
+
// }
|
3426
|
+
|
3427
|
+
|
3428
|
+
// static protected DateFormat excelDateFormat;
|
3429
|
+
|
3430
|
+
// static public long parseExcelTimestamp(String timestamp) {
|
3431
|
+
// if (excelDateFormat == null) {
|
3432
|
+
// excelDateFormat = new SimpleDateFormat("MM/dd/yy HH:mm");
|
3433
|
+
// }
|
3434
|
+
// try {
|
3435
|
+
// return excelDateFormat.parse(timestamp).getTime();
|
3436
|
+
// } catch (ParseException e) {
|
3437
|
+
// e.printStackTrace();
|
3438
|
+
// return -1;
|
3439
|
+
// }
|
3440
|
+
// }
|
3441
|
+
|
3442
|
+
|
3443
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3444
|
+
|
3445
|
+
|
3446
|
+
// public void setObject(int row, int column, Object value) {
|
3447
|
+
// if (value == null) {
|
3448
|
+
// data[row][column] = null;
|
3449
|
+
// } else if (value instanceof String) {
|
3450
|
+
// set(row, column, (String) value);
|
3451
|
+
// } else if (value instanceof Float) {
|
3452
|
+
// setFloat(row, column, ((Float) value).floatValue());
|
3453
|
+
// } else if (value instanceof Integer) {
|
3454
|
+
// setInt(row, column, ((Integer) value).intValue());
|
3455
|
+
// } else {
|
3456
|
+
// set(row, column, value.toString());
|
3457
|
+
// }
|
3458
|
+
// }
|
3459
|
+
|
3460
|
+
|
3461
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3462
|
+
|
3463
|
+
|
3464
|
+
/**
|
3465
|
+
* Get a String value from the table. If the row is longer than the table
|
3466
|
+
*
|
3467
|
+
* @webref table:method
|
3468
|
+
* @brief Get an String value from the specified row and column
|
3469
|
+
* @param row ID number of the row to reference
|
3470
|
+
* @param column ID number of the column to reference
|
3471
|
+
* @see Table#getInt(int, int)
|
3472
|
+
* @see Table#getFloat(int, int)
|
3473
|
+
* @see Table#getStringColumn(String)
|
3474
|
+
* @see Table#setInt(int, int, int)
|
3475
|
+
* @see Table#setFloat(int, int, float)
|
3476
|
+
* @see Table#setString(int, int, String)
|
3477
|
+
*/
|
3478
|
+
public String getString(int row, int column) {
|
3479
|
+
checkBounds(row, column);
|
3480
|
+
if (columnTypes[column] == STRING) {
|
3481
|
+
String[] stringData = (String[]) columns[column];
|
3482
|
+
return stringData[row];
|
3483
|
+
} else if (columnTypes[column] == CATEGORY) {
|
3484
|
+
int cat = getInt(row, column);
|
3485
|
+
if (cat == missingCategory) {
|
3486
|
+
return missingString;
|
3487
|
+
}
|
3488
|
+
return columnCategories[column].key(cat);
|
3489
|
+
} else if (columnTypes[column] == FLOAT) {
|
3490
|
+
if (Float.isNaN(getFloat(row, column))) {
|
3491
|
+
return null;
|
3492
|
+
}
|
3493
|
+
} else if (columnTypes[column] == DOUBLE) {
|
3494
|
+
if (Double.isNaN(getFloat(row, column))) {
|
3495
|
+
return null;
|
3496
|
+
}
|
3497
|
+
}
|
3498
|
+
return String.valueOf(Array.get(columns[column], row));
|
3499
|
+
}
|
3500
|
+
|
3501
|
+
|
3502
|
+
/**
|
3503
|
+
* @param columnName title of the column to reference
|
3504
|
+
*/
|
3505
|
+
public String getString(int row, String columnName) {
|
3506
|
+
return getString(row, getColumnIndex(columnName));
|
3507
|
+
}
|
3508
|
+
|
3509
|
+
|
3510
|
+
/**
|
3511
|
+
* Treat entries with this string as "missing". Also used for categorial.
|
3512
|
+
*/
|
3513
|
+
public void setMissingString(String value) {
|
3514
|
+
missingString = value;
|
3515
|
+
}
|
3516
|
+
|
3517
|
+
|
3518
|
+
/**
|
3519
|
+
* @webref table:method
|
3520
|
+
* @brief Store a String value in the specified row and column
|
3521
|
+
* @param row ID number of the target row
|
3522
|
+
* @param column ID number of the target column
|
3523
|
+
* @param value value to assign
|
3524
|
+
* @see Table#setInt(int, int, int)
|
3525
|
+
* @see Table#setFloat(int, int, float)
|
3526
|
+
* @see Table#getInt(int, int)
|
3527
|
+
* @see Table#getFloat(int, int)
|
3528
|
+
* @see Table#getString(int, int)
|
3529
|
+
* @see Table#getStringColumn(String)
|
3530
|
+
*/
|
3531
|
+
public void setString(int row, int column, String value) {
|
3532
|
+
ensureBounds(row, column);
|
3533
|
+
if (columnTypes[column] != STRING) {
|
3534
|
+
throw new IllegalArgumentException("Column " + column + " is not a String column.");
|
3535
|
+
}
|
3536
|
+
String[] stringData = (String[]) columns[column];
|
3537
|
+
stringData[row] = value;
|
3538
|
+
}
|
3539
|
+
|
3540
|
+
/**
|
3541
|
+
* @param columnName title of the target column
|
3542
|
+
*/
|
3543
|
+
public void setString(int row, String columnName, String value) {
|
3544
|
+
int column = checkColumnIndex(columnName);
|
3545
|
+
setString(row, column, value);
|
3546
|
+
}
|
3547
|
+
|
3548
|
+
/**
|
3549
|
+
* @webref table:method
|
3550
|
+
* @brief Gets all values in the specified column
|
3551
|
+
* @param columnName title of the column to search
|
3552
|
+
* @see Table#getInt(int, int)
|
3553
|
+
* @see Table#getFloat(int, int)
|
3554
|
+
* @see Table#getString(int, int)
|
3555
|
+
* @see Table#setInt(int, int, int)
|
3556
|
+
* @see Table#setFloat(int, int, float)
|
3557
|
+
* @see Table#setString(int, int, String)
|
3558
|
+
*/
|
3559
|
+
public String[] getStringColumn(String columnName) {
|
3560
|
+
int col = getColumnIndex(columnName);
|
3561
|
+
return (col == -1) ? null : getStringColumn(col);
|
3562
|
+
}
|
3563
|
+
|
3564
|
+
|
3565
|
+
/**
|
3566
|
+
* @param column ID number of the column to search
|
3567
|
+
*/
|
3568
|
+
public String[] getStringColumn(int column) {
|
3569
|
+
String[] outgoing = new String[rowCount];
|
3570
|
+
for (int i = 0; i < rowCount; i++) {
|
3571
|
+
outgoing[i] = getString(i, column);
|
3572
|
+
}
|
3573
|
+
return outgoing;
|
3574
|
+
}
|
3575
|
+
|
3576
|
+
|
3577
|
+
public String[] getStringRow(int row) {
|
3578
|
+
String[] outgoing = new String[columns.length];
|
3579
|
+
for (int col = 0; col < columns.length; col++) {
|
3580
|
+
outgoing[col] = getString(row, col);
|
3581
|
+
}
|
3582
|
+
return outgoing;
|
3583
|
+
}
|
3584
|
+
|
3585
|
+
|
3586
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3587
|
+
|
3588
|
+
|
3589
|
+
/**
|
3590
|
+
* Return the row that contains the first String that matches.
|
3591
|
+
* @param value the String to match
|
3592
|
+
* @param column ID number of the column to search
|
3593
|
+
*/
|
3594
|
+
public int findRowIndex(String value, int column) {
|
3595
|
+
checkColumn(column);
|
3596
|
+
if (columnTypes[column] == STRING) {
|
3597
|
+
String[] stringData = (String[]) columns[column];
|
3598
|
+
if (value == null) {
|
3599
|
+
for (int row = 0; row < rowCount; row++) {
|
3600
|
+
if (stringData[row] == null) return row;
|
3601
|
+
}
|
3602
|
+
} else {
|
3603
|
+
for (int row = 0; row < rowCount; row++) {
|
3604
|
+
if (stringData[row] != null && stringData[row].equals(value)) {
|
3605
|
+
return row;
|
3606
|
+
}
|
3607
|
+
}
|
3608
|
+
}
|
3609
|
+
} else { // less efficient, includes conversion as necessary
|
3610
|
+
for (int row = 0; row < rowCount; row++) {
|
3611
|
+
String str = getString(row, column);
|
3612
|
+
if (str == null) {
|
3613
|
+
if (value == null) {
|
3614
|
+
return row;
|
3615
|
+
}
|
3616
|
+
} else if (str.equals(value)) {
|
3617
|
+
return row;
|
3618
|
+
}
|
3619
|
+
}
|
3620
|
+
}
|
3621
|
+
return -1;
|
3622
|
+
}
|
3623
|
+
|
3624
|
+
|
3625
|
+
/**
|
3626
|
+
* Return the row that contains the first String that matches.
|
3627
|
+
* @param value the String to match
|
3628
|
+
* @param columnName title of the column to search
|
3629
|
+
*/
|
3630
|
+
public int findRowIndex(String value, String columnName) {
|
3631
|
+
return findRowIndex(value, getColumnIndex(columnName));
|
3632
|
+
}
|
3633
|
+
|
3634
|
+
|
3635
|
+
/**
|
3636
|
+
* Return a list of rows that contain the String passed in. If there are no
|
3637
|
+
* matches, a zero length array will be returned (not a null array).
|
3638
|
+
* @param value the String to match
|
3639
|
+
* @param column ID number of the column to search
|
3640
|
+
*/
|
3641
|
+
public int[] findRowIndices(String value, int column) {
|
3642
|
+
int[] outgoing = new int[rowCount];
|
3643
|
+
int count = 0;
|
3644
|
+
|
3645
|
+
checkColumn(column);
|
3646
|
+
if (columnTypes[column] == STRING) {
|
3647
|
+
String[] stringData = (String[]) columns[column];
|
3648
|
+
if (value == null) {
|
3649
|
+
for (int row = 0; row < rowCount; row++) {
|
3650
|
+
if (stringData[row] == null) {
|
3651
|
+
outgoing[count++] = row;
|
3652
|
+
}
|
3653
|
+
}
|
3654
|
+
} else {
|
3655
|
+
for (int row = 0; row < rowCount; row++) {
|
3656
|
+
if (stringData[row] != null && stringData[row].equals(value)) {
|
3657
|
+
outgoing[count++] = row;
|
3658
|
+
}
|
3659
|
+
}
|
3660
|
+
}
|
3661
|
+
} else { // less efficient, includes conversion as necessary
|
3662
|
+
for (int row = 0; row < rowCount; row++) {
|
3663
|
+
String str = getString(row, column);
|
3664
|
+
if (str == null) {
|
3665
|
+
if (value == null) {
|
3666
|
+
outgoing[count++] = row;
|
3667
|
+
}
|
3668
|
+
} else if (str.equals(value)) {
|
3669
|
+
outgoing[count++] = row;
|
3670
|
+
}
|
3671
|
+
}
|
3672
|
+
}
|
3673
|
+
return PApplet.subset(outgoing, 0, count);
|
3674
|
+
}
|
3675
|
+
|
3676
|
+
|
3677
|
+
/**
|
3678
|
+
* Return a list of rows that contain the String passed in. If there are no
|
3679
|
+
* matches, a zero length array will be returned (not a null array).
|
3680
|
+
* @param value the String to match
|
3681
|
+
* @param columnName title of the column to search
|
3682
|
+
*/
|
3683
|
+
public int[] findRowIndices(String value, String columnName) {
|
3684
|
+
return findRowIndices(value, getColumnIndex(columnName));
|
3685
|
+
}
|
3686
|
+
|
3687
|
+
|
3688
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3689
|
+
|
3690
|
+
/**
|
3691
|
+
* @webref table:method
|
3692
|
+
* @brief Finds a row that contains the given value
|
3693
|
+
* @param value the value to match
|
3694
|
+
* @param column ID number of the column to search
|
3695
|
+
* @see Table#getRow(int)
|
3696
|
+
* @see Table#rows()
|
3697
|
+
* @see Table#findRows(String, int)
|
3698
|
+
* @see Table#matchRow(String, int)
|
3699
|
+
* @see Table#matchRows(String, int)
|
3700
|
+
*/
|
3701
|
+
public TableRow findRow(String value, int column) {
|
3702
|
+
int row = findRowIndex(value, column);
|
3703
|
+
return (row == -1) ? null : new RowPointer(this, row);
|
3704
|
+
}
|
3705
|
+
|
3706
|
+
|
3707
|
+
/**
|
3708
|
+
* @param columnName title of the column to search
|
3709
|
+
*/
|
3710
|
+
public TableRow findRow(String value, String columnName) {
|
3711
|
+
return findRow(value, getColumnIndex(columnName));
|
3712
|
+
}
|
3713
|
+
|
3714
|
+
|
3715
|
+
/**
|
3716
|
+
* @webref table:method
|
3717
|
+
* @brief Finds multiple rows that contain the given value
|
3718
|
+
* @param value the value to match
|
3719
|
+
* @param column ID number of the column to search
|
3720
|
+
* @see Table#getRow(int)
|
3721
|
+
* @see Table#rows()
|
3722
|
+
* @see Table#findRow(String, int)
|
3723
|
+
* @see Table#matchRow(String, int)
|
3724
|
+
* @see Table#matchRows(String, int)
|
3725
|
+
*/
|
3726
|
+
public Iterable<TableRow> findRows(final String value, final int column) {
|
3727
|
+
return new Iterable<TableRow>() {
|
3728
|
+
public Iterator<TableRow> iterator() {
|
3729
|
+
return findRowIterator(value, column);
|
3730
|
+
}
|
3731
|
+
};
|
3732
|
+
}
|
3733
|
+
|
3734
|
+
|
3735
|
+
/**
|
3736
|
+
* @param columnName title of the column to search
|
3737
|
+
*/
|
3738
|
+
public Iterable<TableRow> findRows(final String value, final String columnName) {
|
3739
|
+
return findRows(value, getColumnIndex(columnName));
|
3740
|
+
}
|
3741
|
+
|
3742
|
+
|
3743
|
+
/**
|
3744
|
+
* @brief Finds multiple rows that contain the given value
|
3745
|
+
* @param value the value to match
|
3746
|
+
* @param column ID number of the column to search
|
3747
|
+
*/
|
3748
|
+
public Iterator<TableRow> findRowIterator(String value, int column) {
|
3749
|
+
return new RowIndexIterator(this, findRowIndices(value, column));
|
3750
|
+
}
|
3751
|
+
|
3752
|
+
|
3753
|
+
/**
|
3754
|
+
* @param columnName title of the column to search
|
3755
|
+
*/
|
3756
|
+
public Iterator<TableRow> findRowIterator(String value, String columnName) {
|
3757
|
+
return findRowIterator(value, getColumnIndex(columnName));
|
3758
|
+
}
|
3759
|
+
|
3760
|
+
|
3761
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3762
|
+
|
3763
|
+
|
3764
|
+
/**
|
3765
|
+
* Return the row that contains the first String that matches.
|
3766
|
+
* @param regexp the String to match
|
3767
|
+
* @param column ID number of the column to search
|
3768
|
+
*/
|
3769
|
+
public int matchRowIndex(String regexp, int column) {
|
3770
|
+
checkColumn(column);
|
3771
|
+
if (columnTypes[column] == STRING) {
|
3772
|
+
String[] stringData = (String[]) columns[column];
|
3773
|
+
for (int row = 0; row < rowCount; row++) {
|
3774
|
+
if (stringData[row] != null &&
|
3775
|
+
PApplet.match(stringData[row], regexp) != null) {
|
3776
|
+
return row;
|
3777
|
+
}
|
3778
|
+
}
|
3779
|
+
} else { // less efficient, includes conversion as necessary
|
3780
|
+
for (int row = 0; row < rowCount; row++) {
|
3781
|
+
String str = getString(row, column);
|
3782
|
+
if (str != null &&
|
3783
|
+
PApplet.match(str, regexp) != null) {
|
3784
|
+
return row;
|
3785
|
+
}
|
3786
|
+
}
|
3787
|
+
}
|
3788
|
+
return -1;
|
3789
|
+
}
|
3790
|
+
|
3791
|
+
|
3792
|
+
/**
|
3793
|
+
* Return the row that contains the first String that matches.
|
3794
|
+
* @param what the String to match
|
3795
|
+
* @param columnName title of the column to search
|
3796
|
+
*/
|
3797
|
+
public int matchRowIndex(String what, String columnName) {
|
3798
|
+
return matchRowIndex(what, getColumnIndex(columnName));
|
3799
|
+
}
|
3800
|
+
|
3801
|
+
|
3802
|
+
/**
|
3803
|
+
* Return a list of rows that contain the String passed in. If there are no
|
3804
|
+
* matches, a zero length array will be returned (not a null array).
|
3805
|
+
* @param regexp the String to match
|
3806
|
+
* @param column ID number of the column to search
|
3807
|
+
*/
|
3808
|
+
public int[] matchRowIndices(String regexp, int column) {
|
3809
|
+
int[] outgoing = new int[rowCount];
|
3810
|
+
int count = 0;
|
3811
|
+
|
3812
|
+
checkColumn(column);
|
3813
|
+
if (columnTypes[column] == STRING) {
|
3814
|
+
String[] stringData = (String[]) columns[column];
|
3815
|
+
for (int row = 0; row < rowCount; row++) {
|
3816
|
+
if (stringData[row] != null &&
|
3817
|
+
PApplet.match(stringData[row], regexp) != null) {
|
3818
|
+
outgoing[count++] = row;
|
3819
|
+
}
|
3820
|
+
}
|
3821
|
+
} else { // less efficient, includes conversion as necessary
|
3822
|
+
for (int row = 0; row < rowCount; row++) {
|
3823
|
+
String str = getString(row, column);
|
3824
|
+
if (str != null &&
|
3825
|
+
PApplet.match(str, regexp) != null) {
|
3826
|
+
outgoing[count++] = row;
|
3827
|
+
}
|
3828
|
+
}
|
3829
|
+
}
|
3830
|
+
return PApplet.subset(outgoing, 0, count);
|
3831
|
+
}
|
3832
|
+
|
3833
|
+
|
3834
|
+
/**
|
3835
|
+
* Return a list of rows that match the regex passed in. If there are no
|
3836
|
+
* matches, a zero length array will be returned (not a null array).
|
3837
|
+
* @param what the String to match
|
3838
|
+
* @param columnName title of the column to search
|
3839
|
+
*/
|
3840
|
+
public int[] matchRowIndices(String what, String columnName) {
|
3841
|
+
return matchRowIndices(what, getColumnIndex(columnName));
|
3842
|
+
}
|
3843
|
+
|
3844
|
+
|
3845
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3846
|
+
|
3847
|
+
/**
|
3848
|
+
* @webref table:method
|
3849
|
+
* @brief Finds a row that matches the given expression
|
3850
|
+
* @param regexp the regular expression to match
|
3851
|
+
* @param column ID number of the column to search
|
3852
|
+
* @see Table#getRow(int)
|
3853
|
+
* @see Table#rows()
|
3854
|
+
* @see Table#findRow(String, int)
|
3855
|
+
* @see Table#findRows(String, int)
|
3856
|
+
* @see Table#matchRows(String, int)
|
3857
|
+
*/
|
3858
|
+
public TableRow matchRow(String regexp, int column) {
|
3859
|
+
int row = matchRowIndex(regexp, column);
|
3860
|
+
return (row == -1) ? null : new RowPointer(this, row);
|
3861
|
+
}
|
3862
|
+
|
3863
|
+
|
3864
|
+
/**
|
3865
|
+
* @param columnName title of the column to search
|
3866
|
+
*/
|
3867
|
+
public TableRow matchRow(String regexp, String columnName) {
|
3868
|
+
return matchRow(regexp, getColumnIndex(columnName));
|
3869
|
+
}
|
3870
|
+
|
3871
|
+
|
3872
|
+
/**
|
3873
|
+
* @webref table:method
|
3874
|
+
* @brief Finds multiple rows that match the given expression
|
3875
|
+
* @param regexp the regular expression to match
|
3876
|
+
* @param column ID number of the column to search
|
3877
|
+
* @see Table#getRow(int)
|
3878
|
+
* @see Table#rows()
|
3879
|
+
* @see Table#findRow(String, int)
|
3880
|
+
* @see Table#findRows(String, int)
|
3881
|
+
* @see Table#matchRow(String, int)
|
3882
|
+
*/
|
3883
|
+
public Iterable<TableRow> matchRows(final String regexp, final int column) {
|
3884
|
+
return new Iterable<TableRow>() {
|
3885
|
+
public Iterator<TableRow> iterator() {
|
3886
|
+
return matchRowIterator(regexp, column);
|
3887
|
+
}
|
3888
|
+
};
|
3889
|
+
}
|
3890
|
+
|
3891
|
+
|
3892
|
+
/**
|
3893
|
+
* @param columnName title of the column to search
|
3894
|
+
*/
|
3895
|
+
public Iterable<TableRow> matchRows(String regexp, String columnName) {
|
3896
|
+
return matchRows(regexp, getColumnIndex(columnName));
|
3897
|
+
}
|
3898
|
+
|
3899
|
+
|
3900
|
+
/**
|
3901
|
+
* @webref table:method
|
3902
|
+
* @brief Finds multiple rows that match the given expression
|
3903
|
+
* @param value the regular expression to match
|
3904
|
+
* @param column ID number of the column to search
|
3905
|
+
*/
|
3906
|
+
public Iterator<TableRow> matchRowIterator(String value, int column) {
|
3907
|
+
return new RowIndexIterator(this, matchRowIndices(value, column));
|
3908
|
+
}
|
3909
|
+
|
3910
|
+
|
3911
|
+
/**
|
3912
|
+
* @param columnName title of the column to search
|
3913
|
+
*/
|
3914
|
+
public Iterator<TableRow> matchRowIterator(String value, String columnName) {
|
3915
|
+
return matchRowIterator(value, getColumnIndex(columnName));
|
3916
|
+
}
|
3917
|
+
|
3918
|
+
|
3919
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3920
|
+
|
3921
|
+
|
3922
|
+
/**
|
3923
|
+
* Replace a String with another. Set empty entries null by using
|
3924
|
+
* replace("", null) or use replace(null, "") to go the other direction.
|
3925
|
+
* If this is a typed table, only String columns will be modified.
|
3926
|
+
* @param orig
|
3927
|
+
* @param replacement
|
3928
|
+
*/
|
3929
|
+
public void replace(String orig, String replacement) {
|
3930
|
+
for (int col = 0; col < columns.length; col++) {
|
3931
|
+
replace(orig, replacement, col);
|
3932
|
+
}
|
3933
|
+
}
|
3934
|
+
|
3935
|
+
|
3936
|
+
public void replace(String orig, String replacement, int col) {
|
3937
|
+
if (columnTypes[col] == STRING) {
|
3938
|
+
String[] stringData = (String[]) columns[col];
|
3939
|
+
|
3940
|
+
if (orig != null) {
|
3941
|
+
for (int row = 0; row < rowCount; row++) {
|
3942
|
+
if (orig.equals(stringData[row])) {
|
3943
|
+
stringData[row] = replacement;
|
3944
|
+
}
|
3945
|
+
}
|
3946
|
+
} else { // null is a special case (and faster anyway)
|
3947
|
+
for (int row = 0; row < rowCount; row++) {
|
3948
|
+
if (stringData[row] == null) {
|
3949
|
+
stringData[row] = replacement;
|
3950
|
+
}
|
3951
|
+
}
|
3952
|
+
}
|
3953
|
+
}
|
3954
|
+
}
|
3955
|
+
|
3956
|
+
|
3957
|
+
public void replace(String orig, String replacement, String colName) {
|
3958
|
+
replace(orig, replacement, getColumnIndex(colName));
|
3959
|
+
}
|
3960
|
+
|
3961
|
+
|
3962
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3963
|
+
|
3964
|
+
|
3965
|
+
public void replaceAll(String regex, String replacement) {
|
3966
|
+
for (int col = 0; col < columns.length; col++) {
|
3967
|
+
replaceAll(regex, replacement, col);
|
3968
|
+
}
|
3969
|
+
}
|
3970
|
+
|
3971
|
+
|
3972
|
+
public void replaceAll(String regex, String replacement, int column) {
|
3973
|
+
checkColumn(column);
|
3974
|
+
if (columnTypes[column] == STRING) {
|
3975
|
+
String[] stringData = (String[]) columns[column];
|
3976
|
+
for (int row = 0; row < rowCount; row++) {
|
3977
|
+
if (stringData[row] != null) {
|
3978
|
+
stringData[row] = stringData[row].replaceAll(regex, replacement);
|
3979
|
+
}
|
3980
|
+
}
|
3981
|
+
} else {
|
3982
|
+
throw new IllegalArgumentException("replaceAll() can only be used on String columns");
|
3983
|
+
}
|
3984
|
+
}
|
3985
|
+
|
3986
|
+
|
3987
|
+
/**
|
3988
|
+
* Run String.replaceAll() on all entries in a column.
|
3989
|
+
* Only works with columns that are already String values.
|
3990
|
+
* @param regex the String to match
|
3991
|
+
* @param columnName title of the column to search
|
3992
|
+
*/
|
3993
|
+
public void replaceAll(String regex, String replacement, String columnName) {
|
3994
|
+
replaceAll(regex, replacement, getColumnIndex(columnName));
|
3995
|
+
}
|
3996
|
+
|
3997
|
+
|
3998
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
3999
|
+
|
4000
|
+
|
4001
|
+
/**
|
4002
|
+
* Remove any of the specified characters from the entire table.
|
4003
|
+
*
|
4004
|
+
* @webref table:method
|
4005
|
+
* @brief Removes characters from the table
|
4006
|
+
* @param tokens a list of individual characters to be removed
|
4007
|
+
* @see Table#trim()
|
4008
|
+
*/
|
4009
|
+
public void removeTokens(String tokens) {
|
4010
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
4011
|
+
removeTokens(tokens, col);
|
4012
|
+
}
|
4013
|
+
}
|
4014
|
+
|
4015
|
+
|
4016
|
+
/**
|
4017
|
+
* Removed any of the specified characters from a column. For instance,
|
4018
|
+
* the following code removes dollar signs and commas from column 2:
|
4019
|
+
* <pre>
|
4020
|
+
* table.removeTokens(",$", 2);
|
4021
|
+
* </pre>
|
4022
|
+
*
|
4023
|
+
* @param column ID number of the column to process
|
4024
|
+
*/
|
4025
|
+
public void removeTokens(String tokens, int column) {
|
4026
|
+
for (int row = 0; row < rowCount; row++) {
|
4027
|
+
String s = getString(row, column);
|
4028
|
+
if (s != null) {
|
4029
|
+
char[] c = s.toCharArray();
|
4030
|
+
int index = 0;
|
4031
|
+
for (int j = 0; j < c.length; j++) {
|
4032
|
+
if (tokens.indexOf(c[j]) == -1) {
|
4033
|
+
if (index != j) {
|
4034
|
+
c[index] = c[j];
|
4035
|
+
}
|
4036
|
+
index++;
|
4037
|
+
}
|
4038
|
+
}
|
4039
|
+
if (index != c.length) {
|
4040
|
+
setString(row, column, new String(c, 0, index));
|
4041
|
+
}
|
4042
|
+
}
|
4043
|
+
}
|
4044
|
+
}
|
4045
|
+
|
4046
|
+
/**
|
4047
|
+
* @param columnName title of the column to process
|
4048
|
+
*/
|
4049
|
+
public void removeTokens(String tokens, String columnName) {
|
4050
|
+
removeTokens(tokens, getColumnIndex(columnName));
|
4051
|
+
}
|
4052
|
+
|
4053
|
+
|
4054
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4055
|
+
|
4056
|
+
|
4057
|
+
/**
|
4058
|
+
* @webref table:method
|
4059
|
+
* @brief Trims whitespace from values
|
4060
|
+
* @see Table#removeTokens(String)
|
4061
|
+
*/
|
4062
|
+
public void trim() {
|
4063
|
+
columnTitles = PApplet.trim(columnTitles);
|
4064
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
4065
|
+
trim(col);
|
4066
|
+
}
|
4067
|
+
// remove empty columns
|
4068
|
+
int lastColumn = getColumnCount() - 1;
|
4069
|
+
//while (isEmptyColumn(lastColumn) && lastColumn >= 0) {
|
4070
|
+
while (isEmptyArray(getStringColumn(lastColumn)) && lastColumn >= 0) {
|
4071
|
+
lastColumn--;
|
4072
|
+
}
|
4073
|
+
setColumnCount(lastColumn + 1);
|
4074
|
+
|
4075
|
+
// trim() works from both sides
|
4076
|
+
while (getColumnCount() > 0 && isEmptyArray(getStringColumn(0))) {
|
4077
|
+
removeColumn(0);
|
4078
|
+
}
|
4079
|
+
|
4080
|
+
// remove empty rows (starting from the end)
|
4081
|
+
int lastRow = lastRowIndex();
|
4082
|
+
//while (isEmptyRow(lastRow) && lastRow >= 0) {
|
4083
|
+
while (isEmptyArray(getStringRow(lastRow)) && lastRow >= 0) {
|
4084
|
+
lastRow--;
|
4085
|
+
}
|
4086
|
+
setRowCount(lastRow + 1);
|
4087
|
+
|
4088
|
+
while (getRowCount() > 0 && isEmptyArray(getStringRow(0))) {
|
4089
|
+
removeRow(0);
|
4090
|
+
}
|
4091
|
+
}
|
4092
|
+
|
4093
|
+
|
4094
|
+
protected boolean isEmptyArray(String[] contents) {
|
4095
|
+
for (String entry : contents) {
|
4096
|
+
if (entry != null && entry.length() > 0) {
|
4097
|
+
return false;
|
4098
|
+
}
|
4099
|
+
}
|
4100
|
+
return true;
|
4101
|
+
}
|
4102
|
+
|
4103
|
+
|
4104
|
+
/*
|
4105
|
+
protected boolean isEmptyColumn(int column) {
|
4106
|
+
String[] contents = getStringColumn(column);
|
4107
|
+
for (String entry : contents) {
|
4108
|
+
if (entry != null && entry.length() > 0) {
|
4109
|
+
return false;
|
4110
|
+
}
|
4111
|
+
}
|
4112
|
+
return true;
|
4113
|
+
}
|
4114
|
+
|
4115
|
+
|
4116
|
+
protected boolean isEmptyRow(int row) {
|
4117
|
+
String[] contents = getStringRow(row);
|
4118
|
+
for (String entry : contents) {
|
4119
|
+
if (entry != null && entry.length() > 0) {
|
4120
|
+
return false;
|
4121
|
+
}
|
4122
|
+
}
|
4123
|
+
return true;
|
4124
|
+
}
|
4125
|
+
*/
|
4126
|
+
|
4127
|
+
|
4128
|
+
/**
|
4129
|
+
* @param column ID number of the column to trim
|
4130
|
+
*/
|
4131
|
+
public void trim(int column) {
|
4132
|
+
if (columnTypes[column] == STRING) {
|
4133
|
+
String[] stringData = (String[]) columns[column];
|
4134
|
+
for (int row = 0; row < rowCount; row++) {
|
4135
|
+
if (stringData[row] != null) {
|
4136
|
+
stringData[row] = PApplet.trim(stringData[row]);
|
4137
|
+
}
|
4138
|
+
}
|
4139
|
+
}
|
4140
|
+
}
|
4141
|
+
|
4142
|
+
/**
|
4143
|
+
* @param columnName title of the column to trim
|
4144
|
+
*/
|
4145
|
+
public void trim(String columnName) {
|
4146
|
+
trim(getColumnIndex(columnName));
|
4147
|
+
}
|
4148
|
+
|
4149
|
+
|
4150
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4151
|
+
|
4152
|
+
|
4153
|
+
/** Make sure this is a legit column, and if not, expand the table. */
|
4154
|
+
protected void ensureColumn(int col) {
|
4155
|
+
if (col >= columns.length) {
|
4156
|
+
setColumnCount(col + 1);
|
4157
|
+
}
|
4158
|
+
}
|
4159
|
+
|
4160
|
+
|
4161
|
+
/** Make sure this is a legit row, and if not, expand the table. */
|
4162
|
+
protected void ensureRow(int row) {
|
4163
|
+
if (row >= rowCount) {
|
4164
|
+
setRowCount(row + 1);
|
4165
|
+
}
|
4166
|
+
}
|
4167
|
+
|
4168
|
+
|
4169
|
+
/** Make sure this is a legit row and column. If not, expand the table. */
|
4170
|
+
protected void ensureBounds(int row, int col) {
|
4171
|
+
ensureRow(row);
|
4172
|
+
ensureColumn(col);
|
4173
|
+
}
|
4174
|
+
|
4175
|
+
|
4176
|
+
/** Throw an error if this row doesn't exist. */
|
4177
|
+
protected void checkRow(int row) {
|
4178
|
+
if (row < 0 || row >= rowCount) {
|
4179
|
+
throw new ArrayIndexOutOfBoundsException("Row " + row + " does not exist.");
|
4180
|
+
}
|
4181
|
+
}
|
4182
|
+
|
4183
|
+
|
4184
|
+
/** Throw an error if this column doesn't exist. */
|
4185
|
+
protected void checkColumn(int column) {
|
4186
|
+
if (column < 0 || column >= columns.length) {
|
4187
|
+
throw new ArrayIndexOutOfBoundsException("Column " + column + " does not exist.");
|
4188
|
+
}
|
4189
|
+
}
|
4190
|
+
|
4191
|
+
|
4192
|
+
/** Throw an error if this entry is out of bounds. */
|
4193
|
+
protected void checkBounds(int row, int column) {
|
4194
|
+
checkRow(row);
|
4195
|
+
checkColumn(column);
|
4196
|
+
}
|
4197
|
+
|
4198
|
+
|
4199
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4200
|
+
|
4201
|
+
|
4202
|
+
static class HashMapBlows {
|
4203
|
+
HashMap<String,Integer> dataToIndex = new HashMap<>();
|
4204
|
+
ArrayList<String> indexToData = new ArrayList<>();
|
4205
|
+
|
4206
|
+
HashMapBlows() { }
|
4207
|
+
|
4208
|
+
HashMapBlows(DataInputStream input) throws IOException {
|
4209
|
+
read(input);
|
4210
|
+
}
|
4211
|
+
|
4212
|
+
/** gets the index, and creates one if it doesn't already exist. */
|
4213
|
+
int index(String key) {
|
4214
|
+
Integer value = dataToIndex.get(key);
|
4215
|
+
if (value != null) {
|
4216
|
+
return value;
|
4217
|
+
}
|
4218
|
+
|
4219
|
+
int v = dataToIndex.size();
|
4220
|
+
dataToIndex.put(key, v);
|
4221
|
+
indexToData.add(key);
|
4222
|
+
return v;
|
4223
|
+
}
|
4224
|
+
|
4225
|
+
String key(int index) {
|
4226
|
+
return indexToData.get(index);
|
4227
|
+
}
|
4228
|
+
|
4229
|
+
boolean hasCategory(int index) {
|
4230
|
+
return index < size() && indexToData.get(index) != null;
|
4231
|
+
}
|
4232
|
+
|
4233
|
+
void setCategory(int index, String name) {
|
4234
|
+
while (indexToData.size() <= index) {
|
4235
|
+
indexToData.add(null);
|
4236
|
+
}
|
4237
|
+
indexToData.set(index, name);
|
4238
|
+
dataToIndex.put(name, index);
|
4239
|
+
}
|
4240
|
+
|
4241
|
+
int size() {
|
4242
|
+
return dataToIndex.size();
|
4243
|
+
}
|
4244
|
+
|
4245
|
+
void write(DataOutputStream output) throws IOException {
|
4246
|
+
output.writeInt(size());
|
4247
|
+
for (String str : indexToData) {
|
4248
|
+
output.writeUTF(str);
|
4249
|
+
}
|
4250
|
+
}
|
4251
|
+
|
4252
|
+
private void writeln(PrintWriter writer) throws IOException {
|
4253
|
+
for (String str : indexToData) {
|
4254
|
+
writer.println(str);
|
4255
|
+
}
|
4256
|
+
writer.flush();
|
4257
|
+
writer.close();
|
4258
|
+
}
|
4259
|
+
|
4260
|
+
void read(DataInputStream input) throws IOException {
|
4261
|
+
int count = input.readInt();
|
4262
|
+
//System.out.println("found " + count + " entries in category map");
|
4263
|
+
dataToIndex = new HashMap<>(count);
|
4264
|
+
for (int i = 0; i < count; i++) {
|
4265
|
+
String str = input.readUTF();
|
4266
|
+
//System.out.println(i + " " + str);
|
4267
|
+
dataToIndex.put(str, i);
|
4268
|
+
indexToData.add(str);
|
4269
|
+
}
|
4270
|
+
}
|
4271
|
+
}
|
4272
|
+
|
4273
|
+
|
4274
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4275
|
+
|
4276
|
+
|
4277
|
+
// class HashMapSucks extends HashMap<String,Integer> {
|
4278
|
+
//
|
4279
|
+
// void increment(String what) {
|
4280
|
+
// Integer value = get(what);
|
4281
|
+
// if (value == null) {
|
4282
|
+
// put(what, 1);
|
4283
|
+
// } else {
|
4284
|
+
// put(what, value + 1);
|
4285
|
+
// }
|
4286
|
+
// }
|
4287
|
+
//
|
4288
|
+
// void check(String what) {
|
4289
|
+
// if (get(what) == null) {
|
4290
|
+
// put(what, 0);
|
4291
|
+
// }
|
4292
|
+
// }
|
4293
|
+
// }
|
4294
|
+
|
4295
|
+
|
4296
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4297
|
+
|
4298
|
+
/**
|
4299
|
+
* Sorts (orders) a table based on the values in a column.
|
4300
|
+
*
|
4301
|
+
* @webref table:method
|
4302
|
+
* @brief Orders a table based on the values in a column
|
4303
|
+
* @param columnName the name of the column to sort
|
4304
|
+
* @see Table#trim()
|
4305
|
+
*/
|
4306
|
+
public void sort(String columnName) {
|
4307
|
+
sort(getColumnIndex(columnName), false);
|
4308
|
+
}
|
4309
|
+
|
4310
|
+
/**
|
4311
|
+
* @param column the column ID, e.g. 0, 1, 2
|
4312
|
+
*/
|
4313
|
+
public void sort(int column) {
|
4314
|
+
sort(column, false);
|
4315
|
+
}
|
4316
|
+
|
4317
|
+
|
4318
|
+
public void sortReverse(String columnName) {
|
4319
|
+
sort(getColumnIndex(columnName), true);
|
4320
|
+
}
|
4321
|
+
|
4322
|
+
|
4323
|
+
public void sortReverse(int column) {
|
4324
|
+
sort(column, true);
|
4325
|
+
}
|
4326
|
+
|
4327
|
+
|
4328
|
+
protected void sort(final int column, final boolean reverse) {
|
4329
|
+
final int[] order = IntList.fromRange(getRowCount()).array();
|
4330
|
+
Sort s = new Sort() {
|
4331
|
+
|
4332
|
+
@Override
|
4333
|
+
public int size() {
|
4334
|
+
return getRowCount();
|
4335
|
+
}
|
4336
|
+
|
4337
|
+
@Override
|
4338
|
+
public int compare(int index1, int index2) {
|
4339
|
+
int a = reverse ? order[index2] : order[index1];
|
4340
|
+
int b = reverse ? order[index1] : order[index2];
|
4341
|
+
|
4342
|
+
switch (getColumnType(column)) {
|
4343
|
+
case INT:
|
4344
|
+
return getInt(a, column) - getInt(b, column);
|
4345
|
+
case LONG:
|
4346
|
+
long diffl = getLong(a, column) - getLong(b, column);
|
4347
|
+
return diffl == 0 ? 0 : (diffl < 0 ? -1 : 1);
|
4348
|
+
case FLOAT:
|
4349
|
+
float difff = getFloat(a, column) - getFloat(b, column);
|
4350
|
+
return difff == 0 ? 0 : (difff < 0 ? -1 : 1);
|
4351
|
+
case DOUBLE:
|
4352
|
+
double diffd = getDouble(a, column) - getDouble(b, column);
|
4353
|
+
return diffd == 0 ? 0 : (diffd < 0 ? -1 : 1);
|
4354
|
+
case STRING:
|
4355
|
+
String string1 = getString(a, column);
|
4356
|
+
if (string1 == null) {
|
4357
|
+
string1 = ""; // avoid NPE when cells are left empty
|
4358
|
+
}
|
4359
|
+
String string2 = getString(b, column);
|
4360
|
+
if (string2 == null) {
|
4361
|
+
string2 = "";
|
4362
|
+
}
|
4363
|
+
return string1.compareToIgnoreCase(string2);
|
4364
|
+
case CATEGORY:
|
4365
|
+
return getInt(a, column) - getInt(b, column);
|
4366
|
+
default:
|
4367
|
+
throw new IllegalArgumentException("Invalid column type: " + getColumnType(column));
|
4368
|
+
}
|
4369
|
+
}
|
4370
|
+
|
4371
|
+
@Override
|
4372
|
+
public void swap(int a, int b) {
|
4373
|
+
int temp = order[a];
|
4374
|
+
order[a] = order[b];
|
4375
|
+
order[b] = temp;
|
4376
|
+
}
|
4377
|
+
|
4378
|
+
};
|
4379
|
+
s.run();
|
4380
|
+
|
4381
|
+
//Object[] newColumns = new Object[getColumnCount()];
|
4382
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
4383
|
+
switch (getColumnType(col)) {
|
4384
|
+
case INT:
|
4385
|
+
case CATEGORY:
|
4386
|
+
int[] oldInt = (int[]) columns[col];
|
4387
|
+
int[] newInt = new int[rowCount];
|
4388
|
+
for (int row = 0; row < getRowCount(); row++) {
|
4389
|
+
newInt[row] = oldInt[order[row]];
|
4390
|
+
}
|
4391
|
+
columns[col] = newInt;
|
4392
|
+
break;
|
4393
|
+
case LONG:
|
4394
|
+
long[] oldLong = (long[]) columns[col];
|
4395
|
+
long[] newLong = new long[rowCount];
|
4396
|
+
for (int row = 0; row < getRowCount(); row++) {
|
4397
|
+
newLong[row] = oldLong[order[row]];
|
4398
|
+
}
|
4399
|
+
columns[col] = newLong;
|
4400
|
+
break;
|
4401
|
+
case FLOAT:
|
4402
|
+
float[] oldFloat = (float[]) columns[col];
|
4403
|
+
float[] newFloat = new float[rowCount];
|
4404
|
+
for (int row = 0; row < getRowCount(); row++) {
|
4405
|
+
newFloat[row] = oldFloat[order[row]];
|
4406
|
+
}
|
4407
|
+
columns[col] = newFloat;
|
4408
|
+
break;
|
4409
|
+
case DOUBLE:
|
4410
|
+
double[] oldDouble = (double[]) columns[col];
|
4411
|
+
double[] newDouble = new double[rowCount];
|
4412
|
+
for (int row = 0; row < getRowCount(); row++) {
|
4413
|
+
newDouble[row] = oldDouble[order[row]];
|
4414
|
+
}
|
4415
|
+
columns[col] = newDouble;
|
4416
|
+
break;
|
4417
|
+
case STRING:
|
4418
|
+
String[] oldString = (String[]) columns[col];
|
4419
|
+
String[] newString = new String[rowCount];
|
4420
|
+
for (int row = 0; row < getRowCount(); row++) {
|
4421
|
+
newString[row] = oldString[order[row]];
|
4422
|
+
}
|
4423
|
+
columns[col] = newString;
|
4424
|
+
break;
|
4425
|
+
}
|
4426
|
+
}
|
4427
|
+
}
|
4428
|
+
|
4429
|
+
|
4430
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4431
|
+
|
4432
|
+
|
4433
|
+
public String[] getUnique(String columnName) {
|
4434
|
+
return getUnique(getColumnIndex(columnName));
|
4435
|
+
}
|
4436
|
+
|
4437
|
+
|
4438
|
+
public String[] getUnique(int column) {
|
4439
|
+
StringList list = new StringList(getStringColumn(column));
|
4440
|
+
return list.getUnique();
|
4441
|
+
}
|
4442
|
+
|
4443
|
+
|
4444
|
+
public IntDict getTally(String columnName) {
|
4445
|
+
return getTally(getColumnIndex(columnName));
|
4446
|
+
}
|
4447
|
+
|
4448
|
+
|
4449
|
+
public IntDict getTally(int column) {
|
4450
|
+
StringList list = new StringList(getStringColumn(column));
|
4451
|
+
return list.getTally();
|
4452
|
+
}
|
4453
|
+
|
4454
|
+
|
4455
|
+
public IntDict getOrder(String columnName) {
|
4456
|
+
return getOrder(getColumnIndex(columnName));
|
4457
|
+
}
|
4458
|
+
|
4459
|
+
|
4460
|
+
public IntDict getOrder(int column) {
|
4461
|
+
StringList list = new StringList(getStringColumn(column));
|
4462
|
+
return list.getOrder();
|
4463
|
+
}
|
4464
|
+
|
4465
|
+
|
4466
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4467
|
+
|
4468
|
+
|
4469
|
+
public IntList getIntList(String columnName) {
|
4470
|
+
return new IntList(getIntColumn(columnName));
|
4471
|
+
}
|
4472
|
+
|
4473
|
+
|
4474
|
+
public IntList getIntList(int column) {
|
4475
|
+
return new IntList(getIntColumn(column));
|
4476
|
+
}
|
4477
|
+
|
4478
|
+
|
4479
|
+
public FloatList getFloatList(String columnName) {
|
4480
|
+
return new FloatList(getFloatColumn(columnName));
|
4481
|
+
}
|
4482
|
+
|
4483
|
+
|
4484
|
+
public FloatList getFloatList(int column) {
|
4485
|
+
return new FloatList(getFloatColumn(column));
|
4486
|
+
}
|
4487
|
+
|
4488
|
+
|
4489
|
+
public StringList getStringList(String columnName) {
|
4490
|
+
return new StringList(getStringColumn(columnName));
|
4491
|
+
}
|
4492
|
+
|
4493
|
+
|
4494
|
+
public StringList getStringList(int column) {
|
4495
|
+
return new StringList(getStringColumn(column));
|
4496
|
+
}
|
4497
|
+
|
4498
|
+
|
4499
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4500
|
+
|
4501
|
+
|
4502
|
+
public IntDict getIntDict(String keyColumnName, String valueColumnName) {
|
4503
|
+
return new IntDict(getStringColumn(keyColumnName),
|
4504
|
+
getIntColumn(valueColumnName));
|
4505
|
+
}
|
4506
|
+
|
4507
|
+
|
4508
|
+
public IntDict getIntDict(int keyColumn, int valueColumn) {
|
4509
|
+
return new IntDict(getStringColumn(keyColumn),
|
4510
|
+
getIntColumn(valueColumn));
|
4511
|
+
}
|
4512
|
+
|
4513
|
+
|
4514
|
+
public FloatDict getFloatDict(String keyColumnName, String valueColumnName) {
|
4515
|
+
return new FloatDict(getStringColumn(keyColumnName),
|
4516
|
+
getFloatColumn(valueColumnName));
|
4517
|
+
}
|
4518
|
+
|
4519
|
+
|
4520
|
+
public FloatDict getFloatDict(int keyColumn, int valueColumn) {
|
4521
|
+
return new FloatDict(getStringColumn(keyColumn),
|
4522
|
+
getFloatColumn(valueColumn));
|
4523
|
+
}
|
4524
|
+
|
4525
|
+
|
4526
|
+
public StringDict getStringDict(String keyColumnName, String valueColumnName) {
|
4527
|
+
return new StringDict(getStringColumn(keyColumnName),
|
4528
|
+
getStringColumn(valueColumnName));
|
4529
|
+
}
|
4530
|
+
|
4531
|
+
|
4532
|
+
public StringDict getStringDict(int keyColumn, int valueColumn) {
|
4533
|
+
return new StringDict(getStringColumn(keyColumn),
|
4534
|
+
getStringColumn(valueColumn));
|
4535
|
+
}
|
4536
|
+
|
4537
|
+
|
4538
|
+
public Map<String, TableRow> getRowMap(String columnName) {
|
4539
|
+
int col = getColumnIndex(columnName);
|
4540
|
+
return (col == -1) ? null : getRowMap(col);
|
4541
|
+
}
|
4542
|
+
|
4543
|
+
|
4544
|
+
/**
|
4545
|
+
* Return a mapping that connects the entry from a column back to the row
|
4546
|
+
* from which it came. For instance:
|
4547
|
+
* <pre>
|
4548
|
+
* Table t = loadTable("country-data.tsv", "header");
|
4549
|
+
* // use the contents of the 'country' column to index the table
|
4550
|
+
* Map<String, TableRow> lookup = t.getRowMap("country");
|
4551
|
+
* // get the row that has "us" in the "country" column:
|
4552
|
+
* TableRow usRow = lookup.get("us");
|
4553
|
+
* // get an entry from the 'population' column
|
4554
|
+
* int population = usRow.getInt("population");
|
4555
|
+
* </pre>
|
4556
|
+
*/
|
4557
|
+
public Map<String, TableRow> getRowMap(int column) {
|
4558
|
+
Map<String, TableRow> outgoing = new HashMap<>();
|
4559
|
+
for (int row = 0; row < getRowCount(); row++) {
|
4560
|
+
String id = getString(row, column);
|
4561
|
+
outgoing.put(id, new RowPointer(this, row));
|
4562
|
+
}
|
4563
|
+
// for (TableRow row : rows()) {
|
4564
|
+
// String id = row.getString(column);
|
4565
|
+
// outgoing.put(id, row);
|
4566
|
+
// }
|
4567
|
+
return outgoing;
|
4568
|
+
}
|
4569
|
+
|
4570
|
+
|
4571
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4572
|
+
|
4573
|
+
|
4574
|
+
// /**
|
4575
|
+
// * Return an object that maps the String values in one column back to the
|
4576
|
+
// * row from which they came. For instance, if the "name" of each row is
|
4577
|
+
// * found in the first column, getColumnRowLookup(0) would return an object
|
4578
|
+
// * that would map each name back to its row.
|
4579
|
+
// */
|
4580
|
+
// protected HashMap<String,Integer> getRowLookup(int col) {
|
4581
|
+
// HashMap<String,Integer> outgoing = new HashMap<String, Integer>();
|
4582
|
+
// for (int row = 0; row < getRowCount(); row++) {
|
4583
|
+
// outgoing.put(getString(row, col), row);
|
4584
|
+
// }
|
4585
|
+
// return outgoing;
|
4586
|
+
// }
|
4587
|
+
|
4588
|
+
|
4589
|
+
// incomplete, basically this is silly to write all this repetitive code when
|
4590
|
+
// it can be implemented in ~3 lines of code...
|
4591
|
+
// /**
|
4592
|
+
// * Return an object that maps the data from one column to the data of found
|
4593
|
+
// * in another column.
|
4594
|
+
// */
|
4595
|
+
// public HashMap<?,?> getLookup(int col1, int col2) {
|
4596
|
+
// HashMap outgoing = null;
|
4597
|
+
//
|
4598
|
+
// switch (columnTypes[col1]) {
|
4599
|
+
// case INT: {
|
4600
|
+
// if (columnTypes[col2] == INT) {
|
4601
|
+
// outgoing = new HashMap<Integer, Integer>();
|
4602
|
+
// for (int row = 0; row < getRowCount(); row++) {
|
4603
|
+
// outgoing.put(getInt(row, col1), getInt(row, col2));
|
4604
|
+
// }
|
4605
|
+
// } else if (columnTypes[col2] == LONG) {
|
4606
|
+
// outgoing = new HashMap<Integer, Long>();
|
4607
|
+
// for (int row = 0; row < getRowCount(); row++) {
|
4608
|
+
// outgoing.put(getInt(row, col1), getLong(row, col2));
|
4609
|
+
// }
|
4610
|
+
// } else if (columnTypes[col2] == FLOAT) {
|
4611
|
+
// outgoing = new HashMap<Integer, Float>();
|
4612
|
+
// for (int row = 0; row < getRowCount(); row++) {
|
4613
|
+
// outgoing.put(getInt(row, col1), getFloat(row, col2));
|
4614
|
+
// }
|
4615
|
+
// } else if (columnTypes[col2] == DOUBLE) {
|
4616
|
+
// outgoing = new HashMap<Integer, Double>();
|
4617
|
+
// for (int row = 0; row < getRowCount(); row++) {
|
4618
|
+
// outgoing.put(getInt(row, col1), getDouble(row, col2));
|
4619
|
+
// }
|
4620
|
+
// } else if (columnTypes[col2] == STRING) {
|
4621
|
+
// outgoing = new HashMap<Integer, String>();
|
4622
|
+
// for (int row = 0; row < getRowCount(); row++) {
|
4623
|
+
// outgoing.put(getInt(row, col1), get(row, col2));
|
4624
|
+
// }
|
4625
|
+
// }
|
4626
|
+
// break;
|
4627
|
+
// }
|
4628
|
+
// }
|
4629
|
+
// return outgoing;
|
4630
|
+
// }
|
4631
|
+
|
4632
|
+
|
4633
|
+
// public StringIntPairs getColumnRowLookup(int col) {
|
4634
|
+
// StringIntPairs sc = new StringIntPairs();
|
4635
|
+
// String[] column = getStringColumn(col);
|
4636
|
+
// for (int i = 0; i < column.length; i++) {
|
4637
|
+
// sc.set(column[i], i);
|
4638
|
+
// }
|
4639
|
+
// return sc;
|
4640
|
+
// }
|
4641
|
+
|
4642
|
+
|
4643
|
+
// public String[] getUniqueEntries(int column) {
|
4644
|
+
//// HashMap indices = new HashMap();
|
4645
|
+
//// for (int row = 0; row < rowCount; row++) {
|
4646
|
+
//// indices.put(data[row][column], this); // 'this' is a dummy
|
4647
|
+
//// }
|
4648
|
+
// StringIntPairs sc = getStringCount(column);
|
4649
|
+
// return sc.keys();
|
4650
|
+
// }
|
4651
|
+
//
|
4652
|
+
//
|
4653
|
+
// public StringIntPairs getStringCount(String columnName) {
|
4654
|
+
// return getStringCount(getColumnIndex(columnName));
|
4655
|
+
// }
|
4656
|
+
//
|
4657
|
+
//
|
4658
|
+
// public StringIntPairs getStringCount(int column) {
|
4659
|
+
// StringIntPairs outgoing = new StringIntPairs();
|
4660
|
+
// for (int row = 0; row < rowCount; row++) {
|
4661
|
+
// String entry = data[row][column];
|
4662
|
+
// if (entry != null) {
|
4663
|
+
// outgoing.increment(entry);
|
4664
|
+
// }
|
4665
|
+
// }
|
4666
|
+
// return outgoing;
|
4667
|
+
// }
|
4668
|
+
//
|
4669
|
+
//
|
4670
|
+
// /**
|
4671
|
+
// * Return an object that maps the String values in one column back to the
|
4672
|
+
// * row from which they came. For instance, if the "name" of each row is
|
4673
|
+
// * found in the first column, getColumnRowLookup(0) would return an object
|
4674
|
+
// * that would map each name back to its row.
|
4675
|
+
// */
|
4676
|
+
// public StringIntPairs getColumnRowLookup(int col) {
|
4677
|
+
// StringIntPairs sc = new StringIntPairs();
|
4678
|
+
// String[] column = getStringColumn(col);
|
4679
|
+
// for (int i = 0; i < column.length; i++) {
|
4680
|
+
// sc.set(column[i], i);
|
4681
|
+
// }
|
4682
|
+
// return sc;
|
4683
|
+
// }
|
4684
|
+
|
4685
|
+
|
4686
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4687
|
+
|
4688
|
+
|
4689
|
+
// TODO naming/whether to include
|
4690
|
+
protected Table createSubset(int[] rowSubset) {
|
4691
|
+
Table newbie = new Table();
|
4692
|
+
newbie.setColumnTitles(columnTitles); // also sets columns.length
|
4693
|
+
newbie.columnTypes = columnTypes;
|
4694
|
+
newbie.setRowCount(rowSubset.length);
|
4695
|
+
|
4696
|
+
for (int i = 0; i < rowSubset.length; i++) {
|
4697
|
+
int row = rowSubset[i];
|
4698
|
+
for (int col = 0; col < columns.length; col++) {
|
4699
|
+
switch (columnTypes[col]) {
|
4700
|
+
case STRING: newbie.setString(i, col, getString(row, col)); break;
|
4701
|
+
case INT: newbie.setInt(i, col, getInt(row, col)); break;
|
4702
|
+
case LONG: newbie.setLong(i, col, getLong(row, col)); break;
|
4703
|
+
case FLOAT: newbie.setFloat(i, col, getFloat(row, col)); break;
|
4704
|
+
case DOUBLE: newbie.setDouble(i, col, getDouble(row, col)); break;
|
4705
|
+
}
|
4706
|
+
}
|
4707
|
+
}
|
4708
|
+
return newbie;
|
4709
|
+
}
|
4710
|
+
|
4711
|
+
|
4712
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4713
|
+
|
4714
|
+
|
4715
|
+
/**
|
4716
|
+
* Searches the entire table for float values.
|
4717
|
+
* Returns missing float (Float.NaN by default) if no valid numbers found.
|
4718
|
+
*/
|
4719
|
+
protected float getMaxFloat() {
|
4720
|
+
boolean found = false;
|
4721
|
+
float max = PConstants.MIN_FLOAT;
|
4722
|
+
for (int row = 0; row < getRowCount(); row++) {
|
4723
|
+
for (int col = 0; col < getColumnCount(); col++) {
|
4724
|
+
float value = getFloat(row, col);
|
4725
|
+
if (!Float.isNaN(value)) { // TODO no, this should be comparing to the missing value
|
4726
|
+
if (!found) {
|
4727
|
+
max = value;
|
4728
|
+
found = true;
|
4729
|
+
} else if (value > max) {
|
4730
|
+
max = value;
|
4731
|
+
}
|
4732
|
+
}
|
4733
|
+
}
|
4734
|
+
}
|
4735
|
+
return found ? max : missingFloat;
|
4736
|
+
}
|
4737
|
+
|
4738
|
+
|
4739
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
4740
|
+
|
4741
|
+
|
4742
|
+
// converts a TSV or CSV file to binary.. do not use
|
4743
|
+
protected void convertBasic(BufferedReader reader, boolean tsv,
|
4744
|
+
File outputFile) throws IOException {
|
4745
|
+
FileOutputStream fos = new FileOutputStream(outputFile);
|
4746
|
+
BufferedOutputStream bos = new BufferedOutputStream(fos, 16384);
|
4747
|
+
DataOutputStream output = new DataOutputStream(bos);
|
4748
|
+
output.writeInt(0); // come back for row count
|
4749
|
+
output.writeInt(getColumnCount());
|
4750
|
+
if (columnTitles != null) {
|
4751
|
+
output.writeBoolean(true);
|
4752
|
+
for (String title : columnTitles) {
|
4753
|
+
output.writeUTF(title);
|
4754
|
+
}
|
4755
|
+
} else {
|
4756
|
+
output.writeBoolean(false);
|
4757
|
+
}
|
4758
|
+
for (int type : columnTypes) {
|
4759
|
+
output.writeInt(type);
|
4760
|
+
}
|
4761
|
+
|
4762
|
+
String line = null;
|
4763
|
+
//setRowCount(1);
|
4764
|
+
int prev = -1;
|
4765
|
+
int row = 0;
|
4766
|
+
while ((line = reader.readLine()) != null) {
|
4767
|
+
convertRow(output, tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader));
|
4768
|
+
row++;
|
4769
|
+
|
4770
|
+
if (row % 10000 == 0) {
|
4771
|
+
if (row < rowCount) {
|
4772
|
+
int pct = (100 * row) / rowCount;
|
4773
|
+
if (pct != prev) {
|
4774
|
+
System.out.println(pct + "%");
|
4775
|
+
prev = pct;
|
4776
|
+
}
|
4777
|
+
}
|
4778
|
+
// try {
|
4779
|
+
// Thread.sleep(5);
|
4780
|
+
// } catch (InterruptedException e) {
|
4781
|
+
// e.printStackTrace();
|
4782
|
+
// }
|
4783
|
+
}
|
4784
|
+
}
|
4785
|
+
// shorten or lengthen based on what's left
|
4786
|
+
// if (row != getRowCount()) {
|
4787
|
+
// setRowCount(row);
|
4788
|
+
// }
|
4789
|
+
|
4790
|
+
// has to come afterwards, since these tables get built out during the conversion
|
4791
|
+
int col = 0;
|
4792
|
+
for (HashMapBlows hmb : columnCategories) {
|
4793
|
+
if (hmb == null) {
|
4794
|
+
output.writeInt(0);
|
4795
|
+
} else {
|
4796
|
+
hmb.write(output);
|
4797
|
+
hmb.writeln(PApplet.createWriter(new File(columnTitles[col] + ".categories")));
|
4798
|
+
// output.writeInt(hmb.size());
|
4799
|
+
// for (Map.Entry<String,Integer> e : hmb.entrySet()) {
|
4800
|
+
// output.writeUTF(e.getKey());
|
4801
|
+
// output.writeInt(e.getValue());
|
4802
|
+
// }
|
4803
|
+
}
|
4804
|
+
col++;
|
4805
|
+
}
|
4806
|
+
|
4807
|
+
output.flush();
|
4808
|
+
output.close();
|
4809
|
+
|
4810
|
+
// come back and write the row count
|
4811
|
+
RandomAccessFile raf = new RandomAccessFile(outputFile, "rw");
|
4812
|
+
raf.writeInt(rowCount);
|
4813
|
+
raf.close();
|
4814
|
+
}
|
4815
|
+
|
4816
|
+
|
4817
|
+
protected void convertRow(DataOutputStream output, String[] pieces) throws IOException {
|
4818
|
+
if (pieces.length > getColumnCount()) {
|
4819
|
+
throw new IllegalArgumentException("Row with too many columns: " +
|
4820
|
+
PApplet.join(pieces, ","));
|
4821
|
+
}
|
4822
|
+
// pieces.length may be less than columns.length, so loop over pieces
|
4823
|
+
for (int col = 0; col < pieces.length; col++) {
|
4824
|
+
switch (columnTypes[col]) {
|
4825
|
+
case STRING:
|
4826
|
+
output.writeUTF(pieces[col]);
|
4827
|
+
break;
|
4828
|
+
case INT:
|
4829
|
+
output.writeInt(PApplet.parseInt(pieces[col], missingInt));
|
4830
|
+
break;
|
4831
|
+
case LONG:
|
4832
|
+
try {
|
4833
|
+
output.writeLong(Long.parseLong(pieces[col]));
|
4834
|
+
} catch (NumberFormatException nfe) {
|
4835
|
+
output.writeLong(missingLong);
|
4836
|
+
}
|
4837
|
+
break;
|
4838
|
+
case FLOAT:
|
4839
|
+
output.writeFloat(PApplet.parseFloat(pieces[col], missingFloat));
|
4840
|
+
break;
|
4841
|
+
case DOUBLE:
|
4842
|
+
try {
|
4843
|
+
output.writeDouble(Double.parseDouble(pieces[col]));
|
4844
|
+
} catch (NumberFormatException nfe) {
|
4845
|
+
output.writeDouble(missingDouble);
|
4846
|
+
}
|
4847
|
+
break;
|
4848
|
+
case CATEGORY:
|
4849
|
+
String peace = pieces[col];
|
4850
|
+
if (peace.equals(missingString)) {
|
4851
|
+
output.writeInt(missingCategory);
|
4852
|
+
} else {
|
4853
|
+
output.writeInt(columnCategories[col].index(peace));
|
4854
|
+
}
|
4855
|
+
break;
|
4856
|
+
}
|
4857
|
+
}
|
4858
|
+
for (int col = pieces.length; col < getColumnCount(); col++) {
|
4859
|
+
switch (columnTypes[col]) {
|
4860
|
+
case STRING:
|
4861
|
+
output.writeUTF("");
|
4862
|
+
break;
|
4863
|
+
case INT:
|
4864
|
+
output.writeInt(missingInt);
|
4865
|
+
break;
|
4866
|
+
case LONG:
|
4867
|
+
output.writeLong(missingLong);
|
4868
|
+
break;
|
4869
|
+
case FLOAT:
|
4870
|
+
output.writeFloat(missingFloat);
|
4871
|
+
break;
|
4872
|
+
case DOUBLE:
|
4873
|
+
output.writeDouble(missingDouble);
|
4874
|
+
break;
|
4875
|
+
case CATEGORY:
|
4876
|
+
output.writeInt(missingCategory);
|
4877
|
+
break;
|
4878
|
+
|
4879
|
+
}
|
4880
|
+
}
|
4881
|
+
}
|
4882
|
+
|
4883
|
+
|
4884
|
+
/*
|
4885
|
+
private void convertRowCol(DataOutputStream output, int row, int col, String piece) {
|
4886
|
+
switch (columnTypes[col]) {
|
4887
|
+
case STRING:
|
4888
|
+
String[] stringData = (String[]) columns[col];
|
4889
|
+
stringData[row] = piece;
|
4890
|
+
break;
|
4891
|
+
case INT:
|
4892
|
+
int[] intData = (int[]) columns[col];
|
4893
|
+
intData[row] = PApplet.parseInt(piece, missingInt);
|
4894
|
+
break;
|
4895
|
+
case LONG:
|
4896
|
+
long[] longData = (long[]) columns[col];
|
4897
|
+
try {
|
4898
|
+
longData[row] = Long.parseLong(piece);
|
4899
|
+
} catch (NumberFormatException nfe) {
|
4900
|
+
longData[row] = missingLong;
|
4901
|
+
}
|
4902
|
+
break;
|
4903
|
+
case FLOAT:
|
4904
|
+
float[] floatData = (float[]) columns[col];
|
4905
|
+
floatData[row] = PApplet.parseFloat(piece, missingFloat);
|
4906
|
+
break;
|
4907
|
+
case DOUBLE:
|
4908
|
+
double[] doubleData = (double[]) columns[col];
|
4909
|
+
try {
|
4910
|
+
doubleData[row] = Double.parseDouble(piece);
|
4911
|
+
} catch (NumberFormatException nfe) {
|
4912
|
+
doubleData[row] = missingDouble;
|
4913
|
+
}
|
4914
|
+
break;
|
4915
|
+
default:
|
4916
|
+
throw new IllegalArgumentException("That's not a valid column type.");
|
4917
|
+
}
|
4918
|
+
}
|
4919
|
+
*/
|
4920
|
+
|
4921
|
+
|
4922
|
+
/** Make a copy of the current table */
|
4923
|
+
public Table copy() {
|
4924
|
+
return new Table(rows());
|
4925
|
+
}
|
4926
|
+
|
4927
|
+
|
4928
|
+
public void write(PrintWriter writer) {
|
4929
|
+
writeTSV(writer);
|
4930
|
+
}
|
4931
|
+
|
4932
|
+
|
4933
|
+
public void print() {
|
4934
|
+
writeTSV(new PrintWriter(System.out));
|
4935
|
+
}
|
4936
|
+
}
|