ko-fx/src/main/java/org/apidesign/html/kofx/LoadJSON.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 19 Jun 2013 22:56:40 +0200
branchclassloader
changeset 131 926603cd86ac
parent 120 6e402f180e69
child 138 4cc150dc4665
permissions -rw-r--r--
Using net.java.html.js.JavaScriptBody
     1 /**
     2  * HTML via Java(tm) Language Bindings
     3  * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details. apidesign.org
    13  * designates this particular file as subject to the
    14  * "Classpath" exception as provided by apidesign.org
    15  * in the License file that accompanied this code.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program. Look for COPYING file in the top folder.
    19  * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    20  */
    21 package org.apidesign.html.kofx;
    22 
    23 import java.io.IOException;
    24 import java.io.InputStream;
    25 import java.io.InputStreamReader;
    26 import java.io.OutputStream;
    27 import java.io.PushbackInputStream;
    28 import java.io.Reader;
    29 import java.net.HttpURLConnection;
    30 import java.net.MalformedURLException;
    31 import java.net.URL;
    32 import java.net.URLConnection;
    33 import java.util.Iterator;
    34 import java.util.concurrent.Executor;
    35 import java.util.concurrent.Executors;
    36 import java.util.logging.Level;
    37 import java.util.logging.Logger;
    38 import javafx.application.Platform;
    39 import net.java.html.js.JavaScriptBody;
    40 import netscape.javascript.JSObject;
    41 import org.apidesign.html.json.spi.JSONCall;
    42 import org.json.JSONArray;
    43 import org.json.JSONException;
    44 import org.json.JSONObject;
    45 import org.json.JSONTokener;
    46 
    47 /** This is an implementation package - just
    48  * include its JAR on classpath and use official {@link Context} API
    49  * to access the functionality.
    50  *
    51  * @author Jaroslav Tulach <jtulach@netbeans.org>
    52  */
    53 final class LoadJSON implements Runnable {
    54     private static final Logger LOG = FXContext.LOG;
    55     private static final Executor REQ = Executors.newCachedThreadPool();
    56 
    57     private final JSONCall call;
    58     private final URL base;
    59     private Throwable error;
    60     private Object json;
    61 
    62 
    63     private LoadJSON(JSONCall call) {
    64         this.call = call;
    65         URL b;
    66         try {
    67             b = new URL(findBaseURL());
    68         } catch (MalformedURLException ex) {
    69             LOG.log(Level.SEVERE, "Can't find base url for " + call.composeURL("dummy"), ex);
    70             b = null;
    71         }
    72         this.base = b;
    73     }
    74 
    75     public static void loadJSON(JSONCall call) {
    76         REQ.execute(new LoadJSON((call)));
    77     }
    78 
    79     @Override
    80     public void run() {
    81         if (Platform.isFxApplicationThread()) {
    82             if (error != null) {
    83                 call.notifyError(error);
    84             } else {
    85                 call.notifySuccess(json);
    86             }
    87             return;
    88         }
    89         final String url;
    90         if (call.isJSONP()) {
    91             url = call.composeURL("dummy");
    92         } else {
    93             url = call.composeURL(null);
    94         }
    95         try {
    96             final URL u = new URL(base, url.replace(" ", "%20"));
    97             URLConnection conn = u.openConnection();
    98             if (conn instanceof HttpURLConnection) {
    99                 HttpURLConnection huc = (HttpURLConnection) conn;
   100                 if (call.getMethod() != null) {
   101                     huc.setRequestMethod(call.getMethod());
   102                 }
   103                 if (call.isDoOutput()) {
   104                     huc.setDoOutput(true);
   105                     final OutputStream os = huc.getOutputStream();
   106                     call.writeData(os);
   107                     os.flush();
   108                 }
   109             }
   110             final PushbackInputStream is = new PushbackInputStream(
   111                 conn.getInputStream(), 1
   112             );
   113             boolean array = false;
   114             boolean string = false;
   115             if (call.isJSONP()) {
   116                 for (;;) {
   117                     int ch = is.read();
   118                     if (ch == -1) {
   119                         break;
   120                     }
   121                     if (ch == '[') {
   122                         is.unread(ch);
   123                         array = true;
   124                         break;
   125                     }
   126                     if (ch == '{') {
   127                         is.unread(ch);
   128                         break;
   129                     }
   130                 }
   131             } else {
   132                 int ch = is.read();
   133                 array = ch == '[';
   134                 is.unread(ch);
   135                 if (!array && ch != '{') {
   136                     string = true;
   137                 }
   138             }
   139             try {
   140                 if (string) {
   141                     throw new JSONException("");
   142                 }
   143                 Reader r = new InputStreamReader(is, "UTF-8");
   144 
   145                 JSONTokener tok = new JSONTokener(r);
   146                 Object obj;
   147                 obj = array ? new JSONArray(tok) : new JSONObject(tok);
   148                 json = convertToArray(obj);
   149             } catch (JSONException ex) {
   150                 Reader r = new InputStreamReader(is, "UTF-8");
   151                 StringBuilder sb = new StringBuilder();
   152                 for (;;) {
   153                     int ch = r.read();
   154                     if (ch == -1) {
   155                         break;
   156                     }
   157                     sb.append((char)ch);
   158                 }
   159                 json = sb.toString();
   160             }
   161         } catch (IOException ex) {
   162             error = ex;
   163             LOG.log(Level.WARNING, "Cannot connect to " + url, ex);
   164         } finally {
   165             Platform.runLater(this);
   166         }
   167     }
   168 
   169     private static Object convertToArray(Object o) throws JSONException {
   170         if (o instanceof JSONArray) {
   171             JSONArray ja = (JSONArray)o;
   172             Object[] arr = new Object[ja.length()];
   173             for (int i = 0; i < arr.length; i++) {
   174                 arr[i] = convertToArray(ja.get(i));
   175             }
   176             return arr;
   177         } else if (o instanceof JSONObject) {
   178             JSONObject obj = (JSONObject)o;
   179             Iterator it = obj.keys();
   180             while (it.hasNext()) {
   181                 String key = (String)it.next();
   182                 obj.put(key, convertToArray(obj.get(key)));
   183             }
   184             return obj;
   185         } else {
   186             return o;
   187         }
   188     }
   189     
   190     public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
   191         if (jsonObject instanceof JSONObject) {
   192             JSONObject obj = (JSONObject)jsonObject;
   193             for (int i = 0; i < props.length; i++) {
   194                 try {
   195                     values[i] = obj.has(props[i]) ? obj.get(props[i]) : null;
   196                 } catch (JSONException ex) {
   197                     LoadJSON.LOG.log(Level.SEVERE, "Can't read " + props[i] + " from " + jsonObject, ex);
   198                 }
   199             }
   200         }
   201         if (jsonObject instanceof JSObject) {
   202             JSObject obj = (JSObject)jsonObject;
   203             for (int i = 0; i < props.length; i++) {
   204                 Object val = obj.getMember(props[i]);
   205                 values[i] = isDefined(val) ? val : null;
   206             }
   207         }
   208     }
   209     
   210     public static Object parse(InputStream is) throws IOException {
   211         try {
   212             InputStreamReader r = new InputStreamReader(is, "UTF-8");
   213             JSONTokener t = new JSONTokener(r);
   214             return new JSONObject(t);
   215         } catch (JSONException ex) {
   216             throw new IOException(ex);
   217         }
   218     }
   219 
   220     @JavaScriptBody(args = {  }, body = 
   221           "var h;"
   222         + "if (!!window && !!window.location && !!window.location.href)\n"
   223         + "  h = window.location.href;\n"
   224         + "else "
   225         + "  h = null;"
   226         + "return h;\n"
   227     )
   228     private static native String findBaseURL();
   229     
   230     private static boolean isDefined(Object val) {
   231         return !"undefined".equals(val);
   232     }
   233 }