ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sat, 02 Aug 2014 12:59:31 +0200
changeset 790 30f20d9c0986
parent 760 785fe0c29404
child 838 bdc3d696dd4a
permissions -rw-r--r--
Fixing Javadoc to succeed on JDK8
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     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]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    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.
    42  */
    43 package org.netbeans.html.wstyrus;
    44 
    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;
    52 import java.net.URL;
    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.apidesign.html.json.spi.JSONCall;
    63 import org.json.JSONArray;
    64 import org.json.JSONException;
    65 import org.json.JSONObject;
    66 import org.json.JSONTokener;
    67 
    68 /** This is an implementation package - just
    69  * include its JAR on classpath and use official {@link Context} API
    70  * to access the functionality.
    71  *
    72  * @author Jaroslav Tulach
    73  */
    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() {
    77         @Override
    78         public Thread newThread(Runnable runnable) {
    79             Thread thread = Executors.defaultThreadFactory().newThread(runnable);
    80             thread.setDaemon(true);
    81             return thread;
    82         }
    83     });
    84 
    85     private final JSONCall call;
    86     private final URL base;
    87 
    88 
    89     private LoadJSON(JSONCall call) {
    90         this.call = call;
    91         this.base = null;
    92     }
    93 
    94     public static void loadJSON(JSONCall call) {
    95         assert !"WebSocket".equals(call.getMethod());
    96         REQ.execute(new LoadJSON((call)));
    97     }
    98 
    99     @Override
   100     public void run() {
   101         final String url;
   102         Throwable error = null;
   103         Object json = null;
   104         
   105         if (call.isJSONP()) {
   106             url = call.composeURL("dummy");
   107         } else {
   108             url = call.composeURL(null);
   109         }
   110         try {
   111             final URL u = new URL(base, url.replace(" ", "%20"));
   112             URLConnection conn = u.openConnection();
   113             if (call.isDoOutput()) {
   114                 conn.setDoOutput(true);
   115             }
   116             if (call.getMethod() != null && conn instanceof HttpURLConnection) {
   117                 ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
   118             }
   119             if (call.isDoOutput()) {
   120                 final OutputStream os = conn.getOutputStream();
   121                 call.writeData(os);
   122                 os.flush();
   123             }
   124             final PushbackInputStream is = new PushbackInputStream(
   125                 conn.getInputStream(), 1
   126             );
   127             boolean[] arrayOrString = { false, false };
   128             detectJSONType(call.isJSONP(), is, arrayOrString);
   129             try {
   130                 if (arrayOrString[1]) {
   131                     throw new JSONException("");
   132                 }
   133                 JSONTokener tok = createTokener(is);
   134                 Object obj;
   135                 obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok);
   136                 json = convertToArray(obj);
   137             } catch (JSONException ex) {
   138                 Reader r = new InputStreamReader(is, "UTF-8");
   139                 StringBuilder sb = new StringBuilder();
   140                 for (;;) {
   141                     int ch = r.read();
   142                     if (ch == -1) {
   143                         break;
   144                     }
   145                     sb.append((char)ch);
   146                 }
   147                 json = sb.toString();
   148             }
   149         } catch (IOException ex) {
   150             error = ex;
   151         } finally {
   152             if (error != null) {
   153                 call.notifyError(error);
   154             } else {
   155                 call.notifySuccess(json);
   156             }
   157         }
   158     }
   159 
   160     private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
   161         for (;;) {
   162             int ch = is.read();
   163             if (ch == -1) {
   164                 arrayOrString[1] = true;
   165                 break;
   166             }
   167             if (Character.isWhitespace(ch)) {
   168                 continue;
   169             }
   170 
   171             if (ch == '[') {
   172                 is.unread(ch);
   173                 arrayOrString[0] = true;
   174                 break;
   175             }
   176             if (ch == '{') {
   177                 is.unread(ch);
   178                 break;
   179             }
   180             if (!skipAnything) {
   181                 is.unread(ch);
   182                 arrayOrString[1] = true;
   183                 break;
   184             }
   185         }
   186     }
   187 
   188     private static JSONTokener createTokener(InputStream is) throws IOException {
   189         Reader r = new InputStreamReader(is, "UTF-8");
   190         try {
   191             return new JSONTokener(r);
   192         } catch (LinkageError ex) {
   193             // phones may carry outdated version of JSONTokener
   194             StringBuilder sb = new StringBuilder();
   195             for (;;) {
   196                 int ch = r.read();
   197                 if (ch == -1) {
   198                     break;
   199                 }
   200                 sb.append((char)ch);
   201             }
   202             return new JSONTokener(sb.toString());
   203         }
   204     }
   205 
   206     static Object convertToArray(Object o) throws JSONException {
   207         if (o instanceof JSONArray) {
   208             JSONArray ja = (JSONArray)o;
   209             Object[] arr = new Object[ja.length()];
   210             for (int i = 0; i < arr.length; i++) {
   211                 arr[i] = convertToArray(ja.get(i));
   212             }
   213             return arr;
   214         } else if (o instanceof JSONObject) {
   215             JSONObject obj = (JSONObject)o;
   216             Iterator it = obj.keys();
   217             List<Object> collect = new ArrayList<Object>();
   218             while (it.hasNext()) {
   219                 String key = (String)it.next();
   220                 final Object val = obj.get(key);
   221                 final Object newVal = convertToArray(val);
   222                 if (val != newVal) {
   223                     collect.add(key);
   224                     collect.add(newVal);
   225                 }
   226             }
   227             int size = collect.size();
   228             for (int i = 0; i < size; i += 2) {
   229                 obj.put((String) collect.get(i), collect.get(i + 1));
   230             }
   231             return obj;
   232         } else if (o == JSONObject.NULL) {
   233             return null;
   234         } else {
   235             return o;
   236         }
   237     }
   238     
   239     public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
   240         if (jsonObject instanceof JSONObject) {
   241             JSONObject obj = (JSONObject)jsonObject;
   242             for (int i = 0; i < props.length; i++) {
   243                 Object val = obj.opt(props[i]);
   244                 if (val == JSONObject.NULL) {
   245                     val = null;
   246                 }
   247                 values[i] = val;
   248             }
   249             return;
   250         }
   251         for (int i = 0; i < props.length; i++) {
   252             values[i] = getProperty(jsonObject, props[i]);
   253         }
   254     }
   255     
   256     @JavaScriptBody(args = {"object", "property"},
   257             body
   258             = "if (property === null) return object;\n"
   259             + "if (object === null) return null;\n"
   260             + "var p = object[property]; return p ? p : null;"
   261     )
   262     private static Object getProperty(Object object, String property) {
   263         return null;
   264     }
   265     
   266     public static Object parse(InputStream is) throws IOException {
   267         try {
   268             PushbackInputStream push = new PushbackInputStream(is, 1);
   269             boolean[] arrayOrString = { false, false };
   270             detectJSONType(false, push, arrayOrString);
   271             JSONTokener t = createTokener(push);
   272             Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t);
   273             return convertToArray(obj);
   274         } catch (JSONException ex) {
   275             throw new IOException(ex);
   276         }
   277     }
   278 }