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: */ jtulach@1057: package org.netbeans.html.xhr4j; 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; jtulach@1057: import java.io.UnsupportedEncodingException; jaroslav@74: import java.net.HttpURLConnection; jaroslav@37: import java.net.URL; jaroslav@74: import java.net.URLConnection; jtulach@1057: import java.util.concurrent.Callable; 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; 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); jtulach@1057: thread.setName("xhr4j daemon"); 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); jtulach@1057: String response = readStream(is); jtulach@1057: if (call.isJSONP()) { jtulach@1057: response = '(' + response; jaroslav@73: } jtulach@1057: json = new Result(response, arrayOrString[0], arrayOrString[1]); 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@1057: private static final class Result implements Callable { jtulach@1057: private final String response; jtulach@1057: private final boolean array; jtulach@1057: private final boolean plain; jtulach@1057: jtulach@1057: Result(String response, boolean array, boolean plain) { jtulach@1057: this.response = response; jtulach@1057: this.array = array; jtulach@1057: this.plain = plain; jtulach@1057: } jtulach@1057: jtulach@1057: @Override jtulach@1057: public Object call() throws Exception { jtulach@1057: if (plain) { jtulach@1057: return response; jtulach@1057: } else { jtulach@1057: if (array) { jtulach@1057: Object r = parse(response); jtulach@1057: Object[] arr = r instanceof Object[] ? (Object[])r : new Object[] { r }; jtulach@1057: for (int i = 0; i < arr.length; i++) { jtulach@1057: arr[i] = new JSObjToStr(response, arr[i]); jtulach@1057: } jtulach@1057: return arr; jtulach@1057: } else { jtulach@1057: return new JSObjToStr(response, parse(response)); jtulach@1057: } jtulach@1057: } jtulach@1057: } jtulach@1057: } jtulach@1057: private static final class JSObjToStr { jtulach@1057: final String str; jtulach@1057: final Object obj; jtulach@1057: jtulach@1057: public JSObjToStr(Object str, Object obj) { jtulach@1057: this.str = str == null ? "" : str.toString(); jtulach@1057: this.obj = obj; jtulach@1057: } jtulach@1057: jtulach@1057: @Override jtulach@1057: public String toString() { jtulach@1057: return str; jtulach@1057: } jtulach@1057: } jtulach@1057: jtulach@1057: static String readStream(InputStream is) throws IOException, UnsupportedEncodingException { jtulach@1057: Reader r = new InputStreamReader(is, "UTF-8"); jtulach@1057: StringBuilder sb = new StringBuilder(); jtulach@1057: char[] arr = new char[4096]; jtulach@1057: for (;;) { jtulach@1057: int len = r.read(arr); jtulach@1057: if (len == -1) { jtulach@1057: break; jtulach@1057: } jtulach@1057: sb.append(arr, 0, len); jtulach@1057: } jtulach@1057: return sb.toString(); jtulach@1057: } jtulach@1057: 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@446: @JavaScriptBody(args = {"object", "property"}, jtulach@1057: body jtulach@1057: = "if (property === null) return object;\n" jtulach@1057: + "if (object === null) return null;\n" jtulach@1057: + "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: jtulach@1057: @JavaScriptBody(args = {"s"}, body = "return eval('(' + s + ')');") jtulach@1057: static Object parse(String s) { jtulach@1057: throw new IllegalStateException("No parser context for " + s); jtulach@1057: } jtulach@1057: jtulach@1057: static void extractJSON(Object js, String[] props, Object[] values) { jtulach@1057: if (js instanceof JSObjToStr) { jtulach@1057: js = ((JSObjToStr)js).obj; jtulach@1057: } jtulach@1057: for (int i = 0; i < props.length; i++) { jtulach@1057: values[i] = getProperty(js, props[i]); jaroslav@60: } jaroslav@60: } jtulach@1057: jaroslav@37: }