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.JSONParser;
30 import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParseException;
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 class JSONValue {
39 /**
40 * Parse JSON text into java object from the input source. Please use
41 * parseWithException() if you don't want to ignore the exception.
42 *
43 * @see org.json.simple.parser.JSONParser#parse(Reader)
44 * @see #parseWithException(Reader)
45 *
46 * @param in
47 * @return Instance of the following: org.json.simple.JSONObject,
48 * org.json.simple.JSONArray, java.lang.String, java.lang.Number,
49 * java.lang.Boolean, null
50 *
51 */
52 public static Object parse(Reader in) {
53 try {
54 JSONParser parser = new JSONParser();
55 return parser.parse(in);
56 } catch (Exception e) {
57 return null;
58 }
59 }
60
61 public static Object parse(String s) {
62 StringReader in = new StringReader(s);
63 return parse(in);
64 }
65
66 /**
67 * Parse JSON text into java object from the input source.
68 *
69 * @see org.json.simple.parser.JSONParser
70 *
71 * @param in
72 * @return Instance of the following: org.json.simple.JSONObject,
73 * org.json.simple.JSONArray, java.lang.String, java.lang.Number,
74 * java.lang.Boolean, null
75 *
76 * @throws IOException
77 * @throws JSONParseException
78 */
79 public static Object parseWithException(Reader in) throws IOException, JSONParseException {
80 JSONParser parser = new JSONParser();
81 return parser.parse(in);
82 }
83
84 public static Object parseWithException(String s) throws JSONParseException {
85 JSONParser parser = new JSONParser();
86 return parser.parse(s);
87 }
88
89 /**
90 * Encode an object into JSON text and write it to out.
91 * <p>
92 * If this object is a Map or a List, and it's also a JSONStreamAware or a
93 * JSONAware, JSONStreamAware or JSONAware will be considered firstly.
94 * <p>
95 * DO NOT call this method from writeJSONString(Writer) of a class that
96 * implements both JSONStreamAware and (Map or List) with "this" as the
97 * first parameter, use JSONObject.writeJSONString(Map, Writer) or
98 * JSONArray.writeJSONString(List, Writer) instead.
99 *
100 * @see org.json.simple.JSONObject#writeJSONString(Map, Writer)
101 * @see org.json.simple.JSONArray#writeJSONString(List, Writer)
102 *
103 * @param value
104 * @param writer
105 */
106 @SuppressWarnings("unchecked")
107 public static void writeJSONString(Object value, Writer out) throws IOException {
108 if (value == null) {
109 out.write("null");
110 return;
111 }
112
113 if (value instanceof String) {
114 out.write('\"');
115 out.write(escape((String) value));
116 out.write('\"');
117 return;
118 }
119
120 if (value instanceof Double) {
121 if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
122 out.write("null");
123 } else {
124 out.write(value.toString());
125 }
126 return;
127 }
128
129 if (value instanceof Float) {
130 if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
131 out.write("null");
132 } else {
133 out.write(value.toString());
134 }
135 return;
136 }
137
138 if (value instanceof BigDecimal) {
139 out.write(((BigDecimal) value).toPlainString());
140 return;
141 }
142
143 if (value instanceof Number) {
144 out.write(value.toString());
145 return;
146 }
147
148 if (value instanceof Boolean) {
149 out.write(value.toString());
150 return;
151 }
152
153 if (value instanceof JSONStreamAware) {
154 ((JSONStreamAware) value).writeJSONString(out);
155 return;
156 }
157
158 if (value instanceof JSONAware) {
159 out.write(((JSONAware) value).toJSONString());
160 return;
161 }
162
163 if (value instanceof Map) {
164 JSONObject.writeJSONString((Map<String, Object>) value, out);
165 return;
166 }
167
168 if (value instanceof List) {
169 JSONArray.writeJSONString((List<Object>) value, out);
170 return;
171 }
172
173 out.write(value.toString());
174 }
175
176 /**
177 * Convert an object to JSON text.
178 * <p>
179 * If this object is a Map or a List, and it's also a JSONAware, JSONAware
180 * will be considered firstly.
181 * <p>
182 * DO NOT call this method from toJSONString() of a class that implements
183 * both JSONAware and Map or List with "this" as the parameter, use
184 * JSONObject.toJSONString(Map) or JSONArray.toJSONString(List) instead.
185 *
186 * @see org.json.simple.JSONObject#toJSONString(Map)
187 * @see org.json.simple.JSONArray#toJSONString(List)
188 *
189 * @param value
190 * @return JSON text, or "null" if value is null or it's an NaN or an INF
191 * number.
192 */
193 @SuppressWarnings("unchecked")
194 public static String toJSONString(Object value) {
195 if (value == null) {
196 return "null";
197 }
198
199 if (value instanceof String) {
200 return "\"" + escape((String) value) + "\"";
201 }
202
203 if (value instanceof Double) {
204 if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
205 return "null";
206 } else {
207 return value.toString();
208 }
209 }
210
211 if (value instanceof Float) {
212 if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
213 return "null";
214 } else {
215 return value.toString();
216 }
217 }
218
219 if (value instanceof BigDecimal) {
220 return ((BigDecimal) value).toPlainString();
221 }
222
223 if (value instanceof Number) {
224 return value.toString();
225 }
226
227 if (value instanceof Boolean) {
228 return value.toString();
229 }
230
231 if (value instanceof JSONAware) {
232 return ((JSONAware) value).toJSONString();
233 }
234
235 if (value instanceof Map) {
236 return JSONObject.toJSONString((Map<String, Object>) value);
237 }
238
239 if (value instanceof List) {
240 return JSONArray.toJSONString((List<Object>) value);
241 }
242
243 return value.toString();
244 }
245
246 /**
247 * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters
248 * (U+0000 through U+001F).
249 *
250 * @param s
251 * @return
252 */
253 public static String escape(String s) {
254 if (s == null) {
255 return null;
256 }
257
258 StringBuilder sb = new StringBuilder();
259 escape(s, sb);
260 return sb.toString();
261 }
262
263 /**
264 * @param s
265 * - Must not be null.
266 * @param sb
267 */
268 static void escape(String s, StringBuilder sb) {
269 for (int i = 0; i < s.length(); i++) {
270 char ch = s.charAt(i);
271 switch (ch) {
272 case '"':
273 sb.append("\\\"");
274 break;
275 case '\\':
276 sb.append("\\\\");
277 break;
278 case '\b':
279 sb.append("\\b");
280 break;
281 case '\f':
282 sb.append("\\f");
283 break;
284 case '\n':
285 sb.append("\\n");
286 break;
287 case '\r':
288 sb.append("\\r");
289 break;
290 case '\t':
291 sb.append("\\t");
292 break;
293 case '/':
294 sb.append("\\/");
295 break;
296 default:
297 // Reference: http://www.unicode.org/versions/Unicode5.1.0/
298 if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F')
299 || (ch >= '\u2000' && ch <= '\u20FF')) {
300 String ss = Integer.toHexString(ch);
301 sb.append("\\u");
302 for (int k = 0; k < 4 - ss.length(); k++) {
303 sb.append('0');
304 }
305 sb.append(ss.toUpperCase());
306 } else {
307 sb.append(ch);
308 }
309 }
310 }// for
311 }
312 }