1.1 --- a/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java Wed May 27 22:40:01 2015 +0200
1.2 +++ b/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java Wed May 27 23:37:25 2015 +0200
1.3 @@ -83,10 +83,10 @@
1.4 private static Class<?> browserClass;
1.5 private static Fn.Presenter browserContext;
1.6 private static URI baseUri;
1.7 -
1.8 +
1.9 public KnockoutEnvJSTest() {
1.10 }
1.11 -
1.12 +
1.13 @Factory public static Object[] compatibilityTests() throws Exception {
1.14 try {
1.15 Class.forName("java.lang.FunctionalInterface");
1.16 @@ -94,8 +94,8 @@
1.17 // only runs on JDK8
1.18 return new Object[0];
1.19 }
1.20 -
1.21 -
1.22 +
1.23 +
1.24 Class[] arr = testClasses();
1.25 for (int i = 0; i < arr.length; i++) {
1.26 assertEquals(
1.27 @@ -104,9 +104,9 @@
1.28 "All classes loaded by the same classloader"
1.29 );
1.30 }
1.31 -
1.32 +
1.33 baseUri = DynamicHTTP.initServer();
1.34 -
1.35 +
1.36 final Fn.Presenter p = Scripts.createPresenter(KOCase.JS);
1.37 InputStream is = KnockoutEnvJSTest.class.getResourceAsStream("env.nashorn.1.2-debug.js");
1.38 p.loadScript(new InputStreamReader(is));
1.39 @@ -116,14 +116,14 @@
1.40 loadClass(KnockoutEnvJSTest.class).
1.41 loadPage(baseUri.toString()).
1.42 invoke("initialized");
1.43 -
1.44 +
1.45 Executors.newSingleThreadExecutor().submit(new Runnable() {
1.46 @Override
1.47 public void run() {
1.48 bb.showAndWait();
1.49 }
1.50 });
1.51 -
1.52 +
1.53 ClassLoader l = getClassLoader();
1.54 List<Object> res = new ArrayList<Object>();
1.55 for (int i = 0; i < arr.length; i++) {
1.56 @@ -143,20 +143,20 @@
1.57 }
1.58 }
1.59 }
1.60 -
1.61 +
1.62 private static String skipMsg(String methodName) {
1.63 final String ver = System.getProperty("java.runtime.version"); // NOI18N
1.64 if (
1.65 ver.startsWith("1.8.0_25") ||
1.66 - ver.startsWith("1.8.0_40")
1.67 + ver.startsWith("1.8.0_40")
1.68 ) {
1.69 return "Broken due to JDK-8047764";
1.70 }
1.71 if (
1.72 !"1.8.0_05-b13".equals(ver) &&
1.73 - !"1.8.0_11-b12".equals(ver)
1.74 + !"1.8.0_11-b12".equals(ver)
1.75 ) {
1.76 - // we know that 1.8.0_05 and 1.8.0_11 are broken,
1.77 + // we know that 1.8.0_05 and 1.8.0_11 are broken,
1.78 // let's not speculate about anything else
1.79 return null;
1.80 }
1.81 @@ -178,13 +178,13 @@
1.82 }
1.83 return browserClass.getClassLoader();
1.84 }
1.85 -
1.86 +
1.87 public static synchronized void initialized(Class<?> browserCls) throws Exception {
1.88 browserClass = browserCls;
1.89 browserContext = Fn.activePresenter();
1.90 KnockoutEnvJSTest.class.notifyAll();
1.91 }
1.92 -
1.93 +
1.94 public static void initialized() throws Exception {
1.95 Assert.assertSame(
1.96 KnockoutEnvJSTest.class.getClassLoader(),
1.97 @@ -194,7 +194,7 @@
1.98 KnockoutEnvJSTest.initialized(KnockoutEnvJSTest.class);
1.99 browserContext = Fn.activePresenter();
1.100 }
1.101 -
1.102 +
1.103 @Override
1.104 public BrwsrCtx createContext() {
1.105 KO4J fx = new KO4J(browserContext);
1.106 @@ -216,7 +216,7 @@
1.107 }
1.108 return json;
1.109 }
1.110 -
1.111 +
1.112 @JavaScriptBody(args = {}, body = "return new Object();")
1.113 private static native Object createJSON();
1.114 @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
1.115 @@ -232,7 +232,7 @@
1.116 private static String findBaseURL() {
1.117 return baseUri.toString();
1.118 }
1.119 -
1.120 +
1.121 @Override
1.122 public URI prepareURL(String content, String mimeType, String[] parameters) {
1.123 try {
2.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java Wed May 27 22:40:01 2015 +0200
2.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java Wed May 27 23:37:25 2015 +0200
2.3 @@ -51,11 +51,11 @@
2.4 import net.java.html.json.Models;
2.5 import net.java.html.json.OnReceive;
2.6 import net.java.html.json.Property;
2.7 +import static net.java.html.json.tests.Utils.assertEquals;
2.8 +import static net.java.html.json.tests.Utils.assertNotNull;
2.9 +import static net.java.html.json.tests.Utils.assertNull;
2.10 +import static net.java.html.json.tests.Utils.assertTrue;
2.11 import org.netbeans.html.json.tck.KOTest;
2.12 -import static net.java.html.json.tests.Utils.assertEquals;
2.13 -import static net.java.html.json.tests.Utils.assertNull;
2.14 -import static net.java.html.json.tests.Utils.assertNotNull;
2.15 -import static net.java.html.json.tests.Utils.assertTrue;
2.16
2.17 /** Need to verify that models produce reasonable JSON objects.
2.18 *
2.19 @@ -71,12 +71,12 @@
2.20 private JSONik js;
2.21 private Integer orig;
2.22 private String url;
2.23 -
2.24 +
2.25 @ModelOperation static void assignFetched(JSONik m, Person p) {
2.26 m.setFetched(p);
2.27 }
2.28 private BrwsrCtx ctx;
2.29 -
2.30 +
2.31 @KOTest public void toJSONInABrowser() throws Throwable {
2.32 Person p = Models.bind(new Person(), newContext());
2.33 p.setSex(Sex.MALE);
2.34 @@ -89,19 +89,19 @@
2.35 } catch (Throwable ex) {
2.36 throw new IllegalStateException("Can't parse " + p).initCause(ex);
2.37 }
2.38 -
2.39 +
2.40 Person p2 = Models.fromRaw(newContext(), Person.class, json);
2.41 -
2.42 - assertEquals(p2.getFirstName(), p.getFirstName(),
2.43 +
2.44 + assertEquals(p2.getFirstName(), p.getFirstName(),
2.45 "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
2.46 }
2.47 -
2.48 +
2.49 @KOTest public void toJSONWithEscapeCharactersInABrowser() throws Throwable {
2.50 Person p = Models.bind(new Person(), newContext());
2.51 p.setSex(Sex.MALE);
2.52 p.setFirstName("/*\n * Copyright (c) 2013");
2.53
2.54 -
2.55 +
2.56 final String txt = p.toString();
2.57 Object json;
2.58 try {
2.59 @@ -109,19 +109,19 @@
2.60 } catch (Throwable ex) {
2.61 throw new IllegalStateException("Can't parse " + txt).initCause(ex);
2.62 }
2.63 -
2.64 +
2.65 Person p2 = Models.fromRaw(newContext(), Person.class, json);
2.66 -
2.67 +
2.68 assertEquals(p2.getFirstName(), p.getFirstName(),
2.69 "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
2.70 }
2.71 -
2.72 +
2.73 @KOTest public void toJSONWithDoubleSlashInABrowser() throws Throwable {
2.74 Person p = Models.bind(new Person(), newContext());
2.75 p.setSex(Sex.MALE);
2.76 p.setFirstName("/*\\n * Copyright (c) 2013");
2.77
2.78 -
2.79 +
2.80 final String txt = p.toString();
2.81 Object json;
2.82 try {
2.83 @@ -129,19 +129,19 @@
2.84 } catch (Throwable ex) {
2.85 throw new IllegalStateException("Can't parse " + txt).initCause(ex);
2.86 }
2.87 -
2.88 +
2.89 Person p2 = Models.fromRaw(newContext(), Person.class, json);
2.90 -
2.91 +
2.92 assertEquals(p2.getFirstName(), p.getFirstName(),
2.93 "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
2.94 }
2.95 -
2.96 +
2.97 @KOTest public void toJSONWithApostrophInABrowser() throws Throwable {
2.98 Person p = Models.bind(new Person(), newContext());
2.99 p.setSex(Sex.MALE);
2.100 p.setFirstName("Jimmy 'Jim' Rambo");
2.101
2.102 -
2.103 +
2.104 final String txt = p.toString();
2.105 Object json;
2.106 try {
2.107 @@ -149,15 +149,15 @@
2.108 } catch (Throwable ex) {
2.109 throw new IllegalStateException("Can't parse " + txt).initCause(ex);
2.110 }
2.111 -
2.112 +
2.113 Person p2 = Models.fromRaw(newContext(), Person.class, json);
2.114 -
2.115 +
2.116 assertEquals(p2.getFirstName(), p.getFirstName(),
2.117 "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
2.118 }
2.119
2.120 private static BrwsrCtx onCallback;
2.121 -
2.122 +
2.123 @OnReceive(url="{url}")
2.124 static void fetch(JSONik model, Person p) {
2.125 model.setFetched(p);
2.126 @@ -176,12 +176,12 @@
2.127 model.setFetched(p[0]);
2.128 onCallback = BrwsrCtx.findDefault(model.getClass());
2.129 }
2.130 -
2.131 +
2.132 static void setMessage(JSONik m, Exception t) {
2.133 assertNotNull(t, "Exception provided");
2.134 m.setFetchedResponse("Exception");
2.135 }
2.136 -
2.137 +
2.138 @OnReceive(url="{url}")
2.139 static void fetchPeople(JSONik model, People p) {
2.140 final int size = p.getInfo().size();
2.141 @@ -199,7 +199,7 @@
2.142 }
2.143 model.setFetchedCount(sum);
2.144 }
2.145 -
2.146 +
2.147 @KOTest public void loadAndParseJSON() throws InterruptedException {
2.148 if (js == null) {
2.149 url = Utils.prepareURL(
2.150 @@ -212,18 +212,18 @@
2.151 js.setFetched(null);
2.152 js.fetch(url);
2.153 }
2.154 -
2.155 +
2.156 Person p = js.getFetched();
2.157 if (p == null) {
2.158 throw new InterruptedException();
2.159 }
2.160 -
2.161 +
2.162 assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
2.163 assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
2.164 -
2.165 +
2.166 assertEquals(ctx, onCallback, "Context is the same");
2.167 }
2.168 -
2.169 +
2.170 @KOTest public void loadAndParsePlainText() throws Exception {
2.171 if (js == null) {
2.172 url = Utils.prepareURL(
2.173 @@ -236,23 +236,23 @@
2.174 js.setFetched(null);
2.175 js.fetchPlain(url);
2.176 }
2.177 -
2.178 +
2.179 String s = js.getFetchedResponse();
2.180 if (s == null) {
2.181 throw new InterruptedException();
2.182 }
2.183 -
2.184 +
2.185 assertTrue(s.contains("Sitar"), "The text contains Sitar value: " + s);
2.186 assertTrue(s.contains("MALE"), "The text contains MALE value: " + s);
2.187 -
2.188 +
2.189 Person p = Models.parse(ctx, Person.class, new ByteArrayInputStream(s.getBytes()));
2.190 -
2.191 +
2.192 assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
2.193 assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
2.194 -
2.195 +
2.196 assertEquals(ctx, onCallback, "Same context");
2.197 }
2.198 -
2.199 +
2.200 @KOTest public void loadAndParsePlainTextOnArray() throws Exception {
2.201 if (js == null) {
2.202 url = Utils.prepareURL(
2.203 @@ -265,60 +265,60 @@
2.204 js.setFetched(null);
2.205 js.fetchPlain(url);
2.206 }
2.207 -
2.208 +
2.209 String s = js.getFetchedResponse();
2.210 if (s == null) {
2.211 throw new InterruptedException();
2.212 }
2.213 -
2.214 +
2.215 assertTrue(s.contains("Sitar"), "The text contains Sitar value: " + s);
2.216 assertTrue(s.contains("MALE"), "The text contains MALE value: " + s);
2.217 -
2.218 +
2.219 Person p = Models.parse(ctx, Person.class, new ByteArrayInputStream(s.getBytes()));
2.220 -
2.221 +
2.222 assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
2.223 assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
2.224 -
2.225 +
2.226 assertEquals(ctx, onCallback, "Same context");
2.227 }
2.228 -
2.229 +
2.230 @OnReceive(url="{url}?callme={me}", jsonp = "me")
2.231 static void fetchViaJSONP(JSONik model, Person p) {
2.232 model.setFetched(p);
2.233 }
2.234 -
2.235 +
2.236 @KOTest public void loadAndParseJSONP() throws InterruptedException, Exception {
2.237 if (js == null) {
2.238 url = Utils.prepareURL(
2.239 - JSONTest.class, "$0({'firstName': 'Mitar', 'sex': 'MALE'})",
2.240 + JSONTest.class, "$0({'firstName': 'Mitar', 'sex': 'MALE'})",
2.241 "application/javascript",
2.242 "callme"
2.243 );
2.244 orig = scriptElements();
2.245 assertTrue(orig > 0, "There should be some scripts on the page");
2.246 -
2.247 +
2.248 js = Models.bind(new JSONik(), newContext());
2.249 js.applyBindings();
2.250
2.251 js.setFetched(null);
2.252 js.fetchViaJSONP(url);
2.253 }
2.254 -
2.255 +
2.256 Person p = js.getFetched();
2.257 if (p == null) {
2.258 throw new InterruptedException();
2.259 }
2.260 -
2.261 +
2.262 assertEquals("Mitar", p.getFirstName(), "Unexpected: " + p.getFirstName());
2.263 assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
2.264 -
2.265 +
2.266 int now = scriptElements();
2.267 -
2.268 +
2.269 assertEquals(orig, now, "The set of elements is unchanged. Delta: " + (now - orig));
2.270 }
2.271 -
2.272 -
2.273 -
2.274 +
2.275 +
2.276 +
2.277 @OnReceive(url="{url}", method = "PUT", data = Person.class)
2.278 static void putPerson(JSONik model, String reply) {
2.279 model.setFetchedCount(1);
2.280 @@ -328,13 +328,13 @@
2.281 @KOTest public void putPeopleUsesRightMethod() throws InterruptedException, Exception {
2.282 if (js == null) {
2.283 url = Utils.prepareURL(
2.284 - JSONTest.class, "$0\n$1",
2.285 + JSONTest.class, "$0\n$1",
2.286 "text/plain",
2.287 "http.method", "http.requestBody"
2.288 );
2.289 orig = scriptElements();
2.290 assertTrue(orig > 0, "There should be some scripts on the page");
2.291 -
2.292 +
2.293 js = Models.bind(new JSONik(), newContext());
2.294 js.applyBindings();
2.295
2.296 @@ -342,7 +342,7 @@
2.297 p.setFirstName("Jarda");
2.298 js.putPerson(url, p);
2.299 }
2.300 -
2.301 +
2.302 int cnt = js.getFetchedCount();
2.303 if (cnt == 0) {
2.304 throw new InterruptedException();
2.305 @@ -356,186 +356,231 @@
2.306 } else {
2.307 msg = res;
2.308 }
2.309 -
2.310 +
2.311 assertEquals("PUT", res, "Server was queried with PUT method: " + js.getFetchedResponse());
2.312 -
2.313 +
2.314 assertTrue(msg.contains("Jarda"), "Data transferred to the server: " + msg);
2.315 }
2.316 -
2.317 +
2.318 private static int scriptElements() throws Exception {
2.319 return ((Number)Utils.executeScript(
2.320 - JSONTest.class,
2.321 + JSONTest.class,
2.322 "return window.document.getElementsByTagName('script').length;")).intValue();
2.323 }
2.324
2.325 private static Object parseJSON(String s) throws Exception {
2.326 return Utils.executeScript(
2.327 - JSONTest.class,
2.328 + JSONTest.class,
2.329 "return window.JSON.parse(arguments[0]);", s);
2.330 }
2.331 -
2.332 +
2.333 @KOTest public void loadAndParseJSONSentToArray() throws InterruptedException {
2.334 if (js == null) {
2.335 url = Utils.prepareURL(
2.336 - JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}",
2.337 + JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}",
2.338 "application/json"
2.339 );
2.340 -
2.341 +
2.342 js = Models.bind(new JSONik(), newContext());
2.343 js.applyBindings();
2.344
2.345 js.setFetched(null);
2.346 js.fetchArray(url);
2.347 }
2.348 -
2.349 +
2.350 Person p = js.getFetched();
2.351 if (p == null) {
2.352 throw new InterruptedException();
2.353 }
2.354 -
2.355 +
2.356 assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
2.357 assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
2.358 }
2.359 -
2.360 +
2.361 @KOTest public void loadAndParseJSONArraySingle() throws InterruptedException {
2.362 if (js == null) {
2.363 url = Utils.prepareURL(
2.364 - JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]",
2.365 + JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]",
2.366 "application/json"
2.367 );
2.368 js = Models.bind(new JSONik(), newContext());
2.369 js.applyBindings();
2.370 -
2.371 +
2.372 js.setFetched(null);
2.373 js.fetch(url);
2.374 }
2.375 -
2.376 +
2.377 Person p = js.getFetched();
2.378 if (p == null) {
2.379 throw new InterruptedException();
2.380 }
2.381 -
2.382 +
2.383 assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
2.384 assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
2.385 }
2.386 -
2.387 +
2.388 @KOTest public void loadAndParseArrayInPeople() throws InterruptedException {
2.389 if (js == null) {
2.390 url = Utils.prepareURL(
2.391 - JSONTest.class, "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
2.392 + JSONTest.class, "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
2.393 "application/json"
2.394 );
2.395 js = Models.bind(new JSONik(), newContext());
2.396 js.applyBindings();
2.397 -
2.398 +
2.399 js.fetchPeople(url);
2.400 }
2.401 -
2.402 +
2.403 if (0 == js.getFetchedCount()) {
2.404 throw new InterruptedException();
2.405 }
2.406
2.407 assertEquals(js.getFetchedCount(), 1, "One person loaded: " + js.getFetchedCount());
2.408 -
2.409 +
2.410 Person p = js.getFetched();
2.411 -
2.412 +
2.413 assertNotNull(p, "We should get our person back: " + p);
2.414 assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
2.415 assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
2.416 }
2.417 -
2.418 +
2.419 + @KOTest public void loadAndParseArrayInPeopleWithHeaders() throws InterruptedException {
2.420 + if (js == null) {
2.421 + url = Utils.prepareURL(
2.422 + JSONTest.class, "{'info':[{'firstName': '$0$1$2$3$4', 'sex': 'FEMALE'}]}",
2.423 + "application/json",
2.424 + "http.header.Easy",
2.425 + "http.header.H-a!r*d^e.r",
2.426 + "http.header.Repeat-ed",
2.427 + "http.header.Repeat*ed",
2.428 + "http.header.Same-URL"
2.429 + );
2.430 + js = Models.bind(new JSONik(), newContext());
2.431 + js.applyBindings();
2.432 +
2.433 + js.fetchPeopleWithHeaders(url, "easy", "harder", "rep");
2.434 + }
2.435 +
2.436 + if (0 == js.getFetchedCount()) {
2.437 + throw new InterruptedException();
2.438 + }
2.439 +
2.440 + assertEquals(js.getFetchedCount(), 1, "One person loaded: " + js.getFetchedCount());
2.441 +
2.442 + Person p = js.getFetched();
2.443 +
2.444 + assertNotNull(p, "We should get our person back: " + p);
2.445 + assertEquals("easyharderreprep" + url, p.getFirstName(), "Expecting header mess: " + p.getFirstName());
2.446 + assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
2.447 + }
2.448 +
2.449 + @OnReceive(url="{url}", headers={
2.450 + "Easy: {easy}",
2.451 + "H-a!r*d^e.r: {harder}",
2.452 + "Repeat-ed: {rep}",
2.453 + "Repeat*ed: {rep}",
2.454 + "Same-URL: {url}"
2.455 + })
2.456 + static void fetchPeopleWithHeaders(JSONik model, People p) {
2.457 + final int size = p.getInfo().size();
2.458 + if (size > 0) {
2.459 + model.setFetched(p.getInfo().get(0));
2.460 + }
2.461 + model.setFetchedCount(size);
2.462 + }
2.463 +
2.464 @KOTest public void loadAndParseArrayOfIntegers() throws InterruptedException {
2.465 if (js == null) {
2.466 url = Utils.prepareURL(
2.467 - JSONTest.class, "{'age':[1, 2, 3]}",
2.468 + JSONTest.class, "{'age':[1, 2, 3]}",
2.469 "application/json"
2.470 );
2.471 js = Models.bind(new JSONik(), newContext());
2.472 js.applyBindings();
2.473 -
2.474 +
2.475 js.fetchPeopleAge(url);
2.476 }
2.477 -
2.478 +
2.479 if (0 == js.getFetchedCount()) {
2.480 throw new InterruptedException();
2.481 }
2.482
2.483 assertEquals(js.getFetchedCount(), 6, "1 + 2 + 3 is " + js.getFetchedCount());
2.484 }
2.485 -
2.486 +
2.487 @OnReceive(url="{url}")
2.488 static void fetchPeopleSex(JSONik model, People p) {
2.489 model.setFetchedCount(1);
2.490 model.getFetchedSex().addAll(p.getSex());
2.491 }
2.492 -
2.493 +
2.494 @KOTest public void loadAndParseArrayOfEnums() throws InterruptedException {
2.495 if (js == null) {
2.496 url = Utils.prepareURL(
2.497 - JSONTest.class, "{'sex':['FEMALE', 'MALE', 'MALE']}",
2.498 + JSONTest.class, "{'sex':['FEMALE', 'MALE', 'MALE']}",
2.499 "application/json"
2.500 );
2.501 js = Models.bind(new JSONik(), newContext());
2.502 js.applyBindings();
2.503 -
2.504 +
2.505 js.fetchPeopleSex(url);
2.506 }
2.507 -
2.508 +
2.509 if (0 == js.getFetchedCount()) {
2.510 throw new InterruptedException();
2.511 }
2.512
2.513 assertEquals(js.getFetchedCount(), 1, "Loaded");
2.514 -
2.515 +
2.516 assertEquals(js.getFetchedSex().size(), 3, "Three values " + js.getFetchedSex());
2.517 assertEquals(js.getFetchedSex().get(0), Sex.FEMALE, "Female first " + js.getFetchedSex());
2.518 assertEquals(js.getFetchedSex().get(1), Sex.MALE, "male 2nd " + js.getFetchedSex());
2.519 assertEquals(js.getFetchedSex().get(2), Sex.MALE, "male 3rd " + js.getFetchedSex());
2.520 }
2.521 -
2.522 +
2.523 @KOTest public void loadAndParseJSONArray() throws InterruptedException {
2.524 if (js == null) {
2.525 url = Utils.prepareURL(
2.526 JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'},"
2.527 + "{'firstName': 'Peter', 'sex': 'MALE'}"
2.528 - + "]",
2.529 + + "]",
2.530 "application/json"
2.531 );
2.532 js = Models.bind(new JSONik(), newContext());
2.533 js.applyBindings();
2.534 js.setFetched(null);
2.535 -
2.536 +
2.537 js.fetchArray(url);
2.538 }
2.539 -
2.540 -
2.541 +
2.542 +
2.543 Person p = js.getFetched();
2.544 if (p == null) {
2.545 throw new InterruptedException();
2.546 }
2.547 -
2.548 +
2.549 assertEquals(js.getFetchedCount(), 2, "We got two values: " + js.getFetchedCount());
2.550 assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
2.551 assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
2.552 }
2.553 -
2.554 +
2.555 @KOTest public void loadError() throws InterruptedException {
2.556 if (js == null) {
2.557 js = Models.bind(new JSONik(), newContext());
2.558 js.applyBindings();
2.559 js.setFetched(null);
2.560 -
2.561 +
2.562 js.fetchArray("http://127.0.0.1:54253/does/not/exist.txt");
2.563 }
2.564 -
2.565 -
2.566 +
2.567 +
2.568 if (js.getFetchedResponse() == null) {
2.569 throw new InterruptedException();
2.570 }
2.571
2.572 assertEquals("Exception", js.getFetchedResponse(), "Response " + js.getFetchedResponse());
2.573 }
2.574 -
2.575 +
2.576 @Model(className = "NameAndValue", properties = {
2.577 @Property(name = "name", type = String.class),
2.578 @Property(name = "value", type = long.class),
2.579 @@ -543,7 +588,7 @@
2.580 })
2.581 static class NandV {
2.582 }
2.583 -
2.584 +
2.585 @KOTest public void parseNullNumber() throws Exception {
2.586 String txt = "{ \"name\":\"M\" }";
2.587 ByteArrayInputStream is = new ByteArrayInputStream(txt.getBytes("UTF-8"));
2.588 @@ -566,12 +611,12 @@
2.589 err = null;
2.590 prev = null;
2.591 }
2.592 -
2.593 +
2.594 String str = "{ \"sex\" : \"unknown\" }";
2.595 ByteArrayInputStream is = new ByteArrayInputStream(str.getBytes("UTF-8"));
2.596 Person p = Models.parse(newContext(), Person.class, is);
2.597 assertNull(p.getSex(), "Wrong sex means null, but was: " + p.getSex());
2.598 -
2.599 +
2.600 if (err != null) {
2.601 assertTrue(err.toString().contains("unknown") && err.toString().contains("Sex"), "Expecting error: " + err.toString());
2.602 }
2.603 @@ -584,9 +629,9 @@
2.604 }
2.605 }
2.606
2.607 -
2.608 +
2.609 private static BrwsrCtx newContext() {
2.610 return Utils.newContext(JSONTest.class);
2.611 }
2.612 -
2.613 +
2.614 }
3.1 --- a/json/src/main/java/net/java/html/json/OnReceive.java Wed May 27 22:40:01 2015 +0200
3.2 +++ b/json/src/main/java/net/java/html/json/OnReceive.java Wed May 27 23:37:25 2015 +0200
3.3 @@ -48,7 +48,7 @@
3.4 import java.lang.annotation.Target;
3.5
3.6 /** Static methods in classes annotated by {@link Model}
3.7 - * can be marked by this annotation to establish a
3.8 + * can be marked by this annotation to establish a
3.9 * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>
3.10 * communication point. The first argument should be the
3.11 * associated {@link Model} class. The second argument can
3.12 @@ -57,7 +57,7 @@
3.13 * {@link java.util.List} of such classes.
3.14 * The associated model class then gets new method to invoke a network
3.15 * connection asynchronously. Example follows:
3.16 - *
3.17 + *
3.18 * <pre>
3.19 * {@link Model @Model}(className="MyModel", properties={
3.20 * {@link Property @Property}(name = "people", type=Person.class, array=true)
3.21 @@ -73,18 +73,18 @@
3.22 * return firstName + " " + lastName;
3.23 * }
3.24 * }
3.25 - *
3.26 + *
3.27 * {@link OnReceive @OnReceive}(url = "{protocol}://your.server.com/person/{name}")
3.28 * static void getANewPerson(MyModel m, Person p) {
3.29 * System.out.println("Adding " + p.getFullName() + '!');
3.30 * m.getPeople().add(p);
3.31 * }
3.32 - *
3.33 + *
3.34 * // the above will generate method <code>getANewPerson</code> in class <code>MyModel</code>.
3.35 * // with <code>protocol</code> and <code>name</code> arguments
3.36 * // which asynchronously contacts the server and in case of success calls
3.37 * // your {@link OnReceive @OnReceive} with parsed in data
3.38 - *
3.39 + *
3.40 * {@link Function @Function}
3.41 * static void requestSmith(MyModel m) {
3.42 * m.getANewPerson("http", "Smith");
3.43 @@ -92,7 +92,7 @@
3.44 * }
3.45 * </pre>
3.46 * When the server returns <code>{ "firstName" : "John", "lastName" : "Smith" }</code>
3.47 - * the system will print a message <em>Adding John Smith!</em>. It is not
3.48 + * the system will print a message <em>Adding John Smith!</em>. It is not
3.49 * necessary to fully describe the server message - enumerate only the fields
3.50 * in the response you are interested in. The others will be discarded. So,
3.51 * if the server <code>{ "firstName" : "John", "lastName" : "Smith", "age" : 33 }</code>
3.52 @@ -105,7 +105,7 @@
3.53 * <p>
3.54 * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7138581">on-line demo</a>
3.55 * to see REST access via {@link OnReceive} annotation.
3.56 - *
3.57 + *
3.58 * @author Jaroslav Tulach
3.59 * @since 0.3
3.60 */
3.61 @@ -113,59 +113,90 @@
3.62 @Target(ElementType.METHOD)
3.63 public @interface OnReceive {
3.64 /** The URL to connect to. Can contain variable names surrounded by '{' and '}'.
3.65 - * Those parameters will then become variables of the associated method.
3.66 - *
3.67 + * Those names will then become parameters of the associated method.
3.68 + *
3.69 * @return the (possibly parametrized) url to connect to
3.70 */
3.71 String url();
3.72 -
3.73 +
3.74 + /** Specifies HTTP request headers. Array of header lines
3.75 + * can contain variable names surrounded by '{' and '}'.
3.76 + * Those names will then become parameters of the associated method
3.77 + * (in addition to those added by {@link #url()} specification)
3.78 + * and can only be used with plain JSON(P) requests.
3.79 + * Headers are currently <b>not</b> supported by the
3.80 + * <a href="doc-files/websockets.html">WebSockets protocol</a>.
3.81 + * A sample follows. If you want to transmit <b>X-Birthday</b> header,
3.82 + * you can do it like this:
3.83 + * <pre>
3.84 + * {@code @}{@link OnReceive}(url="http://your.server.org", headers = {
3.85 + * "X-Birthday: {dayOfBirth}"
3.86 + * })
3.87 + * <b>static void</b> knowingTheBirth({@link Model YourModel} model) {
3.88 + * // handle the reply
3.89 + * }</pre>
3.90 + * a method <b>knowingTheBirth</b> is generated in
3.91 + * <code>YourModel</code> class with the <code>dayOfBirth</code> argument
3.92 + * which can be called like this:
3.93 + * <pre>
3.94 + * yourModel.knowingTheBirth("10. 12. 1973");
3.95 + * </pre>
3.96 + *
3.97 + * @return array of header lines - each line should be plain text with
3.98 + * a header name, followed by ":" and value usually specified as
3.99 + * '{' and '}' surrounded variable. The line shouldn't contain
3.100 + * newline or other control characters
3.101 + * @since 1.2
3.102 + */
3.103 + String[] headers() default {};
3.104 +
3.105 /** Support for <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> requires
3.106 * a callback from the server generated page to a function defined in the
3.107 * system. The name of such function is usually specified as a property
3.108 * (of possibly different names). By defining the <code>jsonp</code> attribute
3.109 - * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
3.110 + * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
3.111 * transmission and specifies the name of the property. The property should
3.112 * also be used in the {@link #url()} attribute on appropriate place.
3.113 - *
3.114 + *
3.115 * @return name of a property to carry the name of <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
3.116 * callback function.
3.117 */
3.118 String jsonp() default "";
3.119 -
3.120 +
3.121 /** The model class to be send to the server as JSON data.
3.122 * By default no data are sent. However certain {@link #method() transport methods}
3.123 - * (like <code>"PUT"</code> and <code>"POST"</code>) require the
3.124 + * (like <code>"PUT"</code> and <code>"POST"</code>) require the
3.125 * data to be specified.
3.126 - *
3.127 + *
3.128 * @return name of a class generated using {@link Model @Model} annotation
3.129 * @since 0.3
3.130 */
3.131 Class<?> data() default Object.class;
3.132 -
3.133 +
3.134 /** The HTTP transfer method to use. Defaults to <code>"GET"</code>.
3.135 - * Other typical methods include <code>"HEAD"</code>,
3.136 + * Other typical methods include <code>"HEAD"</code>,
3.137 * <code>"DELETE"</code>, <code>"POST"</code>, <code>"PUT"</code>.
3.138 * The last two mentioned methods require {@link #data()} to be specified.
3.139 * <p>
3.140 - * When {@link #jsonp() JSONP} transport is requested, the method
3.141 + * When {@link #jsonp() JSONP} transport is requested, the method
3.142 * has to be <code>"GET"</code>.
3.143 * <p>
3.144 * Since version 0.5 one can specify "<a href="doc-files/websockets.html">WebSocket</a>"
3.145 * as the communication method.
3.146 - *
3.147 + *
3.148 * @return name of the HTTP transfer method
3.149 * @since 0.3
3.150 */
3.151 String method() default "GET";
3.152 -
3.153 - /** Name of a method in this class which should be called in case of
3.154 - * an error. The method has to be non-private and take one model and
3.155 - * one {@link Exception}
3.156 +
3.157 + /** Name of a method in this class which should be called in case of
3.158 + * an error. The method has to be non-private and take one model and
3.159 + * one {@link Exception}
3.160 * parameter. If this method is not specified, the exception is just
3.161 * printed to console.
3.162 - *
3.163 + *
3.164 * @return name of method in this class
3.165 * @since 0.5
3.166 */
3.167 - public String onError() default "";
3.168 + public String onError() default "";
3.169 }
4.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java Wed May 27 22:40:01 2015 +0200
4.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java Wed May 27 23:37:25 2015 +0200
4.3 @@ -71,24 +71,24 @@
4.4 return t == null ? EmptyTech.EMPTY : t;
4.5 }
4.6
4.7 - static Transfer findTransfer(BrwsrCtx c) {
4.8 + public static Transfer findTransfer(BrwsrCtx c) {
4.9 Transfer t = Contexts.find(c, Transfer.class);
4.10 return t == null ? EmptyTech.EMPTY : t;
4.11 }
4.12
4.13 - static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
4.14 + public static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
4.15 WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
4.16 return t == null ? EmptyTech.EMPTY : t;
4.17 }
4.18 -
4.19 +
4.20 public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
4.21 Transfer t = findTransfer(c);
4.22 t.extract(value, props, values);
4.23 }
4.24 -
4.25 +
4.26 private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
4.27 if (prop == null) return obj;
4.28 -
4.29 +
4.30 String[] arr = { prop };
4.31 Object[] val = { null };
4.32 extract(c, obj, arr, val);
4.33 @@ -146,15 +146,15 @@
4.34 Object o = t.toModel(aClass, data);
4.35 return aClass.cast(o);
4.36 }
4.37 -
4.38 +
4.39 public static boolean isSame(int a, int b) {
4.40 return a == b;
4.41 }
4.42 -
4.43 +
4.44 public static boolean isSame(double a, double b) {
4.45 return a == b;
4.46 }
4.47 -
4.48 +
4.49 public static boolean isSame(Object a, Object b) {
4.50 if (a == b) {
4.51 return true;
4.52 @@ -164,7 +164,7 @@
4.53 }
4.54 return a.equals(b);
4.55 }
4.56 -
4.57 +
4.58 public static int hashPlus(Object o, int h) {
4.59 return o == null ? h : h ^ o.hashCode();
4.60 }
4.61 @@ -193,7 +193,7 @@
4.62 }
4.63 if (Byte.class == type) {
4.64 val = val instanceof Number ? ((Number)val).byteValue() : 0;
4.65 - }
4.66 + }
4.67 if (Double.class == type) {
4.68 val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
4.69 }
4.70 @@ -202,11 +202,11 @@
4.71 }
4.72 return type.cast(val);
4.73 }
4.74 -
4.75 +
4.76 static boolean isNumeric(Object val) {
4.77 return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
4.78 }
4.79 -
4.80 +
4.81 public static String stringValue(Object val) {
4.82 if (val instanceof Boolean) {
4.83 return ((Boolean)val ? "true" : "false");
4.84 @@ -222,7 +222,7 @@
4.85 }
4.86 return (String)val;
4.87 }
4.88 -
4.89 +
4.90 public static Number numberValue(Object val) {
4.91 if (val instanceof String) {
4.92 try {
4.93 @@ -250,7 +250,7 @@
4.94 }
4.95 return (Character)val;
4.96 }
4.97 -
4.98 +
4.99 public static Boolean boolValue(Object val) {
4.100 if (val instanceof String) {
4.101 return Boolean.parseBoolean((String)val);
4.102 @@ -258,10 +258,10 @@
4.103 if (val instanceof Number) {
4.104 return numberValue(val).doubleValue() != 0.0;
4.105 }
4.106 -
4.107 +
4.108 return Boolean.TRUE.equals(val);
4.109 }
4.110 -
4.111 +
4.112 public static Object find(Object object, Bindings model) {
4.113 if (object == null) {
4.114 return null;
4.115 @@ -288,7 +288,7 @@
4.116 final Bindings b = PropertyBindingAccessor.getBindings(proto, true);
4.117 return b == null ? null : b.koData();
4.118 }
4.119 -
4.120 +
4.121 private static Proto findProto(Object object) {
4.122 Proto.Type<?> type = JSON.findType(object.getClass());
4.123 if (type == null) {
4.124 @@ -301,7 +301,7 @@
4.125 public static Object find(Object object) {
4.126 return find(object, null);
4.127 }
4.128 -
4.129 +
4.130 public static void applyBindings(Object object, String id) {
4.131 final Proto proto = findProto(object);
4.132 if (proto == null) {
4.133 @@ -309,31 +309,17 @@
4.134 }
4.135 proto.applyBindings(id);
4.136 }
4.137 -
4.138 - public static void loadJSON(
4.139 - BrwsrCtx c, RcvrJSON callback,
4.140 - String urlBefore, String urlAfter, String method,
4.141 - Object data
4.142 - ) {
4.143 - JSONCall call = PropertyBindingAccessor.createCall(c, callback, urlBefore, urlAfter, method, data);
4.144 - Transfer t = findTransfer(c);
4.145 - t.loadJSON(call);
4.146 - }
4.147 - public static WS openWS(
4.148 - BrwsrCtx c, RcvrJSON r, String url, Object data
4.149 - ) {
4.150 - WS ws = WSImpl.create(findWSTransfer(c), r);
4.151 - ws.send(c, url, data);
4.152 - return ws;
4.153 - }
4.154 -
4.155 +
4.156 public static abstract class WS {
4.157 private WS() {
4.158 }
4.159 -
4.160 - public abstract void send(BrwsrCtx ctx, String url, Object model);
4.161 +
4.162 + public abstract void send(BrwsrCtx ctx, String headers, String url, Object model);
4.163 + public static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
4.164 + return new WSImpl<Socket>(t, r);
4.165 + }
4.166 }
4.167 -
4.168 +
4.169 private static final class WSImpl<Socket> extends WS {
4.170
4.171 private final WSTransfer<Socket> trans;
4.172 @@ -345,19 +331,15 @@
4.173 this.trans = trans;
4.174 this.rcvr = rcvr;
4.175 }
4.176 -
4.177 - static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
4.178 - return new WSImpl<Socket>(t, r);
4.179 - }
4.180
4.181 @Override
4.182 - public void send(BrwsrCtx ctx, String url, Object data) {
4.183 + public void send(BrwsrCtx ctx, String headers, String url, Object data) {
4.184 Socket s = socket;
4.185 if (s == null) {
4.186 if (data != null) {
4.187 throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
4.188 }
4.189 - JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, url, null, "WebSocket", null);
4.190 + JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, url, null, "WebSocket", null);
4.191 socket = trans.open(url, call);
4.192 prevURL = url;
4.193 return;
4.194 @@ -373,12 +355,12 @@
4.195 + " Close the socket by calling it will null data first!"
4.196 );
4.197 }
4.198 - JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, prevURL, null, "WebSocket", data);
4.199 + JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, prevURL, null, "WebSocket", data);
4.200 trans.send(s, call);
4.201 }
4.202 -
4.203 +
4.204 }
4.205 -
4.206 +
4.207 private static final Map<Class,Proto.Type<?>> modelTypes;
4.208 static {
4.209 modelTypes = new HashMap<Class, Proto.Type<?>>();
4.210 @@ -386,11 +368,11 @@
4.211 public static void register(Class c, Proto.Type<?> type) {
4.212 modelTypes.put(c, type);
4.213 }
4.214 -
4.215 +
4.216 public static boolean isModel(Class<?> clazz) {
4.217 - return findType(clazz) != null;
4.218 + return findType(clazz) != null;
4.219 }
4.220 -
4.221 +
4.222 static Proto.Type<?> findType(Class<?> clazz) {
4.223 for (int i = 0; i < 2; i++) {
4.224 Proto.Type<?> from = modelTypes.get(clazz);
4.225 @@ -402,7 +384,7 @@
4.226 }
4.227 return null;
4.228 }
4.229 -
4.230 +
4.231 public static <Model> Model bindTo(Model model, BrwsrCtx c) {
4.232 Proto.Type<Model> from = (Proto.Type<Model>) findType(model.getClass());
4.233 if (from == null) {
4.234 @@ -410,8 +392,8 @@
4.235 }
4.236 return PropertyBindingAccessor.clone(from, model, c);
4.237 }
4.238 -
4.239 - public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data, Collection<? super T> collectTo)
4.240 +
4.241 + public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data, Collection<? super T> collectTo)
4.242 throws IOException {
4.243 Transfer tr = findTransfer(c);
4.244 Object rawJSON = tr.toJSON((InputStream)data);
4.245 @@ -468,7 +450,7 @@
4.246 // ignore and try again
4.247 }
4.248 }
4.249 -
4.250 +
4.251 private static final class EmptyTech
4.252 implements Technology<Object>, Transfer, WSTransfer<Void> {
4.253 private static final EmptyTech EMPTY = new EmptyTech();
4.254 @@ -540,5 +522,5 @@
4.255 public void close(Void socket) {
4.256 }
4.257 }
4.258 -
4.259 +
4.260 }
5.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Wed May 27 22:40:01 2015 +0200
5.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Wed May 27 23:37:25 2015 +0200
5.3 @@ -1132,15 +1132,31 @@
5.4 body.append(" * to open the connection (even if not required). Call with non-null data to\n");
5.5 body.append(" * send messages to server. Call again with <code>null</code> data to close the socket.\n");
5.6 body.append(" */\n");
5.7 + if (onR.headers().length > 0) {
5.8 + error("WebSocket spec does not support headers", e);
5.9 + }
5.10 }
5.11 body.append(" public void ").append(n).append("(");
5.12 StringBuilder urlBefore = new StringBuilder();
5.13 StringBuilder urlAfter = new StringBuilder();
5.14 + StringBuilder headers = new StringBuilder();
5.15 String jsonpVarName = null;
5.16 {
5.17 String sep = "";
5.18 boolean skipJSONP = onR.jsonp().isEmpty();
5.19 - for (String p : findParamNames(e, onR.url(), onR.jsonp(), urlBefore, urlAfter)) {
5.20 + Set<String> receiveParams = new LinkedHashSet<String>();
5.21 + findParamNames(receiveParams, e, onR.url(), onR.jsonp(), urlBefore, urlAfter);
5.22 + for (String headerLine : onR.headers()) {
5.23 + if (headerLine.contains("\r") || headerLine.contains("\n")) {
5.24 + error("Header line cannot contain line separator", e);
5.25 + }
5.26 + findParamNames(receiveParams, e, headerLine, null, headers);
5.27 + headers.append("+ \"\\r\\n\" +\n");
5.28 + }
5.29 + if (headers.length() > 0) {
5.30 + headers.append("\"\"");
5.31 + }
5.32 + for (String p : receiveParams) {
5.33 if (!skipJSONP && p.equals(onR.jsonp())) {
5.34 skipJSONP = true;
5.35 jsonpVarName = p;
5.36 @@ -1180,13 +1196,13 @@
5.37 body.append(") {\n");
5.38 boolean webSocket = onR.method().equals("WebSocket");
5.39 if (webSocket) {
5.40 - if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
5.41 + if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror, headers)) {
5.42 return false;
5.43 }
5.44 body.append(" }\n");
5.45 body.append(" private Object ws_" + e.getSimpleName() + ";\n");
5.46 } else {
5.47 - if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
5.48 + if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror, headers)) {
5.49 return false;
5.50 }
5.51 body.append(" }\n");
5.52 @@ -1198,7 +1214,7 @@
5.53 return true;
5.54 }
5.55
5.56 - private boolean generateJSONReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
5.57 + private boolean generateJSONReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror, StringBuilder headers) {
5.58 body.append(
5.59 " case " + index + ": {\n" +
5.60 " if (type == 2) { /* on error */\n" +
5.61 @@ -1247,7 +1263,8 @@
5.62 " }\n" +
5.63 " }\n"
5.64 );
5.65 - method.append(" proto.loadJSON(" + index + ",\n ");
5.66 + method.append(" proto.loadJSONWithHeaders(" + index + ",\n ");
5.67 + method.append(headers.length() == 0 ? "null" : headers).append(",\n ");
5.68 method.append(urlBefore).append(", ");
5.69 if (jsonpVarName != null) {
5.70 method.append(urlAfter);
5.71 @@ -1271,7 +1288,7 @@
5.72 return false;
5.73 }
5.74
5.75 - private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
5.76 + private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror, StringBuilder headers) {
5.77 body.append(
5.78 " case " + index + ": {\n" +
5.79 " if (type == 0) { /* on open */\n" +
5.80 @@ -1655,37 +1672,35 @@
5.81 return false;
5.82 }
5.83
5.84 - private Iterable<String> findParamNames(
5.85 - Element e, String url, String jsonParam, StringBuilder... both
5.86 + private void findParamNames(
5.87 + Set<String> params, Element e, String url, String jsonParam, StringBuilder... both
5.88 ) {
5.89 - Set<String> params = new LinkedHashSet<String>();
5.90 int wasJSON = 0;
5.91
5.92 for (int pos = 0; ;) {
5.93 int next = url.indexOf('{', pos);
5.94 if (next == -1) {
5.95 both[wasJSON].append('"')
5.96 - .append(url.substring(pos))
5.97 + .append(url.substring(pos).replace("\"", "\\\""))
5.98 .append('"');
5.99 - return params;
5.100 + return;
5.101 }
5.102 int close = url.indexOf('}', next);
5.103 if (close == -1) {
5.104 error("Unbalanced '{' and '}' in " + url, e);
5.105 - return params;
5.106 + return;
5.107 }
5.108 final String paramName = url.substring(next + 1, close);
5.109 - if (params.add(paramName)) {
5.110 - if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
5.111 - both[wasJSON].append('"')
5.112 - .append(url.substring(pos, next))
5.113 - .append('"');
5.114 - wasJSON = 1;
5.115 - } else {
5.116 - both[wasJSON].append('"')
5.117 - .append(url.substring(pos, next))
5.118 - .append("\" + ").append(paramName).append(" + ");
5.119 - }
5.120 + params.add(paramName);
5.121 + if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
5.122 + both[wasJSON].append('"')
5.123 + .append(url.substring(pos, next).replace("\"", "\\\""))
5.124 + .append('"');
5.125 + wasJSON = 1;
5.126 + } else {
5.127 + both[wasJSON].append('"')
5.128 + .append(url.substring(pos, next).replace("\"", "\\\""))
5.129 + .append("\" + ").append(paramName).append(" + ");
5.130 }
5.131 pos = close + 1;
5.132 }
6.1 --- a/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java Wed May 27 22:40:01 2015 +0200
6.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java Wed May 27 23:37:25 2015 +0200
6.3 @@ -43,7 +43,6 @@
6.4 package org.netbeans.html.json.impl;
6.5
6.6 import net.java.html.BrwsrCtx;
6.7 -import org.netbeans.html.json.spi.FunctionBinding;
6.8 import org.netbeans.html.json.spi.JSONCall;
6.9 import org.netbeans.html.json.spi.PropertyBinding;
6.10 import org.netbeans.html.json.spi.Proto;
6.11 @@ -59,7 +58,7 @@
6.12 if (DEFAULT != null) throw new IllegalStateException();
6.13 DEFAULT = this;
6.14 }
6.15 -
6.16 +
6.17 static {
6.18 JSON.initClass(PropertyBinding.class);
6.19 }
6.20 @@ -68,34 +67,36 @@
6.21 Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, boolean readOnly
6.22 );
6.23 protected abstract JSONCall newCall(
6.24 - BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter,
6.25 + BrwsrCtx ctx, RcvrJSON callback,
6.26 + String headers, String urlBefore, String urlAfter,
6.27 String method, Object data
6.28 );
6.29 -
6.30 +
6.31 protected abstract Bindings bindings(Proto proto, boolean initialize);
6.32 protected abstract void notifyChange(Proto proto, int propIndex);
6.33 protected abstract Proto findProto(Proto.Type<?> type, Object object);
6.34 protected abstract <Model> Model cloneTo(Proto.Type<Model> type, Model model, BrwsrCtx c);
6.35 protected abstract Object read(Proto.Type<?> from, BrwsrCtx c, Object data);
6.36 -
6.37 +
6.38 static Bindings getBindings(Proto proto, boolean initialize) {
6.39 return DEFAULT.bindings(proto, initialize);
6.40 }
6.41 -
6.42 +
6.43 static void notifyProtoChange(Proto proto, int propIndex) {
6.44 DEFAULT.notifyChange(proto, propIndex);
6.45 }
6.46 -
6.47 +
6.48 static <M> PropertyBinding create(
6.49 Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model , boolean readOnly
6.50 ) {
6.51 return DEFAULT.newBinding(access, bindings, name, index, model, readOnly);
6.52 }
6.53 - static JSONCall createCall(
6.54 - BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter,
6.55 + public static JSONCall createCall(
6.56 + BrwsrCtx ctx, RcvrJSON callback,
6.57 + String headers, String urlBefore, String urlAfter,
6.58 String method, Object data
6.59 ) {
6.60 - return DEFAULT.newCall(ctx, callback, urlBefore, urlAfter, method, data);
6.61 + return DEFAULT.newCall(ctx, callback, headers, urlBefore, urlAfter, method, data);
6.62 }
6.63 static Proto protoFor(Proto.Type<?> type, Object object) {
6.64 return DEFAULT.findProto(type, object);
7.1 --- a/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java Wed May 27 22:40:01 2015 +0200
7.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java Wed May 27 23:37:25 2015 +0200
7.3 @@ -54,30 +54,36 @@
7.4 */
7.5 public final class JSONCall {
7.6 private final RcvrJSON whenDone;
7.7 + private final String headers;
7.8 private final String urlBefore;
7.9 private final String urlAfter;
7.10 private final String method;
7.11 private final Object data;
7.12 private final BrwsrCtx ctx;
7.13
7.14 - JSONCall(BrwsrCtx ctx, RcvrJSON whenDone, String urlBefore, String urlAfter, String method, Object data) {
7.15 + JSONCall(
7.16 + BrwsrCtx ctx, RcvrJSON whenDone,
7.17 + String headers, String urlBefore, String urlAfter,
7.18 + String method, Object data
7.19 + ) {
7.20 this.ctx = ctx;
7.21 this.whenDone = whenDone;
7.22 + this.headers = headers;
7.23 this.urlBefore = urlBefore;
7.24 this.urlAfter = urlAfter;
7.25 this.method = method;
7.26 this.data = data;
7.27 }
7.28 -
7.29 - /** Do we have some data to send? Can the {@link #writeData(java.io.OutputStream)} method be
7.30 +
7.31 + /** Do we have some data to send? Can the {@link #writeData(java.io.OutputStream)} method be
7.32 * called?
7.33 - *
7.34 + *
7.35 * @return true, if the call has some data to send
7.36 */
7.37 public boolean isDoOutput() {
7.38 return this.data != null;
7.39 }
7.40 -
7.41 +
7.42 public void writeData(OutputStream os) throws IOException {
7.43 if (this.data == null) {
7.44 throw new IOException("No data!");
7.45 @@ -85,15 +91,25 @@
7.46 os.write(this.data.toString().getBytes("UTF-8"));
7.47 os.flush();
7.48 }
7.49 -
7.50 +
7.51 + /** Additional headers to be included in the request.
7.52 + * Usually multiline string to be appended into the header.
7.53 + *
7.54 + * @return <code>null</code> or string with prepared (HTTP) request headers
7.55 + * @since 1.2
7.56 + */
7.57 + public String getHeaders() {
7.58 + return headers;
7.59 + }
7.60 +
7.61 public String getMethod() {
7.62 return method;
7.63 }
7.64 -
7.65 +
7.66 public boolean isJSONP() {
7.67 return urlAfter != null;
7.68 }
7.69 -
7.70 +
7.71 public String composeURL(String jsonpCallback) {
7.72 if ((urlAfter == null) != (jsonpCallback == null)) {
7.73 throw new IllegalStateException();
7.74 @@ -112,7 +128,7 @@
7.75 dispatch(RcvrJSON.MsgEvnt.createMessage(result));
7.76 }
7.77 }
7.78 -
7.79 +
7.80 public void notifyError(Throwable error) {
7.81 if (error == null) {
7.82 dispatch(RcvrJSON.MsgEvnt.createClose());
7.83 @@ -120,7 +136,7 @@
7.84 dispatch(RcvrJSON.MsgEvnt.createError(error));
7.85 }
7.86 }
7.87 -
7.88 +
7.89 private void dispatch(final RcvrJSON.MsgEvnt ev) {
7.90 ctx.execute(new Runnable() {
7.91 @Override
8.1 --- a/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java Wed May 27 22:40:01 2015 +0200
8.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java Wed May 27 23:37:25 2015 +0200
8.3 @@ -50,8 +50,8 @@
8.4 import org.netbeans.html.json.impl.PropertyBindingAccessor;
8.5 import org.netbeans.html.json.impl.RcvrJSON;
8.6
8.7 -/** Describes a property when one is asked to
8.8 - * bind it
8.9 +/** Describes a property when one is asked to
8.10 + * bind it
8.11 *
8.12 * @author Jaroslav Tulach
8.13 */
8.14 @@ -62,8 +62,8 @@
8.15 static {
8.16 new PropertyBindingAccessor() {
8.17 @Override
8.18 - protected JSONCall newCall(BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter, String method, Object data) {
8.19 - return new JSONCall(ctx, callback, urlBefore, urlAfter, method, data);
8.20 + protected JSONCall newCall(BrwsrCtx ctx, RcvrJSON callback, String headers, String urlBefore, String urlAfter, String method, Object data) {
8.21 + return new JSONCall(ctx, callback, headers, urlBefore, urlAfter, method, data);
8.22 }
8.23
8.24 @Override
8.25 @@ -108,33 +108,33 @@
8.26
8.27 /** Changes value of the property. Can be called only on dedicated
8.28 * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
8.29 - *
8.30 + *
8.31 * @param v new value of the property
8.32 */
8.33 public abstract void setValue(Object v);
8.34 -
8.35 +
8.36 /** Obtains current value of the property this binding represents.
8.37 * Can be called only on dedicated
8.38 * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
8.39 - *
8.40 + *
8.41 * @return the value or <code>null</code>
8.42 */
8.43 public abstract Object getValue();
8.44 -
8.45 +
8.46 /** Is this property read only? Or can one call {@link #setValue(java.lang.Object)}?
8.47 - *
8.48 + *
8.49 * @return true, if this property is read only
8.50 */
8.51 public abstract boolean isReadOnly();
8.52
8.53 /** Returns identical version of the binding, but one that holds on the
8.54 * original model object via weak reference.
8.55 - *
8.56 + *
8.57 * @return binding that uses weak reference
8.58 * @since 1.1
8.59 */
8.60 public abstract PropertyBinding weak();
8.61 -
8.62 +
8.63 private static abstract class AImpl<M> extends PropertyBinding {
8.64 public final String name;
8.65 public final boolean readOnly;
8.66 @@ -149,7 +149,7 @@
8.67 this.access = access;
8.68 this.readOnly = readOnly;
8.69 }
8.70 -
8.71 +
8.72 protected abstract M model();
8.73
8.74 @Override
8.75 @@ -182,7 +182,7 @@
8.76 return name;
8.77 }
8.78 } // end of PBData
8.79 -
8.80 +
8.81 private static final class Impl<M> extends AImpl<M> {
8.82 private final M model;
8.83
8.84 @@ -201,7 +201,7 @@
8.85 return new Weak(model, bindings, name, index, access, readOnly);
8.86 }
8.87 }
8.88 -
8.89 +
8.90 private static final class Weak<M> extends AImpl<M> {
8.91 private final Reference<M> ref;
8.92 public Weak(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, boolean readOnly) {
9.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Proto.java Wed May 27 22:40:01 2015 +0200
9.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java Wed May 27 23:37:25 2015 +0200
9.3 @@ -49,7 +49,9 @@
9.4 import net.java.html.json.Model;
9.5 import org.netbeans.html.json.impl.Bindings;
9.6 import org.netbeans.html.json.impl.JSON;
9.7 +import org.netbeans.html.json.impl.JSON.WS;
9.8 import org.netbeans.html.json.impl.JSONList;
9.9 +import org.netbeans.html.json.impl.PropertyBindingAccessor;
9.10 import org.netbeans.html.json.impl.RcvrJSON;
9.11 import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
9.12
9.13 @@ -57,8 +59,8 @@
9.14 * {@link Model} annotation. Contains methods the generated class can
9.15 * use to communicate with behind the scene associated {@link Technology}.
9.16 * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
9.17 - * singletonizer</a>-like interface {@link Type} which provides the
9.18 - * associated {@link Technology} the necessary information about the
9.19 + * singletonizer</a>-like interface {@link Type} which provides the
9.20 + * associated {@link Technology} the necessary information about the
9.21 * generated {@link Model} class.
9.22 *
9.23 * @author Jaroslav Tulach
9.24 @@ -79,8 +81,8 @@
9.25
9.26 /** Browser context this proto object and its associated model
9.27 * are operating-in.
9.28 - *
9.29 - * @return the associated context
9.30 + *
9.31 + * @return the associated context
9.32 */
9.33 public BrwsrCtx getContext() {
9.34 return context;
9.35 @@ -89,19 +91,19 @@
9.36 /** Acquires global lock to compute a {@link ComputedProperty derived property}
9.37 * on this proto object. This proto object must not be locked yet. No
9.38 * dependency tracking is performed.
9.39 - *
9.40 + *
9.41 * @throws IllegalStateException if already locked
9.42 */
9.43 public void acquireLock() throws IllegalStateException {
9.44 acquireLock(null);
9.45 }
9.46 -
9.47 +
9.48 /** Acquires global lock to compute a {@link ComputedProperty derived property}
9.49 * on this proto object. This proto object must not be locked yet. The
9.50 * name of the property is used to track dependencies on own
9.51 * properties of other proto objects - when they are changed, this
9.52 * {@link #valueHasMutated(java.lang.String) property is changed too}.
9.53 - *
9.54 + *
9.55 * @param propName name of property we are about to compute
9.56 * @throws IllegalStateException thrown when there is a cyclic
9.57 * call is detected
9.58 @@ -110,14 +112,14 @@
9.59 public void acquireLock(String propName) throws IllegalStateException {
9.60 Observers.beginComputing(this, propName);
9.61 }
9.62 -
9.63 +
9.64 /** A property on this proto object is about to be accessed. Verifies
9.65 * whether this proto object is accessible - e.g. it has not been
9.66 * {@link #acquireLock() locked yet}. If everything is OK, the
9.67 * <code>propName</code> is recorded in the chain of dependencies
9.68 * tracked by {@link #acquireLock(java.lang.String)} and watched by
9.69 * {@link #valueHasMutated(java.lang.String)}.
9.70 - *
9.71 + *
9.72 * @param propName name of the property that is requested
9.73 * @throws IllegalStateException if the model is locked
9.74 * @since 0.9
9.75 @@ -125,27 +127,27 @@
9.76 public void accessProperty(String propName) throws IllegalStateException {
9.77 Observers.accessingValue(this, propName);
9.78 }
9.79 -
9.80 +
9.81 /** Verifies the model is not locked otherwise throws an exception.
9.82 * @throws IllegalStateException if the model is locked
9.83 */
9.84 public void verifyUnlocked() throws IllegalStateException {
9.85 Observers.verifyUnlocked(this);
9.86 }
9.87 -
9.88 - /** When modifications are over, the model is switched into
9.89 +
9.90 + /** When modifications are over, the model is switched into
9.91 * unlocked state by calling this method.
9.92 */
9.93 public void releaseLock() {
9.94 Observers.finishComputing(this);
9.95 }
9.96 -
9.97 +
9.98 /** Whenever model changes a property. It should notify the
9.99 - * associated technology by calling this method.
9.100 + * associated technology by calling this method.
9.101 * Since 0.8.3: This method may be called by any thread - it reschedules
9.102 * its actual execution into appropriate one by using
9.103 * {@link BrwsrCtx#execute(java.lang.Runnable)}.
9.104 - *
9.105 + *
9.106 * @param propName name of the changed property
9.107 */
9.108 public void valueHasMutated(final String propName) {
9.109 @@ -168,7 +170,7 @@
9.110 * Since 0.8.3: This method may be called by any thread - it reschedules
9.111 * its actual execution into appropriate one by using
9.112 * {@link BrwsrCtx#execute(java.lang.Runnable)}.
9.113 - *
9.114 + *
9.115 * @param propName name of the changed property
9.116 * @param oldValue provides previous value of the property
9.117 * @param newValue provides new value of the property
9.118 @@ -187,21 +189,21 @@
9.119 }
9.120 });
9.121 }
9.122 -
9.123 +
9.124 /** Initializes the associated model in the current {@link #getContext() context}.
9.125 - * In case of <em>knockout.js</em> technology, applies given bindings
9.126 + * In case of <em>knockout.js</em> technology, applies given bindings
9.127 * of the current model to the <em>body</em> element of the page.
9.128 */
9.129 public void applyBindings() {
9.130 initBindings().applyBindings(null);
9.131 }
9.132 -
9.133 +
9.134 /** Initializes the associated model to the specified element's subtree.
9.135 * The technology is taken from the current {@link #getContext() context} and
9.136 - * in case of <em>knockout.js</em> applies given bindings
9.137 + * in case of <em>knockout.js</em> applies given bindings
9.138 * of the current model to the element of the page with 'id' attribute
9.139 * set to the specified <code>id</code> value.
9.140 - *
9.141 + *
9.142 * @param id the id of element to apply the binding to
9.143 * @since 1.1
9.144 * @see Technology.ApplyId
9.145 @@ -209,13 +211,13 @@
9.146 public void applyBindings(String id) {
9.147 initBindings().applyBindings(id);
9.148 }
9.149 -
9.150 +
9.151 /** Invokes the provided runnable in the {@link #getContext() context}
9.152 * of the browser. If the caller is already on the right thread, the
9.153 - * <code>run.run()</code> is invoked immediately and synchronously.
9.154 + * <code>run.run()</code> is invoked immediately and synchronously.
9.155 * Otherwise the method returns immediately and the <code>run()</code>
9.156 * method is performed later
9.157 - *
9.158 + *
9.159 * @param run the action to execute
9.160 */
9.161 public void runInBrowser(Runnable run) {
9.162 @@ -224,10 +226,10 @@
9.163
9.164 /** Invokes the specified function index in the {@link #getContext() context}
9.165 * of the browser. If the caller is already on the right thread, the
9.166 - * index-th function is invoked immediately and synchronously.
9.167 + * index-th function is invoked immediately and synchronously.
9.168 * Otherwise the method returns immediately and the function is invoked
9.169 * later.
9.170 - *
9.171 + *
9.172 * @param index the index of the function as will be passed to
9.173 * {@link Type#call(java.lang.Object, int, java.lang.Object, java.lang.Object)}
9.174 * method
9.175 @@ -247,11 +249,11 @@
9.176 }
9.177 });
9.178 }
9.179 -
9.180 +
9.181 /** Initializes the provided collection with a content of the <code>array</code>.
9.182 - * The initialization can only be done soon after the the collection
9.183 + * The initialization can only be done soon after the the collection
9.184 * is created, otherwise an exception is throw
9.185 - *
9.186 + *
9.187 * @param to the collection to initialize (assumed to be empty)
9.188 * @param array the array to add to the collection
9.189 * @throws IllegalStateException if the system has already been initialized
9.190 @@ -270,7 +272,7 @@
9.191 /** Takes an object representing JSON result and extract some of its
9.192 * properties. It is assumed that the <code>props</code> and
9.193 * <code>values</code> arrays have the same length.
9.194 - *
9.195 + *
9.196 * @param json the JSON object (actual type depends on the associated
9.197 * {@link Technology})
9.198 * @param props list of properties to extract
9.199 @@ -281,7 +283,7 @@
9.200 }
9.201
9.202 /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
9.203 - *
9.204 + *
9.205 * @param <T> type of the model class
9.206 * @param modelClass the type of the class to create
9.207 * @param data the raw JSON data
9.208 @@ -294,29 +296,29 @@
9.209 /** Initializes asynchronous JSON connection to specified URL. Delegates
9.210 * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
9.211 * with no extra parameters.
9.212 - *
9.213 + *
9.214 * @param index the callback index to be used when a reply is received
9.215 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
9.216 - *
9.217 + *
9.218 * @param urlBefore the part of the URL before JSON-P callback parameter
9.219 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
9.220 * @param method method to use for connection to the server
9.221 * @param data string, number or a {@link Model} generated class to send to
9.222 * the server when doing a query
9.223 */
9.224 - public void loadJSON(final int index,
9.225 + public void loadJSON(final int index,
9.226 String urlBefore, String urlAfter, String method,
9.227 final Object data
9.228 ) {
9.229 loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
9.230 }
9.231 -
9.232 - /** Initializes asynchronous JSON connection to specified URL. The
9.233 +
9.234 + /** Initializes asynchronous JSON connection to specified URL. The
9.235 * method returns immediately and later does callback later.
9.236 - *
9.237 + *
9.238 * @param index the callback index to be used when a reply is received
9.239 * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
9.240 - *
9.241 + *
9.242 * @param urlBefore the part of the URL before JSON-P callback parameter
9.243 * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
9.244 * @param method method to use for connection to the server
9.245 @@ -326,7 +328,31 @@
9.246 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
9.247 * @since 0.8.1
9.248 */
9.249 - public void loadJSON(final int index,
9.250 + public void loadJSON(final int index,
9.251 + String urlBefore, String urlAfter, String method,
9.252 + final Object data, final Object... params
9.253 + ) {
9.254 + loadJSONWithHeaders(index, null, urlBefore, urlAfter, method, data, params);
9.255 + }
9.256 +
9.257 + /** Initializes asynchronous JSON connection to specified URL. The
9.258 + * method returns immediately and later does callback later.
9.259 + *
9.260 + * @param index the callback index to be used when a reply is received
9.261 + * to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
9.262 + *
9.263 + * @param headers headers to use for the request or <code>null</code> to use default ones
9.264 + * @param urlBefore the part of the URL before JSON-P callback parameter
9.265 + * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
9.266 + * @param method method to use for connection to the server
9.267 + * @param data string, number or a {@link Model} generated class to send to
9.268 + * the server when doing a query
9.269 + * @param params extra params to pass back when calling
9.270 + * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
9.271 + * @since 1.2
9.272 + */
9.273 + public void loadJSONWithHeaders(final int index,
9.274 + String headers,
9.275 String urlBefore, String urlAfter, String method,
9.276 final Object data, final Object... params
9.277 ) {
9.278 @@ -341,12 +367,16 @@
9.279 type.onMessage(obj, index, 2, msg.getException(), params);
9.280 }
9.281 }
9.282 - JSON.loadJSON(context, new Rcvr(), urlBefore, urlAfter, method, data);
9.283 + JSONCall call = PropertyBindingAccessor.createCall(
9.284 + context, new Rcvr(), headers, urlBefore, urlAfter, method, data
9.285 + );
9.286 + Transfer t = JSON.findTransfer(context);
9.287 + t.loadJSON(call);
9.288 }
9.289 -
9.290 - /** Opens new WebSocket connection to the specified URL.
9.291 - *
9.292 - * @param index the index to use later during callbacks to
9.293 +
9.294 + /** Opens new WebSocket connection to the specified URL.
9.295 + *
9.296 + * @param index the index to use later during callbacks to
9.297 * {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
9.298 * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
9.299 * @param data data to send to server (usually <code>null</code>)
9.300 @@ -359,12 +389,12 @@
9.301 protected void onError(MsgEvnt msg) {
9.302 type.onMessage(obj, index, 2, msg.getException());
9.303 }
9.304 -
9.305 +
9.306 @Override
9.307 protected void onMessage(MsgEvnt msg) {
9.308 type.onMessage(obj, index, 1, msg.getValues());
9.309 }
9.310 -
9.311 +
9.312 @Override
9.313 protected void onClose(MsgEvnt msg) {
9.314 type.onMessage(obj, index, 3, null);
9.315 @@ -375,24 +405,26 @@
9.316 type.onMessage(obj, index, 0, null);
9.317 }
9.318 }
9.319 - return JSON.openWS(context, new WSrcvr(), url, data);
9.320 + WS ws = WS.create(JSON.findWSTransfer(context), new WSrcvr());
9.321 + ws.send(context, null, url, data);
9.322 + return ws;
9.323 }
9.324 -
9.325 +
9.326 /** Sends a message to existing socket.
9.327 - *
9.328 + *
9.329 * @param webSocket the socket to send message to
9.330 * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
9.331 - * preferably the same as the one used when the socket was
9.332 + * preferably the same as the one used when the socket was
9.333 * {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
9.334 * @param data the data to send or <code>null</code> if the socket is
9.335 * supposed to be closed
9.336 */
9.337 public void wsSend(Object webSocket, String url, Object data) {
9.338 - ((JSON.WS)webSocket).send(context, url, data);
9.339 + ((JSON.WS)webSocket).send(context, null, url, data);
9.340 }
9.341
9.342 /** Converts raw data (one of its properties) to string representation.
9.343 - *
9.344 + *
9.345 * @param data the object
9.346 * @param propName the name of object property or <code>null</code>
9.347 * if the whole object should be converted
9.348 @@ -401,9 +433,9 @@
9.349 public String toString(Object data, String propName) {
9.350 return JSON.toString(context, data, propName);
9.351 }
9.352 -
9.353 +
9.354 /** Converts raw data (one of its properties) to a number representation.
9.355 - *
9.356 + *
9.357 * @param data the object
9.358 * @param propName the name of object property or <code>null</code>
9.359 * if the whole object should be converted
9.360 @@ -414,7 +446,7 @@
9.361 }
9.362
9.363 /** Converts raw JSON data into a {@link Model} class representation.
9.364 - *
9.365 + *
9.366 * @param <T> type of the model to create
9.367 * @param type class of the model to create
9.368 * @param data raw JSON data (depends on associated {@link Technology})
9.369 @@ -426,7 +458,7 @@
9.370 }
9.371
9.372 /** Creates new JSON like observable list.
9.373 - *
9.374 + *
9.375 * @param <T> the type of the list elements
9.376 * @param propName name of a property this list is associated with
9.377 * @param onChange index of the property to use when the list is modified
9.378 @@ -441,7 +473,7 @@
9.379
9.380 /** Copies content of one collection to another, re-assigning all its
9.381 * elements from their current context to the new <code>ctx</code>.
9.382 - *
9.383 + *
9.384 * @param <T> type of the collections
9.385 * @param to the target collection to be filled with cloned values
9.386 * @param ctx context for the new collection
9.387 @@ -460,15 +492,15 @@
9.388 }
9.389 }
9.390 }
9.391 -
9.392 +
9.393 //
9.394 // internal state
9.395 //
9.396 -
9.397 +
9.398 final String toStr() {
9.399 return "Proto[" + obj + "]@" + Integer.toHexString(System.identityHashCode(this));
9.400 }
9.401 -
9.402 +
9.403 final Bindings initBindings() {
9.404 if (ko == null) {
9.405 Bindings b = Bindings.apply(context, obj);
9.406 @@ -507,7 +539,7 @@
9.407
9.408 /** Functionality used by the code generated by annotation
9.409 * processor for the {@link net.java.html.json.Model} annotation.
9.410 - *
9.411 + *
9.412 * @param <Model> the generated class
9.413 * @since 0.7
9.414 */
9.415 @@ -519,7 +551,7 @@
9.416
9.417 /** Constructor for subclasses generated by the annotation processor
9.418 * associated with {@link net.java.html.json.Model} annotation.
9.419 - *
9.420 + *
9.421 * @param clazz the generated model class
9.422 * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
9.423 * @param properties number of properties the class has
9.424 @@ -543,7 +575,7 @@
9.425
9.426 /** Registers property for the type. It is expected each index
9.427 * is initialized only once.
9.428 - *
9.429 + *
9.430 * @param name name of the property
9.431 * @param index index of the property
9.432 * @param readOnly is the property read only?
9.433 @@ -555,7 +587,7 @@
9.434 }
9.435
9.436 /** Registers function of given name at given index.
9.437 - *
9.438 + *
9.439 * @param name name of the function
9.440 * @param index name of the type
9.441 */
9.442 @@ -563,10 +595,10 @@
9.443 assert functions[index] == null;
9.444 functions[index] = name;
9.445 }
9.446 -
9.447 +
9.448 /** Creates new proto-object for given {@link Model} class bound to
9.449 * provided context.
9.450 - *
9.451 + *
9.452 * @param obj instance of appropriate {@link Model} class
9.453 * @param context the browser context
9.454 * @return new proto-object that the generated class can use for
9.455 @@ -575,32 +607,32 @@
9.456 public Proto createProto(Object obj, BrwsrCtx context) {
9.457 return new Proto(obj, this, context);
9.458 }
9.459 -
9.460 +
9.461 //
9.462 // Implemented by subclasses
9.463 //
9.464 -
9.465 +
9.466 /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
9.467 * to new value.
9.468 - *
9.469 + *
9.470 * @param model the instance of {@link Model model class}
9.471 * @param index index of the property used during registration
9.472 * @param value the value to set the property to
9.473 */
9.474 protected abstract void setValue(Model model, int index, Object value);
9.475 -
9.476 - /** Obtains and returns value of a
9.477 +
9.478 + /** Obtains and returns value of a
9.479 * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
9.480 - *
9.481 + *
9.482 * @param model the instance of {@link Model model class}
9.483 * @param index index of the property used during registration
9.484 * @return current value of the property
9.485 */
9.486 protected abstract Object getValue(Model model, int index);
9.487 -
9.488 +
9.489 /** Invokes a {@link #registerFunction(java.lang.String, int)} registered function
9.490 * on given object.
9.491 - *
9.492 + *
9.493 * @param model the instance of {@link Model model class}
9.494 * @param index index of the property used during registration
9.495 * @param data the currently selected object the function is about to operate on
9.496 @@ -609,43 +641,43 @@
9.497 */
9.498 protected abstract void call(Model model, int index, Object data, Object event)
9.499 throws Exception;
9.500 -
9.501 +
9.502 /** Re-binds the model object to new browser context.
9.503 - *
9.504 + *
9.505 * @param model the instance of {@link Model model class}
9.506 * @param ctx browser context to clone the object to
9.507 * @return new instance of the model suitable for new context
9.508 */
9.509 protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
9.510 -
9.511 +
9.512 /** Reads raw JSON data and converts them to our model class.
9.513 - *
9.514 + *
9.515 * @param c the browser context to work in
9.516 * @param json raw JSON data to get values from
9.517 * @return new instance of model class filled by the data
9.518 */
9.519 protected abstract Model read(BrwsrCtx c, Object json);
9.520 -
9.521 +
9.522 /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
9.523 * changes its value.
9.524 - *
9.525 + *
9.526 * @param model the object that has the property
9.527 * @param index the index of the property during registration
9.528 */
9.529 protected abstract void onChange(Model model, int index);
9.530 -
9.531 +
9.532 /** Finds out if there is an associated proto-object for given
9.533 * object.
9.534 - *
9.535 + *
9.536 * @param object an object, presumably (but not necessarily) instance of Model class
9.537 * @return associated proto-object or <code>null</code>
9.538 */
9.539 protected abstract Proto protoFor(Object object);
9.540
9.541 - /** Called to report results of asynchronous over-the-wire
9.542 + /** Called to report results of asynchronous over-the-wire
9.543 * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
9.544 * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
9.545 - *
9.546 + *
9.547 * @param model the instance of the model class
9.548 * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
9.549 * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
9.550 @@ -656,11 +688,11 @@
9.551 protected void onMessage(Model model, int index, int type, Object data) {
9.552 onMessage(model, index, type, data, new Object[0]);
9.553 }
9.554 -
9.555 - /** Called to report results of asynchronous over-the-wire
9.556 +
9.557 + /** Called to report results of asynchronous over-the-wire
9.558 * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
9.559 * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
9.560 - *
9.561 + *
9.562 * @param model the instance of the model class
9.563 * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
9.564 * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
9.565 @@ -682,7 +714,7 @@
9.566
9.567 /** Converts and array of raw JSON objects into an array of typed
9.568 * Java {@link Model} classes.
9.569 - *
9.570 + *
9.571 * @param <T> the type of the destination array
9.572 * @param context browser context to use
9.573 * @param src array of raw JSON objects
9.574 @@ -694,7 +726,7 @@
9.575 dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
9.576 }
9.577 }
9.578 -
9.579 +
9.580 /** Compares two objects that can be converted to integers.
9.581 * @param a first value
9.582 * @param b second value
9.583 @@ -719,7 +751,7 @@
9.584 * @param a first value
9.585 * @param b second value
9.586 * @return true if they are equals
9.587 - */
9.588 + */
9.589 public final boolean isSame(Object a, Object b) {
9.590 if (a == b) {
9.591 return true;
9.592 @@ -739,18 +771,18 @@
9.593 public final int hashPlus(Object o, int h) {
9.594 return o == null ? h : h ^ o.hashCode();
9.595 }
9.596 -
9.597 +
9.598 /** Converts an object to its JSON value.
9.599 - *
9.600 + *
9.601 * @param obj the object to convert
9.602 * @return JSON representation of the object
9.603 */
9.604 public final String toJSON(Object obj) {
9.605 return JSON.toJSON(obj);
9.606 }
9.607 -
9.608 +
9.609 /** Converts the value to string.
9.610 - *
9.611 + *
9.612 * @param val the value
9.613 * @return the converted value
9.614 */
9.615 @@ -759,7 +791,7 @@
9.616 }
9.617
9.618 /** Converts the value to number.
9.619 - *
9.620 + *
9.621 * @param val the value
9.622 * @return the converted value
9.623 */
9.624 @@ -768,7 +800,7 @@
9.625 }
9.626
9.627 /** Converts the value to character.
9.628 - *
9.629 + *
9.630 * @param val the value
9.631 * @return the converted value
9.632 */
9.633 @@ -777,16 +809,16 @@
9.634 }
9.635
9.636 /** Converts the value to boolean.
9.637 - *
9.638 + *
9.639 * @param val the value
9.640 * @return the converted value
9.641 */
9.642 public final Boolean boolValue(Object val) {
9.643 return JSON.boolValue(val);
9.644 }
9.645 -
9.646 +
9.647 /** Extracts value of specific type from given object.
9.648 - *
9.649 + *
9.650 * @param <T> the type of object one is interested in
9.651 * @param type the type
9.652 * @param val the object to convert to type
9.653 @@ -833,7 +865,7 @@
9.654 * takes the provided collection, empties it and fills it again
9.655 * with values extracted from <code>value</code> (which is supposed
9.656 * to be an array).
9.657 - *
9.658 + *
9.659 * @param <T> the type of list elements
9.660 * @param arr collection to fill with elements in value
9.661 * @param type the type of elements in the collection
10.1 --- a/json/src/test/java/net/java/html/json/ModelProcessorTest.java Wed May 27 22:40:01 2015 +0200
10.2 +++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java Wed May 27 23:37:25 2015 +0200
10.3 @@ -65,7 +65,7 @@
10.4 + "})\n"
10.5 + "class X {\n"
10.6 + "}\n";
10.7 -
10.8 +
10.9 Compile c = Compile.create(html, code);
10.10 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.11 boolean ok = false;
10.12 @@ -81,7 +81,7 @@
10.13 fail("Should contain warning about Runnable:" + msgs);
10.14 }
10.15 }
10.16 -
10.17 +
10.18 @Test public void verifyWrongTypeInInnerClass() throws IOException {
10.19 String html = "<html><body>"
10.20 + "</body></html>";
10.21 @@ -95,7 +95,7 @@
10.22 + " static class Inner {\n"
10.23 + " }\n"
10.24 + "}\n";
10.25 -
10.26 +
10.27 Compile c = Compile.create(html, code);
10.28 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.29 boolean ok = false;
10.30 @@ -111,7 +111,7 @@
10.31 fail("Should contain warning about Runnable:" + msgs);
10.32 }
10.33 }
10.34 -
10.35 +
10.36 @Test public void warnOnNonStatic() throws IOException {
10.37 String html = "<html><body>"
10.38 + "</body></html>";
10.39 @@ -127,7 +127,7 @@
10.40 + " return prop;\n"
10.41 + " }\n"
10.42 + "}\n";
10.43 -
10.44 +
10.45 Compile c = Compile.create(html, code);
10.46 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.47 boolean ok = false;
10.48 @@ -143,7 +143,7 @@
10.49 fail("Should contain warning about non-static method:" + msgs);
10.50 }
10.51 }
10.52 -
10.53 +
10.54 @Test public void computedCantReturnVoid() throws IOException {
10.55 String html = "<html><body>"
10.56 + "</body></html>";
10.57 @@ -158,7 +158,7 @@
10.58 + " @ComputedProperty static void y(int prop) {\n"
10.59 + " }\n"
10.60 + "}\n";
10.61 -
10.62 +
10.63 Compile c = Compile.create(html, code);
10.64 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.65 boolean ok = false;
10.66 @@ -174,7 +174,7 @@
10.67 fail("Should contain warning about non-static method:" + msgs);
10.68 }
10.69 }
10.70 -
10.71 +
10.72 @Test public void computedCantReturnRunnable() throws IOException {
10.73 String html = "<html><body>"
10.74 + "</body></html>";
10.75 @@ -190,7 +190,7 @@
10.76 + " return null;\n"
10.77 + " }\n"
10.78 + "}\n";
10.79 -
10.80 +
10.81 Compile c = Compile.create(html, code);
10.82 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.83 boolean ok = false;
10.84 @@ -206,7 +206,7 @@
10.85 fail("Should contain warning about non-static method:" + msgs);
10.86 }
10.87 }
10.88 -
10.89 +
10.90 @Test public void canWeCompileWithJDK1_5SourceLevel() throws IOException {
10.91 String html = "<html><body>"
10.92 + "</body></html>";
10.93 @@ -220,11 +220,11 @@
10.94 + "class X {\n"
10.95 + " @ComputedProperty static double derived(long prop) { return prop; }"
10.96 + "}\n";
10.97 -
10.98 +
10.99 Compile c = Compile.create(html, code, "1.5");
10.100 assertTrue(c.getErrors().isEmpty(), "No errors: " + c.getErrors());
10.101 }
10.102 -
10.103 +
10.104 @Test public void putNeedsDataArgument() throws Exception {
10.105 needsAnArg("PUT");
10.106 }
10.107 @@ -232,7 +232,7 @@
10.108 @Test public void postNeedsDataArgument() throws Exception {
10.109 needsAnArg("POST");
10.110 }
10.111 -
10.112 +
10.113 private void needsAnArg(String method) throws Exception {
10.114 String html = "<html><body>"
10.115 + "</body></html>";
10.116 @@ -250,7 +250,7 @@
10.117 + " @OnReceive(method=\"" + method + "\", url=\"whereever\")\n"
10.118 + " static void obtained(XModel m, PQ p) { }\n"
10.119 + "}\n";
10.120 -
10.121 +
10.122 Compile c = Compile.create(html, code);
10.123 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.124 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
10.125 @@ -260,10 +260,10 @@
10.126 }
10.127 }
10.128 fail("Needs an error message about missing data():\n" + c.getErrors());
10.129 -
10.130 +
10.131 }
10.132 -
10.133 -
10.134 +
10.135 +
10.136 @Test public void jsonNeedsToUseGet () throws Exception {
10.137 String html = "<html><body>"
10.138 + "</body></html>";
10.139 @@ -281,7 +281,7 @@
10.140 + " @OnReceive(method=\"POST\", jsonp=\"callback\", url=\"whereever\")\n"
10.141 + " static void obtained(XModel m, PQ p) { }\n"
10.142 + "}\n";
10.143 -
10.144 +
10.145 Compile c = Compile.create(html, code);
10.146 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.147 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
10.148 @@ -291,9 +291,99 @@
10.149 }
10.150 }
10.151 fail("Needs an error message about wrong method:\n" + c.getErrors());
10.152 -
10.153 +
10.154 }
10.155 -
10.156 +
10.157 + @Test public void noHeadersForWebSockets() throws Exception {
10.158 + String html = "<html><body>"
10.159 + + "</body></html>";
10.160 + String code = "package x.y.z;\n"
10.161 + + "import net.java.html.json.Model;\n"
10.162 + + "import net.java.html.json.Property;\n"
10.163 + + "import net.java.html.json.OnReceive;\n"
10.164 + + "@Model(className=\"XModel\", properties={\n"
10.165 + + " @Property(name=\"prop\", type=long.class)\n"
10.166 + + "})\n"
10.167 + + "class X {\n"
10.168 + + " @Model(className=\"PQ\", properties={})\n"
10.169 + + " class PImpl {\n"
10.170 + + " }\n"
10.171 + + " @OnReceive(method=\"WebSocket\", data = PQ.class, headers=\"SomeHeader: {some}\", url=\"whereever\")\n"
10.172 + + " static void obtained(XModel m, PQ p) { }\n"
10.173 + + "}\n";
10.174 +
10.175 + Compile c = Compile.create(html, code);
10.176 + assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.177 + for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
10.178 + String msg = diagnostic.getMessage(Locale.ENGLISH);
10.179 + if (msg.contains("WebSocket spec does not support headers")) {
10.180 + return;
10.181 + }
10.182 + }
10.183 + fail("Needs an error message about headers:\n" + c.getErrors());
10.184 +
10.185 + }
10.186 +
10.187 + @Test public void noNewLinesInHeaderLines() throws Exception {
10.188 + String html = "<html><body>"
10.189 + + "</body></html>";
10.190 + String code = "package x.y.z;\n"
10.191 + + "import net.java.html.json.Model;\n"
10.192 + + "import net.java.html.json.Property;\n"
10.193 + + "import net.java.html.json.OnReceive;\n"
10.194 + + "@Model(className=\"XModel\", properties={\n"
10.195 + + " @Property(name=\"prop\", type=long.class)\n"
10.196 + + "})\n"
10.197 + + "class X {\n"
10.198 + + " @Model(className=\"PQ\", properties={})\n"
10.199 + + " class PImpl {\n"
10.200 + + " }\n"
10.201 + + " @OnReceive(headers=\"SomeHeader\\n: {some}\", url=\"whereever\")\n"
10.202 + + " static void obtained(XModel m, PQ p) { }\n"
10.203 + + "}\n";
10.204 +
10.205 + Compile c = Compile.create(html, code);
10.206 + assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.207 + for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
10.208 + String msg = diagnostic.getMessage(Locale.ENGLISH);
10.209 + if (msg.contains("Header line cannot contain line separator")) {
10.210 + return;
10.211 + }
10.212 + }
10.213 + fail("Needs an error message about headers:\n" + c.getErrors());
10.214 +
10.215 + }
10.216 +
10.217 + @Test public void noReturnInHeaderLines() throws Exception {
10.218 + String html = "<html><body>"
10.219 + + "</body></html>";
10.220 + String code = "package x.y.z;\n"
10.221 + + "import net.java.html.json.Model;\n"
10.222 + + "import net.java.html.json.Property;\n"
10.223 + + "import net.java.html.json.OnReceive;\n"
10.224 + + "@Model(className=\"XModel\", properties={\n"
10.225 + + " @Property(name=\"prop\", type=long.class)\n"
10.226 + + "})\n"
10.227 + + "class X {\n"
10.228 + + " @Model(className=\"PQ\", properties={})\n"
10.229 + + " class PImpl {\n"
10.230 + + " }\n"
10.231 + + " @OnReceive(headers=\"Some\\rHeader: {some}\", url=\"whereever\")\n"
10.232 + + " static void obtained(XModel m, PQ p) { }\n"
10.233 + + "}\n";
10.234 +
10.235 + Compile c = Compile.create(html, code);
10.236 + assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
10.237 + for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
10.238 + String msg = diagnostic.getMessage(Locale.ENGLISH);
10.239 + if (msg.contains("Header line cannot contain line separator")) {
10.240 + return;
10.241 + }
10.242 + }
10.243 + fail("Needs an error message about headers:\n" + c.getErrors());
10.244 +
10.245 + }
10.246 +
10.247 @Test public void onErrorHasToExist() throws IOException {
10.248 Compile res = Compile.create("", "package x;\n"
10.249 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
10.250 @@ -308,7 +398,7 @@
10.251 res.assertErrors();
10.252 res.assertError("not find doesNotExist");
10.253 }
10.254 -
10.255 +
10.256 @Test public void usingListIsOK() throws IOException {
10.257 Compile res = Compile.create("", "package x;\n"
10.258 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
10.259 @@ -370,7 +460,7 @@
10.260 res.assertErrors();
10.261 res.assertError("Cannot have the name");
10.262 }
10.263 -
10.264 +
10.265 @Test public void onWebSocketJustTwoArgs() throws IOException {
10.266 Compile res = Compile.create("", "package x;\n"
10.267 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
10.268 @@ -385,7 +475,7 @@
10.269 res.assertErrors();
10.270 res.assertError("only have two arg");
10.271 }
10.272 -
10.273 +
10.274 @Test public void onErrorWouldHaveToBeStatic() throws IOException {
10.275 Compile res = Compile.create("", "package x;\n"
10.276 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
11.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java Wed May 27 22:40:01 2015 +0200
11.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java Wed May 27 23:37:25 2015 +0200
11.3 @@ -227,6 +227,18 @@
11.4
11.5 }
11.6
11.7 + @OnReceive(url="{url}", headers={
11.8 + "Easy: {easy}",
11.9 + "H-a+r!d?e.r: {harder}",
11.10 + "H-a+r!d?e's\"t: {harder}",
11.11 + "Repeat-ed: {rep}",
11.12 + "Repeat+ed: {rep}",
11.13 + "Same-URL: {url}"
11.14 + })
11.15 + static void fetchPeopleWithHeaders(Modelik model, People p) {
11.16 + model.fetchPeopleWithHeaders("url", "easy", "harder", "rep");
11.17 + }
11.18 +
11.19 @OnReceive(url = "{protocol}://{host}?callback={back}&query={query}", jsonp = "back")
11.20 static void loadPeopleViaJSONP(Modelik thiz, People p) {
11.21 Modelik m = null;
12.1 --- a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java Wed May 27 22:40:01 2015 +0200
12.2 +++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java Wed May 27 23:37:25 2015 +0200
12.3 @@ -76,28 +76,28 @@
12.4 private static List<Resource> resources;
12.5 private static ServerConfiguration conf;
12.6 private static HttpServer server;
12.7 -
12.8 +
12.9 private DynamicHTTP() {
12.10 }
12.11 -
12.12 +
12.13 static URI initServer() throws Exception {
12.14 server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
12.15 final WebSocketAddOn addon = new WebSocketAddOn();
12.16 for (NetworkListener listener : server.getListeners()) {
12.17 listener.registerAddOn(addon);
12.18 - }
12.19 + }
12.20 resources = new ArrayList<Resource>();
12.21
12.22 conf = server.getServerConfiguration();
12.23 final DynamicHTTP dh = new DynamicHTTP();
12.24
12.25 conf.addHttpHandler(dh, "/");
12.26 -
12.27 +
12.28 server.start();
12.29
12.30 return pageURL("http", server, "/test.html");
12.31 }
12.32 -
12.33 +
12.34 @Override
12.35 public void service(Request request, Response response) throws Exception {
12.36 if ("/test.html".equals(request.getRequestURI())) {
12.37 @@ -159,6 +159,8 @@
12.38 sb.append((char) ch);
12.39 }
12.40 params[i] = sb.toString();
12.41 + } else if (r.parameters[i].startsWith("http.header.")) {
12.42 + params[i] = request.getHeader(r.parameters[i].substring(12));
12.43 }
12.44 }
12.45 if (params[i] == null) {
12.46 @@ -171,7 +173,7 @@
12.47 }
12.48 }
12.49 }
12.50 -
12.51 +
12.52 private URI registerWebSocket(Resource r) {
12.53 WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
12.54 return pageURL("ws", server, r.httpPath);
12.55 @@ -184,7 +186,7 @@
12.56 }
12.57 return pageURL("http", server, r.httpPath);
12.58 }
12.59 -
12.60 +
12.61 private static URI pageURL(String proto, HttpServer server, final String page) {
12.62 NetworkListener listener = server.getListeners().iterator().next();
12.63 int port = listener.getPort();
12.64 @@ -194,7 +196,7 @@
12.65 throw new IllegalStateException(ex);
12.66 }
12.67 }
12.68 -
12.69 +
12.70 static final class Resource {
12.71
12.72 final InputStream httpContent;
12.73 @@ -235,7 +237,7 @@
12.74 }
12.75 }
12.76 }
12.77 -
12.78 +
12.79 private static class WS extends WebSocketApplication {
12.80 private final Resource r;
12.81
13.1 --- a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java Wed May 27 22:40:01 2015 +0200
13.2 +++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java Wed May 27 23:37:25 2015 +0200
13.3 @@ -76,28 +76,28 @@
13.4 private static List<Resource> resources;
13.5 private static ServerConfiguration conf;
13.6 private static HttpServer server;
13.7 -
13.8 +
13.9 private DynamicHTTP() {
13.10 }
13.11 -
13.12 +
13.13 static URI initServer() throws Exception {
13.14 server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
13.15 final WebSocketAddOn addon = new WebSocketAddOn();
13.16 for (NetworkListener listener : server.getListeners()) {
13.17 listener.registerAddOn(addon);
13.18 - }
13.19 + }
13.20 resources = new ArrayList<Resource>();
13.21
13.22 conf = server.getServerConfiguration();
13.23 final DynamicHTTP dh = new DynamicHTTP();
13.24
13.25 conf.addHttpHandler(dh, "/");
13.26 -
13.27 +
13.28 server.start();
13.29
13.30 return pageURL("http", server, "/test.html");
13.31 }
13.32 -
13.33 +
13.34 @Override
13.35 public void service(Request request, Response response) throws Exception {
13.36 if ("/test.html".equals(request.getRequestURI())) {
13.37 @@ -159,6 +159,8 @@
13.38 sb.append((char) ch);
13.39 }
13.40 params[i] = sb.toString();
13.41 + } else if (r.parameters[i].startsWith("http.header.")) {
13.42 + params[i] = request.getHeader(r.parameters[i].substring(12));
13.43 }
13.44 }
13.45 if (params[i] == null) {
13.46 @@ -171,7 +173,7 @@
13.47 }
13.48 }
13.49 }
13.50 -
13.51 +
13.52 private URI registerWebSocket(Resource r) {
13.53 WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
13.54 return pageURL("ws", server, r.httpPath);
13.55 @@ -184,7 +186,7 @@
13.56 }
13.57 return pageURL("http", server, r.httpPath);
13.58 }
13.59 -
13.60 +
13.61 private static URI pageURL(String proto, HttpServer server, final String page) {
13.62 NetworkListener listener = server.getListeners().iterator().next();
13.63 int port = listener.getPort();
13.64 @@ -194,7 +196,7 @@
13.65 throw new IllegalStateException(ex);
13.66 }
13.67 }
13.68 -
13.69 +
13.70 static final class Resource {
13.71
13.72 final InputStream httpContent;
13.73 @@ -235,7 +237,7 @@
13.74 }
13.75 }
13.76 }
13.77 -
13.78 +
13.79 private static class WS extends WebSocketApplication {
13.80 private final Resource r;
13.81
14.1 --- a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java Wed May 27 22:40:01 2015 +0200
14.2 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java Wed May 27 23:37:25 2015 +0200
14.3 @@ -59,11 +59,11 @@
14.4 import java.util.concurrent.ThreadFactory;
14.5 import java.util.logging.Logger;
14.6 import net.java.html.js.JavaScriptBody;
14.7 -import org.netbeans.html.json.spi.JSONCall;
14.8 import org.json.JSONArray;
14.9 import org.json.JSONException;
14.10 import org.json.JSONObject;
14.11 import org.json.JSONTokener;
14.12 +import org.netbeans.html.json.spi.JSONCall;
14.13
14.14 /** This is an implementation package - just
14.15 * include its JAR on classpath and use official {@link Context} API
14.16 @@ -101,7 +101,7 @@
14.17 final String url;
14.18 Throwable error = null;
14.19 Object json = null;
14.20 -
14.21 +
14.22 if (call.isJSONP()) {
14.23 url = call.composeURL("dummy");
14.24 } else {
14.25 @@ -113,6 +113,28 @@
14.26 if (call.isDoOutput()) {
14.27 conn.setDoOutput(true);
14.28 }
14.29 + String h = call.getHeaders();
14.30 + if (h != null) {
14.31 + int pos = 0;
14.32 + while (pos < h.length()) {
14.33 + int tagEnd = h.indexOf(':', pos);
14.34 + if (tagEnd == -1) {
14.35 + break;
14.36 + }
14.37 + int r = h.indexOf('\r', tagEnd);
14.38 + int n = h.indexOf('\n', tagEnd);
14.39 + if (r == -1) {
14.40 + r = h.length();
14.41 + }
14.42 + if (n == -1) {
14.43 + n = h.length();
14.44 + }
14.45 + String key = h.substring(pos, tagEnd).trim();
14.46 + String val = h.substring(tagEnd + 1, Math.min(r, n)).trim();
14.47 + conn.setRequestProperty(key, val);;
14.48 + pos = Math.max(r, n);
14.49 + }
14.50 + }
14.51 if (call.getMethod() != null && conn instanceof HttpURLConnection) {
14.52 ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
14.53 }
14.54 @@ -235,7 +257,7 @@
14.55 return o;
14.56 }
14.57 }
14.58 -
14.59 +
14.60 public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
14.61 if (jsonObject instanceof JSONObject) {
14.62 JSONObject obj = (JSONObject)jsonObject;
14.63 @@ -252,7 +274,7 @@
14.64 values[i] = getProperty(jsonObject, props[i]);
14.65 }
14.66 }
14.67 -
14.68 +
14.69 @JavaScriptBody(args = {"object", "property"},
14.70 body
14.71 = "if (property === null) return object;\n"
14.72 @@ -262,7 +284,7 @@
14.73 private static Object getProperty(Object object, String property) {
14.74 return null;
14.75 }
14.76 -
14.77 +
14.78 public static Object parse(InputStream is) throws IOException {
14.79 try {
14.80 PushbackInputStream push = new PushbackInputStream(is, 1);
15.1 --- a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java Wed May 27 22:40:01 2015 +0200
15.2 +++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java Wed May 27 23:37:25 2015 +0200
15.3 @@ -75,28 +75,28 @@
15.4 private static List<Resource> resources;
15.5 private static ServerConfiguration conf;
15.6 private static HttpServer server;
15.7 -
15.8 +
15.9 private TyrusDynamicHTTP() {
15.10 }
15.11 -
15.12 +
15.13 static URI initServer() throws Exception {
15.14 server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
15.15 final WebSocketAddOn addon = new WebSocketAddOn();
15.16 for (NetworkListener listener : server.getListeners()) {
15.17 listener.registerAddOn(addon);
15.18 - }
15.19 + }
15.20 resources = new ArrayList<Resource>();
15.21
15.22 conf = server.getServerConfiguration();
15.23 final TyrusDynamicHTTP dh = new TyrusDynamicHTTP();
15.24
15.25 conf.addHttpHandler(dh, "/");
15.26 -
15.27 +
15.28 server.start();
15.29
15.30 return pageURL("http", server, "/test.html");
15.31 }
15.32 -
15.33 +
15.34 @Override
15.35 public void service(Request request, Response response) throws Exception {
15.36 if ("/test.html".equals(request.getRequestURI())) {
15.37 @@ -158,6 +158,8 @@
15.38 sb.append((char) ch);
15.39 }
15.40 params[i] = sb.toString();
15.41 + } else if (r.parameters[i].startsWith("http.header.")) {
15.42 + params[i] = request.getHeader(r.parameters[i].substring(12));
15.43 }
15.44 }
15.45 if (params[i] == null) {
15.46 @@ -170,7 +172,7 @@
15.47 }
15.48 }
15.49 }
15.50 -
15.51 +
15.52 private URI registerWebSocket(Resource r) {
15.53 WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
15.54 return pageURL("ws", server, r.httpPath);
15.55 @@ -183,7 +185,7 @@
15.56 }
15.57 return pageURL("http", server, r.httpPath);
15.58 }
15.59 -
15.60 +
15.61 private static URI pageURL(String proto, HttpServer server, final String page) {
15.62 NetworkListener listener = server.getListeners().iterator().next();
15.63 int port = listener.getPort();
15.64 @@ -193,7 +195,7 @@
15.65 throw new IllegalStateException(ex);
15.66 }
15.67 }
15.68 -
15.69 +
15.70 static final class Resource {
15.71
15.72 final InputStream httpContent;
15.73 @@ -234,7 +236,7 @@
15.74 }
15.75 }
15.76 }
15.77 -
15.78 +
15.79 private static class WS extends WebSocketApplication {
15.80 private final Resource r;
15.81
15.82 @@ -255,6 +257,6 @@
15.83 }
15.84 }
15.85 private static final Logger LOG = Logger.getLogger(WS.class.getName());
15.86 -
15.87 +
15.88 }
15.89 }
16.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java Wed May 27 22:40:01 2015 +0200
16.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java Wed May 27 23:37:25 2015 +0200
16.3 @@ -46,6 +46,8 @@
16.4 import java.io.IOException;
16.5 import java.io.InputStream;
16.6 import java.io.InputStreamReader;
16.7 +import java.util.ArrayList;
16.8 +import java.util.List;
16.9 import org.netbeans.html.context.spi.Contexts;
16.10 import org.netbeans.html.json.spi.JSONCall;
16.11 import org.netbeans.html.json.spi.Transfer;
16.12 @@ -62,7 +64,7 @@
16.13 implements Transfer {
16.14 KOTransfer() {
16.15 }
16.16 -
16.17 +
16.18 @Override
16.19 public void extract(Object obj, String[] props, Object[] values) {
16.20 if (obj instanceof JSObjToStr) {
16.21 @@ -87,7 +89,29 @@
16.22 call.notifyError(ex);
16.23 }
16.24 }
16.25 - LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data);
16.26 + List<String> headerPairs = new ArrayList<String>();
16.27 + String h = call.getHeaders();
16.28 + if (h != null) {
16.29 + int pos = 0;
16.30 + while (pos < h.length()) {
16.31 + int tagEnd = h.indexOf(':', pos);
16.32 + if (tagEnd == -1) {
16.33 + break;
16.34 + }
16.35 + int r = h.indexOf('\r', tagEnd);
16.36 + int n = h.indexOf('\n', tagEnd);
16.37 + if (r == -1) {
16.38 + r = h.length();
16.39 + }
16.40 + if (n == -1) {
16.41 + n = h.length();
16.42 + }
16.43 + headerPairs.add(h.substring(pos, tagEnd).trim());
16.44 + headerPairs.add(h.substring(tagEnd + 1, Math.min(r, n)).trim());
16.45 + pos = Math.max(r, n);
16.46 + }
16.47 + }
16.48 + LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data, headerPairs.toArray());
16.49 }
16.50 }
16.51
16.52 @@ -104,7 +128,7 @@
16.53 }
16.54 return LoadJSON.parse(sb.toString());
16.55 }
16.56 -
16.57 +
16.58 static void notifySuccess(Object done, Object str, Object data) {
16.59 Object notifyObj;
16.60 if (data instanceof Object[]) {
16.61 @@ -118,11 +142,11 @@
16.62 }
16.63 ((JSONCall)done).notifySuccess(notifyObj);
16.64 }
16.65 -
16.66 +
16.67 static void notifyError(Object done, Object msg) {
16.68 ((JSONCall)done).notifyError(new Exception(msg.toString()));
16.69 }
16.70 -
16.71 +
16.72 private static final class JSObjToStr {
16.73 final String str;
16.74 final Object obj;
17.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java Wed May 27 22:40:01 2015 +0200
17.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java Wed May 27 23:37:25 2015 +0200
17.3 @@ -42,80 +42,16 @@
17.4 */
17.5 package org.netbeans.html.ko4j;
17.6
17.7 -import java.io.ByteArrayOutputStream;
17.8 -import java.io.IOException;
17.9 -import java.io.InputStream;
17.10 -import java.io.InputStreamReader;
17.11 import net.java.html.js.JavaScriptBody;
17.12 import org.netbeans.html.json.spi.JSONCall;
17.13 -import org.netbeans.html.json.spi.Transfer;
17.14 -import org.netbeans.html.json.spi.WSTransfer;
17.15
17.16 /**
17.17 *
17.18 * @author Jaroslav Tulach
17.19 */
17.20 -final class LoadJSON implements Transfer, WSTransfer<LoadWS> {
17.21 +final class LoadJSON {
17.22 private LoadJSON() {}
17.23 -
17.24 - @Override
17.25 - public void extract(Object obj, String[] props, Object[] values) {
17.26 - extractJSON(obj, props, values);
17.27 - }
17.28
17.29 - @Override
17.30 - public void loadJSON(final JSONCall call) {
17.31 - if (call.isJSONP()) {
17.32 - String me = createJSONP(call);
17.33 - loadJSONP(call.composeURL(me), me);
17.34 - } else {
17.35 - String data = null;
17.36 - if (call.isDoOutput()) {
17.37 - try {
17.38 - ByteArrayOutputStream bos = new ByteArrayOutputStream();
17.39 - call.writeData(bos);
17.40 - data = new String(bos.toByteArray(), "UTF-8");
17.41 - } catch (IOException ex) {
17.42 - call.notifyError(ex);
17.43 - }
17.44 - }
17.45 - loadJSON(call.composeURL(null), call, call.getMethod(), data);
17.46 - }
17.47 - }
17.48 -
17.49 - @Override
17.50 - public Object toJSON(InputStream is) throws IOException {
17.51 - StringBuilder sb = new StringBuilder();
17.52 - InputStreamReader r = new InputStreamReader(is);
17.53 - for (;;) {
17.54 - int ch = r.read();
17.55 - if (ch == -1) {
17.56 - break;
17.57 - }
17.58 - sb.append((char)ch);
17.59 - }
17.60 - return parse(sb.toString());
17.61 - }
17.62 -
17.63 - @Override
17.64 - public LoadWS open(String url, JSONCall callback) {
17.65 - return new LoadWS(callback, url);
17.66 - }
17.67 -
17.68 - @Override
17.69 - public void send(LoadWS socket, JSONCall data) {
17.70 - socket.send(data);
17.71 - }
17.72 -
17.73 - @Override
17.74 - public void close(LoadWS socket) {
17.75 - socket.close();
17.76 - }
17.77 -
17.78 - //
17.79 - // implementations
17.80 - //
17.81 -
17.82 @JavaScriptBody(args = {"object", "property"},
17.83 body
17.84 = "if (property === null) return object;\n"
17.85 @@ -157,11 +93,16 @@
17.86 return s;
17.87 }
17.88
17.89 - @JavaScriptBody(args = {"url", "done", "method", "data"}, javacall = true, body = ""
17.90 + @JavaScriptBody(args = {"url", "done", "method", "data", "hp"}, javacall = true, body = ""
17.91 + "var request = new XMLHttpRequest();\n"
17.92 + "if (!method) method = 'GET';\n"
17.93 + "request.open(method, url, true);\n"
17.94 + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
17.95 + + "for (var i = 0; i < hp.length; i += 2) {\n"
17.96 + + " var h = hp[i];\n"
17.97 + + " var v = hp[i + 1];\n"
17.98 + + " request.setRequestHeader(h, v);\n"
17.99 + + "}\n"
17.100 + "request.onreadystatechange = function() {\n"
17.101 + " if (request.readyState !== 4) return;\n"
17.102 + " var r = request.response || request.responseText;\n"
17.103 @@ -182,10 +123,10 @@
17.104 + "else request.send();\n"
17.105 )
17.106 static void loadJSON(
17.107 - String url, JSONCall done, String method, String data
17.108 + String url, JSONCall done, String method, String data, Object[] headerPairs
17.109 ) {
17.110 }
17.111 -
17.112 +
17.113 @JavaScriptBody(args = {"url", "jsonp"}, body
17.114 = "var scrpt = window.document.createElement('script');\n "
17.115 + "scrpt.setAttribute('src', url);\n "
17.116 @@ -203,5 +144,5 @@
17.117 values[i] = getProperty(jsonObject, props[i]);
17.118 }
17.119 }
17.120 -
17.121 +
17.122 }
18.1 --- a/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java Wed May 27 22:40:01 2015 +0200
18.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java Wed May 27 23:37:25 2015 +0200
18.3 @@ -76,28 +76,28 @@
18.4 private static List<Resource> resources;
18.5 private static ServerConfiguration conf;
18.6 private static HttpServer server;
18.7 -
18.8 +
18.9 private DynamicHTTP() {
18.10 }
18.11 -
18.12 +
18.13 static URI initServer() throws Exception {
18.14 server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
18.15 final WebSocketAddOn addon = new WebSocketAddOn();
18.16 for (NetworkListener listener : server.getListeners()) {
18.17 listener.registerAddOn(addon);
18.18 - }
18.19 + }
18.20 resources = new ArrayList<Resource>();
18.21
18.22 conf = server.getServerConfiguration();
18.23 final DynamicHTTP dh = new DynamicHTTP();
18.24
18.25 conf.addHttpHandler(dh, "/");
18.26 -
18.27 +
18.28 server.start();
18.29
18.30 return pageURL("http", server, "/test.html");
18.31 }
18.32 -
18.33 +
18.34 @Override
18.35 public void service(Request request, Response response) throws Exception {
18.36 if ("/test.html".equals(request.getRequestURI())) {
18.37 @@ -159,6 +159,8 @@
18.38 sb.append((char) ch);
18.39 }
18.40 params[i] = sb.toString();
18.41 + } else if (r.parameters[i].startsWith("http.header.")) {
18.42 + params[i] = request.getHeader(r.parameters[i].substring(12));
18.43 }
18.44 }
18.45 if (params[i] == null) {
18.46 @@ -171,7 +173,7 @@
18.47 }
18.48 }
18.49 }
18.50 -
18.51 +
18.52 private URI registerWebSocket(Resource r) {
18.53 WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
18.54 return pageURL("ws", server, r.httpPath);
18.55 @@ -184,7 +186,7 @@
18.56 }
18.57 return pageURL("http", server, r.httpPath);
18.58 }
18.59 -
18.60 +
18.61 private static URI pageURL(String proto, HttpServer server, final String page) {
18.62 NetworkListener listener = server.getListeners().iterator().next();
18.63 int port = listener.getPort();
18.64 @@ -194,7 +196,7 @@
18.65 throw new IllegalStateException(ex);
18.66 }
18.67 }
18.68 -
18.69 +
18.70 static final class Resource {
18.71
18.72 final InputStream httpContent;
18.73 @@ -235,7 +237,7 @@
18.74 }
18.75 }
18.76 }
18.77 -
18.78 +
18.79 private static class WS extends WebSocketApplication {
18.80 private final Resource r;
18.81
19.1 --- a/src/main/javadoc/overview.html Wed May 27 22:40:01 2015 +0200
19.2 +++ b/src/main/javadoc/overview.html Wed May 27 23:37:25 2015 +0200
19.3 @@ -51,62 +51,65 @@
19.4 </head>
19.5 <body>
19.6 <p>
19.7 - Use Java to write application logic; Use HTML5 to render the UI;
19.8 + Use Java to write application logic; Use HTML5 to render the UI;
19.9 {@link net.java.html.json.Model Animate an HTML page from Java}
19.10 (see <a target="_blank" href="http://dew.apidesign.org/dew/#7212206">Duke being rotated</a> by CSS);
19.11 Use {@link net.java.html.json.OnReceive REST} or
19.12 <a href="net/java/html/json/doc-files/websockets.html">WebSockets</a>;
19.13 interact with <a href="net/java/html/js/package-summary.html">JavaScript</a>;
19.14 Get the best of both worlds!
19.15 -
19.16 - The goal of these APIs is to use full featured Java runtime
19.17 - (like real <a href="http://wiki.apidesign.org/wiki/HotSpot">HotSpot VM</a>),
19.18 - but still rely on a very lightweight rendering technology
19.19 - (so it can potentially fit
19.20 +
19.21 + The goal of these APIs is to use full featured Java runtime
19.22 + (like real <a href="http://wiki.apidesign.org/wiki/HotSpot">HotSpot VM</a>),
19.23 + but still rely on a very lightweight rendering technology
19.24 + (so it can potentially fit
19.25 <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> and definitely
19.26 - to various types of phones). What can be more lightweight
19.27 - (from a browser perspective) than
19.28 + to various types of phones). What can be more lightweight
19.29 + (from a browser perspective) than
19.30 <a href="http://wiki.apidesign.org/wiki/HTML">HTML</a>!?
19.31 - By default we use {@link net.java.html.boot.fx JavaFX's WebView}
19.32 - component to display the <a href="http://wiki.apidesign.org/wiki/HTML">HTML</a>.
19.33 - We eliminate the need to manipulate the DOM directly,
19.34 - there is a special {@link net.java.html.json Java to Knockout.js binding}.
19.35 - As a result the <a href="http://knockoutjs.com">HTML uses Knockout.js syntax</a>,
19.36 + By default we use {@link net.java.html.boot.fx JavaFX's WebView}
19.37 + component to display the <a href="http://wiki.apidesign.org/wiki/HTML">HTML</a>.
19.38 + We eliminate the need to manipulate the DOM directly,
19.39 + there is a special {@link net.java.html.json Java to Knockout.js binding}.
19.40 + As a result the <a href="http://knockoutjs.com">HTML uses Knockout.js syntax</a>,
19.41 yet the application code can be written in Java.
19.42 </p>
19.43 -
19.44 +
19.45 <h3>What's Been Improved in Version 1.2?</h3>
19.46 -
19.47 +
19.48 + One can control {@link net.java.html.json.OnReceive#headers() HTTP request headers}
19.49 + when connecting to server using the {@link net.java.html.json.OnReceive}
19.50 + annotation.
19.51 Bugfix of issue <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=250503'>250503</a>.
19.52 -
19.53 +
19.54 <h3>What's New in Version 1.1?</h3>
19.55 -
19.56 +
19.57 <p>
19.58 The content of a {@link net.java.html.BrwsrCtx context}
19.59 can be selected by registering implementations under specific
19.60 - {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers}
19.61 - and requesting them during
19.62 - {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction}
19.63 + {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers}
19.64 + and requesting them during
19.65 + {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction}
19.66 of the context. <code>org.netbeans.html:ko4j</code> module's implementation
19.67 offers <b>ko4j</b>, <b>xhr</b> and <b>websocket</b> identifiers
19.68 - for its registered services
19.69 + for its registered services
19.70 (e.g. {@link org.netbeans.html.json.spi.Technology},
19.71 {@link org.netbeans.html.json.spi.Transfer} and
19.72 {@link org.netbeans.html.json.spi.WSTransfer}).
19.73 <code>org.netbeans.html:ko-ws-tyrus</code>
19.74 - module registers its
19.75 - {@link org.netbeans.html.json.spi.Transfer Java based JSON} and
19.76 + module registers its
19.77 + {@link org.netbeans.html.json.spi.Transfer Java based JSON} and
19.78 {@link org.netbeans.html.json.spi.WSTransfer WebSocket} implementations
19.79 under the name <b>tyrus</b>.
19.80 </p>
19.81 <p>
19.82 A particular DOM subtree
19.83 that a <a target="_blank" href="http://knockoutjs.com">knockout.js</a> model gets
19.84 - applied to can be selected by using
19.85 + applied to can be selected by using
19.86 {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)
19.87 Models.applyBindings(m, id)} with an id of an HTML element.
19.88 - There is new {@link net.java.html.json.Model#targetId()} attribute
19.89 - which controls behavior of the generated <code>applyBindings</code> method.
19.90 + There is new {@link net.java.html.json.Model#targetId()} attribute
19.91 + which controls behavior of the generated <code>applyBindings</code> method.
19.92 If <em>specified and non-empty</em>, then the generated method
19.93 will call {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)}
19.94 with <code>this</code> and the provided {@link net.java.html.json.Model#targetId() target id}.
19.95 @@ -124,15 +127,15 @@
19.96 {@link org.netbeans.html.json.spi.PropertyBinding#weak()} and
19.97 {@link org.netbeans.html.json.spi.FunctionBinding#weak()}) and
19.98 now the Java {@link net.java.html.json.Model models} can garbage collect,
19.99 - when no longer used. Library writers that use
19.100 - {@link net.java.html.js.JavaScriptBody} annotation can also
19.101 + when no longer used. Library writers that use
19.102 + {@link net.java.html.js.JavaScriptBody} annotation can also
19.103 control garbage collection behavior of method arguments by
19.104 setting {@link net.java.html.js.JavaScriptBody#keepAlive() keepAlive=false}
19.105 attribute.
19.106 </p>
19.107 -
19.108 +
19.109 <h3>What's New in Version 1.0?</h3>
19.110 -
19.111 +
19.112 <p>
19.113 {@link net.java.html.json.Property#array() Array properties} are now
19.114 mutable from the <a href="http://knockoutjs.com">knockout.js</a>
19.115 @@ -151,14 +154,14 @@
19.116 {@link net.java.html.BrwsrCtx#execute browser context} to
19.117 prevent endless debugging when one forgets to do so.
19.118 </p>
19.119 -
19.120 +
19.121 <p>
19.122 - What's new in older versions? Click the
19.123 - <a href="#" onclick="return showHistoric(true)">link</a>
19.124 - to view even more
19.125 + What's new in older versions? Click the
19.126 + <a href="#" onclick="return showHistoric(true)">link</a>
19.127 + to view even more
19.128 <a href="#" onclick="return showHistoric(true)">historic changes</a> below:
19.129 </p>
19.130 -
19.131 +
19.132 <a name="historic.changes"></a>
19.133 <div id="historic.changes">
19.134 <script>
19.135 @@ -173,9 +176,9 @@
19.136 }
19.137 showHistoric(false);
19.138 </script>
19.139 -
19.140 +
19.141 <h3>What's New in Version 0.9?</h3>
19.142 -
19.143 +
19.144 <p>
19.145 System can run in {@link net.java.html.boot.BrowserBuilder#classloader(java.lang.ClassLoader) Felix OSGi container} (originally only Equinox).
19.146 {@link net.java.html.json.ComputedProperty Derived properties}
19.147 @@ -184,32 +187,32 @@
19.148 <a target="_blank" href="http://knockoutjs.com">Knockout.js</a> library has been updated
19.149 to version 3.2.0.
19.150 </p>
19.151 -
19.152 +
19.153 <h3>What's New in 0.8.x Versions?</h3>
19.154 -
19.155 +
19.156 <p>
19.157 Setters or array properties on classes generated by {@link net.java.html.json.Model}
19.158 annotation can be accessed from any thread. {@link org.netbeans.html.sound.spi.AudioEnvironment}
19.159 can be registered into {@link net.java.html.BrwsrCtx}. There is
19.160 a {@link net.java.html.json.Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream, java.util.Collection) method}
19.161 - to parse a JSON array and convert it into
19.162 + to parse a JSON array and convert it into
19.163 {@link net.java.html.json.Model model classes}.
19.164 - Improved behavior of <code>enum</code> values in
19.165 + Improved behavior of <code>enum</code> values in
19.166 {@link net.java.html.json.Model knockout bindings}.
19.167 </p>
19.168 -
19.169 +
19.170 <p>
19.171 - Few bugfixes for better portability.
19.172 + Few bugfixes for better portability.
19.173 New API for {@link net.java.html.boot.script.Scripts headless execution}
19.174 on top of <em>Nashorn</em> - does not run <em>knockout for Java</em>
19.175 - fully yet
19.176 + fully yet
19.177 (reported as <a href="https://bugs.openjdk.java.net/browse/JDK-8046013">JDK-8046013</a>),
19.178 - however even in current state it is quite
19.179 + however even in current state it is quite
19.180 {@link net.java.html.boot.script.Scripts useful for testing}
19.181 - of
19.182 + of
19.183 {@link net.java.html.js Java/JavaScript interactions}.
19.184 </p>
19.185 -
19.186 +
19.187 <p>
19.188 {@link net.java.html.boot.fx.FXBrowsers} has been extended
19.189 with new helper methods to make it easier to use HTML+Java
19.190 @@ -226,22 +229,22 @@
19.191 extended to work on systems that don't support
19.192 {@link java.lang.Class#getProtectionDomain}.
19.193 </p>
19.194 -
19.195 +
19.196 <p>
19.197 The first argument of method annotated by
19.198 {@link net.java.html.json.OnReceive} annotation has to
19.199 be the associated {@link net.java.html.json.Model model class}.
19.200 </p>
19.201 -
19.202 +
19.203 <p>
19.204 {@link net.java.html.json.OnReceive} annotation now accepts
19.205 {@link java.util.List} of data values as second argument
19.206 (previously required an array).
19.207 </p>
19.208 -
19.209 -
19.210 +
19.211 +
19.212 <h3>What's New in 0.7.x Versions?</h3>
19.213 -
19.214 +
19.215 <p>
19.216 {@link net.java.html.js.JavaScriptBody} annotation has new attribute
19.217 {@link net.java.html.js.JavaScriptBody#wait4js()} which allows
19.218 @@ -250,17 +253,17 @@
19.219 new attribute as much as possible, as it can speed up execution
19.220 in certain environments.
19.221 </p>
19.222 -
19.223 +
19.224 <p>
19.225 Use {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable)} in
19.226 multi-threaded environment to execute your code on the browser thread.
19.227 - See example
19.228 + See example
19.229 {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable) using Java timer}.
19.230 </p>
19.231 </div>
19.232 -
19.233 +
19.234 <h3>Interesting Entry Points</h3>
19.235 -
19.236 +
19.237 <p>Learn how to {@link net.java.html.json.Model animate an HTML page from Java}
19.238 without referencing single HTML element from the Java code.
19.239 </p>
19.240 @@ -276,51 +279,51 @@
19.241
19.242 <h3>Getting Started</h3>
19.243
19.244 - There are <a href="http://wiki.apidesign.org/wiki/DukeScriptInNetBeans">many ways</a>
19.245 - to start developing
19.246 - <a href="http://html.java.net">Html for Java</a> application.
19.247 + There are <a href="http://wiki.apidesign.org/wiki/DukeScriptInNetBeans">many ways</a>
19.248 + to start developing
19.249 + <a href="http://html.java.net">Html for Java</a> application.
19.250 However to be sure one chooses the most recent setup, it is recommended
19.251 - to switch to good old command line and use a
19.252 + to switch to good old command line and use a
19.253 <a href="http://wiki.apidesign.org/wiki/Knockout4Java">Maven archetype</a>
19.254 - associated with every version of this project. Make sure at least
19.255 + associated with every version of this project. Make sure at least
19.256 <em>JDK7</em> is your installed Java and type:
19.257 - <pre>
19.258 + <pre>
19.259 $ mvn archetype:generate \
19.260 -DarchetypeGroupId=org.apidesign.html \
19.261 -DarchetypeArtifactId=knockout4j-archetype \
19.262 -DarchetypeVersion=0.8 <em># or newer version, if available</em>
19.263 </pre>
19.264 - Answer few questions (for example choose myfirstbrwsrpage as artifactId)
19.265 + Answer few questions (for example choose myfirstbrwsrpage as artifactId)
19.266 and then you can:
19.267 <pre>
19.268 $ cd myfirstbrwsrpage
19.269 $ mvn process-classes exec:java
19.270 </pre>
19.271 - In a few seconds (or minutes if
19.272 + In a few seconds (or minutes if
19.273 <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
19.274 - decides to download the whole Internet of dependencies) you should
19.275 - see a sample Hello World application rendered in a
19.276 + decides to download the whole Internet of dependencies) you should
19.277 + see a sample Hello World application rendered in a
19.278 <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>
19.279 web view component (that of course requires your JDK to come
19.280 - with <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>;
19.281 - <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK7
19.282 - and JDK8 from Oracle</a> contain everything that is needed).
19.283 - The generated application is built around one
19.284 + with <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>;
19.285 + <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK7
19.286 + and JDK8 from Oracle</a> contain everything that is needed).
19.287 + The generated application is built around one
19.288 Java source (uses the {@link net.java.html.json.Model} annotation to
19.289 auto-generate another <code>Data.java</code> class during compilation)
19.290 and one HTML file (uses the <a href="http://knockoutjs.com">Knockout</a>
19.291 - syntax to <code>data-bind</code> the HTML elements to the
19.292 + syntax to <code>data-bind</code> the HTML elements to the
19.293 generated <code>Data</code> model):
19.294 <pre>
19.295 $ ls src/main/java/**/DataModel.java
19.296 $ ls src/main/webapp/pages/index.html
19.297 </pre>
19.298 - That is all you need to get started. Play with the sources,
19.299 + That is all you need to get started. Play with the sources,
19.300 modify them and enjoy
19.301 <a href="http://html.java.net">Html for Java</a>!
19.302 -
19.303 +
19.304 <h2>IDE Support</h2>
19.305 -
19.306 +
19.307 <p>
19.308 This API is part of <a target="_blank"
19.309 href="http://netbeans.org">NetBeans.org</a> project and as such
19.310 @@ -330,77 +333,77 @@
19.311 it builds on standard Java6 APIs and as such it shall work fine
19.312 in any IDE.
19.313 </p>
19.314 -
19.315 +
19.316 <p>
19.317 - A lot of work is done by
19.318 + A lot of work is done by
19.319 <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">
19.320 annotation processors</a>
19.321 that generate various boiler plate code during compilation. This
19.322 is a standard part of Java since JDK6, but for example Eclipse
19.323 is known not to deal with processors well and developers using
19.324 it need to be careful. IntelliJ users hasn't reported any issues
19.325 - and of course, NetBeans IDE support for
19.326 + and of course, NetBeans IDE support for
19.327 <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">processors</a>
19.328 is outstanding.
19.329 </p>
19.330 -
19.331 +
19.332 <p>
19.333 When using {@link net.java.html.js.JavaScriptBody} annotation, it is
19.334 useful to do a bit of post processing of classes. There is a
19.335 - <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
19.336 + <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
19.337 plugin for that.
19.338 - NetBeans IDE will invoke it when doing a build. Other IDEs may
19.339 - need some hint to do so.
19.340 + NetBeans IDE will invoke it when doing a build. Other IDEs may
19.341 + need some hint to do so.
19.342 Anyway: If one does not see all (generated) sources or is getting
19.343 - {@link java.lang.LinkageError}s when executing the application,
19.344 + {@link java.lang.LinkageError}s when executing the application,
19.345 switch to command line and do clean build
19.346 from there:
19.347 </p>
19.348 <pre>$ mvn clean install</pre>
19.349 <p>
19.350 If that succeeds, your IDE of choice will hopefully
19.351 - pick the generated sources up and present the result of the build
19.352 - properly. If not,
19.353 - <a href="https://netbeans.org/downloads/">download NetBeans</a>,
19.354 - you will be pleasantly
19.355 - surprised - for example with our excellent
19.356 - <a href="net/java/html/js/package-summary.html#debugging">Java/JavaScript
19.357 + pick the generated sources up and present the result of the build
19.358 + properly. If not,
19.359 + <a href="https://netbeans.org/downloads/">download NetBeans</a>,
19.360 + you will be pleasantly
19.361 + surprised - for example with our excellent
19.362 + <a href="net/java/html/js/package-summary.html#debugging">Java/JavaScript
19.363 debugging</a> support.
19.364 </p>
19.365 -
19.366 +
19.367 <a name="deploy">
19.368 <h2>Deploy Your Application</h2>
19.369 </a>
19.370 -
19.371 +
19.372 <p>
19.373 It is not goal of this documentation to list all possible ways
19.374 - to package and deploy applications which use this API. However it is
19.375 + to package and deploy applications which use this API. However it is
19.376 important for new comers to see the benefits of using the
19.377 <a href="http://html.java.net">HTML for Java</a> API and as such
19.378 - let's list at least few bundling options, known to work at the time of writing
19.379 + let's list at least few bundling options, known to work at the time of writing
19.380 this documentation.
19.381 </p>
19.382 -
19.383 +
19.384 <p>
19.385 First of all, this is a <em>client technology</em>. You write client applications
19.386 with it which may, but need not connect to a server. You don't need
19.387 - Tomcat or WebLogic to deploy
19.388 + Tomcat or WebLogic to deploy
19.389 <a href="http://html.java.net">HTML for Java</a> applications.
19.390 </p>
19.391 -
19.392 +
19.393 <p>
19.394 <img src='resources/javafx_logo.jpg' width="64"
19.395 height="64" align="left"/>
19.396 The sample project generated by
19.397 <code>org.apidesign.html knockout4j-archetype</code> is configured
19.398 to use <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>
19.399 - as the rendering technology. This setup is primarily suitable for
19.400 + as the rendering technology. This setup is primarily suitable for
19.401 development - it needs no special packaging, starts quickly and
19.402 - allows you to use classical HotSpot VM debuggers. A final
19.403 + allows you to use classical HotSpot VM debuggers. A final
19.404 artifact from the build is also a ZIP file which you can use
19.405 and distribute to your users. Good for desktop applications.
19.406 </p>
19.407 -
19.408 +
19.409 <p>
19.410 <img src='resources/netbeans_logo.jpg' width="64"
19.411 height="64" align="right"/>
19.412 @@ -409,17 +412,17 @@
19.413 All the <a href="http://html.java.net">HTML for Java</a> libraries
19.414 are packaged as <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a>
19.415 bundles and as such they can easily be run in NetBeans as well as
19.416 - in Eclipse. As a result one can use
19.417 + in Eclipse. As a result one can use
19.418 <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a>
19.419 - and have a common module system for both platforms. In addition to that
19.420 + and have a common module system for both platforms. In addition to that
19.421 one can render using
19.422 HTML and have a common UI in both platforms. In such case
19.423 your application would be packaged as a set of
19.424 <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a> bundles.
19.425 - Read
19.426 + Read
19.427 <a href="http://wiki.apidesign.org/wiki/HTML">more</a>...
19.428 </p>
19.429 -
19.430 +
19.431 <p>
19.432 <img src='resources/chrome_logo.png' width="64"
19.433 height="64" align="left"/>
19.434 @@ -429,7 +432,7 @@
19.435 height="64" align="left"/>
19.436 <img src='resources/firefox_logo.png' width="64"
19.437 height="64" align="left"/>
19.438 -
19.439 +
19.440 There is more and more attempts to execute Java bytecode
19.441 in a browser, without any special Java plugin installed.
19.442 The <a href="http://html.java.net">HTML for Java</a> is
19.443 @@ -437,16 +440,16 @@
19.444 applications even on such restricted environments. It uses
19.445 no reflection calls and that allows to statically pre-compile
19.446 the applications into JavaScript. One of such environments
19.447 - is called <a href="http://wiki.apidesign.org/wiki/Bck2Brwsr">Bck2Brwsr</a>,
19.448 + is called <a href="http://wiki.apidesign.org/wiki/Bck2Brwsr">Bck2Brwsr</a>,
19.449 another <a href="http://wiki.apidesign.org/wiki/TeaVM">TeaVM</a>. Both support the
19.450 - {@link net.java.html.js.JavaScriptBody} annotation. Read
19.451 + {@link net.java.html.js.JavaScriptBody} annotation. Read
19.452 <a href="http://wiki.apidesign.org/wiki/Bck2BrwsrViaCLI">more</a> or play
19.453 - a minesweeper game packaged for your browser
19.454 + a minesweeper game packaged for your browser
19.455 (of course <a target="_blank"
19.456 href="http://source.apidesign.org/hg/html~demo/file/ea79b73d590a/minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java">
19.457 written</a> in Java):
19.458 </p>
19.459 -
19.460 +
19.461 <script type="text/html" id="field">
19.462 <table class="field">
19.463 <tbody>
19.464 @@ -477,52 +480,53 @@
19.465 height="64" align="right"/>
19.466 <img src='resources/android_logo.jpg' width="64"
19.467 height="64" align="right"/>
19.468 -
19.469 - Now when we have seen that the
19.470 - <a href="http://html.java.net">HTML for Java</a> applications
19.471 +
19.472 + Now when we have seen that the
19.473 + <a href="http://html.java.net">HTML for Java</a> applications
19.474 can run on any modern browser, we can ask whether they can also
19.475 - fit into a phone!? Yes, they can and especially to phones
19.476 + fit into a phone!? Yes, they can and especially to phones
19.477 that can execute Java code already! Just by changing your
19.478 packaging you can create an APK file and deploy it to your
19.479 - Android phone.
19.480 + Android phone.
19.481 Read <a target="_blank" href="http://wiki.apidesign.org/wiki/DlvkBrwsr">more</a>...
19.482 In case you'd like your application to reach out to second biggest
19.483 - group of smartphone users, don't despair: It
19.484 + group of smartphone users, don't despair: It
19.485 seems the set of devices that can execute
19.486 - <a href="http://html.java.net">HTML for Java</a> applications
19.487 - has been extended to <em>iPads</em> and <em>iPhones</em>. Get the
19.488 + <a href="http://html.java.net">HTML for Java</a> applications
19.489 + has been extended to <em>iPads</em> and <em>iPhones</em>. Get the
19.490 <a target="_blank" href="http://wiki.apidesign.org/wiki/IBrwsr">details here</a>
19.491 and deploy everywhere!
19.492 </p>
19.493 <p>
19.494 - Convinced it makes sense to use
19.495 + Convinced it makes sense to use
19.496 <a href="http://html.java.net">HTML for Java</a>
19.497 - APIs for writing applications that are
19.498 + APIs for writing applications that are
19.499 <em>written once, displayed anywhere</em>? Or do you have an
19.500 environment which is not supported? In such case you can bring
19.501 <a href="http://html.java.net">HTML for Java</a>
19.502 to your environment yourself. Just implement your own
19.503 {@link org.netbeans.html.boot.spi.Fn.Presenter}!
19.504 </p>
19.505 -
19.506 +
19.507 <h2>Other Resources</h2>
19.508 -
19.509 +
19.510 <img src="net/java/html/json/doc-files/DukeHTML.png" width="256" height="184" alt="Duke and HTML5. Together at last!" align="right"/>
19.511 -
19.512 +
19.513 The javadoc for latest and previous versions is also available
19.514 online:
19.515 <ul>
19.516 <li>Current <a target="_blank" href="http://bits.netbeans.org/html+java/dev/">development</a> version
19.517 + <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.1">1.1</a>
19.518 <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.0">1.0</a>
19.519 - <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/0.9">0.9</a>
19.520 + <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/0.9">0.9</a>
19.521 and historic ones (<a target="_blank" href="http://bits.netbeans.org/html+java/0.8.3">0.8.3</a>,
19.522 - <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.2">0.8.2</a>,
19.523 + <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.2">0.8.2</a>,
19.524 <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.1">0.8.1</a>,
19.525 <a target="_blank" href="http://bits.netbeans.org/html+java/0.8">0.8</a>, and
19.526 <a target="_blank" href="http://bits.netbeans.org/html+java/0.7.5">0.7.5</a>)
19.527 </li>
19.528 </ul>
19.529 -
19.530 +
19.531 <style type="text/css">
19.532 table.field td {
19.533 padding: 4px;
19.534 @@ -541,6 +545,6 @@
19.535 table.field td.DISCOVERED {
19.536 background-color: #9DB2B1;
19.537 }
19.538 -</style>
19.539 +</style>
19.540 </body>
19.541 </html>