Skip to content

Commit ebd9a17

Browse files
committed
addressing minor sonarqube concerns
1 parent 82432f0 commit ebd9a17

File tree

1 file changed

+96
-62
lines changed

1 file changed

+96
-62
lines changed

src/main/java/org/json/JSONObject.java

Lines changed: 96 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,6 @@ public class JSONObject {
7979
*/
8080
private static final class Null {
8181

82-
/**
83-
* There is only intended to be a single instance of the NULL object,
84-
* so the clone method returns itself.
85-
*
86-
* @return NULL.
87-
*/
88-
@Override
89-
protected final Object clone() {
90-
return this;
91-
}
92-
9382
/**
9483
* A Null object is equal to the null value and to itself.
9584
*
@@ -180,7 +169,7 @@ public JSONObject(JSONObject jo, String ... names) {
180169
for (int i = 0; i < names.length; i += 1) {
181170
try {
182171
this.putOnce(names[i], jo.opt(names[i]));
183-
} catch (Exception ignore) {
172+
} catch (Exception ignore) { // exception thrown for missing key
184173
}
185174
}
186175
}
@@ -221,83 +210,128 @@ public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration
221210
throw x.syntaxError("A JSONObject text must begin with '{'");
222211
}
223212
for (;;) {
224-
c = x.nextClean();
225-
switch (c) {
213+
if (parseJSONObject(x, jsonParserConfiguration, isInitial)) {
214+
return;
215+
}
216+
}
217+
}
218+
219+
/**
220+
* Parses entirety of JSON object
221+
*
222+
* @param jsonTokener Parses text as tokens
223+
* @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
224+
* @param isInitial True if start of document, else false
225+
* @return True if done building object, else false
226+
*/
227+
private boolean parseJSONObject(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) {
228+
Object obj;
229+
String key;
230+
char c;
231+
c = jsonTokener.nextClean();
232+
233+
switch (c) {
226234
case 0:
227-
throw x.syntaxError("A JSONObject text must end with '}'");
235+
throw jsonTokener.syntaxError("A JSONObject text must end with '}'");
228236
case '}':
229-
if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
230-
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
237+
if (isInitial && jsonParserConfiguration.isStrictMode() && jsonTokener.nextClean() != 0) {
238+
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
231239
}
232-
return;
240+
return true;
233241
default:
234-
obj = x.nextSimpleValue(c);
242+
obj = jsonTokener.nextSimpleValue(c);
235243
key = obj.toString();
236-
}
244+
}
237245

238-
if (jsonParserConfiguration != null && jsonParserConfiguration.isStrictMode()) {
239-
if(obj instanceof Boolean) {
240-
throw x.syntaxError(String.format("Strict mode error: key '%s' cannot be boolean", key));
241-
}
242-
if(obj == JSONObject.NULL) {
243-
throw x.syntaxError(String.format("Strict mode error: key '%s' cannot be null", key));
244-
}
245-
if(obj instanceof Number) {
246-
throw x.syntaxError(String.format("Strict mode error: key '%s' cannot be number", key));
247-
}
248-
}
246+
checkKeyForStrictMode(jsonTokener, jsonParserConfiguration, obj);
249247

250-
// The key is followed by ':'.
248+
// The key is followed by ':'.
249+
c = jsonTokener.nextClean();
250+
if (c != ':') {
251+
throw jsonTokener.syntaxError("Expected a ':' after a key");
252+
}
251253

252-
c = x.nextClean();
253-
if (c != ':') {
254-
throw x.syntaxError("Expected a ':' after a key");
254+
// Use syntaxError(..) to include error location
255+
if (key != null) {
256+
// Check if key exists
257+
boolean keyExists = this.opt(key) != null;
258+
if (keyExists && !jsonParserConfiguration.isOverwriteDuplicateKey()) {
259+
throw jsonTokener.syntaxError("Duplicate key \"" + key + "\"");
255260
}
256261

257-
// Use syntaxError(..) to include error location
258-
259-
if (key != null) {
260-
// Check if key exists
261-
boolean keyExists = this.opt(key) != null;
262-
if (keyExists && !jsonParserConfiguration.isOverwriteDuplicateKey()) {
263-
throw x.syntaxError("Duplicate key \"" + key + "\"");
264-
}
265-
266-
Object value = x.nextValue();
267-
// Only add value if non-null
268-
if (value != null) {
269-
this.put(key, value);
270-
}
262+
Object value = jsonTokener.nextValue();
263+
// Only add value if non-null
264+
if (value != null) {
265+
this.put(key, value);
271266
}
267+
}
272268

273-
// Pairs are separated by ','.
269+
// Pairs are separated by ','.
270+
if (parseEndOfKeyValuePair(jsonTokener, jsonParserConfiguration, isInitial)) {
271+
return true;
272+
}
273+
// Not finished parsing
274+
return false;
275+
}
274276

275-
switch (x.nextClean()) {
277+
/**
278+
* Checks for valid end of key:value pair
279+
* @param jsonTokener Parses text as tokens
280+
* @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
281+
* @param isInitial True if end of JSON object, else false
282+
* @return
283+
*/
284+
private static boolean parseEndOfKeyValuePair(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) {
285+
switch (jsonTokener.nextClean()) {
276286
case ';':
277287
// In strict mode semicolon is not a valid separator
278288
if (jsonParserConfiguration.isStrictMode()) {
279-
throw x.syntaxError("Strict mode error: Invalid character ';' found");
289+
throw jsonTokener.syntaxError("Strict mode error: Invalid character ';' found");
280290
}
291+
break;
281292
case ',':
282-
if (x.nextClean() == '}') {
293+
if (jsonTokener.nextClean() == '}') {
283294
// trailing commas are not allowed in strict mode
284295
if (jsonParserConfiguration.isStrictMode()) {
285-
throw x.syntaxError("Strict mode error: Expected another object element");
296+
throw jsonTokener.syntaxError("Strict mode error: Expected another object element");
286297
}
287-
return;
298+
// End of JSON object
299+
return true;
288300
}
289-
if (x.end()) {
290-
throw x.syntaxError("A JSONObject text must end with '}'");
301+
if (jsonTokener.end()) {
302+
throw jsonTokener.syntaxError("A JSONObject text must end with '}'");
291303
}
292-
x.back();
304+
jsonTokener.back();
293305
break;
294306
case '}':
295-
if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
296-
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
307+
if (isInitial && jsonParserConfiguration.isStrictMode() && jsonTokener.nextClean() != 0) {
308+
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
297309
}
298-
return;
310+
// End of JSON object
311+
return true;
299312
default:
300-
throw x.syntaxError("Expected a ',' or '}'");
313+
throw jsonTokener.syntaxError("Expected a ',' or '}'");
314+
}
315+
// Not at end of JSON object
316+
return false;
317+
}
318+
319+
/**
320+
* Throws error if key violates strictMode
321+
* @param jsonTokener Parses text as tokens
322+
* @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing.
323+
* @param obj Value to be checked
324+
*/
325+
private static void checkKeyForStrictMode(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, Object obj) {
326+
if (jsonParserConfiguration != null && jsonParserConfiguration.isStrictMode()) {
327+
if(obj instanceof Boolean) {
328+
throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be boolean", obj.toString()));
329+
}
330+
if(obj == JSONObject.NULL) {
331+
throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be null", obj.toString()));
332+
}
333+
if(obj instanceof Number) {
334+
throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be number", obj.toString()));
301335
}
302336
}
303337
}

0 commit comments

Comments
 (0)