1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.chemistry.opencmis.commons.impl.json;
20
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.io.StringReader;
24 import java.io.Writer;
25 import java.math.BigDecimal;
26 import java.util.List;
27 import java.util.Map;
28
29 import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParseException;
30 import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParser;
31
32 /**
33 * (Taken from JSON.simple <http://code.google.com/p/json-simple/> and modified
34 * for OpenCMIS.)
35 *
36 * @author FangYidong<fangyidong@yahoo.com.cn>
37 */
38 public final class JSONValue {
39
40 private JSONValue() {
41 }
42
43 /**
44 * Parse JSON text into java object from the input source. Please use
45 * parseWithException() if you don't want to ignore the exception.
46 *
47 * @see org.json.simple.parser.JSONParser#parse(Reader)
48 * @see #parseWithException(Reader)
49 *
50 * @param in
51 * @return Instance of the following: org.json.simple.JSONObject,
52 * org.json.simple.JSONArray, java.lang.String, java.lang.Number,
53 * java.lang.Boolean, null
54 *
55 */
56 public static Object parse(Reader in) {
57 try {
58 JSONParser parser = new JSONParser();
59 return parser.parse(in);
60 } catch (Exception e) {
61 return null;
62 }
63 }
64
65 public static Object parse(String s) {
66 StringReader in = new StringReader(s);
67 return parse(in);
68 }
69
70 /**
71 * Parse JSON text into java object from the input source.
72 *
73 * @see org.json.simple.parser.JSONParser
74 *
75 * @param in
76 * @return Instance of the following: org.json.simple.JSONObject,
77 * org.json.simple.JSONArray, java.lang.String, java.lang.Number,
78 * java.lang.Boolean, null
79 *
80 * @throws IOException
81 * @throws JSONParseException
82 */
83 public static Object parseWithException(Reader in) throws IOException, JSONParseException {
84 JSONParser parser = new JSONParser();
85 return parser.parse(in);
86 }
87
88 public static Object parseWithException(String s) throws JSONParseException {
89 JSONParser parser = new JSONParser();
90 return parser.parse(s);
91 }
92
93 /**
94 * Encode an object into JSON text and write it to out.
95 * <p>
96 * If this object is a Map or a List, and it's also a JSONStreamAware or a
97 * JSONAware, JSONStreamAware or JSONAware will be considered firstly.
98 * <p>
99 * DO NOT call this method from writeJSONString(Writer) of a class that
100 * implements both JSONStreamAware and (Map or List) with "this" as the
101 * first parameter, use JSONObject.writeJSONString(Map, Writer) or
102 * JSONArray.writeJSONString(List, Writer) instead.
103 *
104 * @see org.json.simple.JSONObject#writeJSONString(Map, Writer)
105 * @see org.json.simple.JSONArray#writeJSONString(List, Writer)
106 *
107 * @param value
108 * @param writer
109 */
110 @SuppressWarnings("unchecked")
111 public static void writeJSONString(Object value, Writer out) throws IOException {
112 if (value == null) {
113 out.write("null");
114 return;
115 }
116
117 if (value instanceof String) {
118 out.write('\"');
119 out.write(escape((String) value));
120 out.write('\"');
121 return;
122 }
123
124 if (value instanceof Double) {
125 if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
126 out.write("null");
127 } else {
128 out.write(value.toString());
129 }
130 return;
131 }
132
133 if (value instanceof Float) {
134 if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
135 out.write("null");
136 } else {
137 out.write(value.toString());
138 }
139 return;
140 }
141
142 if (value instanceof BigDecimal) {
143 out.write(((BigDecimal) value).toPlainString());
144 return;
145 }
146
147 if (value instanceof Number) {
148 out.write(value.toString());
149 return;
150 }
151
152 if (value instanceof Boolean) {
153 out.write(value.toString());
154 return;
155 }
156
157 if (value instanceof JSONStreamAware) {
158 ((JSONStreamAware) value).writeJSONString(out);
159 return;
160 }
161
162 if (value instanceof JSONAware) {
163 out.write(((JSONAware) value).toJSONString());
164 return;
165 }
166
167 if (value instanceof Map) {
168 JSONObject.writeJSONString((Map<String, Object>) value, out);
169 return;
170 }
171
172 if (value instanceof List) {
173 JSONArray.writeJSONString((List<Object>) value, out);
174 return;
175 }
176
177 out.write(value.toString());
178 }
179
180 /**
181 * Convert an object to JSON text.
182 * <p>
183 * If this object is a Map or a List, and it's also a JSONAware, JSONAware
184 * will be considered firstly.
185 * <p>
186 * DO NOT call this method from toJSONString() of a class that implements
187 * both JSONAware and Map or List with "this" as the parameter, use
188 * JSONObject.toJSONString(Map) or JSONArray.toJSONString(List) instead.
189 *
190 * @see org.json.simple.JSONObject#toJSONString(Map)
191 * @see org.json.simple.JSONArray#toJSONString(List)
192 *
193 * @param value
194 * @return JSON text, or "null" if value is null or it's an NaN or an INF
195 * number.
196 */
197 @SuppressWarnings("unchecked")
198 public static String toJSONString(Object value) {
199 if (value == null) {
200 return "null";
201 }
202
203 if (value instanceof String) {
204 return "\"" + escape((String) value) + "\"";
205 }
206
207 if (value instanceof Double) {
208 if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
209 return "null";
210 } else {
211 return value.toString();
212 }
213 }
214
215 if (value instanceof Float) {
216 if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
217 return "null";
218 } else {
219 return value.toString();
220 }
221 }
222
223 if (value instanceof BigDecimal) {
224 return ((BigDecimal) value).toPlainString();
225 }
226
227 if (value instanceof Number) {
228 return value.toString();
229 }
230
231 if (value instanceof Boolean) {
232 return value.toString();
233 }
234
235 if (value instanceof JSONAware) {
236 return ((JSONAware) value).toJSONString();
237 }
238
239 if (value instanceof Map) {
240 return JSONObject.toJSONString((Map<String, Object>) value);
241 }
242
243 if (value instanceof List) {
244 return JSONArray.toJSONString((List<Object>) value);
245 }
246
247 return value.toString();
248 }
249
250 /**
251 * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters
252 * (U+0000 through U+001F).
253 *
254 * @param s
255 * @return
256 */
257 public static String escape(String s) {
258 if (s == null) {
259 return null;
260 }
261
262 StringBuilder sb = new StringBuilder();
263 escape(s, sb);
264 return sb.toString();
265 }
266
267 /**
268 * @param s
269 * - Must not be null.
270 * @param sb
271 */
272 static void escape(String s, StringBuilder sb) {
273 for (int i = 0; i < s.length(); i++) {
274 char ch = s.charAt(i);
275 switch (ch) {
276 case '"':
277 sb.append("\\\"");
278 break;
279 case '\\':
280 sb.append("\\\\");
281 break;
282 case '\b':
283 sb.append("\\b");
284 break;
285 case '\f':
286 sb.append("\\f");
287 break;
288 case '\n':
289 sb.append("\\n");
290 break;
291 case '\r':
292 sb.append("\\r");
293 break;
294 case '\t':
295 sb.append("\\t");
296 break;
297 case '/':
298 sb.append("\\/");
299 break;
300 default:
301 // Reference: http://www.unicode.org/versions/Unicode5.1.0/
302 if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F')
303 || (ch >= '\u2000' && ch <= '\u20FF')) {
304 String ss = Integer.toHexString(ch);
305 sb.append("\\u");
306 for (int k = 0; k < 4 - ss.length(); k++) {
307 sb.append('0');
308 }
309 sb.append(ss.toUpperCase());
310 } else {
311 sb.append(ch);
312 }
313 }
314 }// for
315 }
316 }