2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
32 * If you wish your version of this file to be governed by only the CDDL
33 * or only the GPL Version 2, indicate your decision by adding
34 * "[Contributor] elects to include this software in this distribution
35 * under the [CDDL or GPL Version 2] license." If you do not indicate a
36 * single choice of license, a recipient has the option to distribute
37 * your version of this file under either the CDDL, the GPL Version 2 or
38 * to extend the choice of license to its licensees as provided above.
39 * However, if you add GPL Version 2 code and therefore, elected the GPL
40 * Version 2 license, then the option applies only if the new code is
41 * made subject to such option by the copyright holder.
43 package org.netbeans.html.wstyrus;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.io.InputStreamReader;
48 import java.io.OutputStream;
49 import java.io.PushbackInputStream;
50 import java.io.Reader;
51 import java.net.HttpURLConnection;
53 import java.net.URLConnection;
54 import java.util.ArrayList;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.concurrent.Executor;
58 import java.util.concurrent.Executors;
59 import java.util.concurrent.ThreadFactory;
60 import java.util.logging.Logger;
61 import net.java.html.js.JavaScriptBody;
62 import org.json.JSONArray;
63 import org.json.JSONException;
64 import org.json.JSONObject;
65 import org.json.JSONTokener;
66 import org.netbeans.html.json.spi.JSONCall;
68 /** This is an implementation package - just
69 * include its JAR on classpath and use official {@link Context} API
70 * to access the functionality.
72 * @author Jaroslav Tulach
74 final class LoadJSON implements Runnable {
75 private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
76 private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
78 public Thread newThread(Runnable runnable) {
79 Thread thread = Executors.defaultThreadFactory().newThread(runnable);
80 thread.setDaemon(true);
85 private final JSONCall call;
86 private final URL base;
89 private LoadJSON(JSONCall call) {
94 public static void loadJSON(JSONCall call) {
95 assert !"WebSocket".equals(call.getMethod());
96 REQ.execute(new LoadJSON((call)));
102 Throwable error = null;
105 if (call.isJSONP()) {
106 url = call.composeURL("dummy");
108 url = call.composeURL(null);
111 final URL u = new URL(base, url.replace(" ", "%20"));
112 URLConnection conn = u.openConnection();
113 if (call.isDoOutput()) {
114 conn.setDoOutput(true);
116 String h = call.getHeaders();
119 while (pos < h.length()) {
120 int tagEnd = h.indexOf(':', pos);
124 int r = h.indexOf('\r', tagEnd);
125 int n = h.indexOf('\n', tagEnd);
132 String key = h.substring(pos, tagEnd).trim();
133 String val = h.substring(tagEnd + 1, Math.min(r, n)).trim();
134 conn.setRequestProperty(key, val);;
135 pos = Math.max(r, n);
138 if (call.getMethod() != null && conn instanceof HttpURLConnection) {
139 ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
141 if (call.isDoOutput()) {
142 final OutputStream os = conn.getOutputStream();
146 final PushbackInputStream is = new PushbackInputStream(
147 conn.getInputStream(), 1
149 boolean[] arrayOrString = { false, false };
150 detectJSONType(call.isJSONP(), is, arrayOrString);
152 if (arrayOrString[1]) {
153 throw new JSONException("");
155 JSONTokener tok = createTokener(is);
157 obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok);
158 json = convertToArray(obj);
159 } catch (JSONException ex) {
160 Reader r = new InputStreamReader(is, "UTF-8");
161 StringBuilder sb = new StringBuilder();
169 json = sb.toString();
171 } catch (IOException ex) {
175 call.notifyError(error);
177 call.notifySuccess(json);
182 private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
186 arrayOrString[1] = true;
189 if (Character.isWhitespace(ch)) {
195 arrayOrString[0] = true;
204 arrayOrString[1] = true;
210 private static JSONTokener createTokener(InputStream is) throws IOException {
211 Reader r = new InputStreamReader(is, "UTF-8");
213 return new JSONTokener(r);
214 } catch (LinkageError ex) {
215 // phones may carry outdated version of JSONTokener
216 StringBuilder sb = new StringBuilder();
224 return new JSONTokener(sb.toString());
228 static Object convertToArray(Object o) throws JSONException {
229 if (o instanceof JSONArray) {
230 JSONArray ja = (JSONArray)o;
231 Object[] arr = new Object[ja.length()];
232 for (int i = 0; i < arr.length; i++) {
233 arr[i] = convertToArray(ja.get(i));
236 } else if (o instanceof JSONObject) {
237 JSONObject obj = (JSONObject)o;
238 Iterator it = obj.keys();
239 List<Object> collect = new ArrayList<Object>();
240 while (it.hasNext()) {
241 String key = (String)it.next();
242 final Object val = obj.get(key);
243 final Object newVal = convertToArray(val);
249 int size = collect.size();
250 for (int i = 0; i < size; i += 2) {
251 obj.put((String) collect.get(i), collect.get(i + 1));
254 } else if (o == JSONObject.NULL) {
261 public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
262 if (jsonObject instanceof JSONObject) {
263 JSONObject obj = (JSONObject)jsonObject;
264 for (int i = 0; i < props.length; i++) {
265 Object val = obj.opt(props[i]);
266 if (val == JSONObject.NULL) {
273 for (int i = 0; i < props.length; i++) {
274 values[i] = getProperty(jsonObject, props[i]);
278 @JavaScriptBody(args = {"object", "property"}, body =
280 "if (property === null) ret = object;\n" +
281 "else if (object === null) ret = null;\n" +
282 "else ret = object[property];\n" +
283 "return ret ? (typeof ko === 'undefined' ? ret : ko.utils.unwrapObservable(ret)) : null;"
285 private static Object getProperty(Object object, String property) {
289 public static Object parse(InputStream is) throws IOException {
291 PushbackInputStream push = new PushbackInputStream(is, 1);
292 boolean[] arrayOrString = { false, false };
293 detectJSONType(false, push, arrayOrString);
294 JSONTokener t = createTokener(push);
295 Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t);
296 return convertToArray(obj);
297 } catch (JSONException ex) {
298 throw new IOException(ex);