jaroslav@37: /** jaroslav@358: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. jaroslav@37: * jaroslav@551: * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. jaroslav@37: * jaroslav@358: * Oracle and Java are registered trademarks of Oracle and/or its affiliates. jaroslav@358: * Other names may be trademarks of their respective owners. jaroslav@37: * jaroslav@358: * The contents of this file are subject to the terms of either the GNU jaroslav@358: * General Public License Version 2 only ("GPL") or the Common jaroslav@358: * Development and Distribution License("CDDL") (collectively, the jaroslav@358: * "License"). You may not use this file except in compliance with the jaroslav@358: * License. You can obtain a copy of the License at jaroslav@358: * http://www.netbeans.org/cddl-gplv2.html jaroslav@358: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the jaroslav@358: * specific language governing permissions and limitations under the jaroslav@358: * License. When distributing the software, include this License Header jaroslav@358: * Notice in each file and include the License file at jaroslav@358: * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this jaroslav@358: * particular file as subject to the "Classpath" exception as provided jaroslav@358: * by Oracle in the GPL Version 2 section of the License file that jaroslav@358: * accompanied this code. If applicable, add the following below the jaroslav@358: * License Header, with the fields enclosed by brackets [] replaced by jaroslav@358: * your own identifying information: jaroslav@358: * "Portions Copyrighted [year] [name of copyright owner]" jaroslav@358: * jaroslav@358: * Contributor(s): jaroslav@358: * jaroslav@358: * The Original Software is NetBeans. The Initial Developer of the Original jaroslav@551: * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. jaroslav@358: * jaroslav@358: * If you wish your version of this file to be governed by only the CDDL jaroslav@358: * or only the GPL Version 2, indicate your decision by adding jaroslav@358: * "[Contributor] elects to include this software in this distribution jaroslav@358: * under the [CDDL or GPL Version 2] license." If you do not indicate a jaroslav@358: * single choice of license, a recipient has the option to distribute jaroslav@358: * your version of this file under either the CDDL, the GPL Version 2 or jaroslav@358: * to extend the choice of license to its licensees as provided above. jaroslav@358: * However, if you add GPL Version 2 code and therefore, elected the GPL jaroslav@358: * Version 2 license, then the option applies only if the new code is jaroslav@358: * made subject to such option by the copyright holder. jaroslav@37: */ jaroslav@446: package org.netbeans.html.wstyrus; jaroslav@37: jaroslav@37: import java.io.IOException; jaroslav@60: import java.io.InputStream; jaroslav@37: import java.io.InputStreamReader; jaroslav@75: import java.io.OutputStream; jaroslav@37: import java.io.PushbackInputStream; jaroslav@37: import java.io.Reader; jaroslav@74: import java.net.HttpURLConnection; jaroslav@37: import java.net.URL; jaroslav@74: import java.net.URLConnection; jtulach@760: import java.util.ArrayList; jaroslav@37: import java.util.Iterator; jtulach@760: import java.util.List; jaroslav@37: import java.util.concurrent.Executor; jaroslav@37: import java.util.concurrent.Executors; jaroslav@158: import java.util.concurrent.ThreadFactory; jaroslav@37: import java.util.logging.Logger; jaroslav@131: import net.java.html.js.JavaScriptBody; jaroslav@37: import org.json.JSONArray; jaroslav@37: import org.json.JSONException; jaroslav@37: import org.json.JSONObject; jaroslav@37: import org.json.JSONTokener; jtulach@940: import org.netbeans.html.json.spi.JSONCall; jaroslav@37: jaroslav@54: /** This is an implementation package - just jaroslav@54: * include its JAR on classpath and use official {@link Context} API jaroslav@54: * to access the functionality. jaroslav@37: * jtulach@790: * @author Jaroslav Tulach jaroslav@37: */ jaroslav@37: final class LoadJSON implements Runnable { jaroslav@446: private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName()); jaroslav@158: private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() { jaroslav@158: @Override jaroslav@158: public Thread newThread(Runnable runnable) { jaroslav@158: Thread thread = Executors.defaultThreadFactory().newThread(runnable); jaroslav@158: thread.setDaemon(true); jaroslav@158: return thread; jaroslav@158: } jaroslav@158: }); jaroslav@37: jaroslav@37: private final JSONCall call; jaroslav@37: private final URL base; jaroslav@37: jaroslav@37: jaroslav@37: private LoadJSON(JSONCall call) { jaroslav@37: this.call = call; jaroslav@446: this.base = null; jaroslav@37: } jaroslav@37: jaroslav@37: public static void loadJSON(JSONCall call) { jaroslav@258: assert !"WebSocket".equals(call.getMethod()); jaroslav@258: REQ.execute(new LoadJSON((call))); jaroslav@37: } jaroslav@37: jaroslav@37: @Override jaroslav@37: public void run() { jaroslav@37: final String url; jaroslav@446: Throwable error = null; jaroslav@446: Object json = null; jtulach@940: jaroslav@37: if (call.isJSONP()) { jaroslav@37: url = call.composeURL("dummy"); jaroslav@37: } else { jaroslav@37: url = call.composeURL(null); jaroslav@37: } jaroslav@37: try { jaroslav@37: final URL u = new URL(base, url.replace(" ", "%20")); jaroslav@74: URLConnection conn = u.openConnection(); jaroslav@527: if (call.isDoOutput()) { jaroslav@527: conn.setDoOutput(true); jaroslav@527: } jtulach@940: String h = call.getHeaders(); jtulach@940: if (h != null) { jtulach@940: int pos = 0; jtulach@940: while (pos < h.length()) { jtulach@940: int tagEnd = h.indexOf(':', pos); jtulach@940: if (tagEnd == -1) { jtulach@940: break; jtulach@940: } jtulach@940: int r = h.indexOf('\r', tagEnd); jtulach@940: int n = h.indexOf('\n', tagEnd); jtulach@940: if (r == -1) { jtulach@940: r = h.length(); jtulach@940: } jtulach@940: if (n == -1) { jtulach@940: n = h.length(); jtulach@940: } jtulach@940: String key = h.substring(pos, tagEnd).trim(); jtulach@940: String val = h.substring(tagEnd + 1, Math.min(r, n)).trim(); jtulach@940: conn.setRequestProperty(key, val);; jtulach@940: pos = Math.max(r, n); jtulach@940: } jtulach@940: } jaroslav@527: if (call.getMethod() != null && conn instanceof HttpURLConnection) { jaroslav@527: ((HttpURLConnection) conn).setRequestMethod(call.getMethod()); jaroslav@527: } jaroslav@527: if (call.isDoOutput()) { jaroslav@527: final OutputStream os = conn.getOutputStream(); jaroslav@527: call.writeData(os); jaroslav@527: os.flush(); jaroslav@74: } jaroslav@74: final PushbackInputStream is = new PushbackInputStream( jaroslav@74: conn.getInputStream(), 1 jaroslav@74: ); jtulach@745: boolean[] arrayOrString = { false, false }; jtulach@745: detectJSONType(call.isJSONP(), is, arrayOrString); jaroslav@73: try { jtulach@745: if (arrayOrString[1]) { jaroslav@73: throw new JSONException(""); jaroslav@73: } jaroslav@525: JSONTokener tok = createTokener(is); jaroslav@73: Object obj; jtulach@745: obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok); jaroslav@73: json = convertToArray(obj); jaroslav@73: } catch (JSONException ex) { jaroslav@73: Reader r = new InputStreamReader(is, "UTF-8"); jaroslav@73: StringBuilder sb = new StringBuilder(); jaroslav@73: for (;;) { jaroslav@73: int ch = r.read(); jaroslav@73: if (ch == -1) { jaroslav@73: break; jaroslav@73: } jaroslav@73: sb.append((char)ch); jaroslav@73: } jaroslav@73: json = sb.toString(); jaroslav@73: } jaroslav@73: } catch (IOException ex) { jaroslav@37: error = ex; jaroslav@37: } finally { jaroslav@446: if (error != null) { jaroslav@446: call.notifyError(error); jaroslav@446: } else { jaroslav@446: call.notifySuccess(json); jaroslav@446: } jaroslav@37: } jaroslav@37: } jaroslav@37: jtulach@745: private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException { jtulach@745: for (;;) { jtulach@745: int ch = is.read(); jtulach@745: if (ch == -1) { jtulach@745: arrayOrString[1] = true; jtulach@745: break; jtulach@745: } jtulach@745: if (Character.isWhitespace(ch)) { jtulach@745: continue; jtulach@745: } jtulach@745: jtulach@745: if (ch == '[') { jtulach@745: is.unread(ch); jtulach@745: arrayOrString[0] = true; jtulach@745: break; jtulach@745: } jtulach@745: if (ch == '{') { jtulach@745: is.unread(ch); jtulach@745: break; jtulach@745: } jtulach@745: if (!skipAnything) { jtulach@745: is.unread(ch); jtulach@745: arrayOrString[1] = true; jtulach@745: break; jtulach@745: } jtulach@745: } jtulach@745: } jtulach@745: jaroslav@525: private static JSONTokener createTokener(InputStream is) throws IOException { jaroslav@525: Reader r = new InputStreamReader(is, "UTF-8"); jaroslav@525: try { jaroslav@525: return new JSONTokener(r); jaroslav@525: } catch (LinkageError ex) { jaroslav@525: // phones may carry outdated version of JSONTokener jaroslav@525: StringBuilder sb = new StringBuilder(); jaroslav@525: for (;;) { jaroslav@525: int ch = r.read(); jaroslav@525: if (ch == -1) { jaroslav@525: break; jaroslav@525: } jaroslav@525: sb.append((char)ch); jaroslav@525: } jaroslav@525: return new JSONTokener(sb.toString()); jaroslav@525: } jaroslav@525: } jaroslav@525: jaroslav@247: static Object convertToArray(Object o) throws JSONException { jaroslav@37: if (o instanceof JSONArray) { jaroslav@37: JSONArray ja = (JSONArray)o; jaroslav@37: Object[] arr = new Object[ja.length()]; jaroslav@37: for (int i = 0; i < arr.length; i++) { jaroslav@37: arr[i] = convertToArray(ja.get(i)); jaroslav@37: } jaroslav@37: return arr; jaroslav@37: } else if (o instanceof JSONObject) { jaroslav@37: JSONObject obj = (JSONObject)o; jaroslav@37: Iterator it = obj.keys(); jtulach@760: List collect = new ArrayList(); jaroslav@37: while (it.hasNext()) { jaroslav@37: String key = (String)it.next(); jtulach@760: final Object val = obj.get(key); jtulach@760: final Object newVal = convertToArray(val); jtulach@760: if (val != newVal) { jtulach@760: collect.add(key); jtulach@760: collect.add(newVal); jtulach@760: } jtulach@760: } jtulach@760: int size = collect.size(); jtulach@760: for (int i = 0; i < size; i += 2) { jtulach@760: obj.put((String) collect.get(i), collect.get(i + 1)); jaroslav@37: } jaroslav@37: return obj; jtulach@750: } else if (o == JSONObject.NULL) { jtulach@750: return null; jaroslav@37: } else { jaroslav@37: return o; jaroslav@37: } jaroslav@37: } jtulach@940: jaroslav@37: public static void extractJSON(Object jsonObject, String[] props, Object[] values) { jaroslav@37: if (jsonObject instanceof JSONObject) { jaroslav@37: JSONObject obj = (JSONObject)jsonObject; jaroslav@37: for (int i = 0; i < props.length; i++) { jtulach@749: Object val = obj.opt(props[i]); jtulach@749: if (val == JSONObject.NULL) { jtulach@749: val = null; jaroslav@37: } jtulach@749: values[i] = val; jaroslav@37: } jaroslav@446: return; jaroslav@37: } jaroslav@446: for (int i = 0; i < props.length; i++) { jaroslav@446: values[i] = getProperty(jsonObject, props[i]); jaroslav@37: } jaroslav@37: } jtulach@940: jaroslav@446: @JavaScriptBody(args = {"object", "property"}, jaroslav@446: body jaroslav@446: = "if (property === null) return object;\n" jaroslav@446: + "if (object === null) return null;\n" jaroslav@446: + "var p = object[property]; return p ? p : null;" jaroslav@446: ) jaroslav@446: private static Object getProperty(Object object, String property) { jaroslav@446: return null; jaroslav@446: } jtulach@940: jaroslav@60: public static Object parse(InputStream is) throws IOException { jaroslav@60: try { jtulach@745: PushbackInputStream push = new PushbackInputStream(is, 1); jtulach@745: boolean[] arrayOrString = { false, false }; jtulach@745: detectJSONType(false, push, arrayOrString); jtulach@745: JSONTokener t = createTokener(push); jtulach@745: Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t); jtulach@745: return convertToArray(obj); jaroslav@60: } catch (JSONException ex) { jaroslav@60: throw new IOException(ex); jaroslav@60: } jaroslav@60: } jaroslav@37: }