jaroslav@123
|
1 |
/**
|
jaroslav@358
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
jaroslav@123
|
3 |
*
|
jaroslav@551
|
4 |
* Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
|
jaroslav@123
|
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@123
|
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@123
|
42 |
*/
|
jtulach@838
|
43 |
package org.netbeans.html.boot.spi;
|
jaroslav@123
|
44 |
|
jaroslav@322
|
45 |
import java.io.Closeable;
|
jtulach@680
|
46 |
import java.io.IOException;
|
jaroslav@349
|
47 |
import java.io.InputStream;
|
jaroslav@349
|
48 |
import java.io.InputStreamReader;
|
jaroslav@163
|
49 |
import java.io.Reader;
|
jaroslav@123
|
50 |
import java.net.URL;
|
jaroslav@349
|
51 |
import java.util.HashMap;
|
jaroslav@349
|
52 |
import java.util.HashSet;
|
jaroslav@349
|
53 |
import java.util.Map;
|
jaroslav@349
|
54 |
import java.util.Set;
|
jaroslav@431
|
55 |
import java.util.concurrent.Executor;
|
jaroslav@322
|
56 |
import net.java.html.js.JavaScriptBody;
|
jaroslav@362
|
57 |
import org.netbeans.html.boot.impl.FnContext;
|
jaroslav@123
|
58 |
|
jaroslav@289
|
59 |
/** Represents single JavaScript function that can be invoked.
|
jaroslav@289
|
60 |
* Created via {@link Presenter#defineFn(java.lang.String, java.lang.String...)}.
|
jaroslav@123
|
61 |
*
|
jtulach@655
|
62 |
* @author Jaroslav Tulach
|
jaroslav@123
|
63 |
*/
|
jaroslav@123
|
64 |
public abstract class Fn {
|
jaroslav@288
|
65 |
private final Presenter presenter;
|
jaroslav@288
|
66 |
|
jaroslav@289
|
67 |
/**
|
jaroslav@289
|
68 |
* @deprecated Ineffective as of 0.6.
|
jtulach@838
|
69 |
* Provide a presenter via {@link #Fn(org.netbeans.html.boot.spi.Fn.Presenter)}
|
jaroslav@289
|
70 |
* constructor
|
jaroslav@289
|
71 |
*/
|
jaroslav@289
|
72 |
@Deprecated
|
jaroslav@289
|
73 |
protected Fn() {
|
jaroslav@289
|
74 |
this(null);
|
jaroslav@289
|
75 |
}
|
jaroslav@289
|
76 |
|
jaroslav@289
|
77 |
/** Creates new function object and associates it with given presenter.
|
jaroslav@289
|
78 |
*
|
jaroslav@289
|
79 |
* @param presenter the browser presenter associated with this function
|
jaroslav@289
|
80 |
* @since 0.6
|
jaroslav@289
|
81 |
*/
|
jaroslav@288
|
82 |
protected Fn(Presenter presenter) {
|
jaroslav@288
|
83 |
this.presenter = presenter;
|
jaroslav@288
|
84 |
}
|
jaroslav@289
|
85 |
|
jaroslav@289
|
86 |
/** True, if currently active presenter is the same as presenter this
|
jtulach@838
|
87 |
* function has been created for via {@link #Fn(org.netbeans.html.boot.spi.Fn.Presenter)}.
|
jaroslav@289
|
88 |
*
|
jaroslav@289
|
89 |
* @return true, if proper presenter is used
|
jaroslav@289
|
90 |
*/
|
jaroslav@288
|
91 |
public final boolean isValid() {
|
jaroslav@451
|
92 |
return presenter != null && FnContext.currentPresenter(false) == presenter;
|
jaroslav@288
|
93 |
}
|
jaroslav@288
|
94 |
|
jaroslav@323
|
95 |
/** Helper method to check if the provided instance is valid function.
|
jaroslav@323
|
96 |
* Checks if the parameter is non-null and if so, does {@link #isValid()}
|
jaroslav@323
|
97 |
* check.
|
jaroslav@323
|
98 |
*
|
jaroslav@323
|
99 |
* @param fnOrNull function or <code>null</code>
|
jaroslav@323
|
100 |
* @return true if the parameter is non-null and valid
|
jaroslav@323
|
101 |
* @since 0.7
|
jaroslav@323
|
102 |
*/
|
jaroslav@323
|
103 |
public static boolean isValid(Fn fnOrNull) {
|
jaroslav@323
|
104 |
return fnOrNull != null && fnOrNull.isValid();
|
jaroslav@323
|
105 |
}
|
jaroslav@323
|
106 |
|
jaroslav@323
|
107 |
/** Helper method to find current presenter and ask it to define new
|
jaroslav@323
|
108 |
* function by calling {@link Presenter#defineFn(java.lang.String, java.lang.String...)}.
|
jaroslav@323
|
109 |
*
|
jaroslav@323
|
110 |
* @param caller the class who wishes to define the function
|
jaroslav@323
|
111 |
* @param code the body of the function (can reference <code>this</code> and <code>names</code> variables)
|
jaroslav@323
|
112 |
* @param names names of individual parameters
|
jaroslav@323
|
113 |
* @return the function object that can be {@link Fn#invoke(java.lang.Object, java.lang.Object...) invoked}
|
jaroslav@451
|
114 |
* - can return <code>null</code> if there is {@link #activePresenter() no presenter}
|
jaroslav@323
|
115 |
* @since 0.7
|
jaroslav@323
|
116 |
*/
|
jaroslav@323
|
117 |
public static Fn define(Class<?> caller, String code, String... names) {
|
jaroslav@451
|
118 |
final Presenter p = FnContext.currentPresenter(false);
|
jaroslav@451
|
119 |
return p == null ? null : p.defineFn(code, names);
|
jaroslav@323
|
120 |
}
|
jaroslav@323
|
121 |
|
jaroslav@349
|
122 |
private static final Map<String,Set<Presenter>> LOADED = new HashMap<String, Set<Presenter>>();
|
jaroslav@427
|
123 |
|
jaroslav@427
|
124 |
/** Wraps function to ensure that the script represented by <code>resource</code>
|
jaroslav@427
|
125 |
* gets loaded into the browser environment before the function <code>fn</code>
|
jaroslav@427
|
126 |
* is executed.
|
jaroslav@427
|
127 |
*
|
jaroslav@560
|
128 |
* @param fn original function to call (if <code>null</code> returns <code>null</code>)
|
jaroslav@427
|
129 |
* @param caller the class who wishes to define/call the function
|
jaroslav@427
|
130 |
* @param resource resources (accessible via {@link ClassLoader#getResource(java.lang.String)})
|
jaroslav@427
|
131 |
* with a <em>JavaScript</em> that is supposed to loaded into the browser
|
jaroslav@427
|
132 |
* environment
|
jaroslav@427
|
133 |
* @return function that ensures the script is loaded and then delegates
|
jaroslav@560
|
134 |
* to <code>fn</code>. Returns <code>null</code> if the input <code>fn</code> is null
|
jaroslav@427
|
135 |
* @since 0.7
|
jaroslav@427
|
136 |
*/
|
jaroslav@349
|
137 |
public static Fn preload(final Fn fn, final Class<?> caller, final String resource) {
|
jaroslav@560
|
138 |
if (fn == null) {
|
jaroslav@560
|
139 |
return null;
|
jaroslav@560
|
140 |
}
|
jaroslav@555
|
141 |
return new Fn(fn.presenter()) {
|
jaroslav@349
|
142 |
@Override
|
jaroslav@349
|
143 |
public Object invoke(Object thiz, Object... args) throws Exception {
|
jaroslav@577
|
144 |
loadResource();
|
jaroslav@577
|
145 |
return fn.invoke(thiz, args);
|
jaroslav@577
|
146 |
}
|
jaroslav@577
|
147 |
|
jaroslav@577
|
148 |
@Override
|
jaroslav@577
|
149 |
public void invokeLater(Object thiz, Object... args) throws Exception {
|
jaroslav@577
|
150 |
loadResource();
|
jaroslav@577
|
151 |
fn.invokeLater(thiz, args);
|
jaroslav@577
|
152 |
}
|
jaroslav@577
|
153 |
|
jaroslav@577
|
154 |
private void loadResource() throws Exception {
|
jaroslav@560
|
155 |
Presenter p = presenter();
|
jaroslav@560
|
156 |
if (p == null) {
|
jaroslav@560
|
157 |
p = FnContext.currentPresenter(false);
|
jaroslav@349
|
158 |
}
|
jaroslav@560
|
159 |
if (p != null) {
|
jaroslav@560
|
160 |
Set<Presenter> there = LOADED.get(resource);
|
jaroslav@560
|
161 |
if (there == null) {
|
jaroslav@560
|
162 |
there = new HashSet<Presenter>();
|
jaroslav@560
|
163 |
LOADED.put(resource, there);
|
jaroslav@560
|
164 |
}
|
jaroslav@560
|
165 |
if (there.add(p)) {
|
jtulach@680
|
166 |
final ClassLoader l = caller.getClassLoader();
|
jtulach@680
|
167 |
InputStream is = l.getResourceAsStream(resource);
|
jtulach@680
|
168 |
if (is == null && resource.startsWith("/")) {
|
jtulach@680
|
169 |
is = l.getResourceAsStream(resource.substring(1));
|
jtulach@680
|
170 |
}
|
jtulach@680
|
171 |
if (is == null) {
|
jtulach@680
|
172 |
throw new IOException("Cannot find " + resource + " in " + l);
|
jtulach@680
|
173 |
}
|
jaroslav@560
|
174 |
try {
|
jaroslav@560
|
175 |
InputStreamReader r = new InputStreamReader(is, "UTF-8");
|
jaroslav@560
|
176 |
p.loadScript(r);
|
jaroslav@560
|
177 |
} finally {
|
jaroslav@560
|
178 |
is.close();
|
jaroslav@560
|
179 |
}
|
jaroslav@349
|
180 |
}
|
jaroslav@349
|
181 |
}
|
jaroslav@349
|
182 |
}
|
jaroslav@349
|
183 |
};
|
jaroslav@349
|
184 |
}
|
jaroslav@577
|
185 |
|
jaroslav@349
|
186 |
|
jaroslav@322
|
187 |
/** The currently active presenter.
|
jaroslav@322
|
188 |
*
|
jaroslav@322
|
189 |
* @return the currently active presenter or <code>null</code>
|
jaroslav@322
|
190 |
* @since 0.7
|
jaroslav@322
|
191 |
*/
|
jaroslav@322
|
192 |
public static Presenter activePresenter() {
|
jaroslav@434
|
193 |
return FnContext.currentPresenter(false);
|
jaroslav@322
|
194 |
}
|
jaroslav@322
|
195 |
|
jaroslav@716
|
196 |
/** Activates given presenter. Used to associate the native
|
jaroslav@716
|
197 |
* JavaScript code specified by
|
jaroslav@716
|
198 |
* {@link JavaScriptBody} annotation with certain presenter:
|
jaroslav@322
|
199 |
* <pre>
|
jaroslav@322
|
200 |
* try ({@link Closeable} c = Fn.activate(presenter)) {
|
jaroslav@322
|
201 |
* doCallsInPresenterContext();
|
jaroslav@322
|
202 |
* }
|
jaroslav@322
|
203 |
* </pre>
|
jaroslav@322
|
204 |
*
|
jaroslav@322
|
205 |
* @param p the presenter that should be active until closable is closed
|
jaroslav@322
|
206 |
* @return the closable to close
|
jaroslav@322
|
207 |
* @since 0.7
|
jaroslav@322
|
208 |
*/
|
jaroslav@322
|
209 |
public static Closeable activate(Presenter p) {
|
jaroslav@322
|
210 |
return FnContext.activate(p);
|
jaroslav@322
|
211 |
}
|
jaroslav@322
|
212 |
|
jaroslav@289
|
213 |
/** Invokes the defined function with specified <code>this</code> and
|
jaroslav@289
|
214 |
* appropriate arguments.
|
jaroslav@289
|
215 |
*
|
jaroslav@289
|
216 |
* @param thiz the meaning of <code>this</code> inside of the JavaScript
|
jaroslav@289
|
217 |
* function - can be <code>null</code>
|
jaroslav@289
|
218 |
* @param args arguments for the function
|
jaroslav@289
|
219 |
* @return return value from the function
|
jaroslav@289
|
220 |
* @throws Exception if something goes wrong, as exception may be thrown
|
jaroslav@289
|
221 |
*/
|
jaroslav@289
|
222 |
public abstract Object invoke(Object thiz, Object... args) throws Exception;
|
jaroslav@593
|
223 |
|
jaroslav@593
|
224 |
/** Invokes the defined function with specified <code>this</code> and
|
jaroslav@593
|
225 |
* appropriate arguments asynchronously. The invocation may be
|
jaroslav@593
|
226 |
* happen <em>"later"</em>.
|
jaroslav@593
|
227 |
*
|
jaroslav@593
|
228 |
* @param thiz the meaning of <code>this</code> inside of the JavaScript
|
jaroslav@593
|
229 |
* function - can be <code>null</code>
|
jaroslav@593
|
230 |
* @param args arguments for the function
|
jaroslav@593
|
231 |
* @throws Exception if something goes wrong, as exception may be thrown
|
jaroslav@593
|
232 |
* @since 0.7.6
|
jaroslav@593
|
233 |
*/
|
jaroslav@593
|
234 |
public void invokeLater(Object thiz, Object... args) throws Exception {
|
jaroslav@593
|
235 |
invoke(this, args);
|
jaroslav@593
|
236 |
}
|
jaroslav@429
|
237 |
|
jaroslav@429
|
238 |
/** Provides the function implementation access to the presenter provided
|
jtulach@838
|
239 |
* in {@link #Fn(org.netbeans.html.boot.spi.Fn.Presenter) the constructor}.
|
jaroslav@429
|
240 |
*
|
jaroslav@530
|
241 |
* @return presenter passed in the constructor (may be, but should not be <code>null</code>)
|
jaroslav@429
|
242 |
* @since 0.7
|
jaroslav@429
|
243 |
*/
|
jaroslav@429
|
244 |
protected final Presenter presenter() {
|
jaroslav@429
|
245 |
return presenter;
|
jaroslav@429
|
246 |
}
|
jaroslav@289
|
247 |
|
jaroslav@289
|
248 |
/** The representation of a <em>presenter</em> - usually a browser window.
|
jaroslav@315
|
249 |
* Should be provided by a library included in the application and registered
|
jaroslav@315
|
250 |
* in <code>META-INF/services</code>, for example with
|
jaroslav@315
|
251 |
* <code>@ServiceProvider(service = Fn.Presenter.class)</code> annotation.
|
jaroslav@431
|
252 |
* <p>
|
jaroslav@431
|
253 |
* Since 0.7 a presenter may implement {@link Executor} interface, in case
|
jaroslav@431
|
254 |
* it supports single threaded execution environment. The executor's
|
jaroslav@431
|
255 |
* {@link Executor#execute(java.lang.Runnable)} method is then supposed
|
jaroslav@431
|
256 |
* to invoke the runnable immediately (in case we are on the right thread
|
jaroslav@431
|
257 |
* already) or return and asynchronously invoke the runnable later on the
|
jaroslav@431
|
258 |
* right thread (if we are on wrong thread).
|
jaroslav@289
|
259 |
*/
|
jaroslav@127
|
260 |
public interface Presenter {
|
jaroslav@289
|
261 |
/** Creates new function with given parameter names and provided body.
|
jaroslav@289
|
262 |
*
|
jaroslav@289
|
263 |
* @param code the body of the function. Can refer to variables named
|
jaroslav@289
|
264 |
* as <code>names</code>
|
jaroslav@289
|
265 |
* @param names names of parameters of the function - these will be
|
jaroslav@289
|
266 |
* available when the <code>code</code> body executes
|
jaroslav@289
|
267 |
*
|
jaroslav@289
|
268 |
* @return function that can be later invoked
|
jaroslav@289
|
269 |
*/
|
jaroslav@127
|
270 |
public Fn defineFn(String code, String... names);
|
jaroslav@289
|
271 |
|
jaroslav@289
|
272 |
/** Opens the browser, loads provided page and when the
|
jaroslav@289
|
273 |
* page is ready, it calls back to the provider runnable.
|
jaroslav@289
|
274 |
*
|
jaroslav@289
|
275 |
* @param page the URL for the page to display
|
jaroslav@289
|
276 |
* @param onPageLoad callback when the page is ready
|
jaroslav@289
|
277 |
*/
|
jaroslav@128
|
278 |
public void displayPage(URL page, Runnable onPageLoad);
|
jaroslav@289
|
279 |
|
jaroslav@289
|
280 |
/** Loads a script into the browser JavaScript interpreter and
|
jaroslav@289
|
281 |
* executes it.
|
jaroslav@289
|
282 |
* @param code the script to execute
|
jaroslav@289
|
283 |
* @throws Exception if something goes wrong, throw an exception
|
jaroslav@289
|
284 |
*/
|
jaroslav@163
|
285 |
public void loadScript(Reader code) throws Exception;
|
jaroslav@123
|
286 |
}
|
jaroslav@430
|
287 |
|
jaroslav@430
|
288 |
/** Additional interface to be implemented by {@link Presenter}s that
|
jaroslav@430
|
289 |
* wish to control what objects are passed into the JavaScript virtual
|
jaroslav@430
|
290 |
* machine.
|
jaroslav@430
|
291 |
* <p>
|
jaroslav@430
|
292 |
* If a JavaScript engine makes callback to Java method that returns
|
jaroslav@430
|
293 |
* a value, the {@link #toJavaScript(java.lang.Object)} method is
|
jaroslav@430
|
294 |
* consulted to convert the Java value to something reasonable inside
|
jaroslav@430
|
295 |
* JavaScript VM.
|
jaroslav@430
|
296 |
* <p>
|
jaroslav@430
|
297 |
* <em>Note:</em> The implementation based on <em>JavaFX</em> <code>WebView</code>
|
jaroslav@430
|
298 |
* uses this interface to convert Java arrays to JavaScript ones.
|
jaroslav@430
|
299 |
*
|
jaroslav@430
|
300 |
* @see Presenter
|
jaroslav@430
|
301 |
* @since 0.7
|
jaroslav@430
|
302 |
*/
|
jaroslav@430
|
303 |
public interface ToJavaScript {
|
jaroslav@430
|
304 |
/** Convert a Java return value into some object suitable for
|
jaroslav@430
|
305 |
* JavaScript virtual machine.
|
jaroslav@430
|
306 |
*
|
jaroslav@430
|
307 |
* @param toReturn the Java object to be returned
|
jaroslav@430
|
308 |
* @return the replacement value to return instead
|
jaroslav@430
|
309 |
*/
|
jaroslav@430
|
310 |
public Object toJavaScript(Object toReturn);
|
jaroslav@430
|
311 |
}
|
jaroslav@446
|
312 |
|
jaroslav@446
|
313 |
/** Additional interface to be implemented by {@link Presenter}s that
|
jaroslav@446
|
314 |
* need to convert JavaScript object (usually array) to Java object
|
jaroslav@446
|
315 |
* when calling back from JavaScript to Java.
|
jaroslav@446
|
316 |
* <p>
|
jaroslav@446
|
317 |
* <em>Note:</em> The implementation based on <em>JavaFX</em>
|
jaroslav@446
|
318 |
* <code>WebView</code> uses this interface to convert JavaScript arrays to
|
jaroslav@446
|
319 |
* Java ones.
|
jaroslav@446
|
320 |
*
|
jaroslav@446
|
321 |
* @since 0.7
|
jaroslav@446
|
322 |
*/
|
jaroslav@446
|
323 |
public interface FromJavaScript {
|
jaroslav@446
|
324 |
/** Convert a JavaScript object into suitable Java representation
|
jaroslav@446
|
325 |
* before a Java method is called with this object as an argument.
|
jaroslav@446
|
326 |
*
|
jaroslav@446
|
327 |
* @param js the JavaScript object
|
jaroslav@446
|
328 |
* @return replacement object for
|
jaroslav@446
|
329 |
*/
|
jaroslav@446
|
330 |
public Object toJava(Object js);
|
jaroslav@446
|
331 |
}
|
jaroslav@123
|
332 |
}
|