1.1 --- a/boot-fx/pom.xml Sat Jun 29 07:01:58 2013 +0200
1.2 +++ b/boot-fx/pom.xml Thu Jul 18 15:39:56 2013 +0200
1.3 @@ -35,5 +35,11 @@
1.4 <version>${project.version}</version>
1.5 <type>jar</type>
1.6 </dependency>
1.7 + <dependency>
1.8 + <groupId>org.testng</groupId>
1.9 + <artifactId>testng</artifactId>
1.10 + <scope>test</scope>
1.11 + <type>jar</type>
1.12 + </dependency>
1.13 </dependencies>
1.14 </project>
2.1 --- a/boot-fx/src/main/java/org/apidesign/html/boot/fx/FXPresenter.java Sat Jun 29 07:01:58 2013 +0200
2.2 +++ b/boot-fx/src/main/java/org/apidesign/html/boot/fx/FXPresenter.java Thu Jul 18 15:39:56 2013 +0200
2.3 @@ -44,7 +44,6 @@
2.4 import javafx.scene.Scene;
2.5 import javafx.scene.control.Button;
2.6 import javafx.scene.layout.BorderPane;
2.7 -import javafx.scene.layout.HBox;
2.8 import javafx.scene.layout.VBox;
2.9 import javafx.scene.text.Text;
2.10 import javafx.scene.web.WebEngine;
2.11 @@ -208,14 +207,7 @@
2.12 FXBrwsr.class.notifyAll();
2.13 }
2.14 BorderPane r = new BorderPane();
2.15 - final boolean showToolbar = "true".equals(this.getParameters().getNamed().get("toolbar")); // NOI18N
2.16 - final boolean useFirebug = "true".equals(this.getParameters().getNamed().get("firebug")); // NOI18N
2.17 - if (showToolbar) {
2.18 - //final ToolBar toolbar = new BrowserToolbar(view, vbox, useFirebug, wc.dbg);
2.19 - //root.setTop(toolbar);
2.20 - }
2.21 Scene scene = new Scene(r, 800, 600);
2.22 - primaryStage.setTitle("Device Emulator");
2.23 primaryStage.setScene(scene);
2.24 primaryStage.show();
2.25 this.root = r;
2.26 @@ -225,17 +217,7 @@
2.27 final WebView view = new WebView();
2.28 final String nbUserDir = this.getParameters().getNamed().get("userdir"); // NOI18N
2.29 WebController wc = new WebController(view, nbUserDir, getParameters().getUnnamed());
2.30 -
2.31 - final VBox vbox = new VBox();
2.32 - vbox.setAlignment(Pos.CENTER);
2.33 - vbox.setStyle("-fx-background-color: #808080;");
2.34 -
2.35 - HBox hbox = new HBox();
2.36 - hbox.setStyle("-fx-background-color: #808080;");
2.37 - hbox.setAlignment(Pos.CENTER);
2.38 - hbox.getChildren().add(vbox);
2.39 - vbox.getChildren().add(view);
2.40 - root.setCenter(hbox);
2.41 + root.setCenter(view);
2.42
2.43 final Worker<Void> w = view.getEngine().getLoadWorker();
2.44 w.stateProperty().addListener(new ChangeListener<Worker.State>() {
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/boot-fx/src/test/java/org/apidesign/html/boot/fx/BootstrapTest.java Thu Jul 18 15:39:56 2013 +0200
3.3 @@ -0,0 +1,84 @@
3.4 +/**
3.5 + * HTML via Java(tm) Language Bindings
3.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.7 + *
3.8 + * This program is free software: you can redistribute it and/or modify
3.9 + * it under the terms of the GNU General Public License as published by
3.10 + * the Free Software Foundation, version 2 of the License.
3.11 + *
3.12 + * This program is distributed in the hope that it will be useful,
3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.15 + * GNU General Public License for more details. apidesign.org
3.16 + * designates this particular file as subject to the
3.17 + * "Classpath" exception as provided by apidesign.org
3.18 + * in the License file that accompanied this code.
3.19 + *
3.20 + * You should have received a copy of the GNU General Public License
3.21 + * along with this program. Look for COPYING file in the top folder.
3.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
3.23 + */
3.24 +package org.apidesign.html.boot.fx;
3.25 +
3.26 +import java.lang.annotation.Annotation;
3.27 +import java.lang.reflect.Method;
3.28 +import java.util.ArrayList;
3.29 +import java.util.List;
3.30 +import java.util.concurrent.Executors;
3.31 +import net.java.html.boot.BrowserBuilder;
3.32 +import static org.testng.Assert.*;
3.33 +import org.testng.annotations.Factory;
3.34 +import org.testng.annotations.Test;
3.35 +
3.36 +/**
3.37 + *
3.38 + * @author Jaroslav Tulach <jtulach@netbeans.org>
3.39 + */
3.40 +public class BootstrapTest {
3.41 + private static Class<?> browserClass;
3.42 +
3.43 + public BootstrapTest() {
3.44 + }
3.45 +
3.46 + @Factory public static Object[] compatibilityTests() throws Exception {
3.47 + final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(BootstrapTest.class).
3.48 + loadPage("empty.html").
3.49 + invoke("initialized");
3.50 +
3.51 + Executors.newSingleThreadExecutor().submit(new Runnable() {
3.52 + @Override
3.53 + public void run() {
3.54 + bb.showAndWait();
3.55 + }
3.56 + });
3.57 +
3.58 + List<Object> res = new ArrayList<Object>();
3.59 + Class<? extends Annotation> test =
3.60 + loadClass().getClassLoader().loadClass(Test.class.getName()).
3.61 + asSubclass(Annotation.class);
3.62 + for (Method m : loadClass().getMethods()) {
3.63 + if (m.getAnnotation(test) != null) {
3.64 + res.add(new KOFx(m));
3.65 + }
3.66 + }
3.67 + return res.toArray();
3.68 + }
3.69 +
3.70 + static synchronized Class<?> loadClass() throws InterruptedException {
3.71 + while (browserClass == null) {
3.72 + BootstrapTest.class.wait();
3.73 + }
3.74 + return browserClass;
3.75 + }
3.76 +
3.77 + public static synchronized void ready(Class<?> browserCls) throws Exception {
3.78 + browserClass = browserCls;
3.79 + BootstrapTest.class.notifyAll();
3.80 + }
3.81 +
3.82 + public static void initialized() throws Exception {
3.83 + Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(BootstrapTest.class.getName());
3.84 + Method m = classpathClass.getMethod("ready", Class.class);
3.85 + m.invoke(null, FXPresenterTst.class);
3.86 + }
3.87 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/boot-fx/src/test/java/org/apidesign/html/boot/fx/FXPresenterTst.java Thu Jul 18 15:39:56 2013 +0200
4.3 @@ -0,0 +1,49 @@
4.4 +/**
4.5 + * HTML via Java(tm) Language Bindings
4.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
4.7 + *
4.8 + * This program is free software: you can redistribute it and/or modify
4.9 + * it under the terms of the GNU General Public License as published by
4.10 + * the Free Software Foundation, version 2 of the License.
4.11 + *
4.12 + * This program is distributed in the hope that it will be useful,
4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.15 + * GNU General Public License for more details. apidesign.org
4.16 + * designates this particular file as subject to the
4.17 + * "Classpath" exception as provided by apidesign.org
4.18 + * in the License file that accompanied this code.
4.19 + *
4.20 + * You should have received a copy of the GNU General Public License
4.21 + * along with this program. Look for COPYING file in the top folder.
4.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
4.23 + */
4.24 +package org.apidesign.html.boot.fx;
4.25 +
4.26 +import net.java.html.js.JavaScriptBody;
4.27 +import static org.testng.Assert.*;
4.28 +import org.testng.annotations.Test;
4.29 +
4.30 +/**
4.31 + *
4.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
4.33 + */
4.34 +public class FXPresenterTst {
4.35 + @Test public void showClassLoader() {
4.36 + R run = new R();
4.37 + callback(run);
4.38 + assertEquals(run.cnt, 1, "Can call even private implementation classes");
4.39 + }
4.40 +
4.41 + @JavaScriptBody(args = { "r" }, javacall = true, body = "r.@java.lang.Runnable::run()();")
4.42 + private static native void callback(Runnable r);
4.43 +
4.44 + private static class R implements Runnable {
4.45 + int cnt;
4.46 +
4.47 + @Override
4.48 + public void run() {
4.49 + cnt++;
4.50 + }
4.51 + }
4.52 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/boot-fx/src/test/java/org/apidesign/html/boot/fx/KOFx.java Thu Jul 18 15:39:56 2013 +0200
5.3 @@ -0,0 +1,97 @@
5.4 +/**
5.5 + * HTML via Java(tm) Language Bindings
5.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5.7 + *
5.8 + * This program is free software: you can redistribute it and/or modify
5.9 + * it under the terms of the GNU General Public License as published by
5.10 + * the Free Software Foundation, version 2 of the License.
5.11 + *
5.12 + * This program is distributed in the hope that it will be useful,
5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.15 + * GNU General Public License for more details. apidesign.org
5.16 + * designates this particular file as subject to the
5.17 + * "Classpath" exception as provided by apidesign.org
5.18 + * in the License file that accompanied this code.
5.19 + *
5.20 + * You should have received a copy of the GNU General Public License
5.21 + * along with this program. Look for COPYING file in the top folder.
5.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
5.23 + */
5.24 +package org.apidesign.html.boot.fx;
5.25 +
5.26 +import java.lang.reflect.InvocationTargetException;
5.27 +import java.lang.reflect.Method;
5.28 +import javafx.application.Platform;
5.29 +import org.testng.IHookCallBack;
5.30 +import org.testng.IHookable;
5.31 +import org.testng.ITest;
5.32 +import org.testng.ITestResult;
5.33 +import org.testng.annotations.Test;
5.34 +
5.35 +/**
5.36 + *
5.37 + * @author Jaroslav Tulach <jtulach@netbeans.org>
5.38 + */
5.39 +public final class KOFx implements ITest, IHookable, Runnable {
5.40 + private final Method m;
5.41 + private Object result;
5.42 + private Object inst;
5.43 +
5.44 + KOFx(Method m) {
5.45 + this.m = m;
5.46 + }
5.47 +
5.48 + @Override
5.49 + public String getTestName() {
5.50 + return m.getName();
5.51 + }
5.52 +
5.53 + @Test
5.54 + public synchronized void executeTest() throws Exception {
5.55 + if (result == null) {
5.56 + Platform.runLater(this);
5.57 + wait();
5.58 + }
5.59 + if (result instanceof Exception) {
5.60 + throw (Exception)result;
5.61 + }
5.62 + if (result instanceof Error) {
5.63 + throw (Error)result;
5.64 + }
5.65 + }
5.66 +
5.67 + @Override
5.68 + public synchronized void run() {
5.69 + boolean notify = true;
5.70 + try {
5.71 + if (inst == null) {
5.72 + inst = m.getDeclaringClass().newInstance();
5.73 + }
5.74 + result = m.invoke(inst);
5.75 + if (result == null) {
5.76 + result = this;
5.77 + }
5.78 + } catch (InvocationTargetException ex) {
5.79 + Throwable r = ex.getTargetException();
5.80 + if (r instanceof InterruptedException) {
5.81 + notify = false;
5.82 + Platform.runLater(this);
5.83 + return;
5.84 + }
5.85 + result = r;
5.86 + } catch (Exception ex) {
5.87 + result = ex;
5.88 + } finally {
5.89 + if (notify) {
5.90 + notifyAll();
5.91 + }
5.92 + }
5.93 + }
5.94 +
5.95 + @Override
5.96 + public void run(IHookCallBack ihcb, ITestResult itr) {
5.97 + ihcb.runTestMethod(itr);
5.98 + }
5.99 +
5.100 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/boot-fx/src/test/resources/org/apidesign/html/boot/fx/empty.html Thu Jul 18 15:39:56 2013 +0200
6.3 @@ -0,0 +1,33 @@
6.4 +<!--
6.5 +
6.6 + HTML via Java(tm) Language Bindings
6.7 + Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.8 +
6.9 + This program is free software: you can redistribute it and/or modify
6.10 + it under the terms of the GNU General Public License as published by
6.11 + the Free Software Foundation, version 2 of the License.
6.12 +
6.13 + This program is distributed in the hope that it will be useful,
6.14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
6.15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.16 + GNU General Public License for more details. apidesign.org
6.17 + designates this particular file as subject to the
6.18 + "Classpath" exception as provided by apidesign.org
6.19 + in the License file that accompanied this code.
6.20 +
6.21 + You should have received a copy of the GNU General Public License
6.22 + along with this program. Look for COPYING file in the top folder.
6.23 + If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
6.24 +
6.25 +-->
6.26 +<!DOCTYPE html>
6.27 +<html>
6.28 + <head>
6.29 + <title>FX Presenter Harness</title>
6.30 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6.31 + <meta name="viewport" content="width=device-width">
6.32 + </head>
6.33 + <body>
6.34 + <div>FX Presenter Harness</div>
6.35 + </body>
6.36 +</html>
7.1 --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Sat Jun 29 07:01:58 2013 +0200
7.2 +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Thu Jul 18 15:39:56 2013 +0200
7.3 @@ -20,6 +20,7 @@
7.4 */
7.5 package net.java.html.boot;
7.6
7.7 +import java.io.File;
7.8 import java.io.IOException;
7.9 import java.lang.reflect.Method;
7.10 import java.net.MalformedURLException;
7.11 @@ -103,8 +104,14 @@
7.12
7.13 /** Page to load into the browser. If the <code>page</code> represents
7.14 * a {@link URL} known to the Java system, the URL is passed to the browser.
7.15 - * Otherwise the system seeks for the resource via {@link Class#getResource(java.lang.String)}
7.16 - * method.
7.17 + * If system property <code>browser.rootdir</code> is specified, then a
7.18 + * file <code>page</code> relative to this directory is used as the URL.
7.19 + * If no such file exists, the system seeks for the
7.20 + * resource via {@link Class#getResource(java.lang.String)}
7.21 + * method (relative to the {@link #loadClass(java.lang.Class) specified class}).
7.22 + * If such resource is not found, a file relative to the location JAR
7.23 + * that contains the {@link #loadClass(java.lang.Class) main class} is
7.24 + * searched for.
7.25 *
7.26 * @param page the location (relative, absolute, or URL) of a page to load
7.27 * @return this browser
7.28 @@ -146,7 +153,12 @@
7.29 URL url = null;
7.30 MalformedURLException mal = null;
7.31 try {
7.32 - url = new URL(resource);
7.33 + String baseURL = System.getProperty("browser.rootdir");
7.34 + if (baseURL != null) {
7.35 + url = new File(baseURL, resource).toURI().toURL();
7.36 + } else {
7.37 + url = new URL(resource);
7.38 + }
7.39 } catch (MalformedURLException ex) {
7.40 mal = ex;
7.41 }
7.42 @@ -154,6 +166,15 @@
7.43 url = clazz.getResource(resource);
7.44 }
7.45 if (url == null) {
7.46 + URL jar = clazz.getProtectionDomain().getCodeSource().getLocation();
7.47 + try {
7.48 + url = new URL(jar, resource);
7.49 + } catch (MalformedURLException ex) {
7.50 + ex.initCause(mal);
7.51 + mal = ex;
7.52 + }
7.53 + }
7.54 + if (url == null) {
7.55 IllegalStateException ise = new IllegalStateException("Can't find resouce: " + resource + " relative to " + clazz);
7.56 if (mal != null) {
7.57 ise.initCause(mal);
8.1 --- a/boot/src/main/java/net/java/html/js/JavaScriptBody.java Sat Jun 29 07:01:58 2013 +0200
8.2 +++ b/boot/src/main/java/net/java/html/js/JavaScriptBody.java Thu Jul 18 15:39:56 2013 +0200
8.3 @@ -59,6 +59,8 @@
8.4 * This is the syntax one can use to call <code>run()</code>
8.5 * method of {@link Runnable}:
8.6 * <pre>r.@java.lang.Runnable::run()()</pre>.
8.7 + * One can also call static methods. Just use:
8.8 + * <pre>var ten = @java.lang.Integer::parseInt(Ljava/lang/String;)("10")</pre>
8.9 *
8.10 * @return true, if the script should be scanned for special callback
8.11 * syntax
9.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Sat Jun 29 07:01:58 2013 +0200
9.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Jul 18 15:39:56 2013 +0200
9.3 @@ -23,14 +23,12 @@
9.4 import java.io.InputStream;
9.5 import java.io.InputStreamReader;
9.6 import java.io.Reader;
9.7 -import java.lang.reflect.Method;
9.8 import java.net.URL;
9.9 import java.util.ArrayList;
9.10 import java.util.Collections;
9.11 import java.util.Enumeration;
9.12 import java.util.List;
9.13 import org.apidesign.html.boot.spi.Fn;
9.14 -import org.objectweb.asm.Type;
9.15
9.16 /**
9.17 *
9.18 @@ -76,82 +74,22 @@
9.19 };
9.20 }
9.21
9.22 - static String callback(String body, ClassLoader loader) {
9.23 - try {
9.24 - return callbackImpl(body, loader);
9.25 - } catch (ClassNotFoundException ex) {
9.26 - throw new IllegalStateException("Can't parse " + body, ex);
9.27 - } catch (NoSuchMethodException ex) {
9.28 - throw new IllegalStateException("Can't parse " + body, ex);
9.29 - }
9.30 - }
9.31 -
9.32 - private static String callbackImpl(String body, ClassLoader loader)
9.33 - throws ClassNotFoundException, NoSuchMethodException {
9.34 - StringBuilder sb = new StringBuilder();
9.35 - int pos = 0;
9.36 - for (;;) {
9.37 - int next = body.indexOf(".@", pos);
9.38 - if (next == -1) {
9.39 - sb.append(body.substring(pos));
9.40 - return sb.toString();
9.41 + static String callback(final String body) {
9.42 + return new JsCallback() {
9.43 + @Override
9.44 + protected CharSequence callMethod(
9.45 + String ident, String fqn, String method, String params
9.46 + ) {
9.47 + StringBuilder sb = new StringBuilder();
9.48 + sb.append("vm.").append(mangle(fqn, method, params));
9.49 + sb.append("(");
9.50 + if (ident != null) {
9.51 + sb.append(ident);
9.52 + }
9.53 + return sb;
9.54 }
9.55 - sb.append(body.substring(pos, next));
9.56 -
9.57 - int sigBeg = body.indexOf('(', next);
9.58 - int sigEnd = body.indexOf(')', sigBeg);
9.59 -
9.60 - int colon4 = body.indexOf("::", next);
9.61 -
9.62 - if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
9.63 - throw new IllegalStateException("Malformed body " + body);
9.64 - }
9.65 -
9.66 - String fqn = body.substring(next + 2, colon4);
9.67 - String method = body.substring(colon4 + 2, sigBeg);
9.68 - String params = body.substring(sigBeg, sigEnd + 1);
9.69 -
9.70 - Class<?> clazz = Class.forName(fqn, false, loader);
9.71 - final Type[] argTps = Type.getArgumentTypes(params);
9.72 - Class<?>[] argCls = new Class<?>[argTps.length];
9.73 - for (int i = 0; i < argCls.length; i++) {
9.74 - argCls[i] = toClass(argTps[i], loader);
9.75 - }
9.76 - Method m = clazz.getMethod(method, argCls);
9.77 -
9.78 - sb.append("['").append(m.getName()).append("(");
9.79 - String sep = "";
9.80 - for (Class<?> pt : m.getParameterTypes()) {
9.81 - sb.append(sep).append(pt.getName());
9.82 - sep = ",";
9.83 - }
9.84 - sb.append(")']");
9.85 -
9.86 - pos = sigEnd + 1;
9.87 - }
9.88 - }
9.89
9.90 - private static Class<?> toClass(final Type t, ClassLoader loader) throws ClassNotFoundException {
9.91 - if (t == Type.INT_TYPE) {
9.92 - return Integer.TYPE;
9.93 - } else if (t == Type.VOID_TYPE) {
9.94 - return Void.TYPE;
9.95 - } else if (t == Type.BOOLEAN_TYPE) {
9.96 - return Boolean.TYPE;
9.97 - } else if (t == Type.BYTE_TYPE) {
9.98 - return Byte.TYPE;
9.99 - } else if (t == Type.CHAR_TYPE) {
9.100 - return Character.TYPE;
9.101 - } else if (t == Type.SHORT_TYPE) {
9.102 - return Short.TYPE;
9.103 - } else if (t == Type.DOUBLE_TYPE) {
9.104 - return Double.TYPE;
9.105 - } else if (t == Type.FLOAT_TYPE) {
9.106 - return Float.TYPE;
9.107 - } else if (t == Type.LONG_TYPE) {
9.108 - return Long.TYPE;
9.109 - }
9.110 - return Class.forName(t.getClassName(), false, loader);
9.111 + }.parse(body);
9.112 }
9.113
9.114 static void loadScript(JsClassLoader jcl, String resource) {
10.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/JavaScriptProcesor.java Sat Jun 29 07:01:58 2013 +0200
10.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/JavaScriptProcesor.java Thu Jul 18 15:39:56 2013 +0200
10.3 @@ -20,10 +20,15 @@
10.4 */
10.5 package org.apidesign.html.boot.impl;
10.6
10.7 +import java.io.IOException;
10.8 +import java.io.Writer;
10.9 import java.util.Collections;
10.10 +import java.util.HashMap;
10.11 import java.util.HashSet;
10.12 import java.util.List;
10.13 +import java.util.Map;
10.14 import java.util.Set;
10.15 +import java.util.TreeMap;
10.16 import javax.annotation.processing.AbstractProcessor;
10.17 import javax.annotation.processing.Completion;
10.18 import javax.annotation.processing.Completions;
10.19 @@ -34,8 +39,14 @@
10.20 import javax.lang.model.element.Element;
10.21 import javax.lang.model.element.ElementKind;
10.22 import javax.lang.model.element.ExecutableElement;
10.23 +import javax.lang.model.element.Modifier;
10.24 +import javax.lang.model.element.PackageElement;
10.25 import javax.lang.model.element.TypeElement;
10.26 import javax.lang.model.element.VariableElement;
10.27 +import javax.lang.model.type.ArrayType;
10.28 +import javax.lang.model.type.ExecutableType;
10.29 +import javax.lang.model.type.TypeKind;
10.30 +import javax.lang.model.type.TypeMirror;
10.31 import javax.tools.Diagnostic;
10.32 import net.java.html.js.JavaScriptBody;
10.33 import net.java.html.js.JavaScriptResource;
10.34 @@ -47,6 +58,9 @@
10.35 */
10.36 @ServiceProvider(service = Processor.class)
10.37 public final class JavaScriptProcesor extends AbstractProcessor {
10.38 + private final Map<String,Map<String,ExecutableElement>> javacalls =
10.39 + new HashMap<String,Map<String,ExecutableElement>>();
10.40 +
10.41 @Override
10.42 public Set<String> getSupportedAnnotationTypes() {
10.43 Set<String> set = new HashSet<String>();
10.44 @@ -76,6 +90,14 @@
10.45 if (!jsb.javacall() && jsb.body().contains(".@")) {
10.46 msg.printMessage(Diagnostic.Kind.WARNING, "Usage of .@ usually requires javacall=true", e);
10.47 }
10.48 + if (jsb.javacall()) {
10.49 + JsCallback verify = new VerifyCallback(e);
10.50 + verify.parse(jsb.body());
10.51 + }
10.52 + }
10.53 + if (roundEnv.processingOver()) {
10.54 + generateCallbackClass(javacalls);
10.55 + javacalls.clear();
10.56 }
10.57 return true;
10.58 }
10.59 @@ -100,5 +122,178 @@
10.60 return null;
10.61 }
10.62
10.63 + private class VerifyCallback extends JsCallback {
10.64 + private final Element e;
10.65 + public VerifyCallback(Element e) {
10.66 + this.e = e;
10.67 + }
10.68 +
10.69 + @Override
10.70 + protected CharSequence callMethod(String ident, String fqn, String method, String params) {
10.71 + final TypeElement type = processingEnv.getElementUtils().getTypeElement(fqn);
10.72 + if (type == null) {
10.73 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
10.74 + "Callback to non-existing class " + fqn, e
10.75 + );
10.76 + return "";
10.77 + }
10.78 + ExecutableElement found = null;
10.79 + StringBuilder foundParams = new StringBuilder();
10.80 + for (Element m : type.getEnclosedElements()) {
10.81 + if (m.getKind() != ElementKind.METHOD) {
10.82 + continue;
10.83 + }
10.84 + if (m.getSimpleName().contentEquals(method)) {
10.85 + String paramTypes = findParamTypes((ExecutableElement)m);
10.86 + if (paramTypes.equals(params)) {
10.87 + found = (ExecutableElement) m;
10.88 + break;
10.89 + }
10.90 + foundParams.append(paramTypes).append("\n");
10.91 + }
10.92 + }
10.93 + if (found == null) {
10.94 + if (foundParams.length() == 0) {
10.95 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
10.96 + "Callback to class " + fqn + " with unknown method " + method, e
10.97 + );
10.98 + } else {
10.99 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
10.100 + "Callback to " + fqn + "." + method + " with wrong parameters: " +
10.101 + params + ". Only known parameters are " + foundParams, e
10.102 + );
10.103 + }
10.104 + } else {
10.105 + Map<String,ExecutableElement> mangledOnes = javacalls.get(findPkg(e));
10.106 + if (mangledOnes == null) {
10.107 + mangledOnes = new TreeMap<String, ExecutableElement>();
10.108 + javacalls.put(findPkg(e), mangledOnes);
10.109 + }
10.110 + String mangled = JsCallback.mangle(fqn, method, findParamTypes(found));
10.111 + mangledOnes.put(mangled, found);
10.112 + }
10.113 + return "";
10.114 + }
10.115 +
10.116 + private String findParamTypes(ExecutableElement method) {
10.117 + ExecutableType t = (ExecutableType) method.asType();
10.118 + StringBuilder sb = new StringBuilder();
10.119 + sb.append('(');
10.120 + for (TypeMirror tm : t.getParameterTypes()) {
10.121 + if (tm.getKind().isPrimitive()) {
10.122 + switch (tm.getKind()) {
10.123 + case INT: sb.append('I'); break;
10.124 + case BOOLEAN: sb.append('Z'); break;
10.125 + case BYTE: sb.append('B'); break;
10.126 + case CHAR: sb.append('C'); break;
10.127 + case SHORT: sb.append('S'); break;
10.128 + case DOUBLE: sb.append('D'); break;
10.129 + case FLOAT: sb.append('F'); break;
10.130 + case LONG: sb.append('J'); break;
10.131 + default:
10.132 + throw new IllegalStateException("Uknown " + tm.getKind());
10.133 + }
10.134 + } else {
10.135 + while (tm.getKind() == TypeKind.ARRAY) {
10.136 + sb.append('[');
10.137 + tm = ((ArrayType)tm).getComponentType();
10.138 + }
10.139 + sb.append('L');
10.140 + sb.append(tm.toString().replace('.', '/'));
10.141 + sb.append(';');
10.142 + }
10.143 + }
10.144 + sb.append(')');
10.145 + return sb.toString();
10.146 + }
10.147 + }
10.148 +
10.149 + private void generateCallbackClass(Map<String,Map<String, ExecutableElement>> process) {
10.150 + for (Map.Entry<String, Map<String, ExecutableElement>> pkgEn : process.entrySet()) {
10.151 + String pkgName = pkgEn.getKey();
10.152 + Map<String, ExecutableElement> map = pkgEn.getValue();
10.153 + StringBuilder source = new StringBuilder();
10.154 + source.append("package ").append(pkgName).append(";\n");
10.155 + source.append("public final class $JsCallbacks$ {\n");
10.156 + source.append(" static final $JsCallbacks$ VM = new $JsCallbacks$();\n");
10.157 + source.append(" private $JsCallbacks$() {}\n");
10.158 + for (Map.Entry<String, ExecutableElement> entry : map.entrySet()) {
10.159 + final String mangled = entry.getKey();
10.160 + final ExecutableElement m = entry.getValue();
10.161 + final boolean isStatic = m.getModifiers().contains(Modifier.STATIC);
10.162 +
10.163 + source.append("\n public java.lang.Object ")
10.164 + .append(mangled)
10.165 + .append("(");
10.166 +
10.167 + String sep = "";
10.168 + if (!isStatic) {
10.169 + source.append(((TypeElement)m.getEnclosingElement()).getQualifiedName());
10.170 + source.append(" self");
10.171 + sep = ", ";
10.172 + }
10.173 +
10.174 + int cnt = 0;
10.175 + for (VariableElement ve : m.getParameters()) {
10.176 + source.append(sep);
10.177 + source.append(ve.asType());
10.178 + source.append(" arg").append(++cnt);
10.179 + sep = ", ";
10.180 + }
10.181 + source.append(")");
10.182 + sep = "\n throws ";
10.183 + for (TypeMirror thrwn : m.getThrownTypes()) {
10.184 + source.append(sep).append(thrwn.toString());
10.185 + sep = ",";
10.186 + }
10.187 + source.append(" {\n");
10.188 + source.append(" ");
10.189 + if (m.getReturnType().getKind() != TypeKind.VOID) {
10.190 + source.append("return ");
10.191 + }
10.192 + if (isStatic) {
10.193 + source.append(((TypeElement)m.getEnclosingElement()).getQualifiedName());
10.194 + source.append('.');
10.195 + } else {
10.196 + source.append("self.");
10.197 + }
10.198 + source.append(m.getSimpleName());
10.199 + source.append("(");
10.200 + cnt = 0;
10.201 + sep = "";
10.202 + for (VariableElement ve : m.getParameters()) {
10.203 + source.append(sep);
10.204 + source.append("arg").append(++cnt);
10.205 + sep = ", ";
10.206 + }
10.207 + source.append(");\n");
10.208 + if (m.getReturnType().getKind() == TypeKind.VOID) {
10.209 + source.append(" return null;\n");
10.210 + }
10.211 + source.append(" }\n");
10.212 + }
10.213 + source.append("}\n");
10.214 + final String srcName = pkgName + ".$JsCallbacks$";
10.215 + try {
10.216 + Writer w = processingEnv.getFiler().createSourceFile(srcName,
10.217 + map.values().toArray(new Element[map.size()])
10.218 + ).openWriter();
10.219 + w.write(source.toString());
10.220 + w.close();
10.221 + return;
10.222 + } catch (IOException ex) {
10.223 + processingEnv.getMessager().printMessage(
10.224 + Diagnostic.Kind.ERROR, "Can't write " + srcName + ": " + ex.getMessage()
10.225 + );
10.226 + }
10.227 + }
10.228 + }
10.229 +
10.230 + private static String findPkg(Element e) {
10.231 + while (e.getKind() != ElementKind.PACKAGE) {
10.232 + e = e.getEnclosingElement();
10.233 + }
10.234 + return ((PackageElement)e).getQualifiedName().toString();
10.235 + }
10.236
10.237 }
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/JsCallback.java Thu Jul 18 15:39:56 2013 +0200
11.3 @@ -0,0 +1,126 @@
11.4 +/**
11.5 + * HTML via Java(tm) Language Bindings
11.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
11.7 + *
11.8 + * This program is free software: you can redistribute it and/or modify
11.9 + * it under the terms of the GNU General Public License as published by
11.10 + * the Free Software Foundation, version 2 of the License.
11.11 + *
11.12 + * This program is distributed in the hope that it will be useful,
11.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
11.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11.15 + * GNU General Public License for more details. apidesign.org
11.16 + * designates this particular file as subject to the
11.17 + * "Classpath" exception as provided by apidesign.org
11.18 + * in the License file that accompanied this code.
11.19 + *
11.20 + * You should have received a copy of the GNU General Public License
11.21 + * along with this program. Look for COPYING file in the top folder.
11.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
11.23 + */
11.24 +package org.apidesign.html.boot.impl;
11.25 +
11.26 +
11.27 +/**
11.28 + *
11.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
11.30 + */
11.31 +abstract class JsCallback {
11.32 + final String parse(String body) {
11.33 + StringBuilder sb = new StringBuilder();
11.34 + int pos = 0;
11.35 + for (;;) {
11.36 + int next = body.indexOf(".@", pos);
11.37 + if (next == -1) {
11.38 + sb.append(body.substring(pos));
11.39 + body = sb.toString();
11.40 + break;
11.41 + }
11.42 + int ident = next;
11.43 + while (ident > 0) {
11.44 + if (!Character.isJavaIdentifierPart(body.charAt(--ident))) {
11.45 + ident++;
11.46 + break;
11.47 + }
11.48 + }
11.49 + String refId = body.substring(ident, next);
11.50 +
11.51 + sb.append(body.substring(pos, ident));
11.52 +
11.53 + int sigBeg = body.indexOf('(', next);
11.54 + int sigEnd = body.indexOf(')', sigBeg);
11.55 + int colon4 = body.indexOf("::", next);
11.56 + if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
11.57 + throw new IllegalStateException("Malformed body " + body);
11.58 + }
11.59 + String fqn = body.substring(next + 2, colon4);
11.60 + String method = body.substring(colon4 + 2, sigBeg);
11.61 + String params = body.substring(sigBeg, sigEnd + 1);
11.62 +
11.63 + int paramBeg = body.indexOf('(', sigEnd + 1);
11.64 + if (paramBeg == -1) {
11.65 + throw new IllegalStateException("Malformed body " + body);
11.66 + }
11.67 +
11.68 + sb.append(callMethod(refId, fqn, method, params));
11.69 + if (body.charAt(paramBeg + 1) != (')')) {
11.70 + sb.append(",");
11.71 + }
11.72 + pos = paramBeg + 1;
11.73 + }
11.74 + pos = 0;
11.75 + sb = null;
11.76 + for (;;) {
11.77 + int next = body.indexOf("@", pos);
11.78 + if (next == -1) {
11.79 + if (sb == null) {
11.80 + return body;
11.81 + }
11.82 + sb.append(body.substring(pos));
11.83 + return sb.toString();
11.84 + }
11.85 + if (sb == null) {
11.86 + sb = new StringBuilder();
11.87 + }
11.88 +
11.89 + sb.append(body.substring(pos, next));
11.90 +
11.91 + int sigBeg = body.indexOf('(', next);
11.92 + int sigEnd = body.indexOf(')', sigBeg);
11.93 + int colon4 = body.indexOf("::", next);
11.94 + if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
11.95 + throw new IllegalStateException("Malformed body " + body);
11.96 + }
11.97 + String fqn = body.substring(next + 1, colon4);
11.98 + String method = body.substring(colon4 + 2, sigBeg);
11.99 + String params = body.substring(sigBeg, sigEnd + 1);
11.100 +
11.101 + int paramBeg = body.indexOf('(', sigEnd + 1);
11.102 +
11.103 + sb.append(callMethod(null, fqn, method, params));
11.104 + pos = paramBeg + 1;
11.105 + }
11.106 + }
11.107 +
11.108 + protected abstract CharSequence callMethod(
11.109 + String ident, String fqn, String method, String params
11.110 + );
11.111 +
11.112 + static String mangle(String fqn, String method, String params) {
11.113 + if (params.startsWith("(")) {
11.114 + params = params.substring(1);
11.115 + }
11.116 + if (params.endsWith(")")) {
11.117 + params = params.substring(0, params.length() - 1);
11.118 + }
11.119 + return
11.120 + replace(fqn) + "$" + replace(method) + "$" + replace(params);
11.121 + }
11.122 +
11.123 + private static String replace(String orig) {
11.124 + return orig.replace("_", "_1").
11.125 + replace(";", "_2").
11.126 + replace("[", "_3").
11.127 + replace('.', '_').replace('/', '_');
11.128 + }
11.129 +}
12.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java Sat Jun 29 07:01:58 2013 +0200
12.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java Thu Jul 18 15:39:56 2013 +0200
12.3 @@ -218,11 +218,14 @@
12.4 super.visitLdcInsn(body);
12.5 super.visitIntInsn(Opcodes.SIPUSH, args.size());
12.6 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
12.7 + boolean needsVM = false;
12.8 for (int i = 0; i < args.size(); i++) {
12.9 - String name = args.get(i);
12.10 + assert !needsVM;
12.11 + String argName = args.get(i);
12.12 + needsVM = "vm".equals(argName);
12.13 super.visitInsn(Opcodes.DUP);
12.14 super.visitIntInsn(Opcodes.BIPUSH, i);
12.15 - super.visitLdcInsn(name);
12.16 + super.visitLdcInsn(argName);
12.17 super.visitInsn(Opcodes.AASTORE);
12.18 }
12.19 super.visitMethodInsn(Opcodes.INVOKESTATIC,
12.20 @@ -249,6 +252,7 @@
12.21 private boolean nowReturn;
12.22 private Type returnType;
12.23 private int index;
12.24 + private int loadIndex = offset;
12.25
12.26 public SV() {
12.27 super(Opcodes.ASM4);
12.28 @@ -262,15 +266,15 @@
12.29 return;
12.30 }
12.31 FindInMethod.super.visitInsn(Opcodes.DUP);
12.32 - FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
12.33 - FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), index + offset);
12.34 + FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
12.35 + FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
12.36 String factory;
12.37 switch (descriptor) {
12.38 case 'I': factory = "java/lang/Integer"; break;
12.39 - case 'J': factory = "java/lang/Long"; break;
12.40 + case 'J': factory = "java/lang/Long"; loadIndex++; break;
12.41 case 'S': factory = "java/lang/Short"; break;
12.42 case 'F': factory = "java/lang/Float"; break;
12.43 - case 'D': factory = "java/lang/Double"; break;
12.44 + case 'D': factory = "java/lang/Double"; loadIndex++; break;
12.45 case 'Z': factory = "java/lang/Boolean"; break;
12.46 case 'C': factory = "java/lang/Character"; break;
12.47 case 'B': factory = "java/lang/Byte"; break;
12.48 @@ -280,7 +284,6 @@
12.49 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
12.50 );
12.51 FindInMethod.super.visitInsn(Opcodes.AASTORE);
12.52 - index++;
12.53 }
12.54
12.55 @Override
12.56 @@ -309,10 +312,9 @@
12.57
12.58 private void loadObject() {
12.59 FindInMethod.super.visitInsn(Opcodes.DUP);
12.60 - FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index);
12.61 - FindInMethod.super.visitVarInsn(Opcodes.ALOAD, index + offset);
12.62 + FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
12.63 + FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
12.64 FindInMethod.super.visitInsn(Opcodes.AASTORE);
12.65 - index++;
12.66 }
12.67
12.68 }
12.69 @@ -320,6 +322,15 @@
12.70 SignatureReader sr = new SignatureReader(desc);
12.71 sr.accept(sv);
12.72
12.73 + if (needsVM) {
12.74 + FindInMethod.super.visitInsn(Opcodes.DUP);
12.75 + FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
12.76 + int lastSlash = FindInClass.this.name.lastIndexOf('/');
12.77 + String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
12.78 + FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
12.79 + FindInMethod.super.visitInsn(Opcodes.AASTORE);
12.80 + }
12.81 +
12.82 super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
12.83 "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
12.84 );
12.85 @@ -401,10 +412,11 @@
12.86 @Override
12.87 public void visitEnd() {
12.88 if (body != null) {
12.89 - generateJSBody(args, javacall ?
12.90 - FnUtils.callback(body, JsClassLoader.this) :
12.91 - body
12.92 - );
12.93 + if (javacall) {
12.94 + body = FnUtils.callback(body);
12.95 + args.add("vm");
12.96 + }
12.97 + generateJSBody(args, body);
12.98 }
12.99 }
12.100 }
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/Compile.java Thu Jul 18 15:39:56 2013 +0200
13.3 @@ -0,0 +1,269 @@
13.4 +/**
13.5 + * HTML via Java(tm) Language Bindings
13.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
13.7 + *
13.8 + * This program is free software: you can redistribute it and/or modify
13.9 + * it under the terms of the GNU General Public License as published by
13.10 + * the Free Software Foundation, version 2 of the License.
13.11 + *
13.12 + * This program is distributed in the hope that it will be useful,
13.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
13.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13.15 + * GNU General Public License for more details. apidesign.org
13.16 + * designates this particular file as subject to the
13.17 + * "Classpath" exception as provided by apidesign.org
13.18 + * in the License file that accompanied this code.
13.19 + *
13.20 + * You should have received a copy of the GNU General Public License
13.21 + * along with this program. Look for COPYING file in the top folder.
13.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
13.23 + */
13.24 +package org.apidesign.html.boot.impl;
13.25 +
13.26 +import java.io.ByteArrayInputStream;
13.27 +import java.io.ByteArrayOutputStream;
13.28 +import java.io.IOException;
13.29 +import java.io.InputStream;
13.30 +import java.io.OutputStream;
13.31 +import java.net.URI;
13.32 +import java.net.URISyntaxException;
13.33 +import java.util.ArrayList;
13.34 +import java.util.Arrays;
13.35 +import java.util.HashMap;
13.36 +import java.util.List;
13.37 +import java.util.Locale;
13.38 +import java.util.Map;
13.39 +import java.util.regex.Matcher;
13.40 +import java.util.regex.Pattern;
13.41 +import javax.tools.Diagnostic;
13.42 +import javax.tools.DiagnosticListener;
13.43 +import javax.tools.FileObject;
13.44 +import javax.tools.ForwardingJavaFileManager;
13.45 +import javax.tools.JavaFileManager;
13.46 +import javax.tools.JavaFileObject;
13.47 +import javax.tools.JavaFileObject.Kind;
13.48 +import javax.tools.SimpleJavaFileObject;
13.49 +import javax.tools.StandardJavaFileManager;
13.50 +import javax.tools.StandardLocation;
13.51 +import javax.tools.ToolProvider;
13.52 +import static org.testng.Assert.assertTrue;
13.53 +import static org.testng.Assert.assertFalse;
13.54 +import static org.testng.Assert.fail;
13.55 +
13.56 +/**
13.57 + *
13.58 + * @author Jaroslav Tulach <jtulach@netbeans.org>
13.59 + */
13.60 +final class Compile implements DiagnosticListener<JavaFileObject> {
13.61 + private final List<Diagnostic<? extends JavaFileObject>> errors =
13.62 + new ArrayList<Diagnostic<? extends JavaFileObject>>();
13.63 + private final Map<String, byte[]> classes;
13.64 + private final String pkg;
13.65 + private final String cls;
13.66 + private final String html;
13.67 + private final String sourceLevel;
13.68 +
13.69 + private Compile(String html, String code, String sl) throws IOException {
13.70 + this.pkg = findPkg(code);
13.71 + this.cls = findCls(code);
13.72 + this.html = html;
13.73 + this.sourceLevel = sl;
13.74 + classes = compile(html, code);
13.75 + }
13.76 +
13.77 + /** Performs compilation of given HTML page and associated Java code
13.78 + */
13.79 + public static Compile create(String html, String code) throws IOException {
13.80 + return create(html, code, "1.7");
13.81 + }
13.82 + static Compile create(String html, String code, String sourceLevel) throws IOException {
13.83 + return new Compile(html, code, sourceLevel);
13.84 + }
13.85 +
13.86 + /** Checks for given class among compiled resources */
13.87 + public byte[] get(String res) {
13.88 + return classes.get(res);
13.89 + }
13.90 +
13.91 + /** Obtains errors created during compilation.
13.92 + */
13.93 + public List<Diagnostic<? extends JavaFileObject>> getErrors() {
13.94 + List<Diagnostic<? extends JavaFileObject>> err;
13.95 + err = new ArrayList<Diagnostic<? extends JavaFileObject>>();
13.96 + for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
13.97 + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
13.98 + err.add(diagnostic);
13.99 + }
13.100 + }
13.101 + return err;
13.102 + }
13.103 +
13.104 + private Map<String, byte[]> compile(final String html, final String code) throws IOException {
13.105 + StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
13.106 +
13.107 + final Map<String, ByteArrayOutputStream> class2BAOS;
13.108 + class2BAOS = new HashMap<String, ByteArrayOutputStream>();
13.109 +
13.110 + JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
13.111 + @Override
13.112 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
13.113 + return code;
13.114 + }
13.115 + };
13.116 + final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
13.117 + @Override
13.118 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
13.119 + return html;
13.120 + }
13.121 +
13.122 + @Override
13.123 + public InputStream openInputStream() throws IOException {
13.124 + return new ByteArrayInputStream(html.getBytes());
13.125 + }
13.126 + };
13.127 +
13.128 + final URI scratch;
13.129 + try {
13.130 + scratch = new URI("mem://mem3");
13.131 + } catch (URISyntaxException ex) {
13.132 + throw new IOException(ex);
13.133 + }
13.134 +
13.135 + JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
13.136 + @Override
13.137 + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
13.138 + if (kind == Kind.CLASS) {
13.139 + final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
13.140 +
13.141 + class2BAOS.put(className.replace('.', '/') + ".class", buffer);
13.142 + return new SimpleJavaFileObject(sibling.toUri(), kind) {
13.143 + @Override
13.144 + public OutputStream openOutputStream() throws IOException {
13.145 + return buffer;
13.146 + }
13.147 + };
13.148 + }
13.149 +
13.150 + if (kind == Kind.SOURCE) {
13.151 + final String n = className.replace('.', '/') + ".java";
13.152 + final URI un;
13.153 + try {
13.154 + un = new URI("mem://" + n);
13.155 + } catch (URISyntaxException ex) {
13.156 + throw new IOException(ex);
13.157 + }
13.158 + return new VirtFO(un/*sibling.toUri()*/, kind, n);
13.159 + }
13.160 +
13.161 + throw new IllegalStateException();
13.162 + }
13.163 +
13.164 + @Override
13.165 + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
13.166 + if (location == StandardLocation.SOURCE_PATH) {
13.167 + if (packageName.equals(pkg)) {
13.168 + return htmlFile;
13.169 + }
13.170 + }
13.171 +
13.172 + return null;
13.173 + }
13.174 +
13.175 + @Override
13.176 + public boolean isSameFile(FileObject a, FileObject b) {
13.177 + if (a instanceof VirtFO && b instanceof VirtFO) {
13.178 + return ((VirtFO)a).getName().equals(((VirtFO)b).getName());
13.179 + }
13.180 +
13.181 + return super.isSameFile(a, b);
13.182 + }
13.183 +
13.184 + class VirtFO extends SimpleJavaFileObject {
13.185 +
13.186 + private final String n;
13.187 +
13.188 + public VirtFO(URI uri, Kind kind, String n) {
13.189 + super(uri, kind);
13.190 + this.n = n;
13.191 + }
13.192 + private final ByteArrayOutputStream data = new ByteArrayOutputStream();
13.193 +
13.194 + @Override
13.195 + public OutputStream openOutputStream() throws IOException {
13.196 + return data;
13.197 + }
13.198 +
13.199 + @Override
13.200 + public String getName() {
13.201 + return n;
13.202 + }
13.203 +
13.204 + @Override
13.205 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
13.206 + data.close();
13.207 + return new String(data.toByteArray());
13.208 + }
13.209 + }
13.210 + };
13.211 +
13.212 + ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", sourceLevel, "-target", "1.7"), null, Arrays.asList(file)).call();
13.213 +
13.214 + Map<String, byte[]> result = new HashMap<String, byte[]>();
13.215 +
13.216 + for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
13.217 + result.put(e.getKey(), e.getValue().toByteArray());
13.218 + }
13.219 +
13.220 + return result;
13.221 + }
13.222 +
13.223 +
13.224 + @Override
13.225 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
13.226 + errors.add(diagnostic);
13.227 + }
13.228 + private static String findPkg(String java) throws IOException {
13.229 + Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
13.230 + Matcher m = p.matcher(java);
13.231 + if (!m.find()) {
13.232 + throw new IOException("Can't find package declaration in the java file");
13.233 + }
13.234 + String pkg = m.group(1);
13.235 + return pkg;
13.236 + }
13.237 + private static String findCls(String java) throws IOException {
13.238 + Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
13.239 + Matcher m = p.matcher(java);
13.240 + if (!m.find()) {
13.241 + throw new IOException("Can't find package declaration in the java file");
13.242 + }
13.243 + String cls = m.group(1);
13.244 + return cls;
13.245 + }
13.246 +
13.247 + String getHtml() {
13.248 + String fqn = "'" + pkg + '.' + cls + "'";
13.249 + return html.replace("'${fqn}'", fqn);
13.250 + }
13.251 + void assertErrors() {
13.252 + assertFalse(getErrors().isEmpty(), "There are supposed to be some errors");
13.253 + }
13.254 +
13.255 + void assertError(String expMsg) {
13.256 + StringBuilder sb = new StringBuilder();
13.257 + sb.append("Can't find ").append(expMsg).append(" among:");
13.258 + for (Diagnostic<? extends JavaFileObject> e : errors) {
13.259 + String msg = e.getMessage(Locale.US);
13.260 + if (msg.contains(expMsg)) {
13.261 + return;
13.262 + }
13.263 + sb.append("\n");
13.264 + sb.append(msg);
13.265 + }
13.266 + fail(sb.toString());
13.267 + }
13.268 +
13.269 + void assertNoErrors() {
13.270 + assertTrue(getErrors().isEmpty(), "No errors expected: " + getErrors());
13.271 + }
13.272 +}
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JavaScriptProcesorTest.java Thu Jul 18 15:39:56 2013 +0200
14.3 @@ -0,0 +1,107 @@
14.4 +/**
14.5 + * HTML via Java(tm) Language Bindings
14.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
14.7 + *
14.8 + * This program is free software: you can redistribute it and/or modify
14.9 + * it under the terms of the GNU General Public License as published by
14.10 + * the Free Software Foundation, version 2 of the License.
14.11 + *
14.12 + * This program is distributed in the hope that it will be useful,
14.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
14.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14.15 + * GNU General Public License for more details. apidesign.org
14.16 + * designates this particular file as subject to the
14.17 + * "Classpath" exception as provided by apidesign.org
14.18 + * in the License file that accompanied this code.
14.19 + *
14.20 + * You should have received a copy of the GNU General Public License
14.21 + * along with this program. Look for COPYING file in the top folder.
14.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
14.23 + */
14.24 +package org.apidesign.html.boot.impl;
14.25 +
14.26 +import java.io.IOException;
14.27 +import java.lang.reflect.Field;
14.28 +import java.lang.reflect.Method;
14.29 +import static org.testng.Assert.assertEquals;
14.30 +import static org.testng.Assert.assertTrue;
14.31 +import org.testng.annotations.Test;
14.32 +
14.33 +/**
14.34 + *
14.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
14.36 + */
14.37 +public class JavaScriptProcesorTest {
14.38 +
14.39 + @Test public void detectCallbackToNonExistingClass() throws IOException {
14.40 + String code = "package x.y.z;\n"
14.41 + + "import net.java.html.js.JavaScriptBody;\n"
14.42 + + "class X {\n"
14.43 + + " @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
14.44 + + " \"r.@java.lang.Runable::run()();\"\n" // typo
14.45 + + " )\n"
14.46 + + " private static native void callback(Runnable r);\n"
14.47 + + "}\n";
14.48 +
14.49 + Compile c = Compile.create("", code);
14.50 + c.assertErrors();
14.51 + c.assertError("java.lang.Runable"); // typo
14.52 + }
14.53 +
14.54 + @Test public void detectCallbackToNonExistingMethod() throws IOException {
14.55 + String code = "package x.y.z;\n"
14.56 + + "import net.java.html.js.JavaScriptBody;\n"
14.57 + + "class X {\n"
14.58 + + " @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
14.59 + + " \"r.@java.lang.Runnable::cancel()();\"\n"
14.60 + + " )\n"
14.61 + + " private static native void callback(Runnable r);\n"
14.62 + + "}\n";
14.63 +
14.64 + Compile c = Compile.create("", code);
14.65 + c.assertErrors();
14.66 + c.assertError("method cancel");
14.67 + }
14.68 +
14.69 + @Test public void detectCallbackToNonExistingParams() throws IOException {
14.70 + String code = "package x.y.z;\n"
14.71 + + "import net.java.html.js.JavaScriptBody;\n"
14.72 + + "class X {\n"
14.73 + + " @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
14.74 + + " \"r.@java.lang.Runnable::run(I)(10);\"\n"
14.75 + + " )\n"
14.76 + + " private static native void callback(Runnable r);\n"
14.77 + + "}\n";
14.78 +
14.79 + Compile c = Compile.create("", code);
14.80 + c.assertErrors();
14.81 + c.assertError("wrong parameters: (I)");
14.82 + }
14.83 +
14.84 + @Test public void objectTypeParamsAreOK() throws IOException {
14.85 + String code = "package x.y.z;\n"
14.86 + + "import net.java.html.js.JavaScriptBody;\n"
14.87 + + "class X {\n"
14.88 + + " @JavaScriptBody(args={\"r\"}, javacall=true, body =\n"
14.89 + + " \"r.@java.lang.Object::equals(Ljava/lang/Object;)(null);\"\n"
14.90 + + " )\n"
14.91 + + " private static native void testEqual(Object r);\n"
14.92 + + "}\n";
14.93 +
14.94 + Compile c = Compile.create("", code);
14.95 + c.assertNoErrors();
14.96 + }
14.97 +
14.98 + @Test public void generatesCallbacksThatReturnObject() throws Exception {
14.99 + Class<?> callbacksForTestPkg = Class.forName("org.apidesign.html.boot.impl.$JsCallbacks$");
14.100 + Method m = callbacksForTestPkg.getDeclaredMethod("java_lang_Runnable$run$", Runnable.class);
14.101 + assertEquals(m.getReturnType(), Object.class, "All methods always return object");
14.102 + }
14.103 +
14.104 + @Test public void hasInstanceField() throws Exception {
14.105 + Class<?> callbacksForTestPkg = Class.forName("org.apidesign.html.boot.impl.$JsCallbacks$");
14.106 + Field f = callbacksForTestPkg.getDeclaredField("VM");
14.107 + f.setAccessible(true);
14.108 + assertTrue(callbacksForTestPkg.isInstance(f.get(null)), "Singleton field VM");
14.109 + }
14.110 +}
15.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderBase.java Sat Jun 29 07:01:58 2013 +0200
15.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderBase.java Thu Jul 18 15:39:56 2013 +0200
15.3 @@ -136,4 +136,44 @@
15.4 throw ex.getTargetException();
15.5 }
15.6 }
15.7 +
15.8 + @Test public void callJavaScriptMethodOnOwnClass() throws Throwable {
15.9 + try {
15.10 + Object thiz = methodClass.newInstance();
15.11 + Method st = methodClass.getMethod("returnYourSelf", methodClass);
15.12 + assertEquals(st.invoke(null, thiz), thiz, "Returns this");
15.13 + } catch (InvocationTargetException ex) {
15.14 + throw ex.getTargetException();
15.15 + }
15.16 + }
15.17 +
15.18 + @Test public void callStaticJavaMethod() throws Throwable {
15.19 + Method st = methodClass.getMethod("staticCallback", int.class, int.class);
15.20 + assertEquals(st.invoke(null, 6, 7), 42, "Meaning of JavaScript?");
15.21 + }
15.22 +
15.23 + @Test public void callStaticStringParamMethod() throws Throwable {
15.24 + Method st = methodClass.getMethod("parseInt", String.class);
15.25 + assertEquals(st.invoke(null, "42"), 42, "Meaning of JavaScript?");
15.26 + }
15.27 +
15.28 + @Test public void firstLong() throws Throwable {
15.29 + Method st = methodClass.getMethod("chooseLong", boolean.class, boolean.class, long.class, long.class);
15.30 + assertEquals(st.invoke(null, true, false, 10, 20), 10L, "Take first value");
15.31 + }
15.32 +
15.33 + @Test public void secondLong() throws Throwable {
15.34 + Method st = methodClass.getMethod("chooseLong", boolean.class, boolean.class, long.class, long.class);
15.35 + assertEquals(st.invoke(null, false, true, 10, 20), 20L, "Take 2nd value");
15.36 + }
15.37 +
15.38 + @Test public void bothLong() throws Throwable {
15.39 + Method st = methodClass.getMethod("chooseLong", boolean.class, boolean.class, long.class, long.class);
15.40 + assertEquals(st.invoke(null, true, true, 10, 20), 30L, "Take both values");
15.41 + }
15.42 +
15.43 + @Test public void recordError() throws Throwable {
15.44 + Method st = methodClass.getMethod("recordError", Object.class);
15.45 + assertEquals(st.invoke(methodClass.newInstance(), "Hello"), "Hello", "The same parameter returned");
15.46 + }
15.47 }
15.48 \ No newline at end of file
16.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsMethods.java Sat Jun 29 07:01:58 2013 +0200
16.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsMethods.java Thu Jul 18 15:39:56 2013 +0200
16.3 @@ -30,6 +30,8 @@
16.4 */
16.5 @JavaScriptResource("jsmethods.js")
16.6 public class JsMethods {
16.7 + private Object value;
16.8 +
16.9 @JavaScriptBody(args = {}, body = "return 42;")
16.10 public static Object fortyTwo() {
16.11 return -42;
16.12 @@ -54,7 +56,7 @@
16.13 return false;
16.14 }
16.15
16.16 - @JavaScriptBody(args = { "r" }, javacall=true, body = "r.@java.lang.Runnable::run()()")
16.17 + @JavaScriptBody(args = { "r" }, javacall=true, body = "r.@java.lang.Runnable::run()();")
16.18 public static native void callback(Runnable r);
16.19
16.20 @JavaScriptBody(args = { "at", "arr" }, javacall = true, body =
16.21 @@ -72,4 +74,34 @@
16.22
16.23 @JavaScriptBody(args = { "x", "y" }, body = "return mul(x, y);")
16.24 public static native int useExternalMul(int x, int y);
16.25 +
16.26 + @JavaScriptBody(args = { "m" }, javacall = true, body = "return m.@org.apidesign.html.boot.impl.JsMethods::getThis()();")
16.27 + public static native JsMethods returnYourSelf(JsMethods m);
16.28 +
16.29 + @JavaScriptBody(args = { "x", "y" }, javacall = true, body = "return @org.apidesign.html.boot.impl.JsMethods::useExternalMul(II)(x, y);")
16.30 + public static native int staticCallback(int x, int y);
16.31 +
16.32 + @JavaScriptBody(args = { "v" }, javacall = true, body = "return @java.lang.Integer::parseInt(Ljava/lang/String;)(v);")
16.33 + public static native int parseInt(String v);
16.34 +
16.35 + @JavaScriptBody(args = { "useA", "useB", "a", "b" }, body = "var l = 0;"
16.36 + + "if (useA) l += a;\n"
16.37 + + "if (useB) l += b;\n"
16.38 + + "return l;\n"
16.39 + )
16.40 + public static native long chooseLong(boolean useA, boolean useB, long a, long b);
16.41 +
16.42 + protected void onError(Object o) throws Exception {
16.43 + value = o;
16.44 + }
16.45 +
16.46 + Object getError() {
16.47 + return value;
16.48 + }
16.49 +
16.50 + @JavaScriptBody(args = { "err" }, javacall = true, body =
16.51 + "this.@org.apidesign.html.boot.impl.JsMethods::onError(Ljava/lang/Object;)(err);"
16.52 + + "return this.@org.apidesign.html.boot.impl.JsMethods::getError()();"
16.53 + )
16.54 + public native Object recordError(Object err);
16.55 }
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
17.2 +++ b/geo/pom.xml Thu Jul 18 15:39:56 2013 +0200
17.3 @@ -0,0 +1,47 @@
17.4 +<?xml version="1.0"?>
17.5 +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
17.6 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
17.7 + <modelVersion>4.0.0</modelVersion>
17.8 + <parent>
17.9 + <groupId>org.apidesign</groupId>
17.10 + <artifactId>html</artifactId>
17.11 + <version>0.4-SNAPSHOT</version>
17.12 + </parent>
17.13 + <groupId>org.apidesign.html</groupId>
17.14 + <artifactId>net.java.html.geo</artifactId>
17.15 + <version>0.4-SNAPSHOT</version>
17.16 + <name>Geolocation API</name>
17.17 + <url>http://maven.apache.org</url>
17.18 + <properties>
17.19 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17.20 + </properties>
17.21 + <build>
17.22 + <plugins>
17.23 + <plugin>
17.24 + <groupId>org.apache.maven.plugins</groupId>
17.25 + <artifactId>maven-javadoc-plugin</artifactId>
17.26 + <configuration>
17.27 + <subpackages>net.java.html.geo</subpackages>
17.28 + <skip>false</skip>
17.29 + </configuration>
17.30 + </plugin>
17.31 + </plugins>
17.32 + </build>
17.33 + <dependencies>
17.34 + <dependency>
17.35 + <groupId>org.testng</groupId>
17.36 + <artifactId>testng</artifactId>
17.37 + </dependency>
17.38 + <dependency>
17.39 + <groupId>org.netbeans.api</groupId>
17.40 + <artifactId>org-openide-util-lookup</artifactId>
17.41 + <type>jar</type>
17.42 + </dependency>
17.43 + <dependency>
17.44 + <groupId>org.apidesign.html</groupId>
17.45 + <artifactId>net.java.html.boot</artifactId>
17.46 + <version>0.4-SNAPSHOT</version>
17.47 + <type>jar</type>
17.48 + </dependency>
17.49 + </dependencies>
17.50 +</project>
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
18.2 +++ b/geo/src/main/java/net/java/html/geo/OnLocation.java Thu Jul 18 15:39:56 2013 +0200
18.3 @@ -0,0 +1,73 @@
18.4 +/**
18.5 + * HTML via Java(tm) Language Bindings
18.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
18.7 + *
18.8 + * This program is free software: you can redistribute it and/or modify
18.9 + * it under the terms of the GNU General Public License as published by
18.10 + * the Free Software Foundation, version 2 of the License.
18.11 + *
18.12 + * This program is distributed in the hope that it will be useful,
18.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
18.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18.15 + * GNU General Public License for more details. apidesign.org
18.16 + * designates this particular file as subject to the
18.17 + * "Classpath" exception as provided by apidesign.org
18.18 + * in the License file that accompanied this code.
18.19 + *
18.20 + * You should have received a copy of the GNU General Public License
18.21 + * along with this program. Look for COPYING file in the top folder.
18.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
18.23 + */
18.24 +package net.java.html.geo;
18.25 +
18.26 +import java.lang.annotation.ElementType;
18.27 +import java.lang.annotation.Retention;
18.28 +import java.lang.annotation.RetentionPolicy;
18.29 +import java.lang.annotation.Target;
18.30 +
18.31 +/** Generates a handle which can configure, {@link Position.Handle#start() start}
18.32 + * and {@link Position.Handle#stop() stop} requests for obtaining current
18.33 + * location of the application/device/user. Put the {@link OnLocation} annotation
18.34 + * on top of a (non-private) method in your class which takes one argument
18.35 + * {@link Position}. Based on name of your method (unless a class name is
18.36 + * directly specified via {@link #className()} attribute) a handle class is
18.37 + * generated. For example if your method name is <code>callMeWhenYouKnowWhereYouAre</code>
18.38 + * a package private class <code>CallMeWhenYouKnowWhereYouAreHandle</code> will
18.39 + * be generated in the same package. One can use its <code>createQuery</code>
18.40 + * and <code>createWatch</code> static method to get one time/repeated time
18.41 + * instance of a {@link Position.Handle handle}. After configuring the
18.42 + * {@link Position.Handle handle} via its setter methods, one can
18.43 + * {@link Position.Handle#start() start} the location request.
18.44 + * <p>
18.45 + * In case something goes wrong a method in the same class named {@link #onError()}
18.46 + * can be specified (should take one {@link Exception} parameter).
18.47 + * <p>
18.48 + * The overall behavior of the system mimics <a href="http://www.w3.org/TR/2012/PRÂgeolocationÂAPIÂ20120510/">
18.49 + * W3C's Geolocation API</a>.
18.50 + *
18.51 + * @author Jaroslav Tulach <jtulach@netbeans.org>
18.52 + */
18.53 +@Retention(RetentionPolicy.SOURCE)
18.54 +@Target(ElementType.METHOD)
18.55 +public @interface OnLocation {
18.56 + /** Name of the {@link Position.Handle handle} class to generate. By
18.57 + * default the name is derived from the name of annotated method by
18.58 + * capitalizing the first character and appending <code>Handle</code>.
18.59 + * <p>
18.60 + * The generated class contains two static methods: <code>createQuery</code>
18.61 + * and <code>createWatch</code> which take no parameters if this method
18.62 + * is static or one parameter (of this class) if this method is instance
18.63 + * one. Both static methods return {@link Position.Handle}.
18.64 + *
18.65 + * @return string suitable for a new class in the same package
18.66 + */
18.67 + public String className() default "";
18.68 +
18.69 + /** Name of a method in this class which should be called in case of
18.70 + * an error. The method has to be non-private and take {@link Exception}
18.71 + * parameter. By default the exception printed to console.
18.72 + *
18.73 + * @return name of method in this class
18.74 + */
18.75 + public String onError() default "";
18.76 +}
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
19.2 +++ b/geo/src/main/java/net/java/html/geo/Position.java Thu Jul 18 15:39:56 2013 +0200
19.3 @@ -0,0 +1,248 @@
19.4 +/**
19.5 + * HTML via Java(tm) Language Bindings
19.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
19.7 + *
19.8 + * This program is free software: you can redistribute it and/or modify
19.9 + * it under the terms of the GNU General Public License as published by
19.10 + * the Free Software Foundation, version 2 of the License.
19.11 + *
19.12 + * This program is distributed in the hope that it will be useful,
19.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
19.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19.15 + * GNU General Public License for more details. apidesign.org
19.16 + * designates this particular file as subject to the
19.17 + * "Classpath" exception as provided by apidesign.org
19.18 + * in the License file that accompanied this code.
19.19 + *
19.20 + * You should have received a copy of the GNU General Public License
19.21 + * along with this program. Look for COPYING file in the top folder.
19.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
19.23 + */
19.24 +package net.java.html.geo;
19.25 +
19.26 +import java.util.logging.Level;
19.27 +import java.util.logging.Logger;
19.28 +import org.apidesign.html.geo.impl.JsG;
19.29 +
19.30 +/** Class that represents a geolocation position provided as a callback
19.31 + * to {@link Handle#onLocation(net.java.html.geo.Position)} method. The
19.32 + * class getters mimic closely the structure of the position object as
19.33 + * specified by <a href="http://www.w3.org/TR/2012/PRÂgeolocationÂAPIÂ20120510/">
19.34 + * W3C's Geolocation API</a>.
19.35 + *
19.36 + * @author Jaroslav Tulach <jtulach@netbeans.org>
19.37 + */
19.38 +public final class Position {
19.39 + static final Logger LOG = Logger.getLogger(Position.class.getName());
19.40 + private final long timestamp;
19.41 + private final Coordinates coords;
19.42 +
19.43 + Position(Object position) {
19.44 + Object obj = JsG.get(position, "timestamp");
19.45 + timestamp = obj instanceof Number ? ((Number)obj).longValue() : 0L;
19.46 + coords = new Coordinates(JsG.get(position, "coords"));
19.47 + }
19.48 +
19.49 + /** The actual location of the position.
19.50 + * @return non-null coordinates
19.51 + */
19.52 + public Coordinates getCoords() {
19.53 + return coords;
19.54 + }
19.55 +
19.56 + /** The time when the position has been recorded.
19.57 + * @return time in milliseconds since era (e.g. Jan 1, 1970).
19.58 + */
19.59 + public long getTimestamp() {
19.60 + return timestamp;
19.61 + }
19.62 +
19.63 + /** Actual location of a {@link Position}.
19.64 + * Mimics closely <a href="http://www.w3.org/TR/2012/PRÂgeolocationÂAPIÂ20120510/">
19.65 + * W3C's Geolocation API</a>.
19.66 + */
19.67 + public static final class Coordinates {
19.68 + private final Object data;
19.69 +
19.70 + Coordinates(Object data) {
19.71 + this.data = data;
19.72 + }
19.73 +
19.74 + public double getLatitude() {
19.75 + return ((Number)JsG.get(data, "latitude")).doubleValue(); // NOI18N
19.76 + }
19.77 +
19.78 + public double getLongitude() {
19.79 + return ((Number)JsG.get(data, "longitude")).doubleValue(); // NOI18N
19.80 + }
19.81 +
19.82 + public double getAccuracy() {
19.83 + return ((Number)JsG.get(data, "accuracy")).doubleValue(); // NOI18N
19.84 + }
19.85 +
19.86 + public Double getAltitude() {
19.87 + return (Double)JsG.get(data, "altitude"); // NOI18N
19.88 + }
19.89 +
19.90 + public Double getAltitudeAccuracy() {
19.91 + return (Double)JsG.get(data, "altitudeAccuracy"); // NOI18N
19.92 + }
19.93 +
19.94 + public Double getHeading() {
19.95 + return (Double)JsG.get(data, "heading"); // NOI18N
19.96 + }
19.97 +
19.98 + public Double getSpeed() {
19.99 + return (Double)JsG.get(data, "speed"); // NOI18N
19.100 + }
19.101 + } // end of Coordinates
19.102 +
19.103 + /** Rather than subclassing this class directly consider using {@link OnLocation}
19.104 + * annotation. Such annotation will generate a subclass for you automatically
19.105 + * with two static methods <code>createQuery</code> and <code>createWatch</code>
19.106 + * which can be used to obtain instance of this class.
19.107 + */
19.108 + public static abstract class Handle {
19.109 + private final boolean oneTime;
19.110 + private boolean enableHighAccuracy;
19.111 + private long timeout;
19.112 + private long maximumAge;
19.113 + volatile JsH handle;
19.114 +
19.115 + /** Creates new instance of this handle.
19.116 + *
19.117 + * @param oneTime <code>true</code> if the handle represents one time
19.118 + * <em>query</em>. <code>false</code> if it represents a <em>watch</em>
19.119 + */
19.120 + protected Handle(boolean oneTime) {
19.121 + super();
19.122 + this.oneTime = oneTime;
19.123 + }
19.124 +
19.125 + /** Callback from the implementation when a (new) position has been
19.126 + * received and identified
19.127 + * @param p the position
19.128 + * @throws Throwable if an exception is thrown, it will be logged by the system
19.129 + */
19.130 + protected abstract void onLocation(Position p) throws Throwable;
19.131 +
19.132 + /** Callback when an error occurs.
19.133 + * @param ex the exception describing what went wrong
19.134 + * @throws Throwable if an exception is thrown, it will be logged by the system
19.135 + */
19.136 + protected abstract void onError(Exception ex) throws Throwable;
19.137 +
19.138 + /** Check whether the location API is supported.
19.139 + * @return true, if one can call {@link #start}.
19.140 + */
19.141 + public final boolean isSupported() {
19.142 + return JsG.hasGeolocation();
19.143 + }
19.144 +
19.145 + /** Turns on high accuracy mode as specified by the
19.146 + * <a href="http://www.w3.org/TR/2012/PRÂgeolocationÂAPIÂ20120510/">
19.147 + * W3C's Geolocation API</a>. By default the mode is disabled.
19.148 + * @param enable <code>true</code> or <code>false</code>
19.149 + */
19.150 + public final void setHighAccuracy(boolean enable) {
19.151 + this.enableHighAccuracy = enable;
19.152 + }
19.153 +
19.154 + /** The amount of milliseconds to wait for a result.
19.155 + * By default infinity.
19.156 + * @param timeout time in milliseconds to wait for a result.
19.157 + */
19.158 + public final void setTimeout(long timeout) {
19.159 + this.timeout = timeout;
19.160 + }
19.161 +
19.162 + /** Sets maximum age of cached results which are acceptable to be
19.163 + * returned. By default maximum age is set to zero.
19.164 + * @param age time in milliseconds of acceptable cached results
19.165 + */
19.166 + public final void setMaximumAge(long age) {
19.167 + this.maximumAge = age;
19.168 + }
19.169 +
19.170 + /** Initializes the <em>query</em> or <em>watch</em> request(s) and
19.171 + * returns immediately. Has no effect if the query has already been
19.172 + * started. If a problem appears while starting the system,
19.173 + * it is immediately reported via the {@link #onError(java.lang.Exception)}
19.174 + * callback. For example, if the {@link #isSupported()} method
19.175 + * returns <code>false</code> an IllegalStateException is created
19.176 + * and sent to the {@link #onError(java.lang.Exception) callback} method.
19.177 + */
19.178 + public final void start() {
19.179 + if (handle != null) {
19.180 + return;
19.181 + }
19.182 + handle = new JsH();
19.183 + try {
19.184 + if (!isSupported()) {
19.185 + throw new IllegalStateException("geolocation API not supported");
19.186 + }
19.187 + handle.start();
19.188 + } catch (Exception ex) {
19.189 + try {
19.190 + onError(ex);
19.191 + } catch (Throwable thr) {
19.192 + LOG.log(Level.INFO, "Problems delivering onError report", thr);
19.193 + }
19.194 + }
19.195 + }
19.196 +
19.197 + /** Stops all pending requests. After this call no further callbacks
19.198 + * can be obtained. Does nothing if no query or watch was in progress.
19.199 + */
19.200 + public final void stop() {
19.201 + JsH h = handle;
19.202 + if (h == null) {
19.203 + return;
19.204 + }
19.205 + handle = null;
19.206 + h.stop();
19.207 + }
19.208 +
19.209 + private final class JsH extends JsG {
19.210 + long watch;
19.211 +
19.212 + @Override
19.213 + public void onLocation(Object position) {
19.214 + if (handle != this) {
19.215 + return;
19.216 + }
19.217 + if (oneTime) {
19.218 + stop();
19.219 + }
19.220 + try {
19.221 + Handle.this.onLocation(new Position(position));
19.222 + } catch (Throwable ex) {
19.223 + LOG.log(Level.SEVERE, null, ex);
19.224 + }
19.225 + }
19.226 +
19.227 + @Override
19.228 + public void onError(Object error) {
19.229 + if (handle != this) {
19.230 + return;
19.231 + }
19.232 + if (oneTime) {
19.233 + stop();
19.234 + }
19.235 + try {
19.236 + Handle.this.onError(new Exception());
19.237 + } catch (Throwable ex) {
19.238 + LOG.log(Level.SEVERE, null, ex);
19.239 + }
19.240 + }
19.241 +
19.242 + final void start() {
19.243 + watch = start(oneTime, enableHighAccuracy, timeout, maximumAge);
19.244 + }
19.245 +
19.246 + protected final void stop() {
19.247 + super.stop(watch);
19.248 + }
19.249 + }
19.250 + }
19.251 +}
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
20.2 +++ b/geo/src/main/java/net/java/html/geo/package.html Thu Jul 18 15:39:56 2013 +0200
20.3 @@ -0,0 +1,37 @@
20.4 +<!--
20.5 +
20.6 + HTML via Java(tm) Language Bindings
20.7 + Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
20.8 +
20.9 + This program is free software: you can redistribute it and/or modify
20.10 + it under the terms of the GNU General Public License as published by
20.11 + the Free Software Foundation, version 2 of the License.
20.12 +
20.13 + This program is distributed in the hope that it will be useful,
20.14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
20.15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20.16 + GNU General Public License for more details. apidesign.org
20.17 + designates this particular file as subject to the
20.18 + "Classpath" exception as provided by apidesign.org
20.19 + in the License file that accompanied this code.
20.20 +
20.21 + You should have received a copy of the GNU General Public License
20.22 + along with this program. Look for COPYING file in the top folder.
20.23 + If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
20.24 +
20.25 +-->
20.26 +<!DOCTYPE html>
20.27 +<html>
20.28 + <head>
20.29 + <title>Geolocation API for Java</title>
20.30 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
20.31 + <meta name="viewport" content="width=device-width">
20.32 + </head>
20.33 + <body>
20.34 + <div>
20.35 + HTML Geo API for Java provides <a href="OnLocation.html">annotation based way</a> of
20.36 + obtaining geolocation information from a browser or any other
20.37 + device capable of providing it.
20.38 + </div>
20.39 + </body>
20.40 +</html>
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
21.2 +++ b/geo/src/main/java/org/apidesign/html/geo/impl/GeoProcessor.java Thu Jul 18 15:39:56 2013 +0200
21.3 @@ -0,0 +1,199 @@
21.4 +/**
21.5 + * HTML via Java(tm) Language Bindings
21.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
21.7 + *
21.8 + * This program is free software: you can redistribute it and/or modify
21.9 + * it under the terms of the GNU General Public License as published by
21.10 + * the Free Software Foundation, version 2 of the License.
21.11 + *
21.12 + * This program is distributed in the hope that it will be useful,
21.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
21.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21.15 + * GNU General Public License for more details. apidesign.org
21.16 + * designates this particular file as subject to the
21.17 + * "Classpath" exception as provided by apidesign.org
21.18 + * in the License file that accompanied this code.
21.19 + *
21.20 + * You should have received a copy of the GNU General Public License
21.21 + * along with this program. Look for COPYING file in the top folder.
21.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
21.23 + */
21.24 +package org.apidesign.html.geo.impl;
21.25 +
21.26 +import java.io.IOException;
21.27 +import java.io.Writer;
21.28 +import java.util.Locale;
21.29 +import java.util.Set;
21.30 +import java.util.logging.Level;
21.31 +import java.util.logging.Logger;
21.32 +import javax.annotation.processing.AbstractProcessor;
21.33 +import javax.annotation.processing.Processor;
21.34 +import javax.annotation.processing.RoundEnvironment;
21.35 +import javax.annotation.processing.SupportedAnnotationTypes;
21.36 +import javax.annotation.processing.SupportedSourceVersion;
21.37 +import javax.lang.model.SourceVersion;
21.38 +import javax.lang.model.element.Element;
21.39 +import javax.lang.model.element.ElementKind;
21.40 +import javax.lang.model.element.ExecutableElement;
21.41 +import javax.lang.model.element.Modifier;
21.42 +import javax.lang.model.element.PackageElement;
21.43 +import javax.lang.model.element.TypeElement;
21.44 +import javax.lang.model.type.TypeMirror;
21.45 +import javax.tools.Diagnostic;
21.46 +import javax.tools.JavaFileObject;
21.47 +import net.java.html.geo.OnLocation;
21.48 +import net.java.html.geo.Position;
21.49 +import org.openide.util.lookup.ServiceProvider;
21.50 +
21.51 +/** Annotation processor to generate callbacks from {@link GeoHandle} class.
21.52 + *
21.53 + * @author Jaroslav Tulach <jtulach@netbeans.org>
21.54 + */
21.55 +@ServiceProvider(service=Processor.class)
21.56 +@SupportedSourceVersion(SourceVersion.RELEASE_6)
21.57 +@SupportedAnnotationTypes({
21.58 + "net.java.html.geo.OnLocation"
21.59 +})
21.60 +public final class GeoProcessor extends AbstractProcessor {
21.61 + private static final Logger LOG = Logger.getLogger(GeoProcessor.class.getName());
21.62 + @Override
21.63 + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
21.64 + boolean ok = true;
21.65 + for (Element e : roundEnv.getElementsAnnotatedWith(OnLocation.class)) {
21.66 + if (!processLocation(e)) {
21.67 + ok = false;
21.68 + }
21.69 + }
21.70 + return ok;
21.71 + }
21.72 +
21.73 + private void error(String msg, Element e) {
21.74 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
21.75 + }
21.76 +
21.77 + private boolean processLocation(Element e) {
21.78 + if (e.getKind() != ElementKind.METHOD) {
21.79 + return false;
21.80 + }
21.81 + ExecutableElement me = (ExecutableElement) e;
21.82 + OnLocation ol = e.getAnnotation(OnLocation.class);
21.83 + if (ol == null) {
21.84 + return true;
21.85 + }
21.86 + if (me.getModifiers().contains(Modifier.PRIVATE)) {
21.87 + error("Method annotated by @OnLocation cannot be private", e);
21.88 + return false;
21.89 + }
21.90 + TypeMirror positionClass = processingEnv.getElementUtils().getTypeElement(Position.class.getName()).asType();
21.91 + if (me.getParameters().size() != 1 || !me.getParameters().get(0).asType().equals(positionClass)) {
21.92 + error("Method annotated by @OnLocation needs to have one net.java.html.geo.Position argument!", e);
21.93 + return false;
21.94 + }
21.95 + String className = ol.className();
21.96 + if (className.isEmpty()) {
21.97 + String n = e.getSimpleName().toString();
21.98 + if (n.isEmpty()) {
21.99 + error("Empty method name", e);
21.100 + return false;
21.101 + }
21.102 + final String firstLetter = n.substring(0, 1).toUpperCase(Locale.ENGLISH);
21.103 + className = firstLetter + n.substring(1) + "Handle";
21.104 + }
21.105 + TypeElement te = (TypeElement)e.getEnclosingElement();
21.106 + PackageElement pe = (PackageElement) te.getEnclosingElement();
21.107 + final String pkg = pe.getQualifiedName().toString();
21.108 + final String fqn = pkg + "." + className;
21.109 + final boolean isStatic = me.getModifiers().contains(Modifier.STATIC);
21.110 + try {
21.111 + JavaFileObject fo = processingEnv.getFiler().createSourceFile(fqn, e);
21.112 + Writer w = fo.openWriter();
21.113 + w.append("package ").append(pkg).append(";\n");
21.114 + w.append("class ").append(className).append(" extends net.java.html.geo.Position.Handle {\n");
21.115 + w.append(" private ").append(te.getSimpleName()).append(" i;\n");
21.116 + w.append(" private ").append(className).append("(boolean oneTime");
21.117 + w.append(", ").append(te.getSimpleName()).append(" i");
21.118 + w.append(") {\n super(oneTime);\n");
21.119 + if (!isStatic) {
21.120 + w.append(" this.i = i;\n");
21.121 + }
21.122 + w.append("}\n");
21.123 + w.append(" static net.java.html.geo.Position.Handle createQuery(");
21.124 + String inst;
21.125 + if (!isStatic) {
21.126 + w.append(te.getSimpleName()).append(" instance");
21.127 + inst = "instance";
21.128 + } else {
21.129 + inst = "null";
21.130 + }
21.131 + w.append(") { return new ").append(className).append("(true, ").append(inst).append("); }\n");
21.132 + w.append(" static net.java.html.geo.Position.Handle createWatch(");
21.133 + if (!isStatic) {
21.134 + w.append(te.getSimpleName()).append(" instance");
21.135 + }
21.136 + w.append(") { return new ").append(className).append("(false, ").append(inst).append("); }\n");
21.137 + w.append(" @Override protected void onError(Exception t) throws Throwable {\n");
21.138 + if (ol.onError().isEmpty()) {
21.139 + w.append(" t.printStackTrace();");
21.140 + } else {
21.141 + if (!findOnError(me, te, ol.onError(), isStatic)) {
21.142 + return false;
21.143 + }
21.144 + if (isStatic) {
21.145 + w.append(" ").append(te.getSimpleName()).append(".");
21.146 + } else {
21.147 + w.append(" i.");
21.148 + }
21.149 + w.append(ol.onError()).append("(t);\n");
21.150 + }
21.151 + w.append(" }\n");
21.152 + w.append(" @Override protected void onLocation(net.java.html.geo.Position p) throws Throwable {\n");
21.153 + if (isStatic) {
21.154 + w.append(" ").append(te.getSimpleName()).append(".");
21.155 + } else {
21.156 + w.append(" i.");
21.157 + }
21.158 + w.append(me.getSimpleName()).append("(p);\n");
21.159 + w.append(" }\n");
21.160 + w.append("}\n");
21.161 + w.close();
21.162 + } catch (IOException ex) {
21.163 + Logger.getLogger(GeoProcessor.class.getName()).log(Level.SEVERE, null, ex);
21.164 + error("Can't write handler class: " + ex.getMessage(), e);
21.165 + return false;
21.166 + }
21.167 +
21.168 + return true;
21.169 + }
21.170 +
21.171 + private boolean findOnError(Element errElem, TypeElement te, String name, boolean onlyStatic) {
21.172 + String err = null;
21.173 + for (Element e : te.getEnclosedElements()) {
21.174 + if (e.getKind() != ElementKind.METHOD) {
21.175 + continue;
21.176 + }
21.177 + if (!e.getSimpleName().contentEquals(name)) {
21.178 + continue;
21.179 + }
21.180 + if (onlyStatic && !e.getModifiers().contains(Modifier.STATIC)) {
21.181 + errElem = e;
21.182 + err = "Would have to be static";
21.183 + continue;
21.184 + }
21.185 + ExecutableElement ee = (ExecutableElement) e;
21.186 + TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
21.187 + if (ee.getParameters().size() != 1 ||
21.188 + !processingEnv.getTypeUtils().isAssignable(excType, ee.getParameters().get(0).asType())
21.189 + ) {
21.190 + errElem = e;
21.191 + err = "Error method needs to take one Exception argument";
21.192 + continue;
21.193 + }
21.194 + return true;
21.195 + }
21.196 + if (err == null) {
21.197 + err = "Cannot find " + name + "(Exception) method in this class";
21.198 + }
21.199 + error(err, errElem);
21.200 + return false;
21.201 + }
21.202 +}
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
22.2 +++ b/geo/src/main/java/org/apidesign/html/geo/impl/JsG.java Thu Jul 18 15:39:56 2013 +0200
22.3 @@ -0,0 +1,85 @@
22.4 +/**
22.5 + * HTML via Java(tm) Language Bindings
22.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
22.7 + *
22.8 + * This program is free software: you can redistribute it and/or modify
22.9 + * it under the terms of the GNU General Public License as published by
22.10 + * the Free Software Foundation, version 2 of the License.
22.11 + *
22.12 + * This program is distributed in the hope that it will be useful,
22.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
22.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22.15 + * GNU General Public License for more details. apidesign.org
22.16 + * designates this particular file as subject to the
22.17 + * "Classpath" exception as provided by apidesign.org
22.18 + * in the License file that accompanied this code.
22.19 + *
22.20 + * You should have received a copy of the GNU General Public License
22.21 + * along with this program. Look for COPYING file in the top folder.
22.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
22.23 + */
22.24 +package org.apidesign.html.geo.impl;
22.25 +
22.26 +import net.java.html.js.JavaScriptBody;
22.27 +
22.28 +/** Implementation class to deal with browser's <code>navigator.geolocation</code>
22.29 + * object.
22.30 + *
22.31 + * @author Jaroslav Tulach <jtulach@netbeans.org>
22.32 + */
22.33 +public abstract class JsG {
22.34 + protected JsG() {
22.35 + if (!getClass().getName().equals("net.java.html.geo.Position$Handle$JsH")) {
22.36 + throw new IllegalStateException();
22.37 + }
22.38 + }
22.39 +
22.40 + public abstract void onLocation(Object position);
22.41 + public abstract void onError(Object error);
22.42 +
22.43 + @JavaScriptBody(args = {}, body = "return !!navigator.geolocation;")
22.44 + public static boolean hasGeolocation() {
22.45 + return false;
22.46 + }
22.47 +
22.48 + @JavaScriptBody(
22.49 + args = { "onlyOnce", "enableHighAccuracy", "timeout", "maximumAge" },
22.50 + javacall = true,
22.51 + body =
22.52 + "var self = this;\n" +
22.53 + "var ok = function (position) {\n" +
22.54 + " self.@org.apidesign.html.geo.impl.JsG::onLocation(Ljava/lang/Object;)(position);\n" +
22.55 + "};\n" +
22.56 + "var fail = function (error) {\n" +
22.57 + " self.@org.apidesign.html.geo.impl.JsG::onError(Ljava/lang/Object;)(error);\n" +
22.58 + "};\n" +
22.59 + "var options = {};\n" +
22.60 + "options.enableHighAccuracy = enableHighAccuracy;\n" +
22.61 + "if (timeout >= 0) options.timeout = timeout;\n" +
22.62 + "if (maximumAge >= 0) options.maximumAge = maximumAge;\n" +
22.63 + "if (onlyOnce) {\n" +
22.64 + " navigator.geolocation.getCurrentPosition(ok, fail);\n" +
22.65 + " return 0;\n" +
22.66 + "} else {\n" +
22.67 + " return navigator.geolocation.watchPosition(ok, fail);\n" +
22.68 + "}\n"
22.69 + )
22.70 + protected long start(
22.71 + boolean onlyOnce,
22.72 + boolean enableHighAccuracy,
22.73 + long timeout,
22.74 + long maximumAge
22.75 + ) {
22.76 + return -1;
22.77 + }
22.78 +
22.79 + @JavaScriptBody(args = { "watch" }, body = "navigator.geolocation.clearWatch(watch);")
22.80 + protected void stop(long watch) {
22.81 + }
22.82 +
22.83 + @JavaScriptBody(args = { "self", "property" }, body = "return self[property];")
22.84 + public static Object get(Object self, String property) {
22.85 + return null;
22.86 + }
22.87 +
22.88 +}
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
23.2 +++ b/geo/src/test/java/net/java/html/geo/OnLocationTest.java Thu Jul 18 15:39:56 2013 +0200
23.3 @@ -0,0 +1,107 @@
23.4 +/**
23.5 + * HTML via Java(tm) Language Bindings
23.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
23.7 + *
23.8 + * This program is free software: you can redistribute it and/or modify
23.9 + * it under the terms of the GNU General Public License as published by
23.10 + * the Free Software Foundation, version 2 of the License.
23.11 + *
23.12 + * This program is distributed in the hope that it will be useful,
23.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
23.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23.15 + * GNU General Public License for more details. apidesign.org
23.16 + * designates this particular file as subject to the
23.17 + * "Classpath" exception as provided by apidesign.org
23.18 + * in the License file that accompanied this code.
23.19 + *
23.20 + * You should have received a copy of the GNU General Public License
23.21 + * along with this program. Look for COPYING file in the top folder.
23.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
23.23 + */
23.24 +package net.java.html.geo;
23.25 +
23.26 +import org.testng.annotations.Test;
23.27 +import static org.testng.Assert.*;
23.28 +
23.29 +/** Testing correctness of the generated code.
23.30 + */
23.31 +public class OnLocationTest {
23.32 + static int cnt;
23.33 + static @OnLocation void onLocation(Position p) {
23.34 + assertNotNull(p, "Position object provided");
23.35 + cnt++;
23.36 + }
23.37 +
23.38 + @Test public void createOneTimeQueryStatic() {
23.39 + net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
23.40 + h.setHighAccuracy(false);
23.41 + h.setTimeout(1000L);
23.42 + h.setMaximumAge(1000L);
23.43 + if (h.isSupported()) h.start();
23.44 + h.stop();
23.45 + }
23.46 +
23.47 + @Test public void onLocationHandleCallback() throws Throwable {
23.48 + net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
23.49 + cnt = 0;
23.50 + h.onLocation(new Position(null));
23.51 + assertEquals(cnt, 1, "The callback has been made");
23.52 + }
23.53 +
23.54 + @Test public void createRepeatableWatchStatic() {
23.55 + net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
23.56 + h.setHighAccuracy(false);
23.57 + h.setTimeout(1000L);
23.58 + h.setMaximumAge(1000L);
23.59 + if (h.isSupported()) h.start();
23.60 + h.stop();
23.61 + }
23.62 +
23.63 + int instCnt;
23.64 + Throwable instT;
23.65 + @OnLocation(onError = "someError") void instance(Position p) throws Error {
23.66 + assertNotNull(p, "Some position passed in");
23.67 + instCnt++;
23.68 + }
23.69 + void someError(Throwable t) throws Exception {
23.70 + instT = t;
23.71 + instCnt++;
23.72 + }
23.73 +
23.74 + @Test public void createOneTimeQueryInstance() {
23.75 + OnLocationTest t = new OnLocationTest();
23.76 +
23.77 + net.java.html.geo.Position.Handle h = InstanceHandle.createQuery(t);
23.78 + h.setHighAccuracy(false);
23.79 + h.setTimeout(1000L);
23.80 + h.setMaximumAge(1000L);
23.81 + if (h.isSupported()) h.start();
23.82 + h.stop();
23.83 + }
23.84 +
23.85 + @Test public void onInstanceCallback() throws Throwable {
23.86 + OnLocationTest t = new OnLocationTest();
23.87 + net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(t);
23.88 + h.onLocation(new Position(null));
23.89 + assertEquals(t.instCnt, 1, "One callback made");
23.90 + }
23.91 +
23.92 + @Test public void onInstanceError() throws Throwable {
23.93 + net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(this);
23.94 + InterruptedException e = new InterruptedException();
23.95 + h.onError(e);
23.96 + assertEquals(instCnt, 1, "One callback made");
23.97 + assertEquals(instT, e, "The same exception passed in");
23.98 + }
23.99 +
23.100 + @Test public void createRepeatableWatch() {
23.101 + OnLocationTest t = new OnLocationTest();
23.102 +
23.103 + net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(t);
23.104 + h.setHighAccuracy(false);
23.105 + h.setTimeout(1000L);
23.106 + h.setMaximumAge(1000L);
23.107 + if (h.isSupported()) h.start();
23.108 + h.stop();
23.109 + }
23.110 +}
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
24.2 +++ b/geo/src/test/java/org/apidesign/html/geo/impl/Compile.java Thu Jul 18 15:39:56 2013 +0200
24.3 @@ -0,0 +1,264 @@
24.4 +/**
24.5 + * HTML via Java(tm) Language Bindings
24.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
24.7 + *
24.8 + * This program is free software: you can redistribute it and/or modify
24.9 + * it under the terms of the GNU General Public License as published by
24.10 + * the Free Software Foundation, version 2 of the License.
24.11 + *
24.12 + * This program is distributed in the hope that it will be useful,
24.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
24.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24.15 + * GNU General Public License for more details. apidesign.org
24.16 + * designates this particular file as subject to the
24.17 + * "Classpath" exception as provided by apidesign.org
24.18 + * in the License file that accompanied this code.
24.19 + *
24.20 + * You should have received a copy of the GNU General Public License
24.21 + * along with this program. Look for COPYING file in the top folder.
24.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
24.23 + */
24.24 +package org.apidesign.html.geo.impl;
24.25 +
24.26 +import java.io.ByteArrayInputStream;
24.27 +import java.io.ByteArrayOutputStream;
24.28 +import java.io.IOException;
24.29 +import java.io.InputStream;
24.30 +import java.io.OutputStream;
24.31 +import java.net.URI;
24.32 +import java.net.URISyntaxException;
24.33 +import java.util.ArrayList;
24.34 +import java.util.Arrays;
24.35 +import java.util.HashMap;
24.36 +import java.util.List;
24.37 +import java.util.Locale;
24.38 +import java.util.Map;
24.39 +import java.util.regex.Matcher;
24.40 +import java.util.regex.Pattern;
24.41 +import javax.tools.Diagnostic;
24.42 +import javax.tools.DiagnosticListener;
24.43 +import javax.tools.FileObject;
24.44 +import javax.tools.ForwardingJavaFileManager;
24.45 +import javax.tools.JavaFileManager;
24.46 +import javax.tools.JavaFileObject;
24.47 +import javax.tools.JavaFileObject.Kind;
24.48 +import javax.tools.SimpleJavaFileObject;
24.49 +import javax.tools.StandardJavaFileManager;
24.50 +import javax.tools.StandardLocation;
24.51 +import javax.tools.ToolProvider;
24.52 +import static org.testng.Assert.*;
24.53 +
24.54 +/**
24.55 + *
24.56 + * @author Jaroslav Tulach <jtulach@netbeans.org>
24.57 + */
24.58 +final class Compile implements DiagnosticListener<JavaFileObject> {
24.59 + private final List<Diagnostic<? extends JavaFileObject>> errors =
24.60 + new ArrayList<Diagnostic<? extends JavaFileObject>>();
24.61 + private final Map<String, byte[]> classes;
24.62 + private final String pkg;
24.63 + private final String cls;
24.64 + private final String html;
24.65 + private final String sourceLevel;
24.66 +
24.67 + private Compile(String html, String code, String sl) throws IOException {
24.68 + this.pkg = findPkg(code);
24.69 + this.cls = findCls(code);
24.70 + this.html = html;
24.71 + this.sourceLevel = sl;
24.72 + classes = compile(html, code);
24.73 + }
24.74 +
24.75 + /** Performs compilation of given HTML page and associated Java code
24.76 + */
24.77 + public static Compile create(String html, String code) throws IOException {
24.78 + return create(html, code, "1.7");
24.79 + }
24.80 + static Compile create(String html, String code, String sourceLevel) throws IOException {
24.81 + return new Compile(html, code, sourceLevel);
24.82 + }
24.83 +
24.84 + /** Checks for given class among compiled resources */
24.85 + public byte[] get(String res) {
24.86 + return classes.get(res);
24.87 + }
24.88 +
24.89 + /** Obtains errors created during compilation.
24.90 + */
24.91 + public List<Diagnostic<? extends JavaFileObject>> getErrors() {
24.92 + List<Diagnostic<? extends JavaFileObject>> err;
24.93 + err = new ArrayList<Diagnostic<? extends JavaFileObject>>();
24.94 + for (Diagnostic<? extends JavaFileObject> diagnostic : errors) {
24.95 + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
24.96 + err.add(diagnostic);
24.97 + }
24.98 + }
24.99 + return err;
24.100 + }
24.101 +
24.102 + private Map<String, byte[]> compile(final String html, final String code) throws IOException {
24.103 + StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null);
24.104 +
24.105 + final Map<String, ByteArrayOutputStream> class2BAOS;
24.106 + class2BAOS = new HashMap<String, ByteArrayOutputStream>();
24.107 +
24.108 + JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
24.109 + @Override
24.110 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
24.111 + return code;
24.112 + }
24.113 + };
24.114 + final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) {
24.115 + @Override
24.116 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
24.117 + return html;
24.118 + }
24.119 +
24.120 + @Override
24.121 + public InputStream openInputStream() throws IOException {
24.122 + return new ByteArrayInputStream(html.getBytes());
24.123 + }
24.124 + };
24.125 +
24.126 + final URI scratch;
24.127 + try {
24.128 + scratch = new URI("mem://mem3");
24.129 + } catch (URISyntaxException ex) {
24.130 + throw new IOException(ex);
24.131 + }
24.132 +
24.133 + JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
24.134 + @Override
24.135 + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
24.136 + if (kind == Kind.CLASS) {
24.137 + final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
24.138 +
24.139 + class2BAOS.put(className.replace('.', '/') + ".class", buffer);
24.140 + return new SimpleJavaFileObject(sibling.toUri(), kind) {
24.141 + @Override
24.142 + public OutputStream openOutputStream() throws IOException {
24.143 + return buffer;
24.144 + }
24.145 + };
24.146 + }
24.147 +
24.148 + if (kind == Kind.SOURCE) {
24.149 + final String n = className.replace('.', '/') + ".java";
24.150 + final URI un;
24.151 + try {
24.152 + un = new URI("mem://" + n);
24.153 + } catch (URISyntaxException ex) {
24.154 + throw new IOException(ex);
24.155 + }
24.156 + return new VirtFO(un/*sibling.toUri()*/, kind, n);
24.157 + }
24.158 +
24.159 + throw new IllegalStateException();
24.160 + }
24.161 +
24.162 + @Override
24.163 + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
24.164 + if (location == StandardLocation.SOURCE_PATH) {
24.165 + if (packageName.equals(pkg)) {
24.166 + return htmlFile;
24.167 + }
24.168 + }
24.169 +
24.170 + return null;
24.171 + }
24.172 +
24.173 + @Override
24.174 + public boolean isSameFile(FileObject a, FileObject b) {
24.175 + if (a instanceof VirtFO && b instanceof VirtFO) {
24.176 + return ((VirtFO)a).getName().equals(((VirtFO)b).getName());
24.177 + }
24.178 +
24.179 + return super.isSameFile(a, b);
24.180 + }
24.181 +
24.182 + class VirtFO extends SimpleJavaFileObject {
24.183 +
24.184 + private final String n;
24.185 +
24.186 + public VirtFO(URI uri, Kind kind, String n) {
24.187 + super(uri, kind);
24.188 + this.n = n;
24.189 + }
24.190 + private final ByteArrayOutputStream data = new ByteArrayOutputStream();
24.191 +
24.192 + @Override
24.193 + public OutputStream openOutputStream() throws IOException {
24.194 + return data;
24.195 + }
24.196 +
24.197 + @Override
24.198 + public String getName() {
24.199 + return n;
24.200 + }
24.201 +
24.202 + @Override
24.203 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
24.204 + data.close();
24.205 + return new String(data.toByteArray());
24.206 + }
24.207 + }
24.208 + };
24.209 +
24.210 + ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", sourceLevel, "-target", "1.7"), null, Arrays.asList(file)).call();
24.211 +
24.212 + Map<String, byte[]> result = new HashMap<String, byte[]>();
24.213 +
24.214 + for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
24.215 + result.put(e.getKey(), e.getValue().toByteArray());
24.216 + }
24.217 +
24.218 + return result;
24.219 + }
24.220 +
24.221 +
24.222 + @Override
24.223 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
24.224 + errors.add(diagnostic);
24.225 + }
24.226 + private static String findPkg(String java) throws IOException {
24.227 + Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE);
24.228 + Matcher m = p.matcher(java);
24.229 + if (!m.find()) {
24.230 + throw new IOException("Can't find package declaration in the java file");
24.231 + }
24.232 + String pkg = m.group(1);
24.233 + return pkg;
24.234 + }
24.235 + private static String findCls(String java) throws IOException {
24.236 + Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE);
24.237 + Matcher m = p.matcher(java);
24.238 + if (!m.find()) {
24.239 + throw new IOException("Can't find package declaration in the java file");
24.240 + }
24.241 + String cls = m.group(1);
24.242 + return cls;
24.243 + }
24.244 +
24.245 + String getHtml() {
24.246 + String fqn = "'" + pkg + '.' + cls + "'";
24.247 + return html.replace("'${fqn}'", fqn);
24.248 + }
24.249 +
24.250 + void assertErrors() {
24.251 + assertFalse(getErrors().isEmpty(), "There are supposed to be some errors");
24.252 + }
24.253 +
24.254 + void assertError(String expMsg) {
24.255 + StringBuilder sb = new StringBuilder();
24.256 + sb.append("Can't find ").append(expMsg).append(" among:");
24.257 + for (Diagnostic<? extends JavaFileObject> e : errors) {
24.258 + String msg = e.getMessage(Locale.US);
24.259 + if (msg.contains(expMsg)) {
24.260 + return;
24.261 + }
24.262 + sb.append("\n");
24.263 + sb.append(msg);
24.264 + }
24.265 + fail(sb.toString());
24.266 + }
24.267 +}
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
25.2 +++ b/geo/src/test/java/org/apidesign/html/geo/impl/GeoProcessorTest.java Thu Jul 18 15:39:56 2013 +0200
25.3 @@ -0,0 +1,92 @@
25.4 +/**
25.5 + * HTML via Java(tm) Language Bindings
25.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
25.7 + *
25.8 + * This program is free software: you can redistribute it and/or modify
25.9 + * it under the terms of the GNU General Public License as published by
25.10 + * the Free Software Foundation, version 2 of the License.
25.11 + *
25.12 + * This program is distributed in the hope that it will be useful,
25.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
25.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25.15 + * GNU General Public License for more details. apidesign.org
25.16 + * designates this particular file as subject to the
25.17 + * "Classpath" exception as provided by apidesign.org
25.18 + * in the License file that accompanied this code.
25.19 + *
25.20 + * You should have received a copy of the GNU General Public License
25.21 + * along with this program. Look for COPYING file in the top folder.
25.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
25.23 + */
25.24 +package org.apidesign.html.geo.impl;
25.25 +
25.26 +import java.io.IOException;
25.27 +import org.testng.annotations.Test;
25.28 +
25.29 +/** Test whether the annotation processor detects errors correctly.
25.30 + *
25.31 + * @author Jaroslav Tulach <jtulach@netbeans.org>
25.32 + */
25.33 +public class GeoProcessorTest {
25.34 +
25.35 + public GeoProcessorTest() {
25.36 + }
25.37 +
25.38 + @Test public void onLocationMethodHasToTakePositionParameter() throws IOException {
25.39 + Compile res = Compile.create("", "package x;\n"
25.40 + + "class UseOnLocation {\n"
25.41 + + " @net.java.html.geo.OnLocation\n"
25.42 + + " public static void cantCallMe() {}\n"
25.43 + + "}\n"
25.44 + );
25.45 + res.assertErrors();
25.46 + res.assertError("one net.java.html.geo.Position argument");
25.47 + }
25.48 +
25.49 + @Test public void onLocationMethodCannotBePrivate() throws IOException {
25.50 + Compile res = Compile.create("", "package x;\n"
25.51 + + "class UseOnLocation {\n"
25.52 + + " @net.java.html.geo.OnLocation\n"
25.53 + + " private static void cantCallMe(net.java.html.geo.Position p) {}\n"
25.54 + + "}\n"
25.55 + );
25.56 + res.assertErrors();
25.57 + res.assertError("cannot be private");
25.58 + }
25.59 +
25.60 + @Test public void onErrorHasToExist() throws IOException {
25.61 + Compile res = Compile.create("", "package x;\n"
25.62 + + "class UseOnLocation {\n"
25.63 + + " @net.java.html.geo.OnLocation(onError=\"doesNotExist\")\n"
25.64 + + " static void cantCallMe(net.java.html.geo.Position p) {}\n"
25.65 + + "}\n"
25.66 + );
25.67 + res.assertErrors();
25.68 + res.assertError("not find doesNotExist");
25.69 + }
25.70 +
25.71 + @Test public void onErrorWouldHaveToBeStatic() throws IOException {
25.72 + Compile res = Compile.create("", "package x;\n"
25.73 + + "class UseOnLocation {\n"
25.74 + + " @net.java.html.geo.OnLocation(onError=\"notStatic\")\n"
25.75 + + " static void cantCallMe(net.java.html.geo.Position p) {}\n"
25.76 + + " void notStatic(Exception e) {}\n"
25.77 + + "}\n"
25.78 + );
25.79 + res.assertErrors();
25.80 + res.assertError("have to be static");
25.81 + }
25.82 +
25.83 + @Test public void onErrorMustAcceptExceptionArgument() throws IOException {
25.84 + Compile res = Compile.create("", "package x;\n"
25.85 + + "class UseOnLocation {\n"
25.86 + + " @net.java.html.geo.OnLocation(onError=\"notStatic\")\n"
25.87 + + " static void cantCallMe(net.java.html.geo.Position p) {}\n"
25.88 + + " static void notStatic(java.io.IOException e) {}\n"
25.89 + + "}\n"
25.90 + );
25.91 + res.assertErrors();
25.92 + res.assertError("take one Exception arg");
25.93 + }
25.94 +
25.95 +}
26.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Sat Jun 29 07:01:58 2013 +0200
26.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Thu Jul 18 15:39:56 2013 +0200
26.3 @@ -38,10 +38,59 @@
26.4 @Property(name="results", type=String.class, array = true),
26.5 @Property(name="callbackCount", type=int.class),
26.6 @Property(name="people", type=PersonImpl.class, array = true),
26.7 - @Property(name="enabled", type=boolean.class)
26.8 + @Property(name="enabled", type=boolean.class),
26.9 + @Property(name="latitude", type=double.class)
26.10 })
26.11 public final class KnockoutTest {
26.12
26.13 + @KOTest public void modifyValueAssertChangeInModelOnDouble() throws Throwable {
26.14 + Object exp = Utils.exposeHTML(KnockoutTest.class,
26.15 + "Latitude: <input id='input' data-bind=\"value: latitude\"></input>\n"
26.16 + );
26.17 + try {
26.18 +
26.19 + KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
26.20 + m.setLatitude(50.5);
26.21 + m.applyBindings();
26.22 +
26.23 + String v = getSetInput(null);
26.24 + assert "50.5".equals(v) : "Value is really 50.5: " + v;
26.25 +
26.26 + getSetInput("49.5");
26.27 + triggerEvent("input", "change");
26.28 +
26.29 + assert 49.5 == m.getLatitude() : "Double property updated: " + m.getLatitude();
26.30 + } catch (Throwable t) {
26.31 + throw t;
26.32 + } finally {
26.33 + Utils.exposeHTML(KnockoutTest.class, "");
26.34 + }
26.35 + }
26.36 +
26.37 + @KOTest public void modifyValueAssertChangeInModelOnBoolean() throws Throwable {
26.38 + Object exp = Utils.exposeHTML(KnockoutTest.class,
26.39 + "Latitude: <input id='input' data-bind=\"value: enabled\"></input>\n"
26.40 + );
26.41 + try {
26.42 +
26.43 + KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
26.44 + m.setEnabled(true);
26.45 + m.applyBindings();
26.46 +
26.47 + String v = getSetInput(null);
26.48 + assert "true".equals(v) : "Value is really true: " + v;
26.49 +
26.50 + getSetInput("false");
26.51 + triggerEvent("input", "change");
26.52 +
26.53 + assert false == m.isEnabled(): "Boolean property updated: " + m.isEnabled();
26.54 + } catch (Throwable t) {
26.55 + throw t;
26.56 + } finally {
26.57 + Utils.exposeHTML(KnockoutTest.class, "");
26.58 + }
26.59 + }
26.60 +
26.61 @KOTest public void modifyValueAssertChangeInModel() throws Exception {
26.62 Object exp = Utils.exposeHTML(KnockoutTest.class,
26.63 "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
27.1 --- a/json/src/main/java/org/apidesign/html/json/impl/JSON.java Sat Jun 29 07:01:58 2013 +0200
27.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/JSON.java Thu Jul 18 15:39:56 2013 +0200
27.3 @@ -20,7 +20,6 @@
27.4 */
27.5 package org.apidesign.html.json.impl;
27.6
27.7 -import org.apidesign.html.context.impl.CtxAccssr;
27.8 import java.io.IOException;
27.9 import java.io.InputStream;
27.10 import java.util.HashMap;
27.11 @@ -101,6 +100,41 @@
27.12 return aClass.cast(o);
27.13 }
27.14
27.15 + public static <T> T extractValue(Class<T> type, Object val) {
27.16 + if (Number.class.isAssignableFrom(type)) {
27.17 + val = numberValue(val);
27.18 + }
27.19 + if (Boolean.class == type) {
27.20 + val = boolValue(val);
27.21 + }
27.22 + return type.cast(val);
27.23 + }
27.24 +
27.25 + public static String stringValue(Object val) {
27.26 + return (String)val;
27.27 + }
27.28 +
27.29 + public static Number numberValue(Object val) {
27.30 + if (val instanceof String) {
27.31 + try {
27.32 + return Double.valueOf((String)val);
27.33 + } catch (NumberFormatException ex) {
27.34 + return Double.NaN;
27.35 + }
27.36 + }
27.37 + return (Number)val;
27.38 + }
27.39 +
27.40 + public static Character charValue(Object val) {
27.41 + return (Character)val;
27.42 + }
27.43 +
27.44 + public static Boolean boolValue(Object val) {
27.45 + if (val instanceof String) {
27.46 + return Boolean.parseBoolean((String)val);
27.47 + }
27.48 + return Boolean.TRUE.equals(val);
27.49 + }
27.50
27.51 public static void loadJSON(
27.52 BrwsrCtx c, Runnable whenDone, Object[] result,
28.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Sat Jun 29 07:01:58 2013 +0200
28.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Thu Jul 18 15:39:56 2013 +0200
28.3 @@ -220,14 +220,12 @@
28.4 for (int i = 0; i < propsGetSet.size(); i += 5) {
28.5 final String set = propsGetSet.get(i + 2);
28.6 String tn = propsGetSet.get(i + 4);
28.7 - if (processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_6) <= 0) {
28.8 - String btn = findBoxedType(tn);
28.9 - if (btn != null) {
28.10 - tn = btn;
28.11 - }
28.12 + String btn = findBoxedType(tn);
28.13 + if (btn != null) {
28.14 + tn = btn;
28.15 }
28.16 if (set != null) {
28.17 - w.append(" case " + (i / 5) + ": data." + strip(set) + "((" + tn + ")value); return;\n");
28.18 + w.append(" case " + (i / 5) + ": data." + strip(set) + "(org.apidesign.html.json.impl.JSON.extractValue(" + tn + ".class, value)); return;\n");
28.19 }
28.20 }
28.21 w.append(" }\n");
28.22 @@ -296,10 +294,10 @@
28.23 } else if (isEnum[0]) {
28.24 w.append(" this.prop_").append(pn);
28.25 w.append(".add(e == null ? null : ");
28.26 - w.append(type).append(".valueOf((String)e));\n");
28.27 + w.append(type).append(".valueOf(org.apidesign.html.json.impl.JSON.stringValue(e)));\n");
28.28 } else {
28.29 if (isPrimitive(type)) {
28.30 - w.append(" this.prop_").append(pn).append(".add(((Number)e).");
28.31 + w.append(" this.prop_").append(pn).append(".add(org.apidesign.html.json.impl.JSON.numberValue(e).");
28.32 w.append(type).append("Value());\n");
28.33 } else {
28.34 w.append(" this.prop_").append(pn).append(".add((");
28.35 @@ -312,18 +310,18 @@
28.36 if (isEnum[0]) {
28.37 w.append(" this.prop_").append(pn);
28.38 w.append(" = ret[" + cnt + "] == null ? null : ");
28.39 - w.append(type).append(".valueOf((String)ret[" + cnt + "]);\n");
28.40 + w.append(type).append(".valueOf(org.apidesign.html.json.impl.JSON.stringValue(ret[" + cnt + "]));\n");
28.41 } else if (isPrimitive(type)) {
28.42 w.append(" this.prop_").append(pn);
28.43 w.append(" = ret[" + cnt + "] == null ? ");
28.44 if ("char".equals(type)) {
28.45 - w.append("0 : ((Character)");
28.46 + w.append("0 : (org.apidesign.html.json.impl.JSON.charValue(");
28.47 } else if ("boolean".equals(type)) {
28.48 - w.append("false : ((Boolean)");
28.49 + w.append("false : (org.apidesign.html.json.impl.JSON.boolValue(");
28.50 } else {
28.51 - w.append("0 : ((Number)");
28.52 + w.append("0 : (org.apidesign.html.json.impl.JSON.numberValue(");
28.53 }
28.54 - w.append("ret[" + cnt + "]).");
28.55 + w.append("ret[" + cnt + "])).");
28.56 w.append(type).append("Value();\n");
28.57 } else if (isModel[0]) {
28.58 w.append(" this.prop_").append(pn).append(" = org.apidesign.html.json.impl.JSON.read");
28.59 @@ -647,7 +645,7 @@
28.60 }
28.61 String n = e.getSimpleName().toString();
28.62 body.append(" private void ").append(n).append("(Object data, Object ev) {\n");
28.63 - body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
28.64 + body.append(" ").append(((TypeElement)clazz).getQualifiedName()).append(".").append(n).append("(");
28.65 body.append(wrapParams(e, null, className, "ev", "data"));
28.66 body.append(");\n");
28.67 body.append(" }\n");
29.1 --- a/json/src/test/java/net/java/html/json/PersonImpl.java Sat Jun 29 07:01:58 2013 +0200
29.2 +++ b/json/src/test/java/net/java/html/json/PersonImpl.java Thu Jul 18 15:39:56 2013 +0200
29.3 @@ -59,6 +59,8 @@
29.4 @Property(array = true, name = "age", type = int.class),
29.5 @Property(array = true, name = "sex", type = Sex.class)
29.6 })
29.7 - public class PeopleImpl {
29.8 + public static class PeopleImpl {
29.9 + @Function static void inInnerClass(People p) {
29.10 + }
29.11 }
29.12 }
30.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/java/Main.java Sat Jun 29 07:01:58 2013 +0200
30.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/java/Main.java Thu Jul 18 15:39:56 2013 +0200
30.3 @@ -12,5 +12,6 @@
30.4 loadClass(TwitterClient.class).
30.5 invoke("initialize", args).
30.6 showAndWait();
30.7 + System.exit(0);
30.8 }
30.9 }
31.1 --- a/pom.xml Sat Jun 29 07:01:58 2013 +0200
31.2 +++ b/pom.xml Thu Jul 18 15:39:56 2013 +0200
31.3 @@ -25,6 +25,7 @@
31.4 <module>context</module>
31.5 <module>boot</module>
31.6 <module>boot-fx</module>
31.7 + <module>geo</module>
31.8 </modules>
31.9 <licenses>
31.10 <license>