Skip to content

Commit eeab190

Browse files
committed
add more object/array methods
1 parent bd280a8 commit eeab190

File tree

15 files changed

+284
-166
lines changed

15 files changed

+284
-166
lines changed

benchmark/src/jmh/java/alpine/json/JsonWriteBenchmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public void setup() throws Exception {
7171

7272
@Benchmark
7373
public void alpine(Blackhole blackhole) throws Exception {
74-
blackhole.consume(Json.write(this.alpineArray[this.index++ % INPUTS.length], Json.Formatting.COMPACT));
74+
blackhole.consume(Json.write(this.alpineArray[this.index++ % INPUTS.length], JsonFormatting.COMPACT));
7575
}
7676

7777
@Benchmark

json/src/main/java/alpine/json/ArrayElement.java

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
import org.jetbrains.annotations.NotNull;
44
import org.jetbrains.annotations.Nullable;
55

6-
import java.util.ArrayList;
7-
import java.util.Iterator;
8-
import java.util.List;
9-
import java.util.Objects;
6+
import java.util.*;
107
import java.util.function.Consumer;
8+
import java.util.function.Predicate;
119
import java.util.stream.Stream;
1210

1311
/**
@@ -39,7 +37,7 @@ public boolean equals(Object object) {
3937

4038
@Override
4139
public String toString() {
42-
return Json.write(this, Json.Formatting.INLINE_PRETTY);
40+
return Json.write(this, JsonFormatting.INLINE);
4341
}
4442

4543
@Override
@@ -95,6 +93,54 @@ public boolean has(Element element) {
9593
return this.elements.contains(element);
9694
}
9795

96+
public boolean has(String value) {
97+
return this.has(Element.string(value));
98+
}
99+
100+
public boolean has(Number value) {
101+
return this.has(Element.number(value));
102+
}
103+
104+
public boolean has(Boolean value) {
105+
return this.has(Element.bool(value));
106+
}
107+
108+
public void each(Consumer<Element> consumer) {
109+
if (consumer == null) {
110+
throw new IllegalArgumentException("Consumer cannot be null!");
111+
}
112+
113+
this.elements.forEach(consumer);
114+
}
115+
116+
public boolean all(Predicate<Element> predicate) {
117+
if (predicate == null) {
118+
throw new IllegalArgumentException("Predicate cannot be null!");
119+
}
120+
121+
for (var element : this.elements) {
122+
if (!predicate.test(element)) {
123+
return false;
124+
}
125+
}
126+
127+
return true;
128+
}
129+
130+
public boolean any(Predicate<Element> predicate) {
131+
if (predicate == null) {
132+
throw new IllegalArgumentException("Predicate cannot be null!");
133+
}
134+
135+
for (var element : this.elements) {
136+
if (predicate.test(element)) {
137+
return true;
138+
}
139+
}
140+
141+
return false;
142+
}
143+
98144
public ArrayElement append(Element element) {
99145
if (element == null) throw new IllegalArgumentException("Element cannot be null!");
100146
return this.copy(list -> list.add(element));
@@ -112,6 +158,23 @@ public ArrayElement append(String element) {
112158
return this.append(Element.string(element));
113159
}
114160

161+
public ArrayElement set(int index, Element element) {
162+
if (element == null) throw new IllegalArgumentException("Element cannot be null!");
163+
return this.copy(list -> list.set(index, element));
164+
}
165+
166+
public ArrayElement set(int index, boolean element) {
167+
return this.set(index, Element.bool(element));
168+
}
169+
170+
public ArrayElement set(int index, Number element) {
171+
return this.set(index, Element.number(element));
172+
}
173+
174+
public ArrayElement set(int index, String element) {
175+
return this.set(index, Element.string(element));
176+
}
177+
115178
public ArrayElement insert(int index, Element element) {
116179
if (element == null) throw new IllegalArgumentException("Element cannot be null!");
117180
return this.copy(list -> list.add(index, element));
@@ -129,30 +192,34 @@ public ArrayElement insert(int index, String element) {
129192
return this.insert(index, Element.string(element));
130193
}
131194

132-
public ArrayElement removeAt(int index) {
195+
public ArrayElement remove(int index) {
133196
return this.copy(list -> list.remove(index));
134197
}
135198

136-
public ArrayElement remove(Element element) {
199+
public ArrayElement removeValue(Element element) {
137200
return this.copy(list -> list.remove(element));
138201
}
139202

140-
public ArrayElement remove(boolean element) {
141-
return this.remove(Element.bool(element));
203+
public ArrayElement removeValue(boolean element) {
204+
return this.removeValue(Element.bool(element));
142205
}
143206

144-
public ArrayElement remove(Number element) {
145-
return this.remove(Element.number(element));
207+
public ArrayElement removeValue(Number element) {
208+
return this.removeValue(Element.number(element));
146209
}
147210

148-
public ArrayElement remove(String element) {
149-
return this.remove(Element.string(element));
211+
public ArrayElement removeValue(String element) {
212+
return this.removeValue(Element.string(element));
150213
}
151214

152215
public ArrayElement clear() {
153216
return this.copy(List::clear);
154217
}
155218

219+
public ArrayElement reverse() {
220+
return this.copy(Collections::reverse);
221+
}
222+
156223
public ArrayElement copy(Consumer<List<Element>> mutator) {
157224
if (mutator == null) throw new IllegalArgumentException("Mutator cannot be null!");
158225
var list = new ArrayList<>(this.elements);

json/src/main/java/alpine/json/BooleanElement.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public boolean equals(Object object) {
2525

2626
@Override
2727
public String toString() {
28-
return Json.write(this, Json.Formatting.INLINE_PRETTY);
28+
return Json.write(this, JsonFormatting.INLINE);
2929
}
3030

3131
public boolean value() {

json/src/main/java/alpine/json/Json.java

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import java.nio.file.Files;
55
import java.nio.file.Path;
66

7-
import static alpine.json.JsonUtility.*;
8-
97
/**
108
* A helper class for reading and writing JSON data.
119
* @author mudkip
@@ -14,6 +12,35 @@ public final class Json {
1412
private static final JsonReader READER = new JsonReader();
1513
private static final JsonWriter WRITER = new JsonWriter();
1614

15+
// Structural
16+
static char BEGIN_OBJECT = '{';
17+
static char END_OBJECT = '}';
18+
static char BEGIN_ARRAY = '[';
19+
static char END_ARRAY = ']';
20+
static char COMMA = ',';
21+
static char COLON = ':';
22+
23+
// Strings
24+
static char QUOTE = '"';
25+
static char BACKSLASH = '\\';
26+
static char UNICODE_ESCAPE = 'u';
27+
28+
// Numbers
29+
static char PLUS = '+';
30+
static char MINUS = '-';
31+
static char BEGIN_DECIMAL = '.';
32+
33+
// Whitespace
34+
static char SPACE = ' ';
35+
static char TAB = '\t';
36+
static char LINE_FEED = '\n';
37+
static char CARRIAGE_RETURN = '\r';
38+
39+
// Literals
40+
static String NULL = "null";
41+
static String TRUE = "true";
42+
static String FALSE = "false";
43+
1744
private Json() {
1845

1946
}
@@ -34,55 +61,30 @@ public static Element read(File file) throws ParsingException {
3461
return read(file.toPath());
3562
}
3663

37-
public static String write(Element element, Formatting formatting) {
64+
public static String write(Element element, JsonFormatting formatting) {
3865
return WRITER.write(element, formatting);
3966
}
4067

41-
public static void write(Path path, Element element, Formatting formatting) {
68+
public static void write(Path path, Element element, JsonFormatting formatting) {
4269
try {
4370
Files.writeString(path, write(element, formatting));
4471
} catch (IOException e) {
4572
throw new RuntimeException("Failed to write JSON to file!", e);
4673
}
4774
}
4875

49-
public static void write(File file, Element element, Formatting formatting) {
76+
public static void write(File file, Element element, JsonFormatting formatting) {
5077
write(file.toPath(), element, formatting);
5178
}
5279

53-
/**
54-
* Defines rules for customizing the formatting when writing JSON data.
55-
* @param indentation The string to use when indenting. Typically empty, a tab character ({@code \t}), or multiple spaces.
56-
* @param newLine The string to use to terminate a line. Typically empty, a Unix line ending ({@code \n}) or a Windows line ending ({@code \r\n}).
57-
* @param comma The string to use between values of an object or array. Typically {@code ,} followed by a space.
58-
* @param colon The string to use between key-value pairs of an object. Typically {@code :} followed by a space.
59-
*/
60-
public record Formatting(String indentation, String newLine, String comma, String colon) {
61-
/**
62-
* Represents a minified format ideal for exchanging data over the network.
63-
*/
64-
public static final Formatting COMPACT = new Formatting(
65-
"",
66-
"\n",
67-
String.valueOf(COMMA),
68-
String.valueOf(COLON));
69-
70-
/**
71-
* Represents a single-line beautified format with spaces between values.
72-
*/
73-
public static final Formatting INLINE_PRETTY = new Formatting(
74-
"",
75-
"\n",
76-
COMPACT.comma + SPACE,
77-
COMPACT.colon + SPACE);
78-
79-
/**
80-
* Represents a multi-line beautified format ideal for human readability.
81-
*/
82-
public static final Formatting PRETTY = new Formatting(
83-
String.valueOf(SPACE).repeat(4),
84-
"\n",
85-
String.valueOf(COMMA),
86-
COMPACT.colon + SPACE);
80+
static boolean isControlCharacter(char character) {
81+
return character <= 0x1F;
82+
}
83+
84+
static boolean isWhitespaceCharacter(char character) {
85+
return character == SPACE
86+
|| character == TAB
87+
|| character == LINE_FEED
88+
|| character == CARRIAGE_RETURN;
8789
}
8890
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package alpine.json;
2+
3+
/**
4+
* Defines rules for customizing the formatting when writing JSON data.
5+
*
6+
* @param indentation The string to use when indenting. Typically empty, a tab character ({@code \t}), or multiple spaces.
7+
* @param newLine The string to use to terminate a line. Typically empty, a Unix line ending ({@code \n}) or a Windows line ending ({@code \r\n}).
8+
* @param comma The string to use between values of an object or array. Typically {@code ,} followed by a space.
9+
* @param colon The string to use between key-value pairs of an object. Typically {@code :} followed by a space.
10+
*/
11+
public record JsonFormatting(String indentation, String newLine, String comma, String colon) {
12+
/**
13+
* Represents a minified format ideal for exchanging data over the network.
14+
*/
15+
public static final JsonFormatting COMPACT = new JsonFormatting(
16+
"",
17+
"\n",
18+
String.valueOf(Json.COMMA),
19+
String.valueOf(Json.COLON));
20+
21+
/**
22+
* Represents a single-line beautified format with spaces between values.
23+
*/
24+
public static final JsonFormatting INLINE = new JsonFormatting(
25+
"",
26+
"\n",
27+
COMPACT.comma + Json.SPACE,
28+
COMPACT.colon + Json.SPACE);
29+
30+
/**
31+
* Represents a multi-line beautified format ideal for human readability.
32+
*/
33+
public static final JsonFormatting PRETTY = new JsonFormatting(
34+
String.valueOf(Json.SPACE).repeat(4),
35+
"\n",
36+
String.valueOf(Json.COMMA),
37+
COMPACT.colon + Json.SPACE);
38+
}

json/src/main/java/alpine/json/JsonReader.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.util.LinkedHashMap;
77

88
import static alpine.json.Element.*;
9-
import static alpine.json.JsonUtility.*;
9+
import static alpine.json.Json.*;
1010
import static java.lang.Character.isDigit;
1111

1212
@ApiStatus.Internal
@@ -139,7 +139,7 @@ private NumberElement parseNumber() throws ParsingException {
139139
if (this.position < length) {
140140
var character = this.characters[this.position];
141141

142-
if (!isWhitespace(character) && character != COMMA && character != END_OBJECT && character != END_ARRAY) {
142+
if (!isWhitespaceCharacter(character) && character != COMMA && character != END_OBJECT && character != END_ARRAY) {
143143
throw new ParsingException(this.input, "Invalid character after number!", this.position);
144144
}
145145
}
@@ -180,7 +180,7 @@ private String parseRawString() throws ParsingException {
180180
break; // fall through
181181
}
182182

183-
if (isControl(character)) {
183+
if (isControlCharacter(character)) {
184184
throw new ParsingException(this.input, "Unescaped control character in string!", this.position);
185185
}
186186

@@ -239,7 +239,7 @@ private String parseRawString() throws ParsingException {
239239
default -> throw new ParsingException(this.input, "Invalid escape character: \\" + escapeCharacter, this.position);
240240
});
241241
}
242-
} else if (isControl(character)) {
242+
} else if (isControlCharacter(character)) {
243243
throw new ParsingException(this.input, "Unescaped control character in string!", this.position);
244244
} else {
245245
builder.append(character);
@@ -330,7 +330,7 @@ private void expect(char character) throws ParsingException {
330330
}
331331

332332
private void skipWhitespace() {
333-
while (this.position < this.characters.length && isWhitespace(this.characters[this.position])) {
333+
while (this.position < this.characters.length && isWhitespaceCharacter(this.characters[this.position])) {
334334
this.position++;
335335
}
336336
}

0 commit comments

Comments
 (0)