This commit is contained in:
benfry
2012-12-10 23:12:13 +00:00
parent 4c4fd98425
commit edab71ce46
13 changed files with 4916 additions and 0 deletions
+300
View File
@@ -0,0 +1,300 @@
package processing.data;
import java.io.*;
import java.util.HashMap;
import processing.core.PApplet;
/**
* A simple table class to use a String as a lookup for an float value.
*/
public class FloatHash {
/** Number of elements in the table */
public int count;
/**
* List of keys, available for sake of speed,
* but should be manipulated (consider it read-only).
*/
protected String[] keys;
/**
* List of values, available for sake of speed,
* but should be manipulated (consider it read-only).
*/
protected float[] values;
/** Internal implementation for faster lookups */
private HashMap<String, Integer> indices = new HashMap<String, Integer>();
public FloatHash() {
count = 0;
keys = new String[10];
values = new float[10];
}
public FloatHash(int length) {
count = 0;
keys = new String[length];
values = new float[length];
}
public FloatHash(PApplet parent, String filename) {
String[] lines = parent.loadStrings(filename);
keys = new String[lines.length];
values = new float[lines.length];
// boolean csv = (lines[0].indexOf('\t') == -1);
for (int i = 0; i < lines.length; i++) {
// String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t');
String[] pieces = PApplet.split(lines[i], '\t');
if (pieces.length == 2) {
keys[count] = pieces[0];
values[count] = PApplet.parseFloat(pieces[1]);
count++;
}
}
}
public int getCount() {
return count;
}
public String key(int index) {
return keys[index];
}
protected void crop() {
if (count != keys.length) {
keys = PApplet.subset(keys, 0, count);
values = PApplet.subset(values, 0, count);
}
}
/**
* Return the internal array being used to store the keys. Allocated but
* unused entries will be removed. This array should not be modified.
*/
public String[] keys() {
crop();
return keys;
}
/**
* Return a copy of the internal keys array. This array can be modified.
*/
public String[] keyArray() {
return keyArray(null);
}
public String[] keyArray(String[] outgoing) {
if (outgoing == null || outgoing.length != count) {
outgoing = new String[count];
}
System.arraycopy(keys, 0, outgoing, 0, count);
return outgoing;
}
public float value(int index) {
return values[index];
}
public float[] values() {
crop();
return values;
}
public int[] valueArray() {
int[] outgoing = new int[count];
System.arraycopy(values, 0, outgoing, 0, count);
return outgoing;
}
public float get(String what) {
int index = index(what);
if (index == -1) return 0;
return values[index];
}
public void set(String who, int amount) {
int index = index(who);
if (index == -1) {
create(who, amount);
} else {
values[index] = amount;
}
}
public void add(String who, int amount) {
int index = index(who);
if (index == -1) {
create(who, amount);
} else {
values[index] += amount;
}
}
public void increment(String who) {
int index = index(who);
if (index == -1) {
create(who, 1);
} else {
values[index]++;
}
}
public int index(String what) {
Integer found = indices.get(what);
return (found == null) ? -1 : found.intValue();
}
protected void create(String what, int much) {
if (count == keys.length) {
keys = PApplet.expand(keys);
// String ktemp[] = new String[count << 1];
// System.arraycopy(keys, 0, ktemp, 0, count);
// keys = ktemp;
values = PApplet.expand(values);
// float vtemp[] = new float[count << 1];
// System.arraycopy(values, 0, vtemp, 0, count);
// values = vtemp;
}
indices.put(what, new Integer(count));
keys[count] = what;
values[count] = much;
count++;
}
public void print() {
write(new PrintWriter(System.out));
}
public void write(PrintWriter writer) {
for (int i = 0; i < count; i++) {
writer.println(keys[i] + "\t" + values[i]);
}
writer.flush();
}
public void remove(String which) {
removeIndex(index(which));
}
public void removeIndex(int which) {
//System.out.println("index is " + which + " and " + keys[which]);
indices.remove(keys[which]);
for (int i = which; i < count-1; i++) {
keys[i] = keys[i+1];
values[i] = values[i+1];
indices.put(keys[i], i);
}
count--;
keys[count] = null;
values[count] = 0;
}
public void swap(int a, int b) {
String tkey = keys[a];
float tvalue = values[a];
keys[a] = keys[b];
values[a] = values[b];
keys[b] = tkey;
values[b] = tvalue;
indices.put(keys[a], new Integer(a));
indices.put(keys[b], new Integer(b));
}
public void sortKeys() {
Sort s = new Sort() {
@Override
public int size() {
return count;
}
@Override
public float compare(int a, int b) {
int result = keys[a].compareToIgnoreCase(keys[b]);
if (result != 0) {
return result;
}
return values[b] - values[a];
}
@Override
public void swap(int a, int b) {
FloatHash.this.swap(a, b);
}
};
s.run();
}
/**
* Sort by values in descending order (largest value will be at [0]).
*/
public void sortValues() {
sortValues(true, true);
}
public void sortValues(final boolean descending) {
sortValues(descending, true);
}
// ascending puts the largest value at the end
// descending puts the largest value at 0
public void sortValues(final boolean descending, final boolean tiebreaker) {
Sort s = new Sort() {
@Override
public int size() {
return count;
}
@Override
public float compare(int a, int b) {
float diff = values[b] - values[a];
if (tiebreaker) {
if (diff == 0) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
return descending ? diff : -diff;
}
@Override
public void swap(int a, int b) {
FloatHash.this.swap(a, b);
}
};
s.run();
}
}
+245
View File
@@ -0,0 +1,245 @@
package processing.data;
import processing.core.PApplet;
public class FloatList {
int count;
float[] data;
public FloatList() {
data = new float[10];
}
public FloatList(float[] list) {
count = list.length;
data = new float[count];
System.arraycopy(list, 0, data, 0, count);
}
public FloatList(String[] list) {
this(PApplet.parseFloat(list));
}
public int size() {
return count;
}
public float get(int index) {
return data[index];
}
public void set(int index, int what) {
if (index >= count) {
data = PApplet.expand(data, index+1);
}
data[index] = what;
}
public void append(int value) {
if (count == data.length) {
data = PApplet.expand(data);
}
data[count++] = value;
}
public void clear() {
count = 0;
}
public float calcMin() {
if (data.length == 0) {
return Float.NaN;
}
float m = Float.NaN;
for (int i = 0; i < data.length; i++) {
// find one good value to start
if (data[i] == data[i]) {
m = data[i];
// calculate the rest
for (int j = i+1; j < data.length; j++) {
float d = data[j];
if (!Float.isNaN(d) && (d < m)) {
m = data[j];
}
}
break;
}
}
return m;
}
public float calcMax() {
if (data.length == 0) {
return Float.NaN;
}
float m = Float.NaN;
for (int i = 0; i < data.length; i++) {
// find one good value to start
if (data[i] == data[i]) {
m = data[i];
// calculate the rest
for (int j = i+1; j < data.length; j++) {
float d = data[j];
if (!Float.isNaN(d) && (d > m)) {
m = data[j];
}
}
break;
}
}
return m;
}
public void removeNaN() {
int index = 0;
for (int i = 0; i < count; i++) {
if (data[i] == data[i]) {
if (i != index) {
data[index] = data[i];
}
index++;
}
}
count = index;
}
public void replaceNaN(float replacement) {
for (int i = 0; i < count; i++) {
if (data[i] != data[i]) {
data[i] = replacement;
}
}
}
public void add(float amt) {
for (int i = 0; i < count; i++) {
data[i] += amt;
}
}
public void sub(float amt) {
for (int i = 0; i < count; i++) {
data[i] -= amt;
}
}
public void mul(float amt) {
for (int i = 0; i < count; i++) {
data[i] *= amt;
}
}
public void div(float amt) {
for (int i = 0; i < count; i++) {
data[i] /= amt;
}
}
/*
static public void shuffle(int[] array) {
java.util.Random rng = new java.util.Random();
int n = array.length;
while (n > 1) {
int k = rng.nextInt(n);
n--;
int temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}
*/
public void crop() {
if (count != data.length) {
data = PApplet.subset(data, 0, count);
}
}
public float[] values() {
crop();
return data;
}
public int[] toIntArray() {
int[] outgoing = new int[count];
for (int i = 0; i < count; i++) {
outgoing[i] = (int) data[i];
}
return outgoing;
}
public long[] toLongArray() {
long[] outgoing = new long[count];
for (int i = 0; i < count; i++) {
outgoing[i] = (long) data[i];
}
return outgoing;
}
public float[] toFloatArray() {
float[] outgoing = new float[count];
System.arraycopy(data, 0, outgoing, 0, count);
return outgoing;
}
public double[] toDoubleArray() {
double[] outgoing = new double[count];
for (int i = 0; i < count; i++) {
outgoing[i] = data[i];
}
return outgoing;
}
public String[] toStringArray() {
String[] outgoing = new String[count];
for (int i = 0; i < count; i++) {
outgoing[i] = String.valueOf(data[i]);
}
return outgoing;
}
/**
* Create a new array with a copy of all the values.
* @return an array sized by the length of the list with each of the values.
*/
// public float[] toArray() {
// return toFloatArray();
// }
/**
* Copy as many values as possible into the specified array.
* @param array
*/
// public void toArray(float[] array) {
// System.arraycopy(data, 0, array, 0, Math.min(count, array.length));
// }
}
+314
View File
@@ -0,0 +1,314 @@
package processing.data;
import java.io.*;
import java.util.HashMap;
import processing.core.PApplet;
/**
* A simple table class to use a String as a lookup for an int value.
*/
public class IntHash {
/** Number of elements in the table */
protected int count;
/**
* List of keys, available for sake of speed,
* but should be manipulated (consider it read-only).
*/
protected String[] keys;
/**
* List of values, available for sake of speed,
* but should be manipulated (consider it read-only).
*/
protected int[] values;
/** Internal implementation for faster lookups */
private HashMap<String, Integer> indices = new HashMap<String, Integer>();
public IntHash() {
count = 0;
keys = new String[10];
values = new int[10];
}
public IntHash(int length) {
count = 0;
keys = new String[length];
values = new int[length];
}
public IntHash(String[] k, int[] v) {
count = Math.min(k.length, v.length);
keys = new String[count];
values = new int[count];
System.arraycopy(k, 0, keys, 0, count);
System.arraycopy(v, 0, values, 0, count);
}
static public IntHash fromTally(String[] list) {
IntHash outgoing = new IntHash();
for (String s : list) {
outgoing.increment(s);
}
outgoing.crop();
return outgoing;
}
static public IntHash fromOrder(String[] list) {
IntHash outgoing = new IntHash();
for (int i = 0; i < list.length; i++) {
outgoing.set(list[i], i);
}
return outgoing;
}
public IntHash(PApplet parent, String filename) {
String[] lines = parent.loadStrings(filename);
keys = new String[lines.length];
values = new int[lines.length];
// boolean csv = (lines[0].indexOf('\t') == -1);
for (int i = 0; i < lines.length; i++) {
// if (lines[i].trim().length() != 0) {
// String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t');
String[] pieces = PApplet.split(lines[i], '\t');
if (pieces.length == 2) {
keys[count] = pieces[0];
values[count] = PApplet.parseInt(pieces[1]);
count++;
}
}
}
public int getCount() {
return count;
}
public String key(int index) {
return keys[index];
}
protected void crop() {
if (count != keys.length) {
keys = PApplet.subset(keys, 0, count);
values = PApplet.subset(values, 0, count);
}
}
/**
* Return the internal array being used to store the keys. Allocated but
* unused entries will be removed. This array should not be modified.
*/
public String[] keys() {
crop();
return keys;
}
/**
* Return a copy of the internal keys array. This array can be modified.
*/
public String[] keyArray() {
String[] outgoing = new String[count];
System.arraycopy(keys, 0, outgoing, 0, count);
return outgoing;
}
public int value(int index) {
return values[index];
}
public int[] values() {
crop();
return values;
}
public int[] valueArray() {
int[] outgoing = new int[count];
System.arraycopy(values, 0, outgoing, 0, count);
return outgoing;
}
public int get(String what) {
int index = index(what);
if (index == -1) return 0;
return values[index];
}
public void set(String who, int amount) {
int index = index(who);
if (index == -1) {
create(who, amount);
} else {
values[index] = amount;
}
}
public void add(String who, int amount) {
int index = index(who);
if (index == -1) {
create(who, amount);
} else {
values[index] += amount;
}
}
public void increment(String who) {
int index = index(who);
if (index == -1) {
create(who, 1);
} else {
values[index]++;
}
}
public int index(String what) {
Integer found = indices.get(what);
return (found == null) ? -1 : found.intValue();
}
protected void create(String what, int much) {
if (count == keys.length) {
String ktemp[] = new String[count << 1];
System.arraycopy(keys, 0, ktemp, 0, count);
keys = ktemp;
int vtemp[] = new int[count << 1];
System.arraycopy(values, 0, vtemp, 0, count);
values = vtemp;
}
indices.put(what, new Integer(count));
keys[count] = what;
values[count] = much;
count++;
}
public void print() {
write(new PrintWriter(System.out));
}
public void write(PrintWriter writer) {
for (int i = 0; i < count; i++) {
writer.println(keys[i] + "\t" + values[i]);
}
writer.flush();
}
public void remove(String which) {
removeIndex(index(which));
}
public void removeIndex(int which) {
//System.out.println("index is " + which + " and " + keys[which]);
indices.remove(keys[which]);
for (int i = which; i < count-1; i++) {
keys[i] = keys[i+1];
values[i] = values[i+1];
indices.put(keys[i], i);
}
count--;
keys[count] = null;
values[count] = 0;
}
protected void swap(int a, int b) {
String tkey = keys[a];
int tvalue = values[a];
keys[a] = keys[b];
values[a] = values[b];
keys[b] = tkey;
values[b] = tvalue;
indices.put(keys[a], new Integer(a));
indices.put(keys[b], new Integer(b));
}
public void sortKeys() {
Sort s = new Sort() {
@Override
public int size() {
return count;
}
@Override
public float compare(int a, int b) {
int result = keys[a].compareToIgnoreCase(keys[b]);
if (result != 0) {
return result;
}
return values[b] - values[a];
}
@Override
public void swap(int a, int b) {
IntHash.this.swap(a, b);
}
};
s.run();
}
/**
* Sort by values in descending order (largest value will be at [0]).
*/
public void sortValues() {
sortValues(true);
}
/**
* Sort by values. Identical values will use the keys as tie-breaker.
* @param descending true to put the largest value at position 0.
*/
public void sortValues(final boolean descending) {
Sort s = new Sort() {
@Override
public int size() {
return count;
}
@Override
public float compare(int a, int b) {
int diff = values[b] - values[a];
if (diff == 0) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
return descending ? diff : -diff;
}
@Override
public void swap(int a, int b) {
IntHash.this.swap(a, b);
}
};
s.run();
}
}
+195
View File
@@ -0,0 +1,195 @@
package processing.data;
import processing.core.PApplet;
/**
* Helper class for a list of floats.
*/
public class IntList {
int count;
int[] data;
public IntList() {
data = new int[10];
}
public IntList(int[] source) {
count = source.length;
data = new int[count];
System.arraycopy(source, 0, data, 0, count);
}
/**
* Get the length of the list.
*/
public int size() {
return count;
}
/**
* Get an entry at a particular index.
*/
public int get(int index) {
return data[index];
}
/**
* Set the entry at a particular index.
*/
public void set(int index, int what) {
if (index >= count) {
data = PApplet.expand(data, index+1);
}
data[index] = what;
}
public int removeIndex(int index) {
int bye = data[index];
PApplet.println(data);
int[] outgoing = new int[count - 1];
System.arraycopy(data, 0, outgoing, 0, index);
count--;
System.arraycopy(data, index + 1, outgoing, 0, count - index);
data = outgoing;
PApplet.println(data);
PApplet.println();
return bye;
}
/**
* Add a new entry to the list.
*/
public void append(int value) {
if (count == data.length) {
data = PApplet.expand(data);
}
data[count++] = value;
}
public int index(int what) {
/*
if (indexCache != null) {
try {
return indexCache.get(what);
} catch (Exception e) { // not there
return -1;
}
}
*/
for (int i = 0; i < count; i++) {
if (data[i] == what) {
return i;
}
}
return -1;
}
// !!! TODO this is not yet correct, because it's not being reset when
// the rest of the entries are changed
// protected void cacheIndices() {
// indexCache = new HashMap<Integer, Integer>();
// for (int i = 0; i < count; i++) {
// indexCache.put(data[i], i);
// }
// }
public boolean contains(int what) {
// if (indexCache == null) {
// cacheIndices();
// }
// return index(what) != -1;
for (int i = 0; i < count; i++) {
if (data[i] == what) {
return true;
}
}
return false;
}
/**
* Remove all entries from the list.
*/
public void clear() {
count = 0;
}
/**
* Improve efficiency by removing allocated but unused entries from the
* internal array used to store the data.
*/
public void crop() {
if (count != data.length) {
data = PApplet.subset(data, 0, count);
}
}
public void add(int amt) {
for (int i = 0; i < count; i++) {
data[i] += amt;
}
}
public void sub(int amt) {
for (int i = 0; i < count; i++) {
data[i] -= amt;
}
}
public void mul(int amt) {
for (int i = 0; i < count; i++) {
data[i] *= amt;
}
}
public void div(int amt) {
for (int i = 0; i < count; i++) {
data[i] /= amt;
}
}
/**
* Returns the actual array being used to store the data.
*/
public int[] values() {
crop();
return data;
}
/**
* Create a new array with a copy of all the values.
* @return an array sized by the length of the list with each of the values.
*/
public int[] toArray() {
int[] outgoing = new int[count];
System.arraycopy(data, 0, outgoing, 0, count);
return outgoing;
}
/**
* Copy as many values as possible into the specified array.
* @param array
*/
public void toArray(int[] array) {
System.arraycopy(data, 0, array, 0, Math.min(count, array.length));
}
}
+908
View File
@@ -0,0 +1,908 @@
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package processing.data;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* A JSONArray is an ordered sequence of values. Its external text form is a
* string wrapped in square brackets with commas separating the values. The
* internal form is an object having <code>get</code> and <code>opt</code>
* methods for accessing the values by index, and <code>put</code> methods for
* adding or replacing values. The values can be any of these types:
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
* <code>Number</code>, <code>String</code>, or the
* <code>JSONObject.NULL object</code>.
* <p>
* The constructor can convert a JSON text into a Java object. The
* <code>toString</code> method converts to JSON text.
* <p>
* A <code>get</code> method returns a value if one can be found, and throws an
* exception if one cannot be found. An <code>opt</code> method returns a
* default value instead of throwing an exception, and so is useful for
* obtaining optional values.
* <p>
* The generic <code>get()</code> and <code>opt()</code> methods return an
* object which you can cast or query for type. There are also typed
* <code>get</code> and <code>opt</code> methods that do type checking and type
* coercion for you.
* <p>
* The texts produced by the <code>toString</code> methods strictly conform to
* JSON syntax rules. The constructors are more forgiving in the texts they will
* accept:
* <ul>
* <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
* before the closing bracket.</li>
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
* &nbsp;<small>(comma)</small> elision.</li>
* <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
* quote)</small>.</li>
* <li>Strings do not need to be quoted at all if they do not begin with a quote
* or single quote, and if they do not contain leading or trailing spaces, and
* if they do not contain any of these characters:
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
* if they are not the reserved words <code>true</code>, <code>false</code>, or
* <code>null</code>.</li>
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
* well as by <code>,</code> <small>(comma)</small>.</li>
* </ul>
*
* @author JSON.org
* @version 2012-11-13
*/
public class JSONArray {
/**
* The arrayList where the JSONArray's properties are kept.
*/
private final ArrayList myArrayList;
/**
* Construct an empty JSONArray.
*/
public JSONArray() {
this.myArrayList = new ArrayList();
}
/**
* Construct a JSONArray from a JSONTokener.
* @param x A JSONTokener
* @throws JSONException If there is a syntax error.
*/
public JSONArray(JSONTokener x) throws JSONException {
this();
if (x.nextClean() != '[') {
throw x.syntaxError("A JSONArray text must start with '['");
}
if (x.nextClean() != ']') {
x.back();
for (;;) {
if (x.nextClean() == ',') {
x.back();
this.myArrayList.add(JSONObject.NULL);
} else {
x.back();
this.myArrayList.add(x.nextValue());
}
switch (x.nextClean()) {
case ';':
case ',':
if (x.nextClean() == ']') {
return;
}
x.back();
break;
case ']':
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
}
}
}
/**
* Construct a JSONArray from a source JSON text.
* @param source A string that begins with
* <code>[</code>&nbsp;<small>(left bracket)</small>
* and ends with <code>]</code>&nbsp;<small>(right bracket)</small>.
* @throws JSONException If there is a syntax error.
*/
public JSONArray(String source) throws JSONException {
this(new JSONTokener(source));
}
/**
* Construct a JSONArray from a Collection.
* @param collection A Collection.
*/
public JSONArray(Collection collection) {
this.myArrayList = new ArrayList();
if (collection != null) {
Iterator iter = collection.iterator();
while (iter.hasNext()) {
this.myArrayList.add(JSONObject.wrap(iter.next()));
}
}
}
/**
* Construct a JSONArray from an array
* @throws JSONException If not an array.
*/
public JSONArray(Object array) throws JSONException {
this();
if (array.getClass().isArray()) {
int length = Array.getLength(array);
for (int i = 0; i < length; i += 1) {
this.put(JSONObject.wrap(Array.get(array, i)));
}
} else {
throw new JSONException(
"JSONArray initial value should be a string or collection or array.");
}
}
/**
* Get the object value associated with an index.
* @param index
* The index must be between 0 and length() - 1.
* @return An object value.
* @throws JSONException If there is no value for the index.
*/
public Object get(int index) throws JSONException {
Object object = this.opt(index);
if (object == null) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
return object;
}
/**
* Get the boolean value associated with an index.
* The string values "true" and "false" are converted to boolean.
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
* @throws JSONException If there is no value for the index or if the
* value is not convertible to boolean.
*/
public boolean getBoolean(int index) throws JSONException {
Object object = this.get(index);
if (object.equals(Boolean.FALSE) ||
(object instanceof String &&
((String)object).equalsIgnoreCase("false"))) {
return false;
} else if (object.equals(Boolean.TRUE) ||
(object instanceof String &&
((String)object).equalsIgnoreCase("true"))) {
return true;
}
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
}
/**
* Get the double value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot
* be converted to a number.
*/
public double getDouble(int index) throws JSONException {
Object object = this.get(index);
try {
return object instanceof Number
? ((Number)object).doubleValue()
: Double.parseDouble((String)object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] is not a number.");
}
}
/**
* Get the int value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value is not a number.
*/
public int getInt(int index) throws JSONException {
Object object = this.get(index);
try {
return object instanceof Number
? ((Number)object).intValue()
: Integer.parseInt((String)object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] is not a number.");
}
}
/**
* Get the JSONArray associated with an index.
* @param index The index must be between 0 and length() - 1.
* @return A JSONArray value.
* @throws JSONException If there is no value for the index. or if the
* value is not a JSONArray
*/
public JSONArray getJSONArray(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof JSONArray) {
return (JSONArray)object;
}
throw new JSONException("JSONArray[" + index +
"] is not a JSONArray.");
}
/**
* Get the JSONObject associated with an index.
* @param index subscript
* @return A JSONObject value.
* @throws JSONException If there is no value for the index or if the
* value is not a JSONObject
*/
public JSONObject getJSONObject(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof JSONObject) {
return (JSONObject)object;
}
throw new JSONException("JSONArray[" + index +
"] is not a JSONObject.");
}
/**
* Get the long value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot
* be converted to a number.
*/
public long getLong(int index) throws JSONException {
Object object = this.get(index);
try {
return object instanceof Number
? ((Number)object).longValue()
: Long.parseLong((String)object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] is not a number.");
}
}
/**
* Get the string associated with an index.
* @param index The index must be between 0 and length() - 1.
* @return A string value.
* @throws JSONException If there is no string value for the index.
*/
public String getString(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof String) {
return (String)object;
}
throw new JSONException("JSONArray[" + index + "] not a string.");
}
/**
* Determine if the value is null.
* @param index The index must be between 0 and length() - 1.
* @return true if the value at the index is null, or if there is no value.
*/
public boolean isNull(int index) {
return JSONObject.NULL.equals(this.opt(index));
}
/**
* Make a string from the contents of this JSONArray. The
* <code>separator</code> string is inserted between each element.
* Warning: This method assumes that the data structure is acyclical.
* @param separator A string that will be inserted between the elements.
* @return a string.
* @throws JSONException If the array contains an invalid number.
*/
public String join(String separator) throws JSONException {
int len = this.length();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i += 1) {
if (i > 0) {
sb.append(separator);
}
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
}
return sb.toString();
}
/**
* Get the number of elements in the JSONArray, included nulls.
*
* @return The length (or size).
*/
public int length() {
return this.myArrayList.size();
}
/**
* Get the optional object value associated with an index.
* @param index The index must be between 0 and length() - 1.
* @return An object value, or null if there is no
* object at that index.
*/
public Object opt(int index) {
return (index < 0 || index >= this.length())
? null
: this.myArrayList.get(index);
}
/**
* Get the optional boolean value associated with an index.
* It returns false if there is no value at that index,
* or if the value is not Boolean.TRUE or the String "true".
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
*/
public boolean optBoolean(int index) {
return this.optBoolean(index, false);
}
/**
* Get the optional boolean value associated with an index.
* It returns the defaultValue if there is no value at that index or if
* it is not a Boolean or the String "true" or "false" (case insensitive).
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue A boolean default.
* @return The truth.
*/
public boolean optBoolean(int index, boolean defaultValue) {
try {
return this.getBoolean(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional double value associated with an index.
* NaN is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public double optDouble(int index) {
return this.optDouble(index, Double.NaN);
}
/**
* Get the optional double value associated with an index.
* The defaultValue is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index subscript
* @param defaultValue The default value.
* @return The value.
*/
public double optDouble(int index, double defaultValue) {
try {
return this.getDouble(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional int value associated with an index.
* Zero is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public int optInt(int index) {
return this.optInt(index, 0);
}
/**
* Get the optional int value associated with an index.
* The defaultValue is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public int optInt(int index, int defaultValue) {
try {
return this.getInt(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional JSONArray associated with an index.
* @param index subscript
* @return A JSONArray value, or null if the index has no value,
* or if the value is not a JSONArray.
*/
public JSONArray optJSONArray(int index) {
Object o = this.opt(index);
return o instanceof JSONArray ? (JSONArray)o : null;
}
/**
* Get the optional JSONObject associated with an index.
* Null is returned if the key is not found, or null if the index has
* no value, or if the value is not a JSONObject.
*
* @param index The index must be between 0 and length() - 1.
* @return A JSONObject value.
*/
public JSONObject optJSONObject(int index) {
Object o = this.opt(index);
return o instanceof JSONObject ? (JSONObject)o : null;
}
/**
* Get the optional long value associated with an index.
* Zero is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public long optLong(int index) {
return this.optLong(index, 0);
}
/**
* Get the optional long value associated with an index.
* The defaultValue is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public long optLong(int index, long defaultValue) {
try {
return this.getLong(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional string value associated with an index. It returns an
* empty string if there is no value at that index. If the value
* is not a string and is not null, then it is coverted to a string.
*
* @param index The index must be between 0 and length() - 1.
* @return A String value.
*/
public String optString(int index) {
return this.optString(index, "");
}
/**
* Get the optional string associated with an index.
* The defaultValue is returned if the key is not found.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return A String value.
*/
public String optString(int index, String defaultValue) {
Object object = this.opt(index);
return JSONObject.NULL.equals(object)
? defaultValue
: object.toString();
}
/**
* Append a boolean value. This increases the array's length by one.
*
* @param value A boolean value.
* @return this.
*/
public JSONArray put(boolean value) {
this.put(value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONArray which is produced from a Collection.
* @param value A Collection value.
* @return this.
*/
public JSONArray put(Collection value) {
this.put(new JSONArray(value));
return this;
}
/**
* Append a double value. This increases the array's length by one.
*
* @param value A double value.
* @throws JSONException if the value is not finite.
* @return this.
*/
public JSONArray put(double value) throws JSONException {
Double d = new Double(value);
JSONObject.testValidity(d);
this.put(d);
return this;
}
/**
* Append an int value. This increases the array's length by one.
*
* @param value An int value.
* @return this.
*/
public JSONArray put(int value) {
this.put(new Integer(value));
return this;
}
/**
* Append an long value. This increases the array's length by one.
*
* @param value A long value.
* @return this.
*/
public JSONArray put(long value) {
this.put(new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONObject which is produced from a Map.
* @param value A Map value.
* @return this.
*/
public JSONArray put(Map value) {
this.put(new JSONObject(value));
return this;
}
/**
* Append an object value. This increases the array's length by one.
* @param value An object value. The value should be a
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
* JSONObject.NULL object.
* @return this.
*/
public JSONArray put(Object value) {
this.myArrayList.add(value);
return this;
}
/**
* Put or replace a boolean value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
* @param index The subscript.
* @param value A boolean value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, boolean value) throws JSONException {
this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONArray which is produced from a Collection.
* @param index The subscript.
* @param value A Collection value.
* @return this.
* @throws JSONException If the index is negative or if the value is
* not finite.
*/
public JSONArray put(int index, Collection value) throws JSONException {
this.put(index, new JSONArray(value));
return this;
}
/**
* Put or replace a double value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad
* it out.
* @param index The subscript.
* @param value A double value.
* @return this.
* @throws JSONException If the index is negative or if the value is
* not finite.
*/
public JSONArray put(int index, double value) throws JSONException {
this.put(index, new Double(value));
return this;
}
/**
* Put or replace an int value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad
* it out.
* @param index The subscript.
* @param value An int value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, int value) throws JSONException {
this.put(index, new Integer(value));
return this;
}
/**
* Put or replace a long value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad
* it out.
* @param index The subscript.
* @param value A long value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, long value) throws JSONException {
this.put(index, new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONObject that is produced from a Map.
* @param index The subscript.
* @param value The Map value.
* @return this.
* @throws JSONException If the index is negative or if the the value is
* an invalid number.
*/
public JSONArray put(int index, Map value) throws JSONException {
this.put(index, new JSONObject(value));
return this;
}
/**
* Put or replace an object value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
* @param index The subscript.
* @param value The value to put into the array. The value should be a
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
* JSONObject.NULL object.
* @return this.
* @throws JSONException If the index is negative or if the the value is
* an invalid number.
*/
public JSONArray put(int index, Object value) throws JSONException {
JSONObject.testValidity(value);
if (index < 0) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
if (index < this.length()) {
this.myArrayList.set(index, value);
} else {
while (index != this.length()) {
this.put(JSONObject.NULL);
}
this.put(value);
}
return this;
}
/**
* Remove an index and close the hole.
* @param index The index of the element to be removed.
* @return The value that was associated with the index,
* or null if there was no value.
*/
public Object remove(int index) {
Object o = this.opt(index);
this.myArrayList.remove(index);
return o;
}
/**
* Produce a JSONObject by combining a JSONArray of names with the values
* of this JSONArray.
* @param names A JSONArray containing a list of key strings. These will be
* paired with the values.
* @return A JSONObject, or null if there are no names or if this JSONArray
* has no values.
* @throws JSONException If any of the names are null.
*/
public JSONObject toJSONObject(JSONArray names) throws JSONException {
if (names == null || names.length() == 0 || this.length() == 0) {
return null;
}
JSONObject jo = new JSONObject();
for (int i = 0; i < names.length(); i += 1) {
jo.put(names.getString(i), this.opt(i));
}
return jo;
}
/**
* Make a JSON text of this JSONArray. For compactness, no
* unnecessary whitespace is added. If it is not possible to produce a
* syntactically correct JSON text then null will be returned instead. This
* could occur if the array contains an invalid number.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return a printable, displayable, transmittable
* representation of the array.
*/
@Override
public String toString() {
try {
return this.toString(0);
} catch (Exception e) {
return null;
}
}
/**
* Make a prettyprinted JSON text of this JSONArray.
* Warning: This method assumes that the data structure is acyclical.
* @param indentFactor The number of spaces to add to each level of
* indentation.
* @return a printable, displayable, transmittable
* representation of the object, beginning
* with <code>[</code>&nbsp;<small>(left bracket)</small> and ending
* with <code>]</code>&nbsp;<small>(right bracket)</small>.
* @throws JSONException
*/
public String toString(int indentFactor) throws JSONException {
StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
return this.write(sw, indentFactor, 0).toString();
}
}
/**
* Write the contents of the JSONArray as JSON text to a writer. For
* compactness, no whitespace is added.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return The writer.
* @throws JSONException
*/
public Writer write(Writer writer) throws JSONException {
return this.write(writer, 0, 0);
}
/**
* Write the contents of the JSONArray as JSON text to a writer. For
* compactness, no whitespace is added.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @param indentFactor
* The number of spaces to add to each level of indentation.
* @param indent
* The indention of the top level.
* @return The writer.
* @throws JSONException
*/
Writer write(Writer writer, int indentFactor, int indent)
throws JSONException {
try {
boolean commanate = false;
int length = this.length();
writer.write('[');
if (length == 1) {
JSONObject.writeValue(writer, this.myArrayList.get(0),
indentFactor, indent);
} else if (length != 0) {
final int newindent = indent + indentFactor;
for (int i = 0; i < length; i += 1) {
if (commanate) {
writer.write(',');
}
if (indentFactor > 0) {
writer.write('\n');
}
JSONObject.indent(writer, newindent);
JSONObject.writeValue(writer, this.myArrayList.get(i),
indentFactor, newindent);
commanate = true;
}
if (indentFactor > 0) {
writer.write('\n');
}
JSONObject.indent(writer, indent);
}
writer.write(']');
return writer;
} catch (IOException e) {
throw new JSONException(e);
}
}
}
@@ -0,0 +1,29 @@
package processing.data;
/**
* The JSONException is thrown by the JSON.org classes when things are amiss.
* @author JSON.org
* @version 2010-12-24
*/
public class JSONException extends Exception {
private static final long serialVersionUID = 0;
private Throwable cause;
/**
* Constructs a JSONException with an explanatory message.
* @param message Detail about the reason for the exception.
*/
public JSONException(String message) {
super(message);
}
public JSONException(Throwable cause) {
super(cause.getMessage());
this.cause = cause;
}
@Override
public Throwable getCause() {
return this.cause;
}
}
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
package processing.data;
/**
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
* method so that a class can change the behavior of
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
* <code>toJSONString</code> method will be used instead of the default behavior
* of using the Object's <code>toString()</code> method and quoting the result.
*/
public interface JSONString {
/**
* The <code>toJSONString</code> method allows a class to produce its own JSON
* serialization.
*
* @return A strictly syntactically correct JSON text.
*/
public String toJSONString();
}
+447
View File
@@ -0,0 +1,447 @@
package processing.data;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* A JSONTokener takes a source string and extracts characters and tokens from
* it. It is used by the JSONObject and JSONArray constructors to parse
* JSON source strings.
* @author JSON.org
* @version 2012-02-16
*/
public class JSONTokener {
private long character;
private boolean eof;
private long index;
private long line;
private char previous;
private Reader reader;
private boolean usePrevious;
/**
* Construct a JSONTokener from a Reader.
*
* @param reader A reader.
*/
public JSONTokener(Reader reader) {
this.reader = reader.markSupported()
? reader
: new BufferedReader(reader);
this.eof = false;
this.usePrevious = false;
this.previous = 0;
this.index = 0;
this.character = 1;
this.line = 1;
}
/**
* Construct a JSONTokener from an InputStream.
*/
public JSONTokener(InputStream inputStream) throws JSONException {
this(new InputStreamReader(inputStream));
}
/**
* Construct a JSONTokener from a string.
*
* @param s A source string.
*/
public JSONTokener(String s) {
this(new StringReader(s));
}
/**
* Back up one character. This provides a sort of lookahead capability,
* so that you can test for a digit or letter before attempting to parse
* the next number or identifier.
*/
public void back() throws JSONException {
if (this.usePrevious || this.index <= 0) {
throw new JSONException("Stepping back two steps is not supported");
}
this.index -= 1;
this.character -= 1;
this.usePrevious = true;
this.eof = false;
}
/**
* Get the hex value of a character (base16).
* @param c A character between '0' and '9' or between 'A' and 'F' or
* between 'a' and 'f'.
* @return An int between 0 and 15, or -1 if c was not a hex digit.
*/
public static int dehexchar(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'A' && c <= 'F') {
return c - ('A' - 10);
}
if (c >= 'a' && c <= 'f') {
return c - ('a' - 10);
}
return -1;
}
public boolean end() {
return this.eof && !this.usePrevious;
}
/**
* Determine if the source string still contains characters that next()
* can consume.
* @return true if not yet at the end of the source.
*/
public boolean more() throws JSONException {
this.next();
if (this.end()) {
return false;
}
this.back();
return true;
}
/**
* Get the next character in the source string.
*
* @return The next character, or 0 if past the end of the source string.
*/
public char next() throws JSONException {
int c;
if (this.usePrevious) {
this.usePrevious = false;
c = this.previous;
} else {
try {
c = this.reader.read();
} catch (IOException exception) {
throw new JSONException(exception);
}
if (c <= 0) { // End of stream
this.eof = true;
c = 0;
}
}
this.index += 1;
if (this.previous == '\r') {
this.line += 1;
this.character = c == '\n' ? 0 : 1;
} else if (c == '\n') {
this.line += 1;
this.character = 0;
} else {
this.character += 1;
}
this.previous = (char) c;
return this.previous;
}
/**
* Consume the next character, and check that it matches a specified
* character.
* @param c The character to match.
* @return The character.
* @throws JSONException if the character does not match.
*/
public char next(char c) throws JSONException {
char n = this.next();
if (n != c) {
throw this.syntaxError("Expected '" + c + "' and instead saw '" +
n + "'");
}
return n;
}
/**
* Get the next n characters.
*
* @param n The number of characters to take.
* @return A string of n characters.
* @throws JSONException
* Substring bounds error if there are not
* n characters remaining in the source string.
*/
public String next(int n) throws JSONException {
if (n == 0) {
return "";
}
char[] chars = new char[n];
int pos = 0;
while (pos < n) {
chars[pos] = this.next();
if (this.end()) {
throw this.syntaxError("Substring bounds error");
}
pos += 1;
}
return new String(chars);
}
/**
* Get the next char in the string, skipping whitespace.
* @throws JSONException
* @return A character, or 0 if there are no more characters.
*/
public char nextClean() throws JSONException {
for (;;) {
char c = this.next();
if (c == 0 || c > ' ') {
return c;
}
}
}
/**
* Return the characters up to the next close quote character.
* Backslash processing is done. The formal JSON format does not
* allow strings in single quotes, but an implementation is allowed to
* accept them.
* @param quote The quoting character, either
* <code>"</code>&nbsp;<small>(double quote)</small> or
* <code>'</code>&nbsp;<small>(single quote)</small>.
* @return A String.
* @throws JSONException Unterminated string.
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (;;) {
c = this.next();
switch (c) {
case 0:
case '\n':
case '\r':
throw this.syntaxError("Unterminated string");
case '\\':
c = this.next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
sb.append((char)Integer.parseInt(this.next(4), 16));
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw this.syntaxError("Illegal escape.");
}
break;
default:
if (c == quote) {
return sb.toString();
}
sb.append(c);
}
}
}
/**
* Get the text up but not including the specified character or the
* end of line, whichever comes first.
* @param delimiter A delimiter character.
* @return A string.
*/
public String nextTo(char delimiter) throws JSONException {
StringBuffer sb = new StringBuffer();
for (;;) {
char c = this.next();
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
this.back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the text up but not including one of the specified delimiter
* characters or the end of line, whichever comes first.
* @param delimiters A set of delimiter characters.
* @return A string, trimmed.
*/
public String nextTo(String delimiters) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (;;) {
c = this.next();
if (delimiters.indexOf(c) >= 0 || c == 0 ||
c == '\n' || c == '\r') {
if (c != 0) {
this.back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the next value. The value can be a Boolean, Double, Integer,
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
* @throws JSONException If syntax error.
*
* @return An object.
*/
public Object nextValue() throws JSONException {
char c = this.nextClean();
String string;
switch (c) {
case '"':
case '\'':
return this.nextString(c);
case '{':
this.back();
return new JSONObject(this);
case '[':
this.back();
return new JSONArray(this);
}
/*
* Handle unquoted text. This could be the values true, false, or
* null, or it can be a number. An implementation (such as this one)
* is allowed to also accept non-standard forms.
*
* Accumulate characters until we reach the end of the text or a
* formatting character.
*/
StringBuffer sb = new StringBuffer();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
this.back();
string = sb.toString().trim();
if ("".equals(string)) {
throw this.syntaxError("Missing value");
}
return JSONObject.stringToValue(string);
}
/**
* Skip characters until the next character is the requested character.
* If the requested character is not found, no characters are skipped.
* @param to A character to skip to.
* @return The requested character, or zero if the requested character
* is not found.
*/
public char skipTo(char to) throws JSONException {
char c;
try {
long startIndex = this.index;
long startCharacter = this.character;
long startLine = this.line;
this.reader.mark(1000000);
do {
c = this.next();
if (c == 0) {
this.reader.reset();
this.index = startIndex;
this.character = startCharacter;
this.line = startLine;
return c;
}
} while (c != to);
} catch (IOException exc) {
throw new JSONException(exc);
}
this.back();
return c;
}
/**
* Make a JSONException to signal a syntax error.
*
* @param message The error message.
* @return A JSONException object, suitable for throwing
*/
public JSONException syntaxError(String message) {
return new JSONException(message + this.toString());
}
/**
* Make a printable string of this JSONTokener.
*
* @return " at {index} [character {character} line {line}]"
*/
@Override
public String toString() {
return " at " + this.index + " [character " + this.character + " line " +
this.line + "]";
}
}
+42
View File
@@ -0,0 +1,42 @@
package processing.data;
public abstract class Sort implements Runnable {
public Sort() { }
public void run() {
int c = size();
if (c > 1) {
sort(0, c - 1);
}
}
protected void sort(int i, int j) {
int pivotIndex = (i+j)/2;
swap(pivotIndex, j);
int k = partition(i-1, j);
swap(k, j);
if ((k-i) > 1) sort(i, k-1);
if ((j-k) > 1) sort(k+1, j);
}
protected int partition(int left, int right) {
int pivot = right;
do {
while (compare(++left, pivot) < 0) ;
while ((right != 0) && (compare(--right, pivot) > 0)) ;
swap(left, right);
} while (left < right);
swap(left, right);
return left;
}
abstract public int size();
abstract public float compare(int a, int b);
abstract public void swap(int a, int b);
}
+303
View File
@@ -0,0 +1,303 @@
package processing.data;
import java.io.*;
import java.util.HashMap;
import processing.core.PApplet;
/**
* A simple table class to use a String as a lookup for another String value.
*/
public class StringHash {
/** Number of elements in the table */
public int count;
/**
* List of keys, available for sake of speed,
* but should be manipulated (consider it read-only).
*/
protected String[] keys;
/**
* List of values, available for sake of speed,
* but should be manipulated (consider it read-only).
*/
protected String[] values;
/** Internal implementation for faster lookups */
private HashMap<String, Integer> indices = new HashMap<String, Integer>();
public StringHash() {
count = 0;
keys = new String[10];
values = new String[10];
}
public StringHash(int length) {
count = 0;
keys = new String[length];
values = new String[length];
}
public StringHash(PApplet parent, String filename) {
String[] lines = parent.loadStrings(filename);
keys = new String[lines.length];
values = new String[lines.length];
// boolean csv = (lines[0].indexOf('\t') == -1);
for (int i = 0; i < lines.length; i++) {
// String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t');
String[] pieces = PApplet.split(lines[i], '\t');
if (pieces.length == 2) {
// keys[count] = pieces[0];
// values[count] = pieces[1];
// count++;
create(pieces[0], pieces[1]);
}
}
}
public void write(PApplet parent, String filename) {
PrintWriter writer = parent.createWriter(filename);
boolean csv =
(filename.toLowerCase().endsWith(".csv") ||
filename.toLowerCase().endsWith(".csv.gz"));
for (int i = 0; i < count; i++) {
if (csv) {
// String k = key(i);
// if (k.indexOf("))
} else {
writer.println(key(i) + "\t" + value(i));
}
}
}
public int getCount() {
return count;
}
public String key(int index) {
return keys[index];
}
public void crop() {
if (count != keys.length) {
keys = PApplet.subset(keys, 0, count);
values = PApplet.subset(values, 0, count);
}
}
/**
* Return the internal array being used to store the keys. Allocated but
* unused entries will be removed. This array should not be modified.
*/
public String[] keys() {
crop();
return keys;
}
/**
* Return a copy of the internal keys array. This array can be modified.
*/
public String[] keyArray() {
String[] outgoing = new String[count];
System.arraycopy(keys, 0, outgoing, 0, count);
return outgoing;
}
public String value(int index) {
return values[index];
}
public String[] values() {
crop();
return values;
}
public String[] valueArray() {
String[] outgoing = new String[count];
System.arraycopy(values, 0, outgoing, 0, count);
return outgoing;
}
public String get(String what) {
int index = keyIndex(what);
if (index == -1) return null;
return values[index];
}
public void set(String key, String val) {
int index = keyIndex(key);
if (index == -1) {
create(key, val);
} else {
values[index] = val;
}
}
public void append(String key, String val) {
int index = keyIndex(key);
if (index == -1) {
create(key, val);
} else {
values[index] += val;
}
}
public int keyIndex(String what) {
Integer found = indices.get(what);
return (found == null) ? -1 : found.intValue();
}
public int valueIndex(String what) {
for (int i = 0; i < count; i++) {
if (values[i].equals(what)) {
return i;
}
}
return -1;
}
protected void create(String k, String v) {
if (count == keys.length) {
keys = PApplet.expand(keys);
values = PApplet.expand(values);
}
indices.put(k, new Integer(count));
keys[count] = k;
values[count] = v;
count++;
}
public void print() {
write(new PrintWriter(System.out));
}
public void write(PrintWriter writer) {
for (int i = 0; i < count; i++) {
writer.println(keys[i] + "\t" + values[i]);
}
writer.flush();
}
public void remove(String which) {
remove(keyIndex(which));
}
public void remove(int which) {
indices.remove(keys[which]);
for (int i = which; i < count-1; i++) {
keys[i] = keys[i+1];
values[i] = values[i+1];
indices.put(keys[i], i);
}
count--;
keys[count] = null;
values[count] = null;
}
public void swap(int a, int b) {
String tkey = keys[a];
String tvalue = values[a];
keys[a] = keys[b];
values[a] = values[b];
keys[b] = tkey;
values[b] = tvalue;
indices.put(keys[a], new Integer(a));
indices.put(keys[b], new Integer(b));
}
public void sortKeys() {
Sort s = new Sort() {
@Override
public int size() {
return count;
}
@Override
public float compare(int a, int b) {
int result = keys[a].compareToIgnoreCase(keys[b]);
if (result != 0) {
return result;
}
return values[a].compareToIgnoreCase(values[b]);
}
@Override
public void swap(int a, int b) {
StringHash.this.swap(a, b);
}
};
s.run();
}
/**
* Sort by values in descending order (largest value will be at [0]).
*/
public void sortValues() {
sortValues(true, true);
}
public void sortValues(final boolean descending) {
sortValues(descending, true);
}
// ascending puts the largest value at the end
// descending puts the largest value at 0
public void sortValues(final boolean descending, final boolean tiebreaker) {
Sort s = new Sort() {
@Override
public int size() {
return count;
}
@Override
public float compare(int a, int b) {
float diff = values[a].compareToIgnoreCase(values[b]);
if (tiebreaker) {
if (diff == 0) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
return descending ? diff : -diff;
}
@Override
public void swap(int a, int b) {
StringHash.this.swap(a, b);
}
};
s.run();
}
}
+226
View File
@@ -0,0 +1,226 @@
package processing.data;
import java.io.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import processing.core.PApplet;
import processing.data.Table;
class HTMLTableScraper {
ArrayList<Table> tables;
TableHTML currentTable;
public HTMLTableScraper(PApplet parent, String where) {
this(parent.createReader(where));
}
public HTMLTableScraper(File file) {
this(PApplet.createReader(file));
}
public HTMLTableScraper(String html) {
this(new StringReader(html));
}
public HTMLTableScraper(Reader reader) {
tables = new ArrayList<Table>();
TableHandler handler = new TableHandler();
parse(reader, handler);
}
// The actual class doing some of the work:
// javax.swing.text.html.parser.ParserDelegator pd;
void parse(Reader reader, HTMLEditorKit.ParserCallback handler) {
HTMLEditorKit.Parser parser = new HTMLEditorKit() {
@Override
public HTMLEditorKit.Parser getParser() {
return super.getParser();
}
}.getParser();
try {
parser.parse(reader, handler, true);
} catch (Exception e) {
e.printStackTrace();
}
}
public int getTableCount() {
return tables.size();
}
public Table getTable(int index) {
return tables.get(index);
}
/**
* Get the list of tables as an array of Table objects.
*/
public Table[] getTables() {
TableHTML[] outgoing = new TableHTML[tables.size()];
tables.toArray(outgoing);
return outgoing;
}
/**
* Get the list of tables as an ArrayList of Table objects.
*/
public ArrayList<Table> getTableList() {
return tables;
}
public void writeTables(PApplet parent, String prefix) {
int digits = 0;
int num = getTableCount();
while (num > 0) {
num /= 10;
digits++;
}
for (int i = 0; i < getTableCount(); i++) {
String name = prefix + PApplet.nf(i, digits);
//tables.get(i).writeCSV(parent.createWriter(name + ".csv"));
parent.saveTable(tables.get(i), name + ".csv");
}
}
////////////////////////////////////////////////////////////////////////////////
class TableHandler extends HTMLEditorKit.ParserCallback {
@Override
public void handleStartTag(HTML.Tag tag, MutableAttributeSet a, int pos) {
if (tag == HTML.Tag.TABLE) {
currentTable = new TableHTML(currentTable);
tables.add(currentTable);
} else if (currentTable != null) {
if (tag == HTML.Tag.TR) {
currentTable.beginTableRow();
} else if (tag == HTML.Tag.TD || tag == HTML.Tag.TH) {
int advance = 1;
String colspanStr = (String) a.getAttribute(HTML.Attribute.COLSPAN);
if (colspanStr != null) {
advance = Integer.parseInt(colspanStr);
}
currentTable.beginTableData(advance);
if (a.getAttribute(HTML.Attribute.ROWSPAN) != null) {
System.err.println("rowspan attribute in this table is being ignored");
}
}
}
}
@Override
public void handleText(char[] c,int pos) {
if (currentTable != null) {
String data = new String(c).trim();
currentTable.setContent(data);
}
}
@Override
public void handleEndTag(HTML.Tag tag, int pos) {
if (currentTable != null) {
if (tag == HTML.Tag.TABLE) {
currentTable = currentTable.parent;
} else if (tag == HTML.Tag.TR) {
currentTable.endTableRow();
} else if (tag == HTML.Tag.TD || tag == HTML.Tag.TH) {
currentTable.endTableData();
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
public class TableHTML extends Table {
// used during parse to capture state
TableHTML parent;
int colAdvance;
int rowIndex, colIndex;
int colCount;
TableHTML(TableHTML parent) {
super();
this.parent = parent;
}
void beginTableRow() {
// make sure we have enough room for these rows
addRow();
// if (rowCount == data.length) {
// String[][] temp = new String[data.length << 1][];
// System.arraycopy(data, 0, temp, 0, rowCount);
// data = temp;
// for (int j = rowCount; j < data.length; j++) {
// data[j] = new String[data[0].length];
// }
// }
}
void beginTableData(int advance) {
colAdvance = advance;
// expand the number of columns if necessary
checkColumn(colIndex + colAdvance - 1);
// if (colIndex + colAdvance > data[0].length) {
// int needed = (colIndex + colAdvance) * 2;
// for (int i = 0; i < data.length; i++) {
// String[] temp = new String[needed];
// System.arraycopy(data[i], 0, temp, 0, colCount);
// data[i] = temp;
// }
// }
}
void setContent(String what) {
//data[rowIndex][colIndex] = what;
String cell = getString(rowIndex, colIndex);
if (cell != null) {
cell += what;
} else {
cell = what;
}
// setString(rowIndex, colIndex, what);
setString(rowIndex, colIndex, cell);
}
void endTableData() {
colIndex += colAdvance;
colCount = Math.max(colIndex, colCount);
}
void endTableRow() {
rowIndex++;
rowCount = rowIndex;
colIndex = 0;
}
}
+255
View File
@@ -0,0 +1,255 @@
package processing.data;
import java.io.*;
import java.util.zip.*;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import processing.core.*;
public class TableODS extends Table {
public TableODS(File odsFile) {
this(getContentXML(odsFile), null, false);
}
public TableODS(File odsFile, boolean actual) {
this(getContentXML(odsFile), null, actual);
}
public TableODS(PApplet parent, String filename) {
this(getContentXML(parent.createInput(filename)), null, false);
}
public TableODS(PApplet parent, String filename, boolean actual) {
this(getContentXML(parent.createInput(filename)), null, actual);
}
public TableODS(PApplet parent, String filename, String worksheet, boolean actual) {
this(getContentXML(parent.createInput(filename)), worksheet, actual);
}
/**
* Parse spreadsheet content.
* @param input InputStream of the content.xml file inside the .ods
*/
protected TableODS(InputStream input, String worksheet, boolean actual) {
try {
InputStreamReader isr = new InputStreamReader(input, "UTF-8");
BufferedReader reader = new BufferedReader(isr);
read(reader, worksheet, actual);
} catch (UnsupportedEncodingException uee) {
uee.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
protected void read(BufferedReader reader, String worksheet, boolean actual) throws IOException, ParserConfigurationException, SAXException {
XML xml = new XML(reader);
// XML x = new XML(reader);
// PApplet.saveStrings(new File("/Users/fry/Desktop/namespacefix.xml"), new String[] { xml.toString() });
// PApplet.saveStrings(new File("/Users/fry/Desktop/newparser.xml"), new String[] { x.toString() });
// table files will have multiple sheets.. argh
// <table:table table:name="Sheet1" table:style-name="ta1" table:print="false">
// <table:table table:name="Sheet2" table:style-name="ta1" table:print="false">
// <table:table table:name="Sheet3" table:style-name="ta1" table:print="false">
XML[] sheets =
xml.getChildren("office:body/office:spreadsheet/table:table");
//xml.getChildren("office:body/office:spreadsheet/table:table/table");
// System.out.println("found " + sheets.length + " sheets.");
for (XML sheet : sheets) {
// System.out.println(sheet.getAttribute("table:name"));
if (worksheet == null || worksheet.equals(sheet.getString("table:name"))) {
parseSheet(sheet, actual);
}
}
}
protected void parseSheet(XML sheet, boolean actual) {
XML[] rows = sheet.getChildren("table:table-row");
//xml.getChildren("office:body/office:spreadsheet/table:table/table:table-row");
int rowIndex = 0;
for (XML row : rows) {
int rowRepeat = row.getInt("table:number-rows-repeated", 1);
// if (rowRepeat != 1) {
// System.out.println(rowRepeat + " " + rowCount + " " + (rowCount + rowRepeat));
// }
boolean rowNotNull = false;
XML[] cells = row.getChildren();
int columnIndex = 0;
for (XML cell : cells) {
int cellRepeat = cell.getInt("table:number-columns-repeated", 1);
// <table:table-cell table:formula="of:=SUM([.E7:.E8])" office:value-type="float" office:value="4150">
// <text:p>4150.00</text:p>
// </table:table-cell>
String cellData = actual ? cell.getString("office:value") : null;
// if there's an office:value in the cell, just roll with that
if (cellData == null) {
int cellKids = cell.getChildCount();
if (cellKids != 0) {
XML[] paragraphElements = cell.getChildren("text:p");
if (paragraphElements.length != 1) {
for (XML el : paragraphElements) {
System.err.println(el.toString());
}
throw new RuntimeException("found more than one text:p element");
}
XML textp = paragraphElements[0];
String textpContent = textp.getContent();
// if there are sub-elements, the content shows up as a child element
// (for which getName() returns null.. which seems wrong)
if (textpContent != null) {
cellData = textpContent; // nothing fancy, the text is in the text:p element
} else {
XML[] textpKids = textp.getChildren();
StringBuffer cellBuffer = new StringBuffer();
for (XML kid : textpKids) {
String kidName = kid.getName();
if (kidName == null) {
appendNotNull(kid, cellBuffer);
} else if (kidName.equals("text:s")) {
int spaceCount = kid.getInt("text:c", 1);
for (int space = 0; space < spaceCount; space++) {
cellBuffer.append(' ');
}
} else if (kidName.equals("text:span")) {
appendNotNull(kid, cellBuffer);
} else if (kidName.equals("text:a")) {
// <text:a xlink:href="http://blah.com/">blah.com</text:a>
if (actual) {
cellBuffer.append(kid.getString("xlink:href"));
} else {
appendNotNull(kid, cellBuffer);
}
} else {
appendNotNull(kid, cellBuffer);
System.err.println(getClass().getName() + ": don't understand: " + kid);
//throw new RuntimeException("I'm not used to this.");
}
}
cellData = cellBuffer.toString();
}
//setString(rowIndex, columnIndex, c); //text[0].getContent());
//columnIndex++;
}
}
for (int r = 0; r < cellRepeat; r++) {
if (cellData != null) {
//System.out.println("setting " + rowIndex + "," + columnIndex + " to " + cellData);
setString(rowIndex, columnIndex, cellData);
}
columnIndex++;
if (cellData != null) {
// if (columnIndex > columnMax) {
// columnMax = columnIndex;
// }
rowNotNull = true;
}
}
}
if (rowNotNull && rowRepeat > 1) {
String[] rowStrings = getStringRow(rowIndex);
for (int r = 1; r < rowRepeat; r++) {
addRow(rowStrings);
}
}
rowIndex += rowRepeat;
// if (rowNotNull) {
// rowMax = rowIndex;
// }
}
// if (rowMax != getRowCount()) {
// System.out.println("removing empty rows: " + rowMax + " instead of " + getRowCount());
// setRowCount(rowMax);
// }
// if (columnMax != getColumnCount()) {
// System.out.println("removing empty columns: " + columnMax + " instead of " + getColumnCount());
// setColumnCount(columnMax);
// }
}
protected void appendNotNull(XML kid, StringBuffer buffer) {
String content = kid.getContent();
if (content != null) {
buffer.append(content);
}
}
// static public PNode getContentXML(File file) {
// return new PNode(getContentReader(file));
// }
// static public BufferedReader getContentReader(File file) {
// return PApplet.createReader(getContentInput(file));
// }
/**
* Read zip file from a local file, and return the InputStream for content.xml.
*/
static protected InputStream getContentXML(File file) {
try {
ZipFile zip = new ZipFile(file);
ZipEntry entry = zip.getEntry("content.xml");
return zip.getInputStream(entry);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Read zip file from an InputStream, and return the InputStream for content.xml.
*/
static protected InputStream getContentXML(InputStream input) {
ZipInputStream zis = new ZipInputStream(input);
ZipEntry entry = null;
try {
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().equals("content.xml")) {
return zis;
// InputStreamReader isr = new InputStreamReader(zis);
// BufferedReader reader = new BufferedReader(isr);
// read(reader, actual);
// break;
//return entry.getInputStream();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}