2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
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]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
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.
43 package org.netbeans.html.boot.fx;
45 import java.io.BufferedReader;
46 import java.io.Reader;
48 import java.util.ArrayList;
49 import java.util.List;
50 import java.util.concurrent.Executor;
51 import java.util.logging.Level;
52 import java.util.logging.Logger;
53 import javafx.application.Platform;
54 import javafx.scene.web.WebEngine;
55 import javafx.scene.web.WebView;
56 import netscape.javascript.JSObject;
57 import org.apidesign.html.boot.spi.Fn;
61 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
63 public abstract class AbstractFXPresenter
64 implements Fn.Presenter, Fn.ToJavaScript, Fn.FromJavaScript, Executor {
65 static final Logger LOG = Logger.getLogger(FXPresenter.class.getName());
66 protected static int cnt;
67 protected List<String> scripts;
68 protected Runnable onLoad;
69 protected WebEngine engine;
72 public Fn defineFn(String code, String... names) {
73 return defineJSFn(code, names);
76 final JSFn defineJSFn(String code, String... names) {
77 StringBuilder sb = new StringBuilder();
78 sb.append("(function() {");
79 sb.append(" return function(");
81 for (String n : names) {
82 sb.append(sep).append(n);
89 if (LOG.isLoggable(Level.FINE)) {
91 "defining function #{0}:\n{1}\n",
92 new Object[] { ++cnt, code }
95 JSObject x = (JSObject) engine.executeScript(sb.toString());
96 return new JSFn(this, x, cnt);
100 public void loadScript(Reader code) throws Exception {
101 BufferedReader r = new BufferedReader(code);
102 StringBuilder sb = new StringBuilder();
104 String l = r.readLine();
108 sb.append(l).append('\n');
110 final String script = sb.toString();
111 if (scripts != null) {
114 engine.executeScript(script);
117 protected final void onPageLoad() {
118 if (scripts != null) {
119 for (String s : scripts) {
120 engine.executeScript(s);
127 public void displayPage(final URL resource, final Runnable onLoad) {
128 this.onLoad = onLoad;
129 final WebView view = findView(resource);
130 this.engine = view.getEngine();
132 if (FXInspect.initialize(engine)) {
133 scripts = new ArrayList<String>();
135 } catch (Throwable ex) {
136 ex.printStackTrace();
139 class Run implements Runnable {
143 if (scripts != null) {
144 view.setContextMenuEnabled(true);
146 engine.load(resource.toExternalForm());
150 if (Platform.isFxApplicationThread()) {
153 Platform.runLater(run);
158 protected abstract void waitFinished();
160 protected abstract WebView findView(final URL resource);
162 final JSObject convertArrays(Object[] arr) {
163 for (int i = 0; i < arr.length; i++) {
164 if (arr[i] instanceof Object[]) {
165 arr[i] = convertArrays((Object[]) arr[i]);
168 final JSObject wrapArr = (JSObject)wrapArrFn().call("array", arr); // NOI18N
172 private JSObject wrapArrImpl;
173 private final JSObject wrapArrFn() {
174 if (wrapArrImpl == null) {
176 wrapArrImpl = (JSObject)defineJSFn(" var k = {};"
177 + " k.array= function() {"
178 + " return Array.prototype.slice.call(arguments);"
181 ).invokeImpl(null, false);
182 } catch (Exception ex) {
183 throw new IllegalStateException(ex);
189 final Object checkArray(Object val) {
190 int length = ((Number) arraySizeFn().call("array", val, null)).intValue();
194 Object[] arr = new Object[length];
195 arraySizeFn().call("array", val, arr);
198 private JSObject arraySize;
199 private final JSObject arraySizeFn() {
200 if (arraySize == null) {
202 arraySize = (JSObject)defineJSFn(" var k = {};"
203 + " k.array = function(arr, to) {"
204 + " if (to === null) {"
205 + " if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;"
208 + " var l = arr.length;"
209 + " for (var i = 0; i < l; i++) to[i] = arr[i];"
214 ).invokeImpl(null, false);
215 } catch (Exception ex) {
216 throw new IllegalStateException(ex);
223 public Object toJava(Object jsArray) {
224 return checkArray(jsArray);
228 public Object toJavaScript(Object toReturn) {
229 if (toReturn instanceof Object[]) {
230 return convertArrays((Object[])toReturn);
236 @Override public void execute(Runnable r) {
237 if (Platform.isFxApplicationThread()) {
240 Platform.runLater(r);
244 private static final class JSFn extends Fn {
246 private final JSObject fn;
247 private static int call;
248 private final int id;
250 public JSFn(AbstractFXPresenter p, JSObject fn, int id) {
257 public Object invoke(Object thiz, Object... args) throws Exception {
258 return invokeImpl(thiz, true, args);
261 final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
263 if (LOG.isLoggable(Level.FINE)) {
264 LOG.log(Level.FINE, "calling {0} function #{1}", new Object[]{++call, id});
266 List<Object> all = new ArrayList<Object>(args.length + 1);
267 all.add(thiz == null ? fn : thiz);
268 for (int i = 0; i < args.length; i++) {
269 if (arrayChecks && args[i] instanceof Object[]) {
270 Object[] arr = (Object[]) args[i];
271 Object conv = ((AbstractFXPresenter)presenter()).convertArrays(arr);
276 Object ret = fn.call("call", all.toArray()); // NOI18N
283 return ((AbstractFXPresenter)presenter()).checkArray(ret);
287 } catch (Exception t) {