Codebase list libandroid-json-org-java / 3a1db15
Adding an Apache-licensed implementation of org.json Change-Id: I1b67bac70bd25220a619e6ebe61f7f1c6f316faa Jesse Wilson authored 14 years ago Thomas Koch committed 11 years ago
12 changed file(s) with 1698 addition(s) and 1698 deletion(s). Raw diff Collapse all Expand all
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 class JSON {
19 /**
20 * Returns the input if it is a JSON-permissable value; throws otherwise.
21 */
22 static double checkDouble(double d) throws JSONException {
23 if (Double.isInfinite(d) || Double.isNaN(d)) {
24 throw new JSONException("Forbidden numeric value: " + d);
25 }
26 return d;
27 }
28
29 static Boolean toBoolean(Object value) {
30 if (value instanceof Boolean) {
31 return (Boolean) value;
32 } else if (value instanceof String) {
33 return Boolean.valueOf(((String) value));
34 } else {
35 return null;
36 }
37 }
38
39 static Double toDouble(Object value) {
40 if (value instanceof Double) {
41 return (Double) value;
42 } else if (value instanceof Number) {
43 return ((Number) value).doubleValue();
44 } else if (value instanceof String) {
45 try {
46 return Double.valueOf((String) value);
47 } catch (NumberFormatException e) {
48 }
49 }
50 return null;
51 }
52
53 static Integer toInteger(Object value) {
54 if (value instanceof Integer) {
55 return (Integer) value;
56 } else if (value instanceof Number) {
57 return ((Number) value).intValue();
58 } else if (value instanceof String) {
59 try {
60 return Double.valueOf((String) value).intValue();
61 } catch (NumberFormatException e) {
62 }
63 }
64 return null;
65 }
66
67 static Long toLong(Object value) {
68 if (value instanceof Long) {
69 return (Long) value;
70 } else if (value instanceof Number) {
71 return ((Number) value).longValue();
72 } else if (value instanceof String) {
73 try {
74 return Double.valueOf((String) value).longValue();
75 } catch (NumberFormatException e) {
76 }
77 }
78 return null;
79 }
80
81 static String toString(Object value) {
82 if (value instanceof String) {
83 return (String) value;
84 } else if (value != null) {
85 return String.valueOf(value);
86 }
87 return null;
88 }
89
90 public static JSONException typeMismatch(Object indexOrName, Object actual,
91 String requiredType) throws JSONException {
92 if (actual == null) {
93 throw new JSONException("Value at " + indexOrName + " is null.");
94 } else {
95 throw new JSONException("Value " + actual + " at " + indexOrName
96 + " of type " + actual.getClass().getName()
97 + " cannot be converted to " + requiredType);
98 }
99 }
100
101 public static JSONException typeMismatch(Object actual, String requiredType)
102 throws JSONException {
103 if (actual == null) {
104 throw new JSONException("Value is null.");
105 } else {
106 throw new JSONException("Value " + actual
107 + " of type " + actual.getClass().getName()
108 + " cannot be converted to " + requiredType);
109 }
110 }
111 }
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 import java.util.List;
19 import java.util.ArrayList;
20 import java.util.Collection;
21
22 // Note: this class was written without inspecting the non-free org.json sourcecode.
23
24 /**
25 * An indexed sequence of JSON-safe values.
26 */
27 public class JSONArray {
28
29 private final List<Object> values;
30
31 public JSONArray() {
32 values = new ArrayList<Object>();
33 }
34
35 /* Accept a raw type for API compatibility */
36 public JSONArray(Collection copyFrom) {
37 this();
38 Collection<?> copyFromTyped = (Collection<?>) copyFrom;
39 values.addAll(copyFromTyped);
40 }
41
42 public JSONArray(JSONTokener readFrom) throws JSONException {
43 /*
44 * Getting the parser to populate this could get tricky. Instead, just
45 * parse to temporary JSONArray and then steal the data from that.
46 */
47 Object object = readFrom.nextValue();
48 if (object instanceof JSONArray) {
49 values = ((JSONArray) object).values;
50 } else {
51 throw JSON.typeMismatch(object, "JSONArray");
52 }
53 }
54
55 public JSONArray(String json) throws JSONException {
56 this(new JSONTokener(json));
57 }
58
59 public int length() {
60 return values.size();
61 }
62
63 public JSONArray put(boolean value) {
64 values.add(value);
65 return this;
66 }
67
68 public JSONArray put(double value) throws JSONException {
69 values.add(JSON.checkDouble(value));
70 return this;
71 }
72
73 public JSONArray put(int value) {
74 values.add(value);
75 return this;
76 }
77
78 public JSONArray put(long value) {
79 values.add(value);
80 return this;
81 }
82
83 public JSONArray put(Object value) {
84 values.add(value);
85 return this;
86 }
87
88 public JSONArray put(int index, boolean value) throws JSONException {
89 return put(index, (Boolean) value);
90 }
91
92 public JSONArray put(int index, double value) throws JSONException {
93 return put(index, (Double) value);
94 }
95
96 public JSONArray put(int index, int value) throws JSONException {
97 return put(index, (Integer) value);
98 }
99
100 public JSONArray put(int index, long value) throws JSONException {
101 return put(index, (Long) value);
102 }
103
104 public JSONArray put(int index, Object value) throws JSONException {
105 if (value instanceof Number) {
106 // deviate from the original by checking all Numbers, not just floats & doubles
107 JSON.checkDouble(((Number) value).doubleValue());
108 }
109 while (values.size() <= index) {
110 values.add(null);
111 }
112 values.set(index, value);
113 return this;
114 }
115
116 public boolean isNull(int index) {
117 Object value = opt(index);
118 return value == null || value == JSONObject.NULL;
119 }
120
121 public Object get(int index) throws JSONException {
122 try {
123 Object value = values.get(index);
124 if (value == null) {
125 throw new JSONException("Value at " + index + " is null.");
126 }
127 return value;
128 } catch (IndexOutOfBoundsException e) {
129 throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")");
130 }
131 }
132
133 public Object opt(int index) {
134 if (index < 0 || index >= values.size()) {
135 return null;
136 }
137 return values.get(index);
138 }
139
140 public boolean getBoolean(int index) throws JSONException {
141 Object object = get(index);
142 Boolean result = JSON.toBoolean(object);
143 if (result == null) {
144 throw JSON.typeMismatch(index, object, "boolean");
145 }
146 return result;
147 }
148
149 public boolean optBoolean(int index) {
150 return optBoolean(index, false);
151 }
152
153 public boolean optBoolean(int index, boolean fallback) {
154 Object object = opt(index);
155 Boolean result = JSON.toBoolean(object);
156 return result != null ? result : fallback;
157 }
158
159 public double getDouble(int index) throws JSONException {
160 Object object = get(index);
161 Double result = JSON.toDouble(object);
162 if (result == null) {
163 throw JSON.typeMismatch(index, object, "double");
164 }
165 return result;
166 }
167
168 public double optDouble(int index) {
169 return optDouble(index, Double.NaN);
170 }
171
172 public double optDouble(int index, double fallback) {
173 Object object = opt(index);
174 Double result = JSON.toDouble(object);
175 return result != null ? result : fallback;
176 }
177
178 public int getInt(int index) throws JSONException {
179 Object object = get(index);
180 Integer result = JSON.toInteger(object);
181 if (result == null) {
182 throw JSON.typeMismatch(index, object, "int");
183 }
184 return result;
185 }
186
187 public int optInt(int index) {
188 return optInt(index, 0);
189 }
190
191 public int optInt(int index, int fallback) {
192 Object object = opt(index);
193 Integer result = JSON.toInteger(object);
194 return result != null ? result : fallback;
195 }
196
197 public long getLong(int index) throws JSONException {
198 Object object = get(index);
199 Long result = JSON.toLong(object);
200 if (result == null) {
201 throw JSON.typeMismatch(index, object, "long");
202 }
203 return result;
204 }
205
206 public long optLong(int index) {
207 return optLong(index, 0L);
208 }
209
210 public long optLong(int index, long fallback) {
211 Object object = opt(index);
212 Long result = JSON.toLong(object);
213 return result != null ? result : fallback;
214 }
215
216 public String getString(int index) throws JSONException {
217 Object object = get(index);
218 String result = JSON.toString(object);
219 if (result == null) {
220 throw JSON.typeMismatch(index, object, "String");
221 }
222 return result;
223 }
224
225 public String optString(int index) {
226 return optString(index, "");
227 }
228
229 public String optString(int index, String fallback) {
230 Object object = opt(index);
231 String result = JSON.toString(object);
232 return result != null ? result : fallback;
233 }
234
235 public JSONArray getJSONArray(int index) throws JSONException {
236 Object object = get(index);
237 if (object instanceof JSONArray) {
238 return (JSONArray) object;
239 } else {
240 throw JSON.typeMismatch(index, object, "JSONArray");
241 }
242 }
243
244 public JSONArray optJSONArray(int index) {
245 Object object = opt(index);
246 return object instanceof JSONArray ? (JSONArray) object : null;
247 }
248
249 public JSONObject getJSONObject(int index) throws JSONException {
250 Object object = get(index);
251 if (object instanceof JSONObject) {
252 return (JSONObject) object;
253 } else {
254 throw JSON.typeMismatch(index, object, "JSONObject");
255 }
256 }
257
258 public JSONObject optJSONObject(int index) {
259 Object object = opt(index);
260 return object instanceof JSONObject ? (JSONObject) object : null;
261 }
262
263 public JSONObject toJSONObject(JSONArray names) throws JSONException {
264 JSONObject result = new JSONObject();
265 int length = Math.min(names.length(), values.size());
266 if (length == 0) {
267 return null;
268 }
269 for (int i = 0; i < length; i++) {
270 String name = JSON.toString(names.opt(i));
271 result.put(name, opt(i));
272 }
273 return result;
274 }
275
276 public String join(String separator) throws JSONException {
277 JSONStringer stringer = new JSONStringer();
278 stringer.open(JSONStringer.Scope.NULL, "");
279 for (int i = 0, size = values.size(); i < size; i++) {
280 if (i > 0) {
281 stringer.out.append(separator);
282 }
283 stringer.value(values.get(i));
284 }
285 stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
286 return stringer.out.toString();
287 }
288
289 @Override public String toString() {
290 try {
291 JSONStringer stringer = new JSONStringer();
292 writeTo(stringer);
293 return stringer.toString();
294 } catch (JSONException e) {
295 return null;
296 }
297 }
298
299 public String toString(int indentSpaces) throws JSONException {
300 JSONStringer stringer = new JSONStringer(indentSpaces);
301 writeTo(stringer);
302 return stringer.toString();
303 }
304
305 void writeTo(JSONStringer stringer) throws JSONException {
306 stringer.array();
307 for (Object value : values) {
308 stringer.value(value);
309 }
310 stringer.endArray();
311 }
312
313 @Override public boolean equals(Object o) {
314 return o instanceof JSONArray && ((JSONArray) o).values.equals(values);
315 }
316
317 @Override public int hashCode() {
318 // diverge from the original, which doesn't implement hashCode
319 return values.hashCode();
320 }
321 }
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 // Note: this class was written without inspecting the non-free org.json sourcecode.
19
20 /**
21 * Thrown to indicate a problem with the JSON API. Such problems include:
22 * <ul>
23 * <li>Attempts to parse or construct malformed documents
24 * <li>Use of null as a name
25 * <li>Use of numeric types not available to JSON, such as {@link Double#NaN
26 * NaN} or {@link Double#POSITIVE_INFINITY infinity}.
27 * <li>Lookups using an out of range index or nonexistant name
28 * <li>Type mismatches on lookups
29 * </ul>
30 *
31 * <p>Although this is a checked exception, it is rarely recoverable. Most
32 * callers should simply wrap this exception in an unchecked exception and
33 * rethrow:
34 * <pre> public JSONArray toJSONObject() {
35 * try {
36 * JSONObject result = new JSONObject();
37 * ...
38 * } catch (JSONException e) {
39 * throw new RuntimeException(e);
40 * }
41 * }</pre>
42 */
43 public class JSONException extends Exception {
44
45 public JSONException(String s) {
46 super(s);
47 }
48 }
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22
23 // Note: this class was written without inspecting the non-free org.json sourcecode.
24
25 /**
26 *
27 *
28 * <p>TODO: Note about self-use
29 */
30 public class JSONObject {
31
32 private static final Double NEGATIVE_ZERO = -0d;
33
34 public static final Object NULL = new Object() {
35 @Override public boolean equals(Object o) {
36 return o == this || o == null; // API specifies this broken equals implementation
37 }
38 @Override public String toString() {
39 return "null";
40 }
41 };
42
43 private final Map<String, Object> nameValuePairs;
44
45 public JSONObject() {
46 nameValuePairs = new HashMap<String, Object>();
47 }
48
49 /* Accept a raw type for API compatibility */
50 public JSONObject(Map copyFrom) {
51 this();
52 Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
53 for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
54 /*
55 * Deviate from the original by checking that keys are non-null and
56 * of the proper type. (We still defer validating the values).
57 */
58 String key = (String) entry.getKey();
59 if (key == null) {
60 throw new NullPointerException();
61 }
62 nameValuePairs.put(key, entry.getValue());
63 }
64 }
65
66 public JSONObject(JSONTokener readFrom) throws JSONException {
67 /*
68 * Getting the parser to populate this could get tricky. Instead, just
69 * parse to temporary JSONObject and then steal the data from that.
70 */
71 Object object = readFrom.nextValue();
72 if (object instanceof JSONObject) {
73 this.nameValuePairs = ((JSONObject) object).nameValuePairs;
74 } else {
75 throw JSON.typeMismatch(object, "JSONObject");
76 }
77 }
78
79 public JSONObject(String json) throws JSONException {
80 this(new JSONTokener(json));
81 }
82
83 public JSONObject(JSONObject copyFrom, String[] names) throws JSONException {
84 this();
85 for (String name : names) {
86 Object value = copyFrom.opt(name);
87 if (value != null) {
88 nameValuePairs.put(name, value);
89 }
90 }
91 }
92
93 public int length() {
94 return nameValuePairs.size();
95 }
96
97 public JSONObject put(String name, boolean value) throws JSONException {
98 nameValuePairs.put(checkName(name), value);
99 return this;
100 }
101
102 public JSONObject put(String name, double value) throws JSONException {
103 nameValuePairs.put(checkName(name), JSON.checkDouble(value));
104 return this;
105 }
106
107 public JSONObject put(String name, int value) throws JSONException {
108 nameValuePairs.put(checkName(name), value);
109 return this;
110 }
111
112 public JSONObject put(String name, long value) throws JSONException {
113 nameValuePairs.put(checkName(name), value);
114 return this;
115 }
116
117 public JSONObject put(String name, Object value) throws JSONException {
118 if (value == null) {
119 nameValuePairs.remove(name);
120 return this;
121 }
122 if (value instanceof Number) {
123 // deviate from the original by checking all Numbers, not just floats & doubles
124 JSON.checkDouble(((Number) value).doubleValue());
125 }
126 nameValuePairs.put(checkName(name), value);
127 return this;
128 }
129
130 public JSONObject putOpt(String name, Object value) throws JSONException {
131 if (name == null || value == null) {
132 return this;
133 }
134 return put(name, value);
135 }
136
137 public JSONObject accumulate(String name, Object value) throws JSONException {
138 Object current = nameValuePairs.get(checkName(name));
139 if (current == null) {
140 put(name, value);
141 } else if (current instanceof JSONArray) {
142 JSONArray array = (JSONArray) current;
143 array.put(value);
144 } else {
145 JSONArray array = new JSONArray();
146 array.put(current);
147 array.put(value); // fails on bogus values
148 nameValuePairs.put(name, array);
149 }
150 return this;
151 }
152
153 String checkName(String name) throws JSONException {
154 if (name == null) {
155 throw new JSONException("Names must be non-null");
156 }
157 return name;
158 }
159
160 public Object remove(String name) {
161 return nameValuePairs.remove(name);
162 }
163
164 public boolean isNull(String name) {
165 Object value = nameValuePairs.get(name);
166 return value == null || value == NULL;
167 }
168
169 public boolean has(String name) {
170 return nameValuePairs.containsKey(name);
171 }
172
173 public Object get(String name) throws JSONException {
174 Object result = nameValuePairs.get(name);
175 if (result == null) {
176 throw new JSONException("No value for " + name);
177 }
178 return result;
179 }
180
181 public Object opt(String name) {
182 return nameValuePairs.get(name);
183 }
184
185 public boolean getBoolean(String name) throws JSONException {
186 Object object = get(name);
187 Boolean result = JSON.toBoolean(object);
188 if (result == null) {
189 throw JSON.typeMismatch(name, object, "boolean");
190 }
191 return result;
192 }
193
194 public boolean optBoolean(String name) {
195 return optBoolean(name, false);
196 }
197
198 public boolean optBoolean(String name, boolean fallback) {
199 Object object = opt(name);
200 Boolean result = JSON.toBoolean(object);
201 return result != null ? result : fallback;
202 }
203
204 public double getDouble(String name) throws JSONException {
205 Object object = get(name);
206 Double result = JSON.toDouble(object);
207 if (result == null) {
208 throw JSON.typeMismatch(name, object, "double");
209 }
210 return result;
211 }
212
213 public double optDouble(String name) {
214 return optDouble(name, Double.NaN);
215 }
216
217 public double optDouble(String name, double fallback) {
218 Object object = opt(name);
219 Double result = JSON.toDouble(object);
220 return result != null ? result : fallback;
221 }
222
223 public int getInt(String name) throws JSONException {
224 Object object = get(name);
225 Integer result = JSON.toInteger(object);
226 if (result == null) {
227 throw JSON.typeMismatch(name, object, "int");
228 }
229 return result;
230 }
231
232 public int optInt(String name) {
233 return optInt(name, 0);
234 }
235
236 public int optInt(String name, int fallback) {
237 Object object = opt(name);
238 Integer result = JSON.toInteger(object);
239 return result != null ? result : fallback;
240 }
241
242 public long getLong(String name) throws JSONException {
243 Object object = get(name);
244 Long result = JSON.toLong(object);
245 if (result == null) {
246 throw JSON.typeMismatch(name, object, "long");
247 }
248 return result;
249 }
250
251 public long optLong(String name) {
252 return optLong(name, 0L);
253 }
254
255 public long optLong(String name, long fallback) {
256 Object object = opt(name);
257 Long result = JSON.toLong(object);
258 return result != null ? result : fallback;
259 }
260
261 public String getString(String name) throws JSONException {
262 Object object = get(name);
263 String result = JSON.toString(object);
264 if (result == null) {
265 throw JSON.typeMismatch(name, object, "String");
266 }
267 return result;
268 }
269
270 public String optString(String name) {
271 return optString(name, "");
272 }
273
274 public String optString(String name, String fallback) {
275 Object object = opt(name);
276 String result = JSON.toString(object);
277 return result != null ? result : fallback;
278 }
279
280 public JSONArray getJSONArray(String name) throws JSONException {
281 Object object = get(name);
282 if (object instanceof JSONArray) {
283 return (JSONArray) object;
284 } else {
285 throw JSON.typeMismatch(name, object, "JSONArray");
286 }
287 }
288
289 public JSONArray optJSONArray(String name) {
290 Object object = opt(name);
291 return object instanceof JSONArray ? (JSONArray) object : null;
292 }
293
294 public JSONObject getJSONObject(String name) throws JSONException {
295 Object object = get(name);
296 if (object instanceof JSONObject) {
297 return (JSONObject) object;
298 } else {
299 throw JSON.typeMismatch(name, object, "JSONObject");
300 }
301 }
302
303 public JSONObject optJSONObject(String name) {
304 Object object = opt(name);
305 return object instanceof JSONObject ? (JSONObject) object : null;
306 }
307
308 public JSONArray toJSONArray(JSONArray names) throws JSONException {
309 JSONArray result = new JSONArray();
310 if (names == null) {
311 return null;
312 }
313 int length = names.length();
314 if (length == 0) {
315 return null;
316 }
317 for (int i = 0; i < length; i++) {
318 String name = JSON.toString(names.opt(i));
319 result.put(opt(name));
320 }
321 return result;
322 }
323
324 /* Return a raw type for API compatibility */
325 public Iterator keys() {
326 return nameValuePairs.keySet().iterator();
327 }
328
329 public JSONArray names() {
330 return nameValuePairs.isEmpty()
331 ? null
332 : new JSONArray(new ArrayList<String>(nameValuePairs.keySet()));
333 }
334
335 @Override public String toString() {
336 try {
337 JSONStringer stringer = new JSONStringer();
338 writeTo(stringer);
339 return stringer.toString();
340 } catch (JSONException e) {
341 return null;
342 }
343 }
344
345 public String toString(int indentSpaces) throws JSONException {
346 JSONStringer stringer = new JSONStringer(indentSpaces);
347 writeTo(stringer);
348 return stringer.toString();
349 }
350
351 void writeTo(JSONStringer stringer) throws JSONException {
352 stringer.object();
353 for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) {
354 stringer.key(entry.getKey()).value(entry.getValue());
355 }
356 stringer.endObject();
357 }
358
359 public static String numberToString(Number number) throws JSONException {
360 if (number == null) {
361 throw new JSONException("Number must be non-null");
362 }
363
364 double doubleValue = number.doubleValue();
365 JSON.checkDouble(doubleValue);
366
367 // the original returns "-0" instead of "-0.0" for negative zero
368 if (number.equals(NEGATIVE_ZERO)) {
369 return "-0";
370 }
371
372 long longValue = number.longValue();
373 if (doubleValue == (double) longValue) {
374 return Long.toString(longValue);
375 }
376
377 return number.toString();
378 }
379
380 public static String quote(String data) {
381 if (data == null) {
382 return "\"\"";
383 }
384 try {
385 JSONStringer stringer = new JSONStringer();
386 stringer.open(JSONStringer.Scope.NULL, "");
387 stringer.value(data);
388 stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
389 return stringer.toString();
390 } catch (JSONException e) {
391 throw new AssertionError();
392 }
393 }
394 }
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.List;
21
22 // Note: this class was written without inspecting the non-free org.json sourcecode.
23
24 /**
25 *
26 */
27 public class JSONStringer {
28
29 /** The output data, containing at most one top-level array or object. */
30 final StringBuilder out = new StringBuilder();
31
32 /**
33 * Lexical scoping elements within this stringer, necessary to insert the
34 * appropriate separator characters (ie. commas and colons) and to detect
35 * nesting errors.
36 */
37 enum Scope {
38
39 /**
40 * An array with no elements requires no separators or newlines before
41 * it is closed.
42 */
43 EMPTY_ARRAY,
44
45 /**
46 * A array with at least one value requires a comma and newline before
47 * the next element.
48 */
49 NONEMPTY_ARRAY,
50
51 /**
52 * An object with no keys or values requires no separators or newlines
53 * before it is closed.
54 */
55 EMPTY_OBJECT,
56
57 /**
58 * An object whose most recent element is a key. The next element must
59 * be a value.
60 */
61 DANGLING_KEY,
62
63 /**
64 * An object with at least one name/value pair requires a comma and
65 * newline before the next element.
66 */
67 NONEMPTY_OBJECT,
68
69 /**
70 * A special bracketless array needed by JSONStringer.join() and
71 * JSONObject.quote() only. Not used for JSON encoding.
72 */
73 NULL,
74 }
75
76 /**
77 * Unlike the original implementation, this stack isn't limited to 20
78 * levels of nesting.
79 */
80 private final List<Scope> stack = new ArrayList<Scope>();
81
82 /**
83 * A string containing a full set of spaces for a single level of
84 * indentation, or null for no pretty printing.
85 */
86 private final String indent;
87
88 public JSONStringer() {
89 indent = null;
90 }
91
92 JSONStringer(int indentSpaces) {
93 char[] indentChars = new char[indentSpaces];
94 Arrays.fill(indentChars, ' ');
95 indent = new String(indentChars);
96 }
97
98 public JSONStringer array() throws JSONException {
99 return open(Scope.EMPTY_ARRAY, "[");
100 }
101
102 public JSONStringer endArray() throws JSONException {
103 return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]");
104 }
105
106 public JSONStringer object() throws JSONException {
107 return open(Scope.EMPTY_OBJECT, "{");
108 }
109
110 public JSONStringer endObject() throws JSONException {
111 return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}");
112 }
113
114 /**
115 * Enters a new scope by appending any necessary whitespace and the given
116 * bracket.
117 */
118 JSONStringer open(Scope empty, String openBracket) throws JSONException {
119 if (stack.isEmpty() && out.length() > 0) {
120 throw new JSONException("Nesting problem: multiple top-level roots");
121 }
122 beforeValue();
123 stack.add(empty);
124 out.append(openBracket);
125 return this;
126 }
127
128 /**
129 * Closes the current scope by appending any necessary whitespace and the
130 * given bracket.
131 */
132 JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException {
133 Scope context = peek();
134 if (context != nonempty && context != empty) {
135 throw new JSONException("Nesting problem");
136 }
137
138 stack.remove(stack.size() - 1);
139 if (context == nonempty) {
140 newline();
141 }
142 out.append(closeBracket);
143 return this;
144 }
145
146 /**
147 * Returns the value on the top of the stack.
148 */
149 private Scope peek() throws JSONException {
150 if (stack.isEmpty()) {
151 throw new JSONException("Nesting problem");
152 }
153 return stack.get(stack.size() - 1);
154 }
155
156 /**
157 * Replace the value on the top of the stack with the given value.
158 */
159 private void replaceTop(Scope topOfStack) {
160 stack.set(stack.size() - 1, topOfStack);
161 }
162
163 public JSONStringer value(Object value) throws JSONException {
164 if (stack.isEmpty()) {
165 throw new JSONException("Nesting problem");
166 }
167
168 if (value instanceof JSONArray) {
169 ((JSONArray) value).writeTo(this);
170 return this;
171
172 } else if (value instanceof JSONObject) {
173 ((JSONObject) value).writeTo(this);
174 return this;
175 }
176
177 beforeValue();
178
179 if (value == null
180 || value instanceof Boolean
181 || value == JSONObject.NULL) {
182 out.append(value);
183
184 } else if (value instanceof Number) {
185 out.append(JSONObject.numberToString((Number) value));
186
187 } else {
188 string(value.toString());
189 }
190
191 return this;
192 }
193
194 public JSONStringer value(boolean value) throws JSONException {
195 if (stack.isEmpty()) {
196 throw new JSONException("Nesting problem");
197 }
198 beforeValue();
199 out.append(value);
200 return this;
201 }
202
203 public JSONStringer value(double value) throws JSONException {
204 if (stack.isEmpty()) {
205 throw new JSONException("Nesting problem");
206 }
207 beforeValue();
208 out.append(JSONObject.numberToString(value));
209 return this;
210 }
211
212 public JSONStringer value(long value) throws JSONException {
213 if (stack.isEmpty()) {
214 throw new JSONException("Nesting problem");
215 }
216 beforeValue();
217 out.append(value);
218 return this;
219 }
220
221 private void string(String value) {
222 out.append("\"");
223 for (int i = 0, length = value.length(); i < length; i++) {
224 char c = value.charAt(i);
225
226 /*
227 * From RFC 4627, "All Unicode characters may be placed within the
228 * quotation marks except for the characters that must be escaped:
229 * quotation mark, reverse solidus, and the control characters
230 * (U+0000 through U+001F)."
231 */
232 switch (c) {
233 case '"':
234 case '\\':
235 case '/':
236 out.append('\\').append(c);
237 break;
238
239 case '\t':
240 out.append("\\t");
241 break;
242
243 case '\b':
244 out.append("\\b");
245 break;
246
247 case '\n':
248 out.append("\\n");
249 break;
250
251 case '\r':
252 out.append("\\r");
253 break;
254
255 case '\f':
256 out.append("\\f");
257 break;
258
259 default:
260 if (c <= 0x1F) {
261 out.append(String.format("\\u%04x", (int) c));
262 } else {
263 out.append(c);
264 }
265 break;
266 }
267
268 }
269 out.append("\"");
270 }
271
272 private void newline() {
273 if (indent == null) {
274 return;
275 }
276
277 out.append("\n");
278 for (int i = 0; i < stack.size(); i++) {
279 out.append(indent);
280 }
281 }
282
283 public JSONStringer key(String name) throws JSONException {
284 if (name == null) {
285 throw new JSONException("Names must be non-null");
286 }
287 beforeKey();
288 string(name);
289 return this;
290 }
291
292 /**
293 * Inserts any necessary separators and whitespace before a name. Also
294 * adjusts the stack to expect the key's value.
295 */
296 private void beforeKey() throws JSONException {
297 Scope context = peek();
298 if (context == Scope.NONEMPTY_OBJECT) { // first in object
299 out.append(',');
300 } else if (context != Scope.EMPTY_OBJECT) { // not in an object!
301 throw new JSONException("Nesting problem");
302 }
303 newline();
304 replaceTop(Scope.DANGLING_KEY);
305 }
306
307 /**
308 * Inserts any necessary separators and whitespace before a literal value,
309 * inline array, or inline object. Also adjusts the stack to expect either a
310 * closing bracket or another element.
311 */
312 private void beforeValue() throws JSONException {
313 if (stack.isEmpty()) {
314 return;
315 }
316
317 Scope context = peek();
318 if (context == Scope.EMPTY_ARRAY) { // first in array
319 replaceTop(Scope.NONEMPTY_ARRAY);
320 newline();
321 } else if (context == Scope.NONEMPTY_ARRAY) { // another in array
322 out.append(',');
323 newline();
324 } else if (context == Scope.DANGLING_KEY) { // value for key
325 out.append(indent == null ? ":" : ": ");
326 replaceTop(Scope.NONEMPTY_OBJECT);
327 } else if (context != Scope.NULL) {
328 throw new JSONException("Nesting problem");
329 }
330 }
331
332 /**
333 * Although it contradicts the general contract of {@link Object#toString},
334 * this method returns null if the stringer contains no data.
335 */
336 @Override public String toString() {
337 return out.length() == 0 ? null : out.toString();
338 }
339 }
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 // Note: this class was written without inspecting the non-free org.json sourcecode.
19
20 /**
21 *
22 */
23 public class JSONTokener {
24
25 /** The input JSON. */
26 private final String in;
27
28 /**
29 * The index of the next character to be returned by {@link #next()}. When
30 * the input is exhausted, this equals the input's length.
31 */
32 private int pos;
33
34 public JSONTokener(String in) {
35 this.in = in;
36 }
37
38 public Object nextValue() throws JSONException {
39 int c = nextCleanInternal();
40 switch (c) {
41 case -1:
42 throw syntaxError("End of input");
43
44 case '{':
45 return readObject();
46
47 case '[':
48 return readArray();
49
50 case '\'':
51 case '"':
52 return nextString((char) c);
53
54 default:
55 pos--;
56 return readLiteral();
57 }
58 }
59
60 private int nextCleanInternal() throws JSONException {
61 while (pos < in.length()) {
62 int c = in.charAt(pos++);
63 switch (c) {
64 case '\t':
65 case ' ':
66 case '\n':
67 case '\r':
68 continue;
69
70 case '/':
71 if (pos == in.length()) {
72 return c;
73 }
74
75 char peek = in.charAt(pos);
76 if (peek != '*' && peek != '/') {
77 return c;
78 }
79
80 skipComment();
81 continue;
82
83 default:
84 return c;
85 }
86 }
87
88 return -1;
89 }
90
91 /**
92 * Advances the position until it is beyond the current comment. The opening
93 * slash '/' should have already been read, and character at the current
94 * position be an asterisk '*' for a C-style comment or a slash '/' for an
95 * end-of-line comment.
96 *
97 * @throws JSONException if a C-style comment was not terminated.
98 */
99 private void skipComment() throws JSONException {
100 if (in.charAt(pos++) == '*') {
101 int commentEnd = in.indexOf("*/", pos);
102 if (commentEnd == -1) {
103 throw syntaxError("Unterminated comment");
104 }
105 pos = commentEnd + 2;
106
107 } else {
108 /*
109 * Skip to the next newline character. If the line is terminated by
110 * "\r\n", the '\n' will be consumed as whitespace by the caller.
111 */
112 for (; pos < in.length(); pos++) {
113 char c = in.charAt(pos);
114 if (c == '\r' || c == '\n') {
115 pos++;
116 break;
117 }
118 }
119 }
120 }
121
122 /**
123 *
124 *
125 * @throws NumberFormatException if any unicode escape sequences are
126 * malformed.
127 */
128 public String nextString(char quote) throws JSONException {
129 /*
130 * For strings that are free of escape sequences, we can just extract
131 * the result as a substring of the input. But if we encounter an escape
132 * sequence, we need to use a StringBuilder to compose the result.
133 */
134 StringBuilder builder = null;
135
136 /* the index of the first character not yet appended to the builder. */
137 int start = pos;
138
139 while (pos < in.length()) {
140 int c = in.charAt(pos++);
141 if (c == quote) {
142 if (builder == null) {
143 // a new string avoids leaking memory
144 return new String(in.substring(start, pos - 1));
145 } else {
146 builder.append(in, start, pos - 1);
147 return builder.toString();
148 }
149 }
150
151 if (c == '\\') {
152 if (pos == in.length()) {
153 throw syntaxError("Unterminated escape sequence");
154 }
155 if (builder == null) {
156 builder = new StringBuilder();
157 }
158 builder.append(in, start, pos - 1);
159 builder.append(readEscapeCharacter());
160 start = pos;
161 }
162 }
163
164 throw syntaxError("Unterminated string");
165 }
166
167 /**
168 * Unescapes the character identified by the character or characters that
169 * immediately follow a backslash. The backslash '\' should have already
170 * been read. This supports both unicode escapes "u000A" and two-character
171 * escapes "\n".
172 *
173 * @throws NumberFormatException if any unicode escape sequences are
174 * malformed.
175 */
176 private char readEscapeCharacter() throws JSONException {
177 char escaped = in.charAt(pos++);
178 switch (escaped) {
179 case 'u':
180 if (pos + 4 > in.length()) {
181 throw syntaxError("Unterminated escape sequence");
182 }
183 String hex = in.substring(pos, pos + 4);
184 pos += 4;
185 return (char) Integer.parseInt(hex, 16);
186
187 case 't':
188 return '\t';
189
190 case 'b':
191 return '\b';
192
193 case 'n':
194 return '\n';
195
196 case 'r':
197 return '\r';
198
199 case 'f':
200 return '\f';
201
202 case '\'':
203 case '"':
204 case '\\':
205 default:
206 return escaped;
207 }
208 }
209
210 /**
211 * Reads a null, boolean, numeric or unquoted string literal value. Numeric
212 * values will be returned as an Integer, Long, or Double, in that order of
213 * preference.
214 */
215 private Object readLiteral() throws JSONException {
216 String literal = nextToInternal("{}[]/\\:,=;# \t\f");
217
218 if (literal.length() == 0) {
219 throw syntaxError("Expected literal value");
220 } else if ("null".equalsIgnoreCase(literal)) {
221 return JSONObject.NULL;
222 } else if ("true".equalsIgnoreCase(literal)) {
223 return Boolean.TRUE;
224 } else if ("false".equalsIgnoreCase(literal)) {
225 return Boolean.FALSE;
226 }
227
228 /* try to parse as an integral type... */
229 if (literal.indexOf('.') == -1) {
230 int base = 10;
231 String number = literal;
232 if (number.startsWith("0x") || number.startsWith("0X")) {
233 number = number.substring(2);
234 base = 16;
235 } else if (number.startsWith("0") && number.length() > 1) {
236 number = number.substring(1);
237 base = 8;
238 }
239 try {
240 long longValue = Long.parseLong(number, base);
241 if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) {
242 return (int) longValue;
243 } else {
244 return longValue;
245 }
246 } catch (NumberFormatException e) {
247 /*
248 * This only happens for integral numbers greater than
249 * Long.MAX_VALUE, numbers in exponential form (5e-10) and
250 * unquoted strings. Fall through to try floating point.
251 */
252 }
253 }
254
255 /* ...next try to parse as a floating point... */
256 try {
257 return Double.valueOf(literal);
258 } catch (NumberFormatException e) {
259 }
260
261 /* ... finally give up. We have an unquoted string */
262 return new String(literal); // a new string avoids leaking memory
263 }
264
265 /**
266 * Returns text from the current position until the first of any of the
267 * given characters or a newline character, excluding that character. The
268 * position is advanced to the excluded character.
269 */
270 private String nextToInternal(String excluded) {
271 int start = pos;
272 for (; pos < in.length(); pos++) {
273 char c = in.charAt(pos);
274 if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) {
275 return in.substring(start, pos);
276 }
277 }
278 return in.substring(start);
279 }
280
281 /**
282 * Reads a sequence of key/value pairs and the trailing closing brace '}' of
283 * an object. The opening brace '{' should have already been read.
284 */
285 private JSONObject readObject() throws JSONException {
286 JSONObject result = new JSONObject();
287
288 /* Peek to see if this is the empty object. */
289 int first = nextCleanInternal();
290 if (first == '}') {
291 return result;
292 } else if (first != -1) {
293 pos--;
294 }
295
296 while (true) {
297 Object name = nextValue();
298 if (!(name instanceof String)) {
299 if (name == null) {
300 throw syntaxError("Names cannot be null");
301 } else {
302 throw syntaxError("Names must be strings, but " + name
303 + " is of type " + name.getClass().getName());
304 }
305 }
306
307 /*
308 * Expect the name/value separator to be either a colon ':', an
309 * equals sign '=', or an arrow "=>". The last two are bogus but we
310 * include them because that's what the original implementation did.
311 */
312 int separator = nextCleanInternal();
313 if (separator != ':' && separator != '=') {
314 throw syntaxError("Expected ':' after " + name);
315 }
316 if (pos < in.length() && in.charAt(pos) == '>') {
317 pos++;
318 }
319
320 result.put((String) name, nextValue());
321
322 switch (nextCleanInternal()) {
323 case '}':
324 return result;
325 case ';':
326 case ',':
327 continue;
328 default:
329 throw syntaxError("Unterminated object");
330 }
331 }
332 }
333
334 /**
335 * Reads a sequence of values and the trailing closing brace ']' of an
336 * array. The opening brace '[' should have already been read. Note that
337 * "[]" yields an empty array, but "[,]" returns a two-element array
338 * equivalent to "[null,null]".
339 */
340 private JSONArray readArray() throws JSONException {
341 JSONArray result = new JSONArray();
342
343 /* to cover input that ends with ",]". */
344 boolean hasTrailingSeparator = false;
345
346 while (true) {
347 switch (nextCleanInternal()) {
348 case -1:
349 throw syntaxError("Unterminated array");
350 case ']':
351 if (hasTrailingSeparator) {
352 result.put(null);
353 }
354 return result;
355 case ',':
356 case ';':
357 /* A separator without a value first means "null". */
358 result.put(null);
359 hasTrailingSeparator = true;
360 continue;
361 default:
362 pos--;
363 }
364
365 result.put(nextValue());
366
367 switch (nextCleanInternal()) {
368 case ']':
369 return result;
370 case ',':
371 case ';':
372 hasTrailingSeparator = true;
373 continue;
374 default:
375 throw syntaxError("Unterminated array");
376 }
377 }
378 }
379
380 public JSONException syntaxError(String text) {
381 return new JSONException(text + this);
382 }
383
384 @Override public String toString() {
385 // consistent with the original implementation
386 return " at character " + pos + " of " + in;
387 }
388
389 /*
390 * Legacy APIs.
391 *
392 * None of the methods below are on the critical path of parsing JSON
393 * documents. They exist only because they were exposed by the original
394 * implementation and may be used by some clients.
395 */
396
397 public boolean more() {
398 return pos < in.length();
399 }
400
401 public char next() {
402 return pos < in.length() ? in.charAt(pos++) : '\0';
403 }
404
405 public char next(char c) throws JSONException {
406 char result = next();
407 if (result != c) {
408 throw syntaxError("Expected " + c + " but was " + result);
409 }
410 return result;
411 }
412
413 public char nextClean() throws JSONException {
414 int nextCleanInt = nextCleanInternal();
415 return nextCleanInt == -1 ? '\0' : (char) nextCleanInt;
416 }
417
418 /**
419 * TODO: note about how this method returns a substring, and could cause a memory leak
420 */
421 public String next(int length) throws JSONException {
422 if (pos + length > in.length()) {
423 throw syntaxError(length + " is out of bounds");
424 }
425 String result = in.substring(pos, pos + length);
426 pos += length;
427 return result;
428 }
429
430 /**
431 * TODO: note about how this method returns a substring, and could cause a memory leak
432 */
433 public String nextTo(String excluded) {
434 if (excluded == null) {
435 throw new NullPointerException();
436 }
437 return nextToInternal(excluded).trim();
438 }
439
440 /**
441 * TODO: note about how this method returns a substring, and could cause a memory leak
442 */
443 public String nextTo(char excluded) {
444 return nextToInternal(String.valueOf(excluded)).trim();
445 }
446
447 public void skipPast(String thru) {
448 int thruStart = in.indexOf(thru, pos);
449 pos = thruStart == -1 ? in.length() : (thruStart + thru.length());
450 }
451
452 public char skipTo(char to) {
453 for (int i = pos, length = in.length(); i < length; i++) {
454 if (in.charAt(i) == to) {
455 pos = i;
456 return to;
457 }
458 }
459 return '\0';
460 }
461
462 public void back() {
463 if (--pos == -1) {
464 pos = 0;
465 }
466 }
467
468 public static int dehexchar(char hex) {
469 if (hex >= '0' && hex <= '9') {
470 return hex - '0';
471 } else if (hex >= 'A' && hex <= 'F') {
472 return hex - 'A' + 10;
473 } else if (hex >= 'a' && hex <= 'f') {
474 return hex - 'a' + 10;
475 } else {
476 return -1;
477 }
478 }
479 }
+0
-112
src/rewrite/java/org/json/JSON.java less more
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 class JSON {
19 /**
20 * Returns the input if it is a JSON-permissable value; throws otherwise.
21 */
22 static double checkDouble(double d) throws JSONException {
23 if (Double.isInfinite(d) || Double.isNaN(d)) {
24 throw new JSONException("Forbidden numeric value: " + d);
25 }
26 return d;
27 }
28
29 static Boolean toBoolean(Object value) {
30 if (value instanceof Boolean) {
31 return (Boolean) value;
32 } else if (value instanceof String) {
33 return Boolean.valueOf(((String) value));
34 } else {
35 return null;
36 }
37 }
38
39 static Double toDouble(Object value) {
40 if (value instanceof Double) {
41 return (Double) value;
42 } else if (value instanceof Number) {
43 return ((Number) value).doubleValue();
44 } else if (value instanceof String) {
45 try {
46 return Double.valueOf((String) value);
47 } catch (NumberFormatException e) {
48 }
49 }
50 return null;
51 }
52
53 static Integer toInteger(Object value) {
54 if (value instanceof Integer) {
55 return (Integer) value;
56 } else if (value instanceof Number) {
57 return ((Number) value).intValue();
58 } else if (value instanceof String) {
59 try {
60 return Double.valueOf((String) value).intValue();
61 } catch (NumberFormatException e) {
62 }
63 }
64 return null;
65 }
66
67 static Long toLong(Object value) {
68 if (value instanceof Long) {
69 return (Long) value;
70 } else if (value instanceof Number) {
71 return ((Number) value).longValue();
72 } else if (value instanceof String) {
73 try {
74 return Double.valueOf((String) value).longValue();
75 } catch (NumberFormatException e) {
76 }
77 }
78 return null;
79 }
80
81 static String toString(Object value) {
82 if (value instanceof String) {
83 return (String) value;
84 } else if (value != null) {
85 return String.valueOf(value);
86 }
87 return null;
88 }
89
90 public static JSONException typeMismatch(Object indexOrName, Object actual,
91 String requiredType) throws JSONException {
92 if (actual == null) {
93 throw new JSONException("Value at " + indexOrName + " is null.");
94 } else {
95 throw new JSONException("Value " + actual + " at " + indexOrName
96 + " of type " + actual.getClass().getName()
97 + " cannot be converted to " + requiredType);
98 }
99 }
100
101 public static JSONException typeMismatch(Object actual, String requiredType)
102 throws JSONException {
103 if (actual == null) {
104 throw new JSONException("Value is null.");
105 } else {
106 throw new JSONException("Value " + actual
107 + " of type " + actual.getClass().getName()
108 + " cannot be converted to " + requiredType);
109 }
110 }
111 }
+0
-322
src/rewrite/java/org/json/JSONArray.java less more
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 import java.util.List;
19 import java.util.ArrayList;
20 import java.util.Collection;
21
22 // Note: this class was written without inspecting the non-free org.json sourcecode.
23
24 /**
25 * An indexed sequence of JSON-safe values.
26 */
27 public class JSONArray {
28
29 private final List<Object> values;
30
31 public JSONArray() {
32 values = new ArrayList<Object>();
33 }
34
35 /* Accept a raw type for API compatibility */
36 public JSONArray(Collection copyFrom) {
37 this();
38 Collection<?> copyFromTyped = (Collection<?>) copyFrom;
39 values.addAll(copyFromTyped);
40 }
41
42 public JSONArray(JSONTokener readFrom) throws JSONException {
43 /*
44 * Getting the parser to populate this could get tricky. Instead, just
45 * parse to temporary JSONArray and then steal the data from that.
46 */
47 Object object = readFrom.nextValue();
48 if (object instanceof JSONArray) {
49 values = ((JSONArray) object).values;
50 } else {
51 throw JSON.typeMismatch(object, "JSONArray");
52 }
53 }
54
55 public JSONArray(String json) throws JSONException {
56 this(new JSONTokener(json));
57 }
58
59 public int length() {
60 return values.size();
61 }
62
63 public JSONArray put(boolean value) {
64 values.add(value);
65 return this;
66 }
67
68 public JSONArray put(double value) throws JSONException {
69 values.add(JSON.checkDouble(value));
70 return this;
71 }
72
73 public JSONArray put(int value) {
74 values.add(value);
75 return this;
76 }
77
78 public JSONArray put(long value) {
79 values.add(value);
80 return this;
81 }
82
83 public JSONArray put(Object value) {
84 values.add(value);
85 return this;
86 }
87
88 public JSONArray put(int index, boolean value) throws JSONException {
89 return put(index, (Boolean) value);
90 }
91
92 public JSONArray put(int index, double value) throws JSONException {
93 return put(index, (Double) value);
94 }
95
96 public JSONArray put(int index, int value) throws JSONException {
97 return put(index, (Integer) value);
98 }
99
100 public JSONArray put(int index, long value) throws JSONException {
101 return put(index, (Long) value);
102 }
103
104 public JSONArray put(int index, Object value) throws JSONException {
105 if (value instanceof Number) {
106 // deviate from the original by checking all Numbers, not just floats & doubles
107 JSON.checkDouble(((Number) value).doubleValue());
108 }
109 while (values.size() <= index) {
110 values.add(null);
111 }
112 values.set(index, value);
113 return this;
114 }
115
116 public boolean isNull(int index) {
117 Object value = opt(index);
118 return value == null || value == JSONObject.NULL;
119 }
120
121 public Object get(int index) throws JSONException {
122 try {
123 Object value = values.get(index);
124 if (value == null) {
125 throw new JSONException("Value at " + index + " is null.");
126 }
127 return value;
128 } catch (IndexOutOfBoundsException e) {
129 throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")");
130 }
131 }
132
133 public Object opt(int index) {
134 if (index < 0 || index >= values.size()) {
135 return null;
136 }
137 return values.get(index);
138 }
139
140 public boolean getBoolean(int index) throws JSONException {
141 Object object = get(index);
142 Boolean result = JSON.toBoolean(object);
143 if (result == null) {
144 throw JSON.typeMismatch(index, object, "boolean");
145 }
146 return result;
147 }
148
149 public boolean optBoolean(int index) {
150 return optBoolean(index, false);
151 }
152
153 public boolean optBoolean(int index, boolean fallback) {
154 Object object = opt(index);
155 Boolean result = JSON.toBoolean(object);
156 return result != null ? result : fallback;
157 }
158
159 public double getDouble(int index) throws JSONException {
160 Object object = get(index);
161 Double result = JSON.toDouble(object);
162 if (result == null) {
163 throw JSON.typeMismatch(index, object, "double");
164 }
165 return result;
166 }
167
168 public double optDouble(int index) {
169 return optDouble(index, Double.NaN);
170 }
171
172 public double optDouble(int index, double fallback) {
173 Object object = opt(index);
174 Double result = JSON.toDouble(object);
175 return result != null ? result : fallback;
176 }
177
178 public int getInt(int index) throws JSONException {
179 Object object = get(index);
180 Integer result = JSON.toInteger(object);
181 if (result == null) {
182 throw JSON.typeMismatch(index, object, "int");
183 }
184 return result;
185 }
186
187 public int optInt(int index) {
188 return optInt(index, 0);
189 }
190
191 public int optInt(int index, int fallback) {
192 Object object = opt(index);
193 Integer result = JSON.toInteger(object);
194 return result != null ? result : fallback;
195 }
196
197 public long getLong(int index) throws JSONException {
198 Object object = get(index);
199 Long result = JSON.toLong(object);
200 if (result == null) {
201 throw JSON.typeMismatch(index, object, "long");
202 }
203 return result;
204 }
205
206 public long optLong(int index) {
207 return optLong(index, 0L);
208 }
209
210 public long optLong(int index, long fallback) {
211 Object object = opt(index);
212 Long result = JSON.toLong(object);
213 return result != null ? result : fallback;
214 }
215
216 public String getString(int index) throws JSONException {
217 Object object = get(index);
218 String result = JSON.toString(object);
219 if (result == null) {
220 throw JSON.typeMismatch(index, object, "String");
221 }
222 return result;
223 }
224
225 public String optString(int index) {
226 return optString(index, "");
227 }
228
229 public String optString(int index, String fallback) {
230 Object object = opt(index);
231 String result = JSON.toString(object);
232 return result != null ? result : fallback;
233 }
234
235 public JSONArray getJSONArray(int index) throws JSONException {
236 Object object = get(index);
237 if (object instanceof JSONArray) {
238 return (JSONArray) object;
239 } else {
240 throw JSON.typeMismatch(index, object, "JSONArray");
241 }
242 }
243
244 public JSONArray optJSONArray(int index) {
245 Object object = opt(index);
246 return object instanceof JSONArray ? (JSONArray) object : null;
247 }
248
249 public JSONObject getJSONObject(int index) throws JSONException {
250 Object object = get(index);
251 if (object instanceof JSONObject) {
252 return (JSONObject) object;
253 } else {
254 throw JSON.typeMismatch(index, object, "JSONObject");
255 }
256 }
257
258 public JSONObject optJSONObject(int index) {
259 Object object = opt(index);
260 return object instanceof JSONObject ? (JSONObject) object : null;
261 }
262
263 public JSONObject toJSONObject(JSONArray names) throws JSONException {
264 JSONObject result = new JSONObject();
265 int length = Math.min(names.length(), values.size());
266 if (length == 0) {
267 return null;
268 }
269 for (int i = 0; i < length; i++) {
270 String name = JSON.toString(names.opt(i));
271 result.put(name, opt(i));
272 }
273 return result;
274 }
275
276 public String join(String separator) throws JSONException {
277 JSONStringer stringer = new JSONStringer();
278 stringer.open(JSONStringer.Scope.NULL, "");
279 for (int i = 0, size = values.size(); i < size; i++) {
280 if (i > 0) {
281 stringer.out.append(separator);
282 }
283 stringer.value(values.get(i));
284 }
285 stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
286 return stringer.out.toString();
287 }
288
289 @Override public String toString() {
290 try {
291 JSONStringer stringer = new JSONStringer();
292 writeTo(stringer);
293 return stringer.toString();
294 } catch (JSONException e) {
295 return null;
296 }
297 }
298
299 public String toString(int indentSpaces) throws JSONException {
300 JSONStringer stringer = new JSONStringer(indentSpaces);
301 writeTo(stringer);
302 return stringer.toString();
303 }
304
305 void writeTo(JSONStringer stringer) throws JSONException {
306 stringer.array();
307 for (Object value : values) {
308 stringer.value(value);
309 }
310 stringer.endArray();
311 }
312
313 @Override public boolean equals(Object o) {
314 return o instanceof JSONArray && ((JSONArray) o).values.equals(values);
315 }
316
317 @Override public int hashCode() {
318 // diverge from the original, which doesn't implement hashCode
319 return values.hashCode();
320 }
321 }
+0
-49
src/rewrite/java/org/json/JSONException.java less more
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 // Note: this class was written without inspecting the non-free org.json sourcecode.
19
20 /**
21 * Thrown to indicate a problem with the JSON API. Such problems include:
22 * <ul>
23 * <li>Attempts to parse or construct malformed documents
24 * <li>Use of null as a name
25 * <li>Use of numeric types not available to JSON, such as {@link Double#NaN
26 * NaN} or {@link Double#POSITIVE_INFINITY infinity}.
27 * <li>Lookups using an out of range index or nonexistant name
28 * <li>Type mismatches on lookups
29 * </ul>
30 *
31 * <p>Although this is a checked exception, it is rarely recoverable. Most
32 * callers should simply wrap this exception in an unchecked exception and
33 * rethrow:
34 * <pre> public JSONArray toJSONObject() {
35 * try {
36 * JSONObject result = new JSONObject();
37 * ...
38 * } catch (JSONException e) {
39 * throw new RuntimeException(e);
40 * }
41 * }</pre>
42 */
43 public class JSONException extends Exception {
44
45 public JSONException(String s) {
46 super(s);
47 }
48 }
+0
-395
src/rewrite/java/org/json/JSONObject.java less more
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22
23 // Note: this class was written without inspecting the non-free org.json sourcecode.
24
25 /**
26 *
27 *
28 * <p>TODO: Note about self-use
29 */
30 public class JSONObject {
31
32 private static final Double NEGATIVE_ZERO = -0d;
33
34 public static final Object NULL = new Object() {
35 @Override public boolean equals(Object o) {
36 return o == this || o == null; // API specifies this broken equals implementation
37 }
38 @Override public String toString() {
39 return "null";
40 }
41 };
42
43 private final Map<String, Object> nameValuePairs;
44
45 public JSONObject() {
46 nameValuePairs = new HashMap<String, Object>();
47 }
48
49 /* Accept a raw type for API compatibility */
50 public JSONObject(Map copyFrom) {
51 this();
52 Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
53 for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
54 /*
55 * Deviate from the original by checking that keys are non-null and
56 * of the proper type. (We still defer validating the values).
57 */
58 String key = (String) entry.getKey();
59 if (key == null) {
60 throw new NullPointerException();
61 }
62 nameValuePairs.put(key, entry.getValue());
63 }
64 }
65
66 public JSONObject(JSONTokener readFrom) throws JSONException {
67 /*
68 * Getting the parser to populate this could get tricky. Instead, just
69 * parse to temporary JSONObject and then steal the data from that.
70 */
71 Object object = readFrom.nextValue();
72 if (object instanceof JSONObject) {
73 this.nameValuePairs = ((JSONObject) object).nameValuePairs;
74 } else {
75 throw JSON.typeMismatch(object, "JSONObject");
76 }
77 }
78
79 public JSONObject(String json) throws JSONException {
80 this(new JSONTokener(json));
81 }
82
83 public JSONObject(JSONObject copyFrom, String[] names) throws JSONException {
84 this();
85 for (String name : names) {
86 Object value = copyFrom.opt(name);
87 if (value != null) {
88 nameValuePairs.put(name, value);
89 }
90 }
91 }
92
93 public int length() {
94 return nameValuePairs.size();
95 }
96
97 public JSONObject put(String name, boolean value) throws JSONException {
98 nameValuePairs.put(checkName(name), value);
99 return this;
100 }
101
102 public JSONObject put(String name, double value) throws JSONException {
103 nameValuePairs.put(checkName(name), JSON.checkDouble(value));
104 return this;
105 }
106
107 public JSONObject put(String name, int value) throws JSONException {
108 nameValuePairs.put(checkName(name), value);
109 return this;
110 }
111
112 public JSONObject put(String name, long value) throws JSONException {
113 nameValuePairs.put(checkName(name), value);
114 return this;
115 }
116
117 public JSONObject put(String name, Object value) throws JSONException {
118 if (value == null) {
119 nameValuePairs.remove(name);
120 return this;
121 }
122 if (value instanceof Number) {
123 // deviate from the original by checking all Numbers, not just floats & doubles
124 JSON.checkDouble(((Number) value).doubleValue());
125 }
126 nameValuePairs.put(checkName(name), value);
127 return this;
128 }
129
130 public JSONObject putOpt(String name, Object value) throws JSONException {
131 if (name == null || value == null) {
132 return this;
133 }
134 return put(name, value);
135 }
136
137 public JSONObject accumulate(String name, Object value) throws JSONException {
138 Object current = nameValuePairs.get(checkName(name));
139 if (current == null) {
140 put(name, value);
141 } else if (current instanceof JSONArray) {
142 JSONArray array = (JSONArray) current;
143 array.put(value);
144 } else {
145 JSONArray array = new JSONArray();
146 array.put(current);
147 array.put(value); // fails on bogus values
148 nameValuePairs.put(name, array);
149 }
150 return this;
151 }
152
153 String checkName(String name) throws JSONException {
154 if (name == null) {
155 throw new JSONException("Names must be non-null");
156 }
157 return name;
158 }
159
160 public Object remove(String name) {
161 return nameValuePairs.remove(name);
162 }
163
164 public boolean isNull(String name) {
165 Object value = nameValuePairs.get(name);
166 return value == null || value == NULL;
167 }
168
169 public boolean has(String name) {
170 return nameValuePairs.containsKey(name);
171 }
172
173 public Object get(String name) throws JSONException {
174 Object result = nameValuePairs.get(name);
175 if (result == null) {
176 throw new JSONException("No value for " + name);
177 }
178 return result;
179 }
180
181 public Object opt(String name) {
182 return nameValuePairs.get(name);
183 }
184
185 public boolean getBoolean(String name) throws JSONException {
186 Object object = get(name);
187 Boolean result = JSON.toBoolean(object);
188 if (result == null) {
189 throw JSON.typeMismatch(name, object, "boolean");
190 }
191 return result;
192 }
193
194 public boolean optBoolean(String name) {
195 return optBoolean(name, false);
196 }
197
198 public boolean optBoolean(String name, boolean fallback) {
199 Object object = opt(name);
200 Boolean result = JSON.toBoolean(object);
201 return result != null ? result : fallback;
202 }
203
204 public double getDouble(String name) throws JSONException {
205 Object object = get(name);
206 Double result = JSON.toDouble(object);
207 if (result == null) {
208 throw JSON.typeMismatch(name, object, "double");
209 }
210 return result;
211 }
212
213 public double optDouble(String name) {
214 return optDouble(name, Double.NaN);
215 }
216
217 public double optDouble(String name, double fallback) {
218 Object object = opt(name);
219 Double result = JSON.toDouble(object);
220 return result != null ? result : fallback;
221 }
222
223 public int getInt(String name) throws JSONException {
224 Object object = get(name);
225 Integer result = JSON.toInteger(object);
226 if (result == null) {
227 throw JSON.typeMismatch(name, object, "int");
228 }
229 return result;
230 }
231
232 public int optInt(String name) {
233 return optInt(name, 0);
234 }
235
236 public int optInt(String name, int fallback) {
237 Object object = opt(name);
238 Integer result = JSON.toInteger(object);
239 return result != null ? result : fallback;
240 }
241
242 public long getLong(String name) throws JSONException {
243 Object object = get(name);
244 Long result = JSON.toLong(object);
245 if (result == null) {
246 throw JSON.typeMismatch(name, object, "long");
247 }
248 return result;
249 }
250
251 public long optLong(String name) {
252 return optLong(name, 0L);
253 }
254
255 public long optLong(String name, long fallback) {
256 Object object = opt(name);
257 Long result = JSON.toLong(object);
258 return result != null ? result : fallback;
259 }
260
261 public String getString(String name) throws JSONException {
262 Object object = get(name);
263 String result = JSON.toString(object);
264 if (result == null) {
265 throw JSON.typeMismatch(name, object, "String");
266 }
267 return result;
268 }
269
270 public String optString(String name) {
271 return optString(name, "");
272 }
273
274 public String optString(String name, String fallback) {
275 Object object = opt(name);
276 String result = JSON.toString(object);
277 return result != null ? result : fallback;
278 }
279
280 public JSONArray getJSONArray(String name) throws JSONException {
281 Object object = get(name);
282 if (object instanceof JSONArray) {
283 return (JSONArray) object;
284 } else {
285 throw JSON.typeMismatch(name, object, "JSONArray");
286 }
287 }
288
289 public JSONArray optJSONArray(String name) {
290 Object object = opt(name);
291 return object instanceof JSONArray ? (JSONArray) object : null;
292 }
293
294 public JSONObject getJSONObject(String name) throws JSONException {
295 Object object = get(name);
296 if (object instanceof JSONObject) {
297 return (JSONObject) object;
298 } else {
299 throw JSON.typeMismatch(name, object, "JSONObject");
300 }
301 }
302
303 public JSONObject optJSONObject(String name) {
304 Object object = opt(name);
305 return object instanceof JSONObject ? (JSONObject) object : null;
306 }
307
308 public JSONArray toJSONArray(JSONArray names) throws JSONException {
309 JSONArray result = new JSONArray();
310 if (names == null) {
311 return null;
312 }
313 int length = names.length();
314 if (length == 0) {
315 return null;
316 }
317 for (int i = 0; i < length; i++) {
318 String name = JSON.toString(names.opt(i));
319 result.put(opt(name));
320 }
321 return result;
322 }
323
324 /* Return a raw type for API compatibility */
325 public Iterator keys() {
326 return nameValuePairs.keySet().iterator();
327 }
328
329 public JSONArray names() {
330 return nameValuePairs.isEmpty()
331 ? null
332 : new JSONArray(new ArrayList<String>(nameValuePairs.keySet()));
333 }
334
335 @Override public String toString() {
336 try {
337 JSONStringer stringer = new JSONStringer();
338 writeTo(stringer);
339 return stringer.toString();
340 } catch (JSONException e) {
341 return null;
342 }
343 }
344
345 public String toString(int indentSpaces) throws JSONException {
346 JSONStringer stringer = new JSONStringer(indentSpaces);
347 writeTo(stringer);
348 return stringer.toString();
349 }
350
351 void writeTo(JSONStringer stringer) throws JSONException {
352 stringer.object();
353 for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) {
354 stringer.key(entry.getKey()).value(entry.getValue());
355 }
356 stringer.endObject();
357 }
358
359 public static String numberToString(Number number) throws JSONException {
360 if (number == null) {
361 throw new JSONException("Number must be non-null");
362 }
363
364 double doubleValue = number.doubleValue();
365 JSON.checkDouble(doubleValue);
366
367 // the original returns "-0" instead of "-0.0" for negative zero
368 if (number.equals(NEGATIVE_ZERO)) {
369 return "-0";
370 }
371
372 long longValue = number.longValue();
373 if (doubleValue == (double) longValue) {
374 return Long.toString(longValue);
375 }
376
377 return number.toString();
378 }
379
380 public static String quote(String data) {
381 if (data == null) {
382 return "\"\"";
383 }
384 try {
385 JSONStringer stringer = new JSONStringer();
386 stringer.open(JSONStringer.Scope.NULL, "");
387 stringer.value(data);
388 stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
389 return stringer.toString();
390 } catch (JSONException e) {
391 throw new AssertionError();
392 }
393 }
394 }
+0
-340
src/rewrite/java/org/json/JSONStringer.java less more
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.List;
21
22 // Note: this class was written without inspecting the non-free org.json sourcecode.
23
24 /**
25 *
26 */
27 public class JSONStringer {
28
29 /** The output data, containing at most one top-level array or object. */
30 final StringBuilder out = new StringBuilder();
31
32 /**
33 * Lexical scoping elements within this stringer, necessary to insert the
34 * appropriate separator characters (ie. commas and colons) and to detect
35 * nesting errors.
36 */
37 enum Scope {
38
39 /**
40 * An array with no elements requires no separators or newlines before
41 * it is closed.
42 */
43 EMPTY_ARRAY,
44
45 /**
46 * A array with at least one value requires a comma and newline before
47 * the next element.
48 */
49 NONEMPTY_ARRAY,
50
51 /**
52 * An object with no keys or values requires no separators or newlines
53 * before it is closed.
54 */
55 EMPTY_OBJECT,
56
57 /**
58 * An object whose most recent element is a key. The next element must
59 * be a value.
60 */
61 DANGLING_KEY,
62
63 /**
64 * An object with at least one name/value pair requires a comma and
65 * newline before the next element.
66 */
67 NONEMPTY_OBJECT,
68
69 /**
70 * A special bracketless array needed by JSONStringer.join() and
71 * JSONObject.quote() only. Not used for JSON encoding.
72 */
73 NULL,
74 }
75
76 /**
77 * Unlike the original implementation, this stack isn't limited to 20
78 * levels of nesting.
79 */
80 private final List<Scope> stack = new ArrayList<Scope>();
81
82 /**
83 * A string containing a full set of spaces for a single level of
84 * indentation, or null for no pretty printing.
85 */
86 private final String indent;
87
88 public JSONStringer() {
89 indent = null;
90 }
91
92 JSONStringer(int indentSpaces) {
93 char[] indentChars = new char[indentSpaces];
94 Arrays.fill(indentChars, ' ');
95 indent = new String(indentChars);
96 }
97
98 public JSONStringer array() throws JSONException {
99 return open(Scope.EMPTY_ARRAY, "[");
100 }
101
102 public JSONStringer endArray() throws JSONException {
103 return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]");
104 }
105
106 public JSONStringer object() throws JSONException {
107 return open(Scope.EMPTY_OBJECT, "{");
108 }
109
110 public JSONStringer endObject() throws JSONException {
111 return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}");
112 }
113
114 /**
115 * Enters a new scope by appending any necessary whitespace and the given
116 * bracket.
117 */
118 JSONStringer open(Scope empty, String openBracket) throws JSONException {
119 if (stack.isEmpty() && out.length() > 0) {
120 throw new JSONException("Nesting problem: multiple top-level roots");
121 }
122 beforeValue();
123 stack.add(empty);
124 out.append(openBracket);
125 return this;
126 }
127
128 /**
129 * Closes the current scope by appending any necessary whitespace and the
130 * given bracket.
131 */
132 JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException {
133 Scope context = peek();
134 if (context != nonempty && context != empty) {
135 throw new JSONException("Nesting problem");
136 }
137
138 stack.remove(stack.size() - 1);
139 if (context == nonempty) {
140 newline();
141 }
142 out.append(closeBracket);
143 return this;
144 }
145
146 /**
147 * Returns the value on the top of the stack.
148 */
149 private Scope peek() throws JSONException {
150 if (stack.isEmpty()) {
151 throw new JSONException("Nesting problem");
152 }
153 return stack.get(stack.size() - 1);
154 }
155
156 /**
157 * Replace the value on the top of the stack with the given value.
158 */
159 private void replaceTop(Scope topOfStack) {
160 stack.set(stack.size() - 1, topOfStack);
161 }
162
163 public JSONStringer value(Object value) throws JSONException {
164 if (stack.isEmpty()) {
165 throw new JSONException("Nesting problem");
166 }
167
168 if (value instanceof JSONArray) {
169 ((JSONArray) value).writeTo(this);
170 return this;
171
172 } else if (value instanceof JSONObject) {
173 ((JSONObject) value).writeTo(this);
174 return this;
175 }
176
177 beforeValue();
178
179 if (value == null
180 || value instanceof Boolean
181 || value == JSONObject.NULL) {
182 out.append(value);
183
184 } else if (value instanceof Number) {
185 out.append(JSONObject.numberToString((Number) value));
186
187 } else {
188 string(value.toString());
189 }
190
191 return this;
192 }
193
194 public JSONStringer value(boolean value) throws JSONException {
195 if (stack.isEmpty()) {
196 throw new JSONException("Nesting problem");
197 }
198 beforeValue();
199 out.append(value);
200 return this;
201 }
202
203 public JSONStringer value(double value) throws JSONException {
204 if (stack.isEmpty()) {
205 throw new JSONException("Nesting problem");
206 }
207 beforeValue();
208 out.append(JSONObject.numberToString(value));
209 return this;
210 }
211
212 public JSONStringer value(long value) throws JSONException {
213 if (stack.isEmpty()) {
214 throw new JSONException("Nesting problem");
215 }
216 beforeValue();
217 out.append(value);
218 return this;
219 }
220
221 private void string(String value) {
222 out.append("\"");
223 for (int i = 0, length = value.length(); i < length; i++) {
224 char c = value.charAt(i);
225
226 /*
227 * From RFC 4627, "All Unicode characters may be placed within the
228 * quotation marks except for the characters that must be escaped:
229 * quotation mark, reverse solidus, and the control characters
230 * (U+0000 through U+001F)."
231 */
232 switch (c) {
233 case '"':
234 case '\\':
235 case '/':
236 out.append('\\').append(c);
237 break;
238
239 case '\t':
240 out.append("\\t");
241 break;
242
243 case '\b':
244 out.append("\\b");
245 break;
246
247 case '\n':
248 out.append("\\n");
249 break;
250
251 case '\r':
252 out.append("\\r");
253 break;
254
255 case '\f':
256 out.append("\\f");
257 break;
258
259 default:
260 if (c <= 0x1F) {
261 out.append(String.format("\\u%04x", (int) c));
262 } else {
263 out.append(c);
264 }
265 break;
266 }
267
268 }
269 out.append("\"");
270 }
271
272 private void newline() {
273 if (indent == null) {
274 return;
275 }
276
277 out.append("\n");
278 for (int i = 0; i < stack.size(); i++) {
279 out.append(indent);
280 }
281 }
282
283 public JSONStringer key(String name) throws JSONException {
284 if (name == null) {
285 throw new JSONException("Names must be non-null");
286 }
287 beforeKey();
288 string(name);
289 return this;
290 }
291
292 /**
293 * Inserts any necessary separators and whitespace before a name. Also
294 * adjusts the stack to expect the key's value.
295 */
296 private void beforeKey() throws JSONException {
297 Scope context = peek();
298 if (context == Scope.NONEMPTY_OBJECT) { // first in object
299 out.append(',');
300 } else if (context != Scope.EMPTY_OBJECT) { // not in an object!
301 throw new JSONException("Nesting problem");
302 }
303 newline();
304 replaceTop(Scope.DANGLING_KEY);
305 }
306
307 /**
308 * Inserts any necessary separators and whitespace before a literal value,
309 * inline array, or inline object. Also adjusts the stack to expect either a
310 * closing bracket or another element.
311 */
312 private void beforeValue() throws JSONException {
313 if (stack.isEmpty()) {
314 return;
315 }
316
317 Scope context = peek();
318 if (context == Scope.EMPTY_ARRAY) { // first in array
319 replaceTop(Scope.NONEMPTY_ARRAY);
320 newline();
321 } else if (context == Scope.NONEMPTY_ARRAY) { // another in array
322 out.append(',');
323 newline();
324 } else if (context == Scope.DANGLING_KEY) { // value for key
325 out.append(indent == null ? ":" : ": ");
326 replaceTop(Scope.NONEMPTY_OBJECT);
327 } else if (context != Scope.NULL) {
328 throw new JSONException("Nesting problem");
329 }
330 }
331
332 /**
333 * Although it contradicts the general contract of {@link Object#toString},
334 * this method returns null if the stringer contains no data.
335 */
336 @Override public String toString() {
337 return out.length() == 0 ? null : out.toString();
338 }
339 }
+0
-480
src/rewrite/java/org/json/JSONTokener.java less more
0 /*
1 * Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.json;
17
18 // Note: this class was written without inspecting the non-free org.json sourcecode.
19
20 /**
21 *
22 */
23 public class JSONTokener {
24
25 /** The input JSON. */
26 private final String in;
27
28 /**
29 * The index of the next character to be returned by {@link #next()}. When
30 * the input is exhausted, this equals the input's length.
31 */
32 private int pos;
33
34 public JSONTokener(String in) {
35 this.in = in;
36 }
37
38 public Object nextValue() throws JSONException {
39 int c = nextCleanInternal();
40 switch (c) {
41 case -1:
42 throw syntaxError("End of input");
43
44 case '{':
45 return readObject();
46
47 case '[':
48 return readArray();
49
50 case '\'':
51 case '"':
52 return nextString((char) c);
53
54 default:
55 pos--;
56 return readLiteral();
57 }
58 }
59
60 private int nextCleanInternal() throws JSONException {
61 while (pos < in.length()) {
62 int c = in.charAt(pos++);
63 switch (c) {
64 case '\t':
65 case ' ':
66 case '\n':
67 case '\r':
68 continue;
69
70 case '/':
71 if (pos == in.length()) {
72 return c;
73 }
74
75 char peek = in.charAt(pos);
76 if (peek != '*' && peek != '/') {
77 return c;
78 }
79
80 skipComment();
81 continue;
82
83 default:
84 return c;
85 }
86 }
87
88 return -1;
89 }
90
91 /**
92 * Advances the position until it is beyond the current comment. The opening
93 * slash '/' should have already been read, and character at the current
94 * position be an asterisk '*' for a C-style comment or a slash '/' for an
95 * end-of-line comment.
96 *
97 * @throws JSONException if a C-style comment was not terminated.
98 */
99 private void skipComment() throws JSONException {
100 if (in.charAt(pos++) == '*') {
101 int commentEnd = in.indexOf("*/", pos);
102 if (commentEnd == -1) {
103 throw syntaxError("Unterminated comment");
104 }
105 pos = commentEnd + 2;
106
107 } else {
108 /*
109 * Skip to the next newline character. If the line is terminated by
110 * "\r\n", the '\n' will be consumed as whitespace by the caller.
111 */
112 for (; pos < in.length(); pos++) {
113 char c = in.charAt(pos);
114 if (c == '\r' || c == '\n') {
115 pos++;
116 break;
117 }
118 }
119 }
120 }
121
122 /**
123 *
124 *
125 * @throws NumberFormatException if any unicode escape sequences are
126 * malformed.
127 */
128 public String nextString(char quote) throws JSONException {
129 /*
130 * For strings that are free of escape sequences, we can just extract
131 * the result as a substring of the input. But if we encounter an escape
132 * sequence, we need to use a StringBuilder to compose the result.
133 */
134 StringBuilder builder = null;
135
136 /* the index of the first character not yet appended to the builder. */
137 int start = pos;
138
139 while (pos < in.length()) {
140 int c = in.charAt(pos++);
141 if (c == quote) {
142 if (builder == null) {
143 // a new string avoids leaking memory
144 return new String(in.substring(start, pos - 1));
145 } else {
146 builder.append(in, start, pos - 1);
147 return builder.toString();
148 }
149 }
150
151 if (c == '\\') {
152 if (pos == in.length()) {
153 throw syntaxError("Unterminated escape sequence");
154 }
155 if (builder == null) {
156 builder = new StringBuilder();
157 }
158 builder.append(in, start, pos - 1);
159 builder.append(readEscapeCharacter());
160 start = pos;
161 }
162 }
163
164 throw syntaxError("Unterminated string");
165 }
166
167 /**
168 * Unescapes the character identified by the character or characters that
169 * immediately follow a backslash. The backslash '\' should have already
170 * been read. This supports both unicode escapes "u000A" and two-character
171 * escapes "\n".
172 *
173 * @throws NumberFormatException if any unicode escape sequences are
174 * malformed.
175 */
176 private char readEscapeCharacter() throws JSONException {
177 char escaped = in.charAt(pos++);
178 switch (escaped) {
179 case 'u':
180 if (pos + 4 > in.length()) {
181 throw syntaxError("Unterminated escape sequence");
182 }
183 String hex = in.substring(pos, pos + 4);
184 pos += 4;
185 return (char) Integer.parseInt(hex, 16);
186
187 case 't':
188 return '\t';
189
190 case 'b':
191 return '\b';
192
193 case 'n':
194 return '\n';
195
196 case 'r':
197 return '\r';
198
199 case 'f':
200 return '\f';
201
202 case '\'':
203 case '"':
204 case '\\':
205 default:
206 return escaped;
207 }
208 }
209
210 /**
211 * Reads a null, boolean, numeric or unquoted string literal value. Numeric
212 * values will be returned as an Integer, Long, or Double, in that order of
213 * preference.
214 */
215 private Object readLiteral() throws JSONException {
216 String literal = nextToInternal("{}[]/\\:,=;# \t\f");
217
218 if (literal.length() == 0) {
219 throw syntaxError("Expected literal value");
220 } else if ("null".equalsIgnoreCase(literal)) {
221 return JSONObject.NULL;
222 } else if ("true".equalsIgnoreCase(literal)) {
223 return Boolean.TRUE;
224 } else if ("false".equalsIgnoreCase(literal)) {
225 return Boolean.FALSE;
226 }
227
228 /* try to parse as an integral type... */
229 if (literal.indexOf('.') == -1) {
230 int base = 10;
231 String number = literal;
232 if (number.startsWith("0x") || number.startsWith("0X")) {
233 number = number.substring(2);
234 base = 16;
235 } else if (number.startsWith("0") && number.length() > 1) {
236 number = number.substring(1);
237 base = 8;
238 }
239 try {
240 long longValue = Long.parseLong(number, base);
241 if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) {
242 return (int) longValue;
243 } else {
244 return longValue;
245 }
246 } catch (NumberFormatException e) {
247 /*
248 * This only happens for integral numbers greater than
249 * Long.MAX_VALUE, numbers in exponential form (5e-10) and
250 * unquoted strings. Fall through to try floating point.
251 */
252 }
253 }
254
255 /* ...next try to parse as a floating point... */
256 try {
257 return Double.valueOf(literal);
258 } catch (NumberFormatException e) {
259 }
260
261 /* ... finally give up. We have an unquoted string */
262 return new String(literal); // a new string avoids leaking memory
263 }
264
265 /**
266 * Returns text from the current position until the first of any of the
267 * given characters or a newline character, excluding that character. The
268 * position is advanced to the excluded character.
269 */
270 private String nextToInternal(String excluded) {
271 int start = pos;
272 for (; pos < in.length(); pos++) {
273 char c = in.charAt(pos);
274 if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) {
275 return in.substring(start, pos);
276 }
277 }
278 return in.substring(start);
279 }
280
281 /**
282 * Reads a sequence of key/value pairs and the trailing closing brace '}' of
283 * an object. The opening brace '{' should have already been read.
284 */
285 private JSONObject readObject() throws JSONException {
286 JSONObject result = new JSONObject();
287
288 /* Peek to see if this is the empty object. */
289 int first = nextCleanInternal();
290 if (first == '}') {
291 return result;
292 } else if (first != -1) {
293 pos--;
294 }
295
296 while (true) {
297 Object name = nextValue();
298 if (!(name instanceof String)) {
299 if (name == null) {
300 throw syntaxError("Names cannot be null");
301 } else {
302 throw syntaxError("Names must be strings, but " + name
303 + " is of type " + name.getClass().getName());
304 }
305 }
306
307 /*
308 * Expect the name/value separator to be either a colon ':', an
309 * equals sign '=', or an arrow "=>". The last two are bogus but we
310 * include them because that's what the original implementation did.
311 */
312 int separator = nextCleanInternal();
313 if (separator != ':' && separator != '=') {
314 throw syntaxError("Expected ':' after " + name);
315 }
316 if (pos < in.length() && in.charAt(pos) == '>') {
317 pos++;
318 }
319
320 result.put((String) name, nextValue());
321
322 switch (nextCleanInternal()) {
323 case '}':
324 return result;
325 case ';':
326 case ',':
327 continue;
328 default:
329 throw syntaxError("Unterminated object");
330 }
331 }
332 }
333
334 /**
335 * Reads a sequence of values and the trailing closing brace ']' of an
336 * array. The opening brace '[' should have already been read. Note that
337 * "[]" yields an empty array, but "[,]" returns a two-element array
338 * equivalent to "[null,null]".
339 */
340 private JSONArray readArray() throws JSONException {
341 JSONArray result = new JSONArray();
342
343 /* to cover input that ends with ",]". */
344 boolean hasTrailingSeparator = false;
345
346 while (true) {
347 switch (nextCleanInternal()) {
348 case -1:
349 throw syntaxError("Unterminated array");
350 case ']':
351 if (hasTrailingSeparator) {
352 result.put(null);
353 }
354 return result;
355 case ',':
356 case ';':
357 /* A separator without a value first means "null". */
358 result.put(null);
359 hasTrailingSeparator = true;
360 continue;
361 default:
362 pos--;
363 }
364
365 result.put(nextValue());
366
367 switch (nextCleanInternal()) {
368 case ']':
369 return result;
370 case ',':
371 case ';':
372 hasTrailingSeparator = true;
373 continue;
374 default:
375 throw syntaxError("Unterminated array");
376 }
377 }
378 }
379
380 public JSONException syntaxError(String text) {
381 return new JSONException(text + this);
382 }
383
384 @Override public String toString() {
385 // consistent with the original implementation
386 return " at character " + pos + " of " + in;
387 }
388
389 /*
390 * Legacy APIs.
391 *
392 * None of the methods below are on the critical path of parsing JSON
393 * documents. They exist only because they were exposed by the original
394 * implementation and may be used by some clients.
395 */
396
397 public boolean more() {
398 return pos < in.length();
399 }
400
401 public char next() {
402 return pos < in.length() ? in.charAt(pos++) : '\0';
403 }
404
405 public char next(char c) throws JSONException {
406 char result = next();
407 if (result != c) {
408 throw syntaxError("Expected " + c + " but was " + result);
409 }
410 return result;
411 }
412
413 public char nextClean() throws JSONException {
414 int nextCleanInt = nextCleanInternal();
415 return nextCleanInt == -1 ? '\0' : (char) nextCleanInt;
416 }
417
418 /**
419 * TODO: note about how this method returns a substring, and could cause a memory leak
420 */
421 public String next(int length) throws JSONException {
422 if (pos + length > in.length()) {
423 throw syntaxError(length + " is out of bounds");
424 }
425 String result = in.substring(pos, pos + length);
426 pos += length;
427 return result;
428 }
429
430 /**
431 * TODO: note about how this method returns a substring, and could cause a memory leak
432 */
433 public String nextTo(String excluded) {
434 if (excluded == null) {
435 throw new NullPointerException();
436 }
437 return nextToInternal(excluded).trim();
438 }
439
440 /**
441 * TODO: note about how this method returns a substring, and could cause a memory leak
442 */
443 public String nextTo(char excluded) {
444 return nextToInternal(String.valueOf(excluded)).trim();
445 }
446
447 public void skipPast(String thru) {
448 int thruStart = in.indexOf(thru, pos);
449 pos = thruStart == -1 ? in.length() : (thruStart + thru.length());
450 }
451
452 public char skipTo(char to) {
453 for (int i = pos, length = in.length(); i < length; i++) {
454 if (in.charAt(i) == to) {
455 pos = i;
456 return to;
457 }
458 }
459 return '\0';
460 }
461
462 public void back() {
463 if (--pos == -1) {
464 pos = 0;
465 }
466 }
467
468 public static int dehexchar(char hex) {
469 if (hex >= '0' && hex <= '9') {
470 return hex - '0';
471 } else if (hex >= 'A' && hex <= 'F') {
472 return hex - 'A' + 10;
473 } else if (hex >= 'a' && hex <= 'f') {
474 return hex - 'a' + 10;
475 } else {
476 return -1;
477 }
478 }
479 }