propane 2.9.0-java → 2.9.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/Rakefile +2 -3
- data/lib/.gitignore +1 -0
- data/lib/export.txt +2 -0
- data/lib/propane/version.rb +1 -1
- data/pom.rb +2 -49
- data/pom.xml +1 -64
- data/propane.gemspec +13 -10
- data/src/main/java/processing/awt/PGraphicsJava2D.java +29 -20
- data/src/main/java/processing/core/PApplet.java +12854 -12803
- data/src/main/java/processing/core/PGraphics.java +17 -22
- data/src/main/java/processing/core/PShape.java +42 -20
- data/src/main/java/processing/core/PShapeSVG.java +20 -5
- data/src/main/java/processing/core/PSurfaceNone.java +1 -4
- data/src/main/java/processing/data/DoubleDict.java +848 -0
- data/src/main/java/processing/data/DoubleList.java +928 -0
- data/src/main/java/processing/data/FloatDict.java +18 -2
- data/src/main/java/processing/data/FloatList.java +26 -2
- data/src/main/java/processing/data/IntDict.java +12 -3
- data/src/main/java/processing/data/IntList.java +24 -1
- data/src/main/java/processing/data/JSONObject.java +2 -2
- data/src/main/java/processing/data/LongDict.java +800 -0
- data/src/main/java/processing/data/LongList.java +937 -0
- data/src/main/java/processing/data/Sort.java +1 -1
- data/src/main/java/processing/data/StringDict.java +12 -3
- data/src/main/java/processing/data/StringList.java +25 -2
- data/src/main/java/processing/data/Table.java +16 -5
- data/src/main/java/processing/data/XML.java +8 -1
- data/src/main/java/processing/opengl/PGL.java +23 -16
- data/src/main/java/processing/opengl/PGraphicsOpenGL.java +13 -10
- data/src/main/java/processing/opengl/PJOGL.java +35 -12
- data/src/main/java/processing/opengl/shaders/LightVert-brcm.glsl +154 -0
- data/src/main/java/processing/opengl/shaders/LightVert-vc4.glsl +2 -2
- data/src/main/java/processing/opengl/shaders/TexLightVert-brcm.glsl +160 -0
- data/src/main/java/processing/opengl/shaders/TexLightVert-vc4.glsl +2 -2
- metadata +22 -13
@@ -227,7 +227,7 @@ public class PGraphics extends PImage implements PConstants {
|
|
227
227
|
* images go out of scope, they will be properly garbage collected.
|
228
228
|
*/
|
229
229
|
protected WeakHashMap<PImage, Object> cacheMap =
|
230
|
-
new WeakHashMap
|
230
|
+
new WeakHashMap<>();
|
231
231
|
|
232
232
|
|
233
233
|
////////////////////////////////////////////////////////////
|
@@ -334,7 +334,7 @@ public class PGraphics extends PImage implements PConstants {
|
|
334
334
|
/** True if colors are not in the range 0..1 */
|
335
335
|
boolean colorModeScale; // = true;
|
336
336
|
|
337
|
-
/**
|
337
|
+
/**
|
338
338
|
* True if colorMode(RGB, 255). Defaults to true so that color()
|
339
339
|
* used as part of a field declaration will properly assign values.
|
340
340
|
*/
|
@@ -2016,6 +2016,7 @@ public class PGraphics extends PImage implements PConstants {
|
|
2016
2016
|
showMissingWarning("clip");
|
2017
2017
|
}
|
2018
2018
|
|
2019
|
+
|
2019
2020
|
/**
|
2020
2021
|
* ( begin auto-generated from noClip.xml )
|
2021
2022
|
*
|
@@ -2269,9 +2270,9 @@ public class PGraphics extends PImage implements PConstants {
|
|
2269
2270
|
*/
|
2270
2271
|
public void curveVertex(float x, float y) {
|
2271
2272
|
curveVertexCheck();
|
2272
|
-
float[]
|
2273
|
-
|
2274
|
-
|
2273
|
+
float[] v = curveVertices[curveVertexCount];
|
2274
|
+
v[X] = x;
|
2275
|
+
v[Y] = y;
|
2275
2276
|
curveVertexCount++;
|
2276
2277
|
|
2277
2278
|
// draw a segment if there are enough points
|
@@ -2292,10 +2293,10 @@ public class PGraphics extends PImage implements PConstants {
|
|
2292
2293
|
*/
|
2293
2294
|
public void curveVertex(float x, float y, float z) {
|
2294
2295
|
curveVertexCheck();
|
2295
|
-
float[]
|
2296
|
-
|
2297
|
-
|
2298
|
-
|
2296
|
+
float[] v = curveVertices[curveVertexCount];
|
2297
|
+
v[X] = x;
|
2298
|
+
v[Y] = y;
|
2299
|
+
v[Z] = z;
|
2299
2300
|
curveVertexCount++;
|
2300
2301
|
|
2301
2302
|
// draw a segment if there are enough points
|
@@ -3904,6 +3905,7 @@ public class PGraphics extends PImage implements PConstants {
|
|
3904
3905
|
}
|
3905
3906
|
|
3906
3907
|
|
3908
|
+
|
3907
3909
|
//////////////////////////////////////////////////////////////
|
3908
3910
|
|
3909
3911
|
// SHAPE
|
@@ -3957,7 +3959,6 @@ public class PGraphics extends PImage implements PConstants {
|
|
3957
3959
|
}
|
3958
3960
|
|
3959
3961
|
|
3960
|
-
|
3961
3962
|
/**
|
3962
3963
|
* ( begin auto-generated from shape.xml )
|
3963
3964
|
*
|
@@ -4055,6 +4056,7 @@ public class PGraphics extends PImage implements PConstants {
|
|
4055
4056
|
}
|
4056
4057
|
|
4057
4058
|
|
4059
|
+
|
4058
4060
|
//////////////////////////////////////////////////////////////
|
4059
4061
|
|
4060
4062
|
// TEXT/FONTS
|
@@ -4918,16 +4920,6 @@ public class PGraphics extends PImage implements PConstants {
|
|
4918
4920
|
}
|
4919
4921
|
|
4920
4922
|
|
4921
|
-
// public void text(String s, float a, float b, float c, float d, float z) {
|
4922
|
-
// if (z != 0) translate(0, 0, z); // slowness, badness
|
4923
|
-
//
|
4924
|
-
// text(s, a, b, c, d);
|
4925
|
-
// textZ = z;
|
4926
|
-
//
|
4927
|
-
// if (z != 0) translate(0, 0, -z); // TEMPORARY HACK! SLOW!
|
4928
|
-
// }
|
4929
|
-
|
4930
|
-
|
4931
4923
|
public void text(int num, float x, float y) {
|
4932
4924
|
text(String.valueOf(num), x, y);
|
4933
4925
|
}
|
@@ -6169,7 +6161,9 @@ public class PGraphics extends PImage implements PConstants {
|
|
6169
6161
|
ellipseMode(s.ellipseMode);
|
6170
6162
|
shapeMode(s.shapeMode);
|
6171
6163
|
|
6172
|
-
blendMode
|
6164
|
+
if (blendMode != s.blendMode) {
|
6165
|
+
blendMode(s.blendMode);
|
6166
|
+
}
|
6173
6167
|
|
6174
6168
|
if (s.tint) {
|
6175
6169
|
tint(s.tintColor);
|
@@ -7384,6 +7378,7 @@ public class PGraphics extends PImage implements PConstants {
|
|
7384
7378
|
pushStyle();
|
7385
7379
|
pushMatrix();
|
7386
7380
|
resetMatrix();
|
7381
|
+
noStroke();
|
7387
7382
|
fill(backgroundColor);
|
7388
7383
|
rect(0, 0, width, height);
|
7389
7384
|
popMatrix();
|
@@ -8128,7 +8123,7 @@ public class PGraphics extends PImage implements PConstants {
|
|
8128
8123
|
*/
|
8129
8124
|
static public void showWarning(String msg) { // ignore
|
8130
8125
|
if (warnings == null) {
|
8131
|
-
warnings = new HashMap
|
8126
|
+
warnings = new HashMap<>();
|
8132
8127
|
}
|
8133
8128
|
if (!warnings.containsKey(msg)) {
|
8134
8129
|
System.err.println(msg);
|
@@ -2040,7 +2040,7 @@ public class PShape implements PConstants {
|
|
2040
2040
|
parent.addName(nom, shape);
|
2041
2041
|
} else {
|
2042
2042
|
if (nameTable == null) {
|
2043
|
-
nameTable = new HashMap
|
2043
|
+
nameTable = new HashMap<>();
|
2044
2044
|
}
|
2045
2045
|
nameTable.put(nom, shape);
|
2046
2046
|
}
|
@@ -2387,14 +2387,14 @@ public class PShape implements PConstants {
|
|
2387
2387
|
/**
|
2388
2388
|
* ( begin auto-generated from PShape_setFill.xml )
|
2389
2389
|
*
|
2390
|
-
* The <b>setFill()</b> method defines the fill color of a <b>PShape</b>.
|
2391
|
-
* This method is used after shapes are created or when a shape is defined explicitly
|
2392
|
-
* (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in the above example.
|
2393
|
-
* When a shape is created with <b>beginShape()</b> and <b>endShape()</b>, its
|
2394
|
-
* attributes may be changed with <b>fill()</b> and <b>stroke()</b> within
|
2395
|
-
* <b>beginShape()</b> and <b>endShape()</b>. However, after the shape is
|
2396
|
-
* created, only the <b>setFill()</b> method can define a new fill value for
|
2397
|
-
* the <b>PShape</b>.
|
2390
|
+
* The <b>setFill()</b> method defines the fill color of a <b>PShape</b>.
|
2391
|
+
* This method is used after shapes are created or when a shape is defined explicitly
|
2392
|
+
* (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in the above example.
|
2393
|
+
* When a shape is created with <b>beginShape()</b> and <b>endShape()</b>, its
|
2394
|
+
* attributes may be changed with <b>fill()</b> and <b>stroke()</b> within
|
2395
|
+
* <b>beginShape()</b> and <b>endShape()</b>. However, after the shape is
|
2396
|
+
* created, only the <b>setFill()</b> method can define a new fill value for
|
2397
|
+
* the <b>PShape</b>.
|
2398
2398
|
*
|
2399
2399
|
* ( end auto-generated )
|
2400
2400
|
*
|
@@ -2543,14 +2543,14 @@ public class PShape implements PConstants {
|
|
2543
2543
|
/**
|
2544
2544
|
* ( begin auto-generated from PShape_setStroke.xml )
|
2545
2545
|
*
|
2546
|
-
* The <b>setStroke()</b> method defines the outline color of a <b>PShape</b>.
|
2547
|
-
* This method is used after shapes are created or when a shape is defined
|
2548
|
-
* explicitly (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in
|
2549
|
-
* the above example. When a shape is created with <b>beginShape()</b> and
|
2550
|
-
* <b>endShape()</b>, its attributes may be changed with <b>fill()</b> and
|
2551
|
-
* <b>stroke()</b> within <b>beginShape()</b> and <b>endShape()</b>.
|
2552
|
-
* However, after the shape is created, only the <b>setStroke()</b> method
|
2553
|
-
* can define a new stroke value for the <b>PShape</b>.
|
2546
|
+
* The <b>setStroke()</b> method defines the outline color of a <b>PShape</b>.
|
2547
|
+
* This method is used after shapes are created or when a shape is defined
|
2548
|
+
* explicitly (e.g. <b>createShape(RECT, 20, 20, 80, 80)</b>) as shown in
|
2549
|
+
* the above example. When a shape is created with <b>beginShape()</b> and
|
2550
|
+
* <b>endShape()</b>, its attributes may be changed with <b>fill()</b> and
|
2551
|
+
* <b>stroke()</b> within <b>beginShape()</b> and <b>endShape()</b>.
|
2552
|
+
* However, after the shape is created, only the <b>setStroke()</b> method
|
2553
|
+
* can define a new stroke value for the <b>PShape</b>.
|
2554
2554
|
*
|
2555
2555
|
* ( end auto-generated )
|
2556
2556
|
*
|
@@ -2891,13 +2891,24 @@ public class PShape implements PConstants {
|
|
2891
2891
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
2892
2892
|
|
2893
2893
|
|
2894
|
-
|
2894
|
+
/**
|
2895
|
+
* Return true if this x, y coordinate is part of this shape. Only works
|
2896
|
+
* with PATH shapes or GROUP shapes that contain other GROUPs or PATHs.
|
2897
|
+
*/
|
2895
2898
|
public boolean contains(float x, float y) {
|
2896
2899
|
if (family == PATH) {
|
2900
|
+
// apply the inverse transformation matrix to the point coordinates
|
2901
|
+
PMatrix inverseCoords = matrix.get();
|
2902
|
+
inverseCoords.invert(); // maybe cache this?
|
2903
|
+
inverseCoords.invert(); // maybe cache this?
|
2904
|
+
PVector p = new PVector();
|
2905
|
+
inverseCoords.mult(new PVector(x,y),p);
|
2906
|
+
|
2907
|
+
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
|
2897
2908
|
boolean c = false;
|
2898
2909
|
for (int i = 0, j = vertexCount-1; i < vertexCount; j = i++) {
|
2899
|
-
if (((vertices[i][Y] > y) != (vertices[j][Y] > y)) &&
|
2900
|
-
(x <
|
2910
|
+
if (((vertices[i][Y] > p.y) != (vertices[j][Y] > p.y)) &&
|
2911
|
+
(p.x <
|
2901
2912
|
(vertices[j][X]-vertices[i][X]) *
|
2902
2913
|
(y-vertices[i][Y]) /
|
2903
2914
|
(vertices[j][1]-vertices[i][Y]) +
|
@@ -2906,7 +2917,18 @@ public class PShape implements PConstants {
|
|
2906
2917
|
}
|
2907
2918
|
}
|
2908
2919
|
return c;
|
2920
|
+
|
2921
|
+
} else if (family == GROUP) {
|
2922
|
+
// If this is a group, loop through children until we find one that
|
2923
|
+
// contains the supplied coordinates. If a child does not support
|
2924
|
+
// contains() throw a warning and continue.
|
2925
|
+
for (int i = 0; i < childCount; i++) {
|
2926
|
+
if (children[i].contains(x, y)) return true;
|
2927
|
+
}
|
2928
|
+
return false;
|
2929
|
+
|
2909
2930
|
} else {
|
2931
|
+
// https://github.com/processing/processing/issues/1280
|
2910
2932
|
throw new IllegalArgumentException("The contains() method is only implemented for paths.");
|
2911
2933
|
}
|
2912
2934
|
}
|
@@ -32,6 +32,8 @@ import java.awt.geom.Point2D;
|
|
32
32
|
|
33
33
|
import java.util.Map;
|
34
34
|
import java.util.HashMap;
|
35
|
+
import java.util.regex.Matcher;
|
36
|
+
import java.util.regex.Pattern;
|
35
37
|
|
36
38
|
|
37
39
|
/**
|
@@ -452,14 +454,27 @@ public class PShapeSVG extends PShape {
|
|
452
454
|
|
453
455
|
String pointsAttr = element.getString("points");
|
454
456
|
if (pointsAttr != null) {
|
455
|
-
|
456
|
-
|
457
|
+
Pattern pattern = Pattern.compile("([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)(,?\\s*)([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)");
|
458
|
+
Matcher matcher = pattern.matcher(pointsAttr);
|
459
|
+
vertexCount = 0;
|
460
|
+
while (matcher.find()) {
|
461
|
+
vertexCount++;
|
462
|
+
}
|
463
|
+
matcher.reset();
|
457
464
|
vertices = new float[vertexCount][2];
|
458
465
|
for (int i = 0; i < vertexCount; i++) {
|
459
|
-
|
460
|
-
vertices[i][X] = Float.parseFloat(
|
461
|
-
vertices[i][Y] = Float.parseFloat(
|
466
|
+
matcher.find();
|
467
|
+
vertices[i][X] = Float.parseFloat(matcher.group(1));
|
468
|
+
vertices[i][Y] = Float.parseFloat(matcher.group(5));
|
462
469
|
}
|
470
|
+
// String[] pointsBuffer = PApplet.splitTokens(pointsAttr);
|
471
|
+
// vertexCount = pointsBuffer.length;
|
472
|
+
// vertices = new float[vertexCount][2];
|
473
|
+
// for (int i = 0; i < vertexCount; i++) {
|
474
|
+
// String pb[] = PApplet.splitTokens(pointsBuffer[i], ", \t\r\n");
|
475
|
+
// vertices[i][X] = Float.parseFloat(pb[0]);
|
476
|
+
// vertices[i][Y] = Float.parseFloat(pb[1]);
|
477
|
+
// }
|
463
478
|
}
|
464
479
|
}
|
465
480
|
|
@@ -58,10 +58,7 @@ public class PSurfaceNone implements PSurface {
|
|
58
58
|
|
59
59
|
|
60
60
|
@Override
|
61
|
-
public void initFrame(PApplet sketch) {
|
62
|
-
int deviceIndex, boolean fullScreen,
|
63
|
-
boolean spanDisplays) {*/
|
64
|
-
//this.sketch = sketch;
|
61
|
+
public void initFrame(PApplet sketch) {
|
65
62
|
throw new IllegalStateException("initFrame() not available with " +
|
66
63
|
getClass().getSimpleName());
|
67
64
|
}
|
@@ -0,0 +1,848 @@
|
|
1
|
+
package processing.data;
|
2
|
+
|
3
|
+
import java.io.*;
|
4
|
+
import java.util.HashMap;
|
5
|
+
import java.util.Iterator;
|
6
|
+
import java.util.Map;
|
7
|
+
|
8
|
+
import processing.core.PApplet;
|
9
|
+
|
10
|
+
|
11
|
+
/**
|
12
|
+
* A simple table class to use a String as a lookup for an double value.
|
13
|
+
*
|
14
|
+
* @webref data:composite
|
15
|
+
* @see IntDict
|
16
|
+
* @see StringDict
|
17
|
+
*/
|
18
|
+
public class DoubleDict {
|
19
|
+
|
20
|
+
/** Number of elements in the table */
|
21
|
+
protected int count;
|
22
|
+
|
23
|
+
protected String[] keys;
|
24
|
+
protected double[] values;
|
25
|
+
|
26
|
+
/** Internal implementation for faster lookups */
|
27
|
+
private HashMap<String, Integer> indices = new HashMap<>();
|
28
|
+
|
29
|
+
|
30
|
+
public DoubleDict() {
|
31
|
+
count = 0;
|
32
|
+
keys = new String[10];
|
33
|
+
values = new double[10];
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Create a new lookup with a specific size. This is more efficient than not
|
39
|
+
* specifying a size. Use it when you know the rough size of the thing you're creating.
|
40
|
+
*
|
41
|
+
* @nowebref
|
42
|
+
*/
|
43
|
+
public DoubleDict(int length) {
|
44
|
+
count = 0;
|
45
|
+
keys = new String[length];
|
46
|
+
values = new double[length];
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Read a set of entries from a Reader that has each key/value pair on
|
52
|
+
* a single line, separated by a tab.
|
53
|
+
*
|
54
|
+
* @nowebref
|
55
|
+
*/
|
56
|
+
public DoubleDict(BufferedReader reader) {
|
57
|
+
String[] lines = PApplet.loadStrings(reader);
|
58
|
+
keys = new String[lines.length];
|
59
|
+
values = new double[lines.length];
|
60
|
+
|
61
|
+
for (int i = 0; i < lines.length; i++) {
|
62
|
+
String[] pieces = PApplet.split(lines[i], '\t');
|
63
|
+
if (pieces.length == 2) {
|
64
|
+
keys[count] = pieces[0];
|
65
|
+
values[count] = PApplet.parseFloat(pieces[1]);
|
66
|
+
indices.put(pieces[0], count);
|
67
|
+
count++;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
/**
|
74
|
+
* @nowebref
|
75
|
+
*/
|
76
|
+
public DoubleDict(String[] keys, double[] values) {
|
77
|
+
if (keys.length != values.length) {
|
78
|
+
throw new IllegalArgumentException("key and value arrays must be the same length");
|
79
|
+
}
|
80
|
+
this.keys = keys;
|
81
|
+
this.values = values;
|
82
|
+
count = keys.length;
|
83
|
+
for (int i = 0; i < count; i++) {
|
84
|
+
indices.put(keys[i], i);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Constructor to allow (more intuitive) inline initialization, e.g.:
|
91
|
+
* <pre>
|
92
|
+
* new FloatDict(new Object[][] {
|
93
|
+
* { "key1", 1 },
|
94
|
+
* { "key2", 2 }
|
95
|
+
* });
|
96
|
+
* </pre>
|
97
|
+
*/
|
98
|
+
public DoubleDict(Object[][] pairs) {
|
99
|
+
count = pairs.length;
|
100
|
+
this.keys = new String[count];
|
101
|
+
this.values = new double[count];
|
102
|
+
for (int i = 0; i < count; i++) {
|
103
|
+
keys[i] = (String) pairs[i][0];
|
104
|
+
values[i] = (Float) pairs[i][1];
|
105
|
+
indices.put(keys[i], i);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
|
110
|
+
public DoubleDict(Map<String, Double> incoming) {
|
111
|
+
count = incoming.size();
|
112
|
+
keys = new String[count];
|
113
|
+
values = new double[count];
|
114
|
+
int index = 0;
|
115
|
+
for (Map.Entry<String, Double> e : incoming.entrySet()) {
|
116
|
+
keys[index] = e.getKey();
|
117
|
+
values[index] = e.getValue();
|
118
|
+
indices.put(keys[index], index);
|
119
|
+
index++;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
|
124
|
+
/**
|
125
|
+
* @webref doubledict:method
|
126
|
+
* @brief Returns the number of key/value pairs
|
127
|
+
*/
|
128
|
+
public int size() {
|
129
|
+
return count;
|
130
|
+
}
|
131
|
+
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Resize the internal data, this can only be used to shrink the list.
|
135
|
+
* Helpful for situations like sorting and then grabbing the top 50 entries.
|
136
|
+
*/
|
137
|
+
public void resize(int length) {
|
138
|
+
if (length == count) return;
|
139
|
+
|
140
|
+
if (length > count) {
|
141
|
+
throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
|
142
|
+
}
|
143
|
+
if (length < 1) {
|
144
|
+
throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
|
145
|
+
}
|
146
|
+
|
147
|
+
String[] newKeys = new String[length];
|
148
|
+
double[] newValues = new double[length];
|
149
|
+
PApplet.arrayCopy(keys, newKeys, length);
|
150
|
+
PApplet.arrayCopy(values, newValues, length);
|
151
|
+
keys = newKeys;
|
152
|
+
values = newValues;
|
153
|
+
count = length;
|
154
|
+
resetIndices();
|
155
|
+
}
|
156
|
+
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Remove all entries.
|
160
|
+
*
|
161
|
+
* @webref doubledict:method
|
162
|
+
* @brief Remove all entries
|
163
|
+
*/
|
164
|
+
public void clear() {
|
165
|
+
count = 0;
|
166
|
+
indices = new HashMap<>();
|
167
|
+
}
|
168
|
+
|
169
|
+
|
170
|
+
private void resetIndices() {
|
171
|
+
indices = new HashMap<>(count);
|
172
|
+
for (int i = 0; i < count; i++) {
|
173
|
+
indices.put(keys[i], i);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
|
178
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
179
|
+
|
180
|
+
|
181
|
+
public class Entry {
|
182
|
+
public String key;
|
183
|
+
public double value;
|
184
|
+
|
185
|
+
Entry(String key, double value) {
|
186
|
+
this.key = key;
|
187
|
+
this.value = value;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
|
192
|
+
public Iterable<Entry> entries() {
|
193
|
+
return new Iterable<Entry>() {
|
194
|
+
|
195
|
+
public Iterator<Entry> iterator() {
|
196
|
+
return entryIterator();
|
197
|
+
}
|
198
|
+
};
|
199
|
+
}
|
200
|
+
|
201
|
+
|
202
|
+
public Iterator<Entry> entryIterator() {
|
203
|
+
return new Iterator<Entry>() {
|
204
|
+
int index = -1;
|
205
|
+
|
206
|
+
public void remove() {
|
207
|
+
removeIndex(index);
|
208
|
+
index--;
|
209
|
+
}
|
210
|
+
|
211
|
+
public Entry next() {
|
212
|
+
++index;
|
213
|
+
Entry e = new Entry(keys[index], values[index]);
|
214
|
+
return e;
|
215
|
+
}
|
216
|
+
|
217
|
+
public boolean hasNext() {
|
218
|
+
return index+1 < size();
|
219
|
+
}
|
220
|
+
};
|
221
|
+
}
|
222
|
+
|
223
|
+
|
224
|
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
225
|
+
|
226
|
+
|
227
|
+
public String key(int index) {
|
228
|
+
return keys[index];
|
229
|
+
}
|
230
|
+
|
231
|
+
|
232
|
+
protected void crop() {
|
233
|
+
if (count != keys.length) {
|
234
|
+
keys = PApplet.subset(keys, 0, count);
|
235
|
+
values = PApplet.subset(values, 0, count);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
|
240
|
+
public Iterable<String> keys() {
|
241
|
+
return new Iterable<String>() {
|
242
|
+
|
243
|
+
@Override
|
244
|
+
public Iterator<String> iterator() {
|
245
|
+
return keyIterator();
|
246
|
+
}
|
247
|
+
};
|
248
|
+
}
|
249
|
+
|
250
|
+
|
251
|
+
// Use this to iterate when you want to be able to remove elements along the way
|
252
|
+
public Iterator<String> keyIterator() {
|
253
|
+
return new Iterator<String>() {
|
254
|
+
int index = -1;
|
255
|
+
|
256
|
+
public void remove() {
|
257
|
+
removeIndex(index);
|
258
|
+
index--;
|
259
|
+
}
|
260
|
+
|
261
|
+
public String next() {
|
262
|
+
return key(++index);
|
263
|
+
}
|
264
|
+
|
265
|
+
public boolean hasNext() {
|
266
|
+
return index+1 < size();
|
267
|
+
}
|
268
|
+
};
|
269
|
+
}
|
270
|
+
|
271
|
+
|
272
|
+
/**
|
273
|
+
* Return a copy of the internal keys array. This array can be modified.
|
274
|
+
*
|
275
|
+
* @webref doubledict:method
|
276
|
+
* @brief Return a copy of the internal keys array
|
277
|
+
*/
|
278
|
+
public String[] keyArray() {
|
279
|
+
crop();
|
280
|
+
return keyArray(null);
|
281
|
+
}
|
282
|
+
|
283
|
+
|
284
|
+
public String[] keyArray(String[] outgoing) {
|
285
|
+
if (outgoing == null || outgoing.length != count) {
|
286
|
+
outgoing = new String[count];
|
287
|
+
}
|
288
|
+
System.arraycopy(keys, 0, outgoing, 0, count);
|
289
|
+
return outgoing;
|
290
|
+
}
|
291
|
+
|
292
|
+
|
293
|
+
public double value(int index) {
|
294
|
+
return values[index];
|
295
|
+
}
|
296
|
+
|
297
|
+
|
298
|
+
/**
|
299
|
+
* @webref doubledict:method
|
300
|
+
* @brief Return the internal array being used to store the values
|
301
|
+
*/
|
302
|
+
public Iterable<Double> values() {
|
303
|
+
return new Iterable<Double>() {
|
304
|
+
|
305
|
+
@Override
|
306
|
+
public Iterator<Double> iterator() {
|
307
|
+
return valueIterator();
|
308
|
+
}
|
309
|
+
};
|
310
|
+
}
|
311
|
+
|
312
|
+
|
313
|
+
public Iterator<Double> valueIterator() {
|
314
|
+
return new Iterator<Double>() {
|
315
|
+
int index = -1;
|
316
|
+
|
317
|
+
public void remove() {
|
318
|
+
removeIndex(index);
|
319
|
+
index--;
|
320
|
+
}
|
321
|
+
|
322
|
+
public Double next() {
|
323
|
+
return value(++index);
|
324
|
+
}
|
325
|
+
|
326
|
+
public boolean hasNext() {
|
327
|
+
return index+1 < size();
|
328
|
+
}
|
329
|
+
};
|
330
|
+
}
|
331
|
+
|
332
|
+
|
333
|
+
/**
|
334
|
+
* Create a new array and copy each of the values into it.
|
335
|
+
*
|
336
|
+
* @webref doubledict:method
|
337
|
+
* @brief Create a new array and copy each of the values into it
|
338
|
+
*/
|
339
|
+
public double[] valueArray() {
|
340
|
+
crop();
|
341
|
+
return valueArray(null);
|
342
|
+
}
|
343
|
+
|
344
|
+
|
345
|
+
/**
|
346
|
+
* Fill an already-allocated array with the values (more efficient than
|
347
|
+
* creating a new array each time). If 'array' is null, or not the same
|
348
|
+
* size as the number of values, a new array will be allocated and returned.
|
349
|
+
*/
|
350
|
+
public double[] valueArray(double[] array) {
|
351
|
+
if (array == null || array.length != size()) {
|
352
|
+
array = new double[count];
|
353
|
+
}
|
354
|
+
System.arraycopy(values, 0, array, 0, count);
|
355
|
+
return array;
|
356
|
+
}
|
357
|
+
|
358
|
+
|
359
|
+
/**
|
360
|
+
* Return a value for the specified key.
|
361
|
+
*
|
362
|
+
* @webref doubledict:method
|
363
|
+
* @brief Return a value for the specified key
|
364
|
+
*/
|
365
|
+
public double get(String key) {
|
366
|
+
int index = index(key);
|
367
|
+
if (index == -1) {
|
368
|
+
throw new IllegalArgumentException("No key named '" + key + "'");
|
369
|
+
}
|
370
|
+
return values[index];
|
371
|
+
}
|
372
|
+
|
373
|
+
|
374
|
+
public double get(String key, double alternate) {
|
375
|
+
int index = index(key);
|
376
|
+
if (index == -1) {
|
377
|
+
return alternate;
|
378
|
+
}
|
379
|
+
return values[index];
|
380
|
+
}
|
381
|
+
|
382
|
+
|
383
|
+
/**
|
384
|
+
* @webref doubledict:method
|
385
|
+
* @brief Create a new key/value pair or change the value of one
|
386
|
+
*/
|
387
|
+
public void set(String key, double amount) {
|
388
|
+
int index = index(key);
|
389
|
+
if (index == -1) {
|
390
|
+
create(key, amount);
|
391
|
+
} else {
|
392
|
+
values[index] = amount;
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
|
397
|
+
public void setIndex(int index, String key, double value) {
|
398
|
+
if (index < 0 || index >= count) {
|
399
|
+
throw new ArrayIndexOutOfBoundsException(index);
|
400
|
+
}
|
401
|
+
keys[index] = key;
|
402
|
+
values[index] = value;
|
403
|
+
}
|
404
|
+
|
405
|
+
|
406
|
+
/**
|
407
|
+
* @webref doubledict:method
|
408
|
+
* @brief Check if a key is a part of the data structure
|
409
|
+
*/
|
410
|
+
public boolean hasKey(String key) {
|
411
|
+
return index(key) != -1;
|
412
|
+
}
|
413
|
+
|
414
|
+
|
415
|
+
/**
|
416
|
+
* @webref doubledict:method
|
417
|
+
* @brief Add to a value
|
418
|
+
*/
|
419
|
+
public void add(String key, double amount) {
|
420
|
+
int index = index(key);
|
421
|
+
if (index == -1) {
|
422
|
+
create(key, amount);
|
423
|
+
} else {
|
424
|
+
values[index] += amount;
|
425
|
+
}
|
426
|
+
}
|
427
|
+
|
428
|
+
|
429
|
+
/**
|
430
|
+
* @webref doubledict:method
|
431
|
+
* @brief Subtract from a value
|
432
|
+
*/
|
433
|
+
public void sub(String key, double amount) {
|
434
|
+
add(key, -amount);
|
435
|
+
}
|
436
|
+
|
437
|
+
|
438
|
+
/**
|
439
|
+
* @webref doubledict:method
|
440
|
+
* @brief Multiply a value
|
441
|
+
*/
|
442
|
+
public void mult(String key, double amount) {
|
443
|
+
int index = index(key);
|
444
|
+
if (index != -1) {
|
445
|
+
values[index] *= amount;
|
446
|
+
}
|
447
|
+
}
|
448
|
+
|
449
|
+
|
450
|
+
/**
|
451
|
+
* @webref doubledict:method
|
452
|
+
* @brief Divide a value
|
453
|
+
*/
|
454
|
+
public void div(String key, double amount) {
|
455
|
+
int index = index(key);
|
456
|
+
if (index != -1) {
|
457
|
+
values[index] /= amount;
|
458
|
+
}
|
459
|
+
}
|
460
|
+
|
461
|
+
|
462
|
+
private void checkMinMax(String functionName) {
|
463
|
+
if (count == 0) {
|
464
|
+
String msg =
|
465
|
+
String.format("Cannot use %s() on an empty %s.",
|
466
|
+
functionName, getClass().getSimpleName());
|
467
|
+
throw new RuntimeException(msg);
|
468
|
+
}
|
469
|
+
}
|
470
|
+
|
471
|
+
|
472
|
+
/**
|
473
|
+
* @webref doublelist:method
|
474
|
+
* @brief Return the smallest value
|
475
|
+
*/
|
476
|
+
public int minIndex() {
|
477
|
+
//checkMinMax("minIndex");
|
478
|
+
if (count == 0) return -1;
|
479
|
+
|
480
|
+
// Will still return NaN if there are 1 or more entries, and they're all NaN
|
481
|
+
double m = Float.NaN;
|
482
|
+
int mi = -1;
|
483
|
+
for (int i = 0; i < count; i++) {
|
484
|
+
// find one good value to start
|
485
|
+
if (values[i] == values[i]) {
|
486
|
+
m = values[i];
|
487
|
+
mi = i;
|
488
|
+
|
489
|
+
// calculate the rest
|
490
|
+
for (int j = i+1; j < count; j++) {
|
491
|
+
double d = values[j];
|
492
|
+
if ((d == d) && (d < m)) {
|
493
|
+
m = values[j];
|
494
|
+
mi = j;
|
495
|
+
}
|
496
|
+
}
|
497
|
+
break;
|
498
|
+
}
|
499
|
+
}
|
500
|
+
return mi;
|
501
|
+
}
|
502
|
+
|
503
|
+
|
504
|
+
// return the key for the minimum value
|
505
|
+
public String minKey() {
|
506
|
+
checkMinMax("minKey");
|
507
|
+
int index = minIndex();
|
508
|
+
if (index == -1) {
|
509
|
+
return null;
|
510
|
+
}
|
511
|
+
return keys[index];
|
512
|
+
}
|
513
|
+
|
514
|
+
|
515
|
+
// return the minimum value, or throw an error if there are no values
|
516
|
+
public double minValue() {
|
517
|
+
checkMinMax("minValue");
|
518
|
+
int index = minIndex();
|
519
|
+
if (index == -1) {
|
520
|
+
return Float.NaN;
|
521
|
+
}
|
522
|
+
return values[index];
|
523
|
+
}
|
524
|
+
|
525
|
+
|
526
|
+
/**
|
527
|
+
* @webref doublelist:method
|
528
|
+
* @brief Return the largest value
|
529
|
+
*/
|
530
|
+
// The index of the entry that has the max value. Reference above is incorrect.
|
531
|
+
public int maxIndex() {
|
532
|
+
//checkMinMax("maxIndex");
|
533
|
+
if (count == 0) {
|
534
|
+
return -1;
|
535
|
+
}
|
536
|
+
// Will still return NaN if there is 1 or more entries, and they're all NaN
|
537
|
+
double m = Double.NaN;
|
538
|
+
int mi = -1;
|
539
|
+
for (int i = 0; i < count; i++) {
|
540
|
+
// find one good value to start
|
541
|
+
if (values[i] == values[i]) {
|
542
|
+
m = values[i];
|
543
|
+
mi = i;
|
544
|
+
|
545
|
+
// calculate the rest
|
546
|
+
for (int j = i+1; j < count; j++) {
|
547
|
+
double d = values[j];
|
548
|
+
if (!Double.isNaN(d) && (d > m)) {
|
549
|
+
m = values[j];
|
550
|
+
mi = j;
|
551
|
+
}
|
552
|
+
}
|
553
|
+
break;
|
554
|
+
}
|
555
|
+
}
|
556
|
+
return mi;
|
557
|
+
}
|
558
|
+
|
559
|
+
|
560
|
+
/** The key for a max value; null if empty or everything is NaN (no max). */
|
561
|
+
public String maxKey() {
|
562
|
+
//checkMinMax("maxKey");
|
563
|
+
int index = maxIndex();
|
564
|
+
if (index == -1) {
|
565
|
+
return null;
|
566
|
+
}
|
567
|
+
return keys[index];
|
568
|
+
}
|
569
|
+
|
570
|
+
|
571
|
+
/** The max value. (Or NaN if no entries or they're all NaN.) */
|
572
|
+
public double maxValue() {
|
573
|
+
//checkMinMax("maxValue");
|
574
|
+
int index = maxIndex();
|
575
|
+
if (index == -1) {
|
576
|
+
return Float.NaN;
|
577
|
+
}
|
578
|
+
return values[index];
|
579
|
+
}
|
580
|
+
|
581
|
+
|
582
|
+
public double sum() {
|
583
|
+
double sum = 0;
|
584
|
+
for (int i = 0; i < count; i++) {
|
585
|
+
sum += values[i];
|
586
|
+
}
|
587
|
+
return sum;
|
588
|
+
}
|
589
|
+
|
590
|
+
|
591
|
+
public int index(String what) {
|
592
|
+
Integer found = indices.get(what);
|
593
|
+
return (found == null) ? -1 : found.intValue();
|
594
|
+
}
|
595
|
+
|
596
|
+
|
597
|
+
protected void create(String what, double much) {
|
598
|
+
if (count == keys.length) {
|
599
|
+
keys = PApplet.expand(keys);
|
600
|
+
values = PApplet.expand(values);
|
601
|
+
}
|
602
|
+
indices.put(what, Integer.valueOf(count));
|
603
|
+
keys[count] = what;
|
604
|
+
values[count] = much;
|
605
|
+
count++;
|
606
|
+
}
|
607
|
+
|
608
|
+
|
609
|
+
/**
|
610
|
+
* @webref doubledict:method
|
611
|
+
* @brief Remove a key/value pair
|
612
|
+
*/
|
613
|
+
public int remove(String key) {
|
614
|
+
int index = index(key);
|
615
|
+
if (index != -1) {
|
616
|
+
removeIndex(index);
|
617
|
+
}
|
618
|
+
return index;
|
619
|
+
}
|
620
|
+
|
621
|
+
|
622
|
+
public String removeIndex(int index) {
|
623
|
+
if (index < 0 || index >= count) {
|
624
|
+
throw new ArrayIndexOutOfBoundsException(index);
|
625
|
+
}
|
626
|
+
String key = keys[index];
|
627
|
+
//System.out.println("index is " + which + " and " + keys[which]);
|
628
|
+
indices.remove(keys[index]);
|
629
|
+
for (int i = index; i < count-1; i++) {
|
630
|
+
keys[i] = keys[i+1];
|
631
|
+
values[i] = values[i+1];
|
632
|
+
indices.put(keys[i], i);
|
633
|
+
}
|
634
|
+
count--;
|
635
|
+
keys[count] = null;
|
636
|
+
values[count] = 0;
|
637
|
+
return key;
|
638
|
+
}
|
639
|
+
|
640
|
+
|
641
|
+
public void swap(int a, int b) {
|
642
|
+
String tkey = keys[a];
|
643
|
+
double tvalue = values[a];
|
644
|
+
keys[a] = keys[b];
|
645
|
+
values[a] = values[b];
|
646
|
+
keys[b] = tkey;
|
647
|
+
values[b] = tvalue;
|
648
|
+
|
649
|
+
// indices.put(keys[a], Integer.valueOf(a));
|
650
|
+
// indices.put(keys[b], Integer.valueOf(b));
|
651
|
+
}
|
652
|
+
|
653
|
+
|
654
|
+
/**
|
655
|
+
* Sort the keys alphabetically (ignoring case). Uses the value as a
|
656
|
+
* tie-breaker (only really possible with a key that has a case change).
|
657
|
+
*
|
658
|
+
* @webref doubledict:method
|
659
|
+
* @brief Sort the keys alphabetically
|
660
|
+
*/
|
661
|
+
public void sortKeys() {
|
662
|
+
sortImpl(true, false, true);
|
663
|
+
}
|
664
|
+
|
665
|
+
|
666
|
+
/**
|
667
|
+
* @webref doubledict:method
|
668
|
+
* @brief Sort the keys alphabetically in reverse
|
669
|
+
*/
|
670
|
+
public void sortKeysReverse() {
|
671
|
+
sortImpl(true, true, true);
|
672
|
+
}
|
673
|
+
|
674
|
+
|
675
|
+
/**
|
676
|
+
* Sort by values in descending order (largest value will be at [0]).
|
677
|
+
*
|
678
|
+
* @webref doubledict:method
|
679
|
+
* @brief Sort by values in ascending order
|
680
|
+
*/
|
681
|
+
public void sortValues() {
|
682
|
+
sortValues(true);
|
683
|
+
}
|
684
|
+
|
685
|
+
|
686
|
+
/**
|
687
|
+
* Set true to ensure that the order returned is identical. Slightly
|
688
|
+
* slower because the tie-breaker for identical values compares the keys.
|
689
|
+
* @param stable
|
690
|
+
*/
|
691
|
+
public void sortValues(boolean stable) {
|
692
|
+
sortImpl(false, false, stable);
|
693
|
+
}
|
694
|
+
|
695
|
+
|
696
|
+
/**
|
697
|
+
* @webref doubledict:method
|
698
|
+
* @brief Sort by values in descending order
|
699
|
+
*/
|
700
|
+
public void sortValuesReverse() {
|
701
|
+
sortValuesReverse(true);
|
702
|
+
}
|
703
|
+
|
704
|
+
|
705
|
+
public void sortValuesReverse(boolean stable) {
|
706
|
+
sortImpl(false, true, stable);
|
707
|
+
}
|
708
|
+
|
709
|
+
|
710
|
+
protected void sortImpl(final boolean useKeys, final boolean reverse,
|
711
|
+
final boolean stable) {
|
712
|
+
Sort s = new Sort() {
|
713
|
+
@Override
|
714
|
+
public int size() {
|
715
|
+
if (useKeys) {
|
716
|
+
return count; // don't worry about NaN values
|
717
|
+
|
718
|
+
} else if (count == 0) { // skip the NaN check, it'll AIOOBE
|
719
|
+
return 0;
|
720
|
+
|
721
|
+
} else { // first move NaN values to the end of the list
|
722
|
+
int right = count - 1;
|
723
|
+
while (values[right] != values[right]) {
|
724
|
+
right--;
|
725
|
+
if (right == -1) {
|
726
|
+
return 0; // all values are NaN
|
727
|
+
}
|
728
|
+
}
|
729
|
+
for (int i = right; i >= 0; --i) {
|
730
|
+
if (Double.isNaN(values[i])) {
|
731
|
+
swap(i, right);
|
732
|
+
--right;
|
733
|
+
}
|
734
|
+
}
|
735
|
+
return right + 1;
|
736
|
+
}
|
737
|
+
}
|
738
|
+
|
739
|
+
@Override
|
740
|
+
public int compare(int a, int b) {
|
741
|
+
double diff = 0;
|
742
|
+
if (useKeys) {
|
743
|
+
diff = keys[a].compareToIgnoreCase(keys[b]);
|
744
|
+
if (diff == 0) {
|
745
|
+
diff = values[a] - values[b];
|
746
|
+
}
|
747
|
+
} else { // sort values
|
748
|
+
diff = values[a] - values[b];
|
749
|
+
if (diff == 0 && stable) {
|
750
|
+
diff = keys[a].compareToIgnoreCase(keys[b]);
|
751
|
+
}
|
752
|
+
}
|
753
|
+
if (diff == 0) {
|
754
|
+
return 0;
|
755
|
+
} else if (reverse) {
|
756
|
+
return diff < 0 ? 1 : -1;
|
757
|
+
} else {
|
758
|
+
return diff < 0 ? -1 : 1;
|
759
|
+
}
|
760
|
+
}
|
761
|
+
|
762
|
+
@Override
|
763
|
+
public void swap(int a, int b) {
|
764
|
+
DoubleDict.this.swap(a, b);
|
765
|
+
}
|
766
|
+
};
|
767
|
+
s.run();
|
768
|
+
|
769
|
+
// Set the indices after sort/swaps (performance fix 160411)
|
770
|
+
resetIndices();
|
771
|
+
}
|
772
|
+
|
773
|
+
|
774
|
+
/**
|
775
|
+
* Sum all of the values in this dictionary, then return a new FloatDict of
|
776
|
+
* each key, divided by the total sum. The total for all values will be ~1.0.
|
777
|
+
* @return a FloatDict with the original keys, mapped to their pct of the total
|
778
|
+
*/
|
779
|
+
public DoubleDict getPercent() {
|
780
|
+
double sum = sum();
|
781
|
+
DoubleDict outgoing = new DoubleDict();
|
782
|
+
for (int i = 0; i < size(); i++) {
|
783
|
+
double percent = value(i) / sum;
|
784
|
+
outgoing.set(key(i), percent);
|
785
|
+
}
|
786
|
+
return outgoing;
|
787
|
+
}
|
788
|
+
|
789
|
+
|
790
|
+
/** Returns a duplicate copy of this object. */
|
791
|
+
public DoubleDict copy() {
|
792
|
+
DoubleDict outgoing = new DoubleDict(count);
|
793
|
+
System.arraycopy(keys, 0, outgoing.keys, 0, count);
|
794
|
+
System.arraycopy(values, 0, outgoing.values, 0, count);
|
795
|
+
for (int i = 0; i < count; i++) {
|
796
|
+
outgoing.indices.put(keys[i], i);
|
797
|
+
}
|
798
|
+
outgoing.count = count;
|
799
|
+
return outgoing;
|
800
|
+
}
|
801
|
+
|
802
|
+
|
803
|
+
public void print() {
|
804
|
+
for (int i = 0; i < size(); i++) {
|
805
|
+
System.out.println(keys[i] + " = " + values[i]);
|
806
|
+
}
|
807
|
+
}
|
808
|
+
|
809
|
+
|
810
|
+
/**
|
811
|
+
* Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
|
812
|
+
*/
|
813
|
+
public void save(File file) {
|
814
|
+
PrintWriter writer = PApplet.createWriter(file);
|
815
|
+
write(writer);
|
816
|
+
writer.close();
|
817
|
+
}
|
818
|
+
|
819
|
+
|
820
|
+
/**
|
821
|
+
* Write tab-delimited entries out to
|
822
|
+
* @param writer
|
823
|
+
*/
|
824
|
+
public void write(PrintWriter writer) {
|
825
|
+
for (int i = 0; i < count; i++) {
|
826
|
+
writer.println(keys[i] + "\t" + values[i]);
|
827
|
+
}
|
828
|
+
writer.flush();
|
829
|
+
}
|
830
|
+
|
831
|
+
|
832
|
+
/**
|
833
|
+
* Return this dictionary as a String in JSON format.
|
834
|
+
*/
|
835
|
+
public String toJSON() {
|
836
|
+
StringList items = new StringList();
|
837
|
+
for (int i = 0; i < count; i++) {
|
838
|
+
items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
|
839
|
+
}
|
840
|
+
return "{ " + items.join(", ") + " }";
|
841
|
+
}
|
842
|
+
|
843
|
+
|
844
|
+
@Override
|
845
|
+
public String toString() {
|
846
|
+
return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
|
847
|
+
}
|
848
|
+
}
|