ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 26 Aug 2014 18:13:30 +0200
changeset 838 bdc3d696dd4a
parent 790 30f20d9c0986
child 940 bdec4103bdb2
permissions -rw-r--r--
During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
jaroslav@37
     1
/**
jaroslav@358
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jaroslav@37
     3
 *
jaroslav@551
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jaroslav@37
     5
 *
jaroslav@358
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
jaroslav@358
     7
 * Other names may be trademarks of their respective owners.
jaroslav@37
     8
 *
jaroslav@358
     9
 * The contents of this file are subject to the terms of either the GNU
jaroslav@358
    10
 * General Public License Version 2 only ("GPL") or the Common
jaroslav@358
    11
 * Development and Distribution License("CDDL") (collectively, the
jaroslav@358
    12
 * "License"). You may not use this file except in compliance with the
jaroslav@358
    13
 * License. You can obtain a copy of the License at
jaroslav@358
    14
 * http://www.netbeans.org/cddl-gplv2.html
jaroslav@358
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jaroslav@358
    16
 * specific language governing permissions and limitations under the
jaroslav@358
    17
 * License.  When distributing the software, include this License Header
jaroslav@358
    18
 * Notice in each file and include the License file at
jaroslav@358
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
jaroslav@358
    20
 * particular file as subject to the "Classpath" exception as provided
jaroslav@358
    21
 * by Oracle in the GPL Version 2 section of the License file that
jaroslav@358
    22
 * accompanied this code. If applicable, add the following below the
jaroslav@358
    23
 * License Header, with the fields enclosed by brackets [] replaced by
jaroslav@358
    24
 * your own identifying information:
jaroslav@358
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
jaroslav@358
    26
 *
jaroslav@358
    27
 * Contributor(s):
jaroslav@358
    28
 *
jaroslav@358
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
jaroslav@551
    30
 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
jaroslav@358
    31
 *
jaroslav@358
    32
 * If you wish your version of this file to be governed by only the CDDL
jaroslav@358
    33
 * or only the GPL Version 2, indicate your decision by adding
jaroslav@358
    34
 * "[Contributor] elects to include this software in this distribution
jaroslav@358
    35
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jaroslav@358
    36
 * single choice of license, a recipient has the option to distribute
jaroslav@358
    37
 * your version of this file under either the CDDL, the GPL Version 2 or
jaroslav@358
    38
 * to extend the choice of license to its licensees as provided above.
jaroslav@358
    39
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jaroslav@358
    40
 * Version 2 license, then the option applies only if the new code is
jaroslav@358
    41
 * made subject to such option by the copyright holder.
jaroslav@37
    42
 */
jaroslav@446
    43
package org.netbeans.html.wstyrus;
jaroslav@37
    44
jaroslav@37
    45
import java.io.IOException;
jaroslav@60
    46
import java.io.InputStream;
jaroslav@37
    47
import java.io.InputStreamReader;
jaroslav@75
    48
import java.io.OutputStream;
jaroslav@37
    49
import java.io.PushbackInputStream;
jaroslav@37
    50
import java.io.Reader;
jaroslav@74
    51
import java.net.HttpURLConnection;
jaroslav@37
    52
import java.net.URL;
jaroslav@74
    53
import java.net.URLConnection;
jtulach@760
    54
import java.util.ArrayList;
jaroslav@37
    55
import java.util.Iterator;
jtulach@760
    56
import java.util.List;
jaroslav@37
    57
import java.util.concurrent.Executor;
jaroslav@37
    58
import java.util.concurrent.Executors;
jaroslav@158
    59
import java.util.concurrent.ThreadFactory;
jaroslav@37
    60
import java.util.logging.Logger;
jaroslav@131
    61
import net.java.html.js.JavaScriptBody;
jtulach@838
    62
import org.netbeans.html.json.spi.JSONCall;
jaroslav@37
    63
import org.json.JSONArray;
jaroslav@37
    64
import org.json.JSONException;
jaroslav@37
    65
import org.json.JSONObject;
jaroslav@37
    66
import org.json.JSONTokener;
jaroslav@37
    67
jaroslav@54
    68
/** This is an implementation package - just
jaroslav@54
    69
 * include its JAR on classpath and use official {@link Context} API
jaroslav@54
    70
 * to access the functionality.
jaroslav@37
    71
 *
jtulach@790
    72
 * @author Jaroslav Tulach
jaroslav@37
    73
 */
jaroslav@37
    74
final class LoadJSON implements Runnable {
jaroslav@446
    75
    private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
jaroslav@158
    76
    private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
jaroslav@158
    77
        @Override
jaroslav@158
    78
        public Thread newThread(Runnable runnable) {
jaroslav@158
    79
            Thread thread = Executors.defaultThreadFactory().newThread(runnable);
jaroslav@158
    80
            thread.setDaemon(true);
jaroslav@158
    81
            return thread;
jaroslav@158
    82
        }
jaroslav@158
    83
    });
jaroslav@37
    84
jaroslav@37
    85
    private final JSONCall call;
jaroslav@37
    86
    private final URL base;
jaroslav@37
    87
jaroslav@37
    88
jaroslav@37
    89
    private LoadJSON(JSONCall call) {
jaroslav@37
    90
        this.call = call;
jaroslav@446
    91
        this.base = null;
jaroslav@37
    92
    }
jaroslav@37
    93
jaroslav@37
    94
    public static void loadJSON(JSONCall call) {
jaroslav@258
    95
        assert !"WebSocket".equals(call.getMethod());
jaroslav@258
    96
        REQ.execute(new LoadJSON((call)));
jaroslav@37
    97
    }
jaroslav@37
    98
jaroslav@37
    99
    @Override
jaroslav@37
   100
    public void run() {
jaroslav@37
   101
        final String url;
jaroslav@446
   102
        Throwable error = null;
jaroslav@446
   103
        Object json = null;
jaroslav@446
   104
        
jaroslav@37
   105
        if (call.isJSONP()) {
jaroslav@37
   106
            url = call.composeURL("dummy");
jaroslav@37
   107
        } else {
jaroslav@37
   108
            url = call.composeURL(null);
jaroslav@37
   109
        }
jaroslav@37
   110
        try {
jaroslav@37
   111
            final URL u = new URL(base, url.replace(" ", "%20"));
jaroslav@74
   112
            URLConnection conn = u.openConnection();
jaroslav@527
   113
            if (call.isDoOutput()) {
jaroslav@527
   114
                conn.setDoOutput(true);
jaroslav@527
   115
            }
jaroslav@527
   116
            if (call.getMethod() != null && conn instanceof HttpURLConnection) {
jaroslav@527
   117
                ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
jaroslav@527
   118
            }
jaroslav@527
   119
            if (call.isDoOutput()) {
jaroslav@527
   120
                final OutputStream os = conn.getOutputStream();
jaroslav@527
   121
                call.writeData(os);
jaroslav@527
   122
                os.flush();
jaroslav@74
   123
            }
jaroslav@74
   124
            final PushbackInputStream is = new PushbackInputStream(
jaroslav@74
   125
                conn.getInputStream(), 1
jaroslav@74
   126
            );
jtulach@745
   127
            boolean[] arrayOrString = { false, false };
jtulach@745
   128
            detectJSONType(call.isJSONP(), is, arrayOrString);
jaroslav@73
   129
            try {
jtulach@745
   130
                if (arrayOrString[1]) {
jaroslav@73
   131
                    throw new JSONException("");
jaroslav@73
   132
                }
jaroslav@525
   133
                JSONTokener tok = createTokener(is);
jaroslav@73
   134
                Object obj;
jtulach@745
   135
                obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok);
jaroslav@73
   136
                json = convertToArray(obj);
jaroslav@73
   137
            } catch (JSONException ex) {
jaroslav@73
   138
                Reader r = new InputStreamReader(is, "UTF-8");
jaroslav@73
   139
                StringBuilder sb = new StringBuilder();
jaroslav@73
   140
                for (;;) {
jaroslav@73
   141
                    int ch = r.read();
jaroslav@73
   142
                    if (ch == -1) {
jaroslav@73
   143
                        break;
jaroslav@73
   144
                    }
jaroslav@73
   145
                    sb.append((char)ch);
jaroslav@73
   146
                }
jaroslav@73
   147
                json = sb.toString();
jaroslav@73
   148
            }
jaroslav@73
   149
        } catch (IOException ex) {
jaroslav@37
   150
            error = ex;
jaroslav@37
   151
        } finally {
jaroslav@446
   152
            if (error != null) {
jaroslav@446
   153
                call.notifyError(error);
jaroslav@446
   154
            } else {
jaroslav@446
   155
                call.notifySuccess(json);
jaroslav@446
   156
            }
jaroslav@37
   157
        }
jaroslav@37
   158
    }
jaroslav@37
   159
jtulach@745
   160
    private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
jtulach@745
   161
        for (;;) {
jtulach@745
   162
            int ch = is.read();
jtulach@745
   163
            if (ch == -1) {
jtulach@745
   164
                arrayOrString[1] = true;
jtulach@745
   165
                break;
jtulach@745
   166
            }
jtulach@745
   167
            if (Character.isWhitespace(ch)) {
jtulach@745
   168
                continue;
jtulach@745
   169
            }
jtulach@745
   170
jtulach@745
   171
            if (ch == '[') {
jtulach@745
   172
                is.unread(ch);
jtulach@745
   173
                arrayOrString[0] = true;
jtulach@745
   174
                break;
jtulach@745
   175
            }
jtulach@745
   176
            if (ch == '{') {
jtulach@745
   177
                is.unread(ch);
jtulach@745
   178
                break;
jtulach@745
   179
            }
jtulach@745
   180
            if (!skipAnything) {
jtulach@745
   181
                is.unread(ch);
jtulach@745
   182
                arrayOrString[1] = true;
jtulach@745
   183
                break;
jtulach@745
   184
            }
jtulach@745
   185
        }
jtulach@745
   186
    }
jtulach@745
   187
jaroslav@525
   188
    private static JSONTokener createTokener(InputStream is) throws IOException {
jaroslav@525
   189
        Reader r = new InputStreamReader(is, "UTF-8");
jaroslav@525
   190
        try {
jaroslav@525
   191
            return new JSONTokener(r);
jaroslav@525
   192
        } catch (LinkageError ex) {
jaroslav@525
   193
            // phones may carry outdated version of JSONTokener
jaroslav@525
   194
            StringBuilder sb = new StringBuilder();
jaroslav@525
   195
            for (;;) {
jaroslav@525
   196
                int ch = r.read();
jaroslav@525
   197
                if (ch == -1) {
jaroslav@525
   198
                    break;
jaroslav@525
   199
                }
jaroslav@525
   200
                sb.append((char)ch);
jaroslav@525
   201
            }
jaroslav@525
   202
            return new JSONTokener(sb.toString());
jaroslav@525
   203
        }
jaroslav@525
   204
    }
jaroslav@525
   205
jaroslav@247
   206
    static Object convertToArray(Object o) throws JSONException {
jaroslav@37
   207
        if (o instanceof JSONArray) {
jaroslav@37
   208
            JSONArray ja = (JSONArray)o;
jaroslav@37
   209
            Object[] arr = new Object[ja.length()];
jaroslav@37
   210
            for (int i = 0; i < arr.length; i++) {
jaroslav@37
   211
                arr[i] = convertToArray(ja.get(i));
jaroslav@37
   212
            }
jaroslav@37
   213
            return arr;
jaroslav@37
   214
        } else if (o instanceof JSONObject) {
jaroslav@37
   215
            JSONObject obj = (JSONObject)o;
jaroslav@37
   216
            Iterator it = obj.keys();
jtulach@760
   217
            List<Object> collect = new ArrayList<Object>();
jaroslav@37
   218
            while (it.hasNext()) {
jaroslav@37
   219
                String key = (String)it.next();
jtulach@760
   220
                final Object val = obj.get(key);
jtulach@760
   221
                final Object newVal = convertToArray(val);
jtulach@760
   222
                if (val != newVal) {
jtulach@760
   223
                    collect.add(key);
jtulach@760
   224
                    collect.add(newVal);
jtulach@760
   225
                }
jtulach@760
   226
            }
jtulach@760
   227
            int size = collect.size();
jtulach@760
   228
            for (int i = 0; i < size; i += 2) {
jtulach@760
   229
                obj.put((String) collect.get(i), collect.get(i + 1));
jaroslav@37
   230
            }
jaroslav@37
   231
            return obj;
jtulach@750
   232
        } else if (o == JSONObject.NULL) {
jtulach@750
   233
            return null;
jaroslav@37
   234
        } else {
jaroslav@37
   235
            return o;
jaroslav@37
   236
        }
jaroslav@37
   237
    }
jaroslav@37
   238
    
jaroslav@37
   239
    public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
jaroslav@37
   240
        if (jsonObject instanceof JSONObject) {
jaroslav@37
   241
            JSONObject obj = (JSONObject)jsonObject;
jaroslav@37
   242
            for (int i = 0; i < props.length; i++) {
jtulach@749
   243
                Object val = obj.opt(props[i]);
jtulach@749
   244
                if (val == JSONObject.NULL) {
jtulach@749
   245
                    val = null;
jaroslav@37
   246
                }
jtulach@749
   247
                values[i] = val;
jaroslav@37
   248
            }
jaroslav@446
   249
            return;
jaroslav@37
   250
        }
jaroslav@446
   251
        for (int i = 0; i < props.length; i++) {
jaroslav@446
   252
            values[i] = getProperty(jsonObject, props[i]);
jaroslav@37
   253
        }
jaroslav@37
   254
    }
jaroslav@37
   255
    
jaroslav@446
   256
    @JavaScriptBody(args = {"object", "property"},
jaroslav@446
   257
            body
jaroslav@446
   258
            = "if (property === null) return object;\n"
jaroslav@446
   259
            + "if (object === null) return null;\n"
jaroslav@446
   260
            + "var p = object[property]; return p ? p : null;"
jaroslav@446
   261
    )
jaroslav@446
   262
    private static Object getProperty(Object object, String property) {
jaroslav@446
   263
        return null;
jaroslav@446
   264
    }
jaroslav@446
   265
    
jaroslav@60
   266
    public static Object parse(InputStream is) throws IOException {
jaroslav@60
   267
        try {
jtulach@745
   268
            PushbackInputStream push = new PushbackInputStream(is, 1);
jtulach@745
   269
            boolean[] arrayOrString = { false, false };
jtulach@745
   270
            detectJSONType(false, push, arrayOrString);
jtulach@745
   271
            JSONTokener t = createTokener(push);
jtulach@745
   272
            Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t);
jtulach@745
   273
            return convertToArray(obj);
jaroslav@60
   274
        } catch (JSONException ex) {
jaroslav@60
   275
            throw new IOException(ex);
jaroslav@60
   276
        }
jaroslav@60
   277
    }
jaroslav@37
   278
}