ko-fx/src/main/java/org/apidesign/html/kofx/LoadJSON.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 03 May 2013 10:27:06 +0200
changeset 60 939453361b2d
parent 54 0cc18086f731
child 73 f7e7223e94d4
permissions -rw-r--r--
A way to parse an input stream and create a JSON object
     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.PushbackInputStream;
    27 import java.io.Reader;
    28 import java.net.MalformedURLException;
    29 import java.net.URL;
    30 import java.util.Iterator;
    31 import java.util.concurrent.Executor;
    32 import java.util.concurrent.Executors;
    33 import java.util.logging.Level;
    34 import java.util.logging.Logger;
    35 import javafx.application.Platform;
    36 import javafx.scene.web.WebEngine;
    37 import netscape.javascript.JSObject;
    38 import org.apidesign.html.json.spi.JSONCall;
    39 import org.json.JSONArray;
    40 import org.json.JSONException;
    41 import org.json.JSONObject;
    42 import org.json.JSONTokener;
    43 import org.openide.util.Exceptions;
    44 
    45 /** This is an implementation package - just
    46  * include its JAR on classpath and use official {@link Context} API
    47  * to access the functionality.
    48  *
    49  * @author Jaroslav Tulach <jtulach@netbeans.org>
    50  */
    51 final class LoadJSON implements Runnable {
    52     private static final Logger LOG = FXContext.LOG;
    53     private static final Executor REQ = Executors.newCachedThreadPool();
    54 
    55     private final JSONCall call;
    56     private final URL base;
    57     private Throwable error;
    58     private Object json;
    59 
    60 
    61     private LoadJSON(JSONCall call) {
    62         this.call = call;
    63         URL b;
    64         try {
    65             b = new URL(findBaseURL());
    66         } catch (MalformedURLException ex) {
    67             LOG.log(Level.SEVERE, "Can't find base url for " + call.composeURL("dummy"), ex);
    68             b = null;
    69         }
    70         this.base = b;
    71     }
    72 
    73     public static void loadJSON(JSONCall call) {
    74         REQ.execute(new LoadJSON((call)));
    75     }
    76 
    77     @Override
    78     public void run() {
    79         if (Platform.isFxApplicationThread()) {
    80             if (error != null) {
    81                 call.notifyError(error);
    82             } else {
    83                 call.notifySuccess(json);
    84             }
    85             return;
    86         }
    87         final String url;
    88         if (call.isJSONP()) {
    89             url = call.composeURL("dummy");
    90         } else {
    91             url = call.composeURL(null);
    92         }
    93         try {
    94             final URL u = new URL(base, url.replace(" ", "%20"));
    95             final PushbackInputStream is = new PushbackInputStream(u.openStream(), 1);
    96             boolean array = false;
    97             if (call.isJSONP()) {
    98                 for (;;) {
    99                     int ch = is.read();
   100                     if (ch == -1) {
   101                         break;
   102                     }
   103                     if (ch == '[') {
   104                         is.unread(ch);
   105                         array = true;
   106                         break;
   107                     }
   108                     if (ch == '{') {
   109                         is.unread(ch);
   110                         break;
   111                     }
   112                 }
   113             } else {
   114                 int ch = is.read();
   115                 array = ch == '[';
   116                 is.unread(ch);
   117             }
   118             Reader r = new InputStreamReader(is, "UTF-8");
   119 
   120             JSONTokener tok = new JSONTokener(r);
   121             Object obj = array ? new JSONArray(tok) : new JSONObject(tok);
   122             json = convertToArray(obj);
   123         } catch (JSONException | IOException ex) {
   124             error = ex;
   125             LOG.log(Level.WARNING, "Cannot connect to " + url, ex);
   126         } finally {
   127             Platform.runLater(this);
   128         }
   129     }
   130 
   131     private static Object convertToArray(Object o) throws JSONException {
   132         if (o instanceof JSONArray) {
   133             JSONArray ja = (JSONArray)o;
   134             Object[] arr = new Object[ja.length()];
   135             for (int i = 0; i < arr.length; i++) {
   136                 arr[i] = convertToArray(ja.get(i));
   137             }
   138             return arr;
   139         } else if (o instanceof JSONObject) {
   140             JSONObject obj = (JSONObject)o;
   141             Iterator it = obj.keys();
   142             while (it.hasNext()) {
   143                 String key = (String)it.next();
   144                 obj.put(key, convertToArray(obj.get(key)));
   145             }
   146             return obj;
   147         } else {
   148             return o;
   149         }
   150     }
   151     
   152     public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
   153         if (jsonObject instanceof JSONObject) {
   154             JSONObject obj = (JSONObject)jsonObject;
   155             for (int i = 0; i < props.length; i++) {
   156                 try {
   157                     values[i] = obj.has(props[i]) ? obj.get(props[i]) : null;
   158                 } catch (JSONException ex) {
   159                     LoadJSON.LOG.log(Level.SEVERE, "Can't read " + props[i] + " from " + jsonObject, ex);
   160                 }
   161             }
   162         }
   163         if (jsonObject instanceof JSObject) {
   164             JSObject obj = (JSObject)jsonObject;
   165             for (int i = 0; i < props.length; i++) {
   166                 Object val = obj.getMember(props[i]);
   167                 values[i] = isDefined(val) ? val : null;
   168             }
   169         }
   170     }
   171     
   172     public static Object parse(InputStream is) throws IOException {
   173         try {
   174             InputStreamReader r = new InputStreamReader(is, "UTF-8");
   175             JSONTokener t = new JSONTokener(r);
   176             return new JSONObject(t);
   177         } catch (JSONException ex) {
   178             throw new IOException(ex);
   179         }
   180     }
   181     
   182     private static String findBaseURL() {
   183         WebEngine eng = (WebEngine) System.getProperties().get("webEngine");
   184         return (String) eng.executeScript(
   185             "var h;"
   186             + "if (!!window && !!window.location && !!window.location.href)\n"
   187             + "  h = window.location.href;\n"
   188             + "else "
   189             + "  h = null;"
   190             + "h\n");
   191     }
   192 
   193     private static boolean isDefined(Object val) {
   194         return !"undefined".equals(val);
   195     }
   196 }