1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Mon Feb 15 05:27:28 2016 +0100
1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Mon Feb 29 05:33:31 2016 +0100
1.3 @@ -73,14 +73,14 @@
1.4 @Property(name="choice", type=KnockoutTest.Choice.class),
1.5 @Property(name="archetype", type=ArchetypeData.class),
1.6 @Property(name="archetypes", type=ArchetypeData.class, array = true),
1.7 -})
1.8 +})
1.9 public final class KnockoutTest {
1.10 private KnockoutModel js;
1.11 -
1.12 +
1.13 enum Choice {
1.14 A, B;
1.15 }
1.16 -
1.17 +
1.18 @ComputedProperty static List<Integer> resultLengths(List<String> results) {
1.19 Integer[] arr = new Integer[results.size()];
1.20 for (int i = 0; i < arr.length; i++) {
1.21 @@ -88,9 +88,9 @@
1.22 }
1.23 return Arrays.asList(arr);
1.24 }
1.25 -
1.26 +
1.27 @KOTest public void modifyValueAssertChangeInModelOnEnum() throws Throwable {
1.28 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.29 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.30 "Latitude: <input id='input' data-bind=\"value: choice\"></input>\n"
1.31 );
1.32 try {
1.33 @@ -143,9 +143,9 @@
1.34 Utils.exposeHTML(KnockoutTest.class, "");
1.35 }
1.36 }
1.37 -
1.38 +
1.39 @KOTest public void modifyValueAssertChangeInModelOnDouble() throws Throwable {
1.40 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.41 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.42 "Latitude: <input id='input' data-bind=\"value: latitude\"></input>\n"
1.43 );
1.44 try {
1.45 @@ -167,7 +167,7 @@
1.46 Utils.exposeHTML(KnockoutTest.class, "");
1.47 }
1.48 }
1.49 -
1.50 +
1.51 @KOTest public void rawObject() throws Exception {
1.52 if (js == null) {
1.53 final BrwsrCtx ctx = newContext();
1.54 @@ -200,7 +200,7 @@
1.55
1.56 p1.setFirstName("Ondra");
1.57 assertEquals(p1.getFirstName(), "Ondra", "1st name updated in original object");
1.58 -
1.59 +
1.60 js.getPeople().add(p1);
1.61 }
1.62
1.63 @@ -239,9 +239,9 @@
1.64 Utils.exposeHTML(KnockoutTest.class, "");
1.65 }
1.66 }
1.67 -
1.68 +
1.69 @KOTest public void modifyValueAssertChangeInModelOnBoolean() throws Throwable {
1.70 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.71 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.72 "Latitude: <input id='input' data-bind=\"value: enabled\"></input>\n"
1.73 );
1.74 try {
1.75 @@ -263,9 +263,9 @@
1.76 Utils.exposeHTML(KnockoutTest.class, "");
1.77 }
1.78 }
1.79 -
1.80 +
1.81 @KOTest public void modifyValueAssertChangeInModel() throws Exception {
1.82 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.83 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.84 "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
1.85 "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
1.86 "<button id=\"hello\">Say Hello!</button>\n"
1.87 @@ -287,7 +287,7 @@
1.88 Utils.exposeHTML(KnockoutTest.class, "");
1.89 }
1.90 }
1.91 -
1.92 +
1.93 private static String getSetSelected(int index, Object value) throws Exception {
1.94 String s = "var index = arguments[0];\n"
1.95 + "var value = arguments[1];\n"
1.96 @@ -305,7 +305,7 @@
1.97 );
1.98 return ret == null ? null : ret.toString();
1.99 }
1.100 -
1.101 +
1.102 @Model(className = "ArchetypeData", properties = {
1.103 @Property(name = "artifactId", type = String.class),
1.104 @Property(name = "groupId", type = String.class),
1.105 @@ -316,35 +316,35 @@
1.106 })
1.107 static class ArchModel {
1.108 }
1.109 -
1.110 +
1.111 @KOTest public void selectWorksOnModels() throws Exception {
1.112 if (js == null) {
1.113 - Utils.exposeHTML(KnockoutTest.class,
1.114 + Utils.exposeHTML(KnockoutTest.class,
1.115 "<select id='input' data-bind=\"options: archetypes,\n" +
1.116 " optionsText: 'name',\n" +
1.117 " value: archetype\">\n" +
1.118 " </select>\n" +
1.119 ""
1.120 );
1.121 -
1.122 +
1.123 js = Models.bind(new KnockoutModel(), newContext());
1.124 js.getArchetypes().add(new ArchetypeData("ko4j", "org.netbeans.html", "0.8.3", "ko4j", "ko4j", null));
1.125 js.getArchetypes().add(new ArchetypeData("crud", "org.netbeans.html", "0.8.3", "crud", "crud", null));
1.126 js.getArchetypes().add(new ArchetypeData("3rd", "org.netbeans.html", "0.8.3", "3rd", "3rd", null));
1.127 js.setArchetype(js.getArchetypes().get(1));
1.128 js.applyBindings();
1.129 -
1.130 +
1.131 String v = getSetSelected(0, null);
1.132 assertEquals("crud", v, "Second index (e.g. crud) is selected: " + v);
1.133 -
1.134 +
1.135 String sel = getSetSelected(2, Models.toRaw(js.getArchetypes().get(2)));
1.136 assertEquals("3rd", sel, "3rd is selected now: " + sel);
1.137 }
1.138 -
1.139 +
1.140 if (js.getArchetype() != js.getArchetypes().get(2)) {
1.141 throw new InterruptedException();
1.142 }
1.143 -
1.144 +
1.145 Utils.exposeHTML(KnockoutTest.class, "");
1.146 }
1.147
1.148 @@ -374,22 +374,22 @@
1.149 assertEquals("org.netbeans.html", v, "groupId has been changed");
1.150 Utils.exposeHTML(KnockoutTest.class, "");
1.151 }
1.152 -
1.153 +
1.154 @KOTest public void modifyValueAssertAsyncChangeInModel() throws Exception {
1.155 if (js == null) {
1.156 - Utils.exposeHTML(KnockoutTest.class,
1.157 + Utils.exposeHTML(KnockoutTest.class,
1.158 "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
1.159 "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
1.160 "<button id=\"hello\">Say Hello!</button>\n"
1.161 );
1.162 -
1.163 +
1.164 js = Models.bind(new KnockoutModel(), newContext());
1.165 js.setName("Kukuc");
1.166 js.applyBindings();
1.167 -
1.168 +
1.169 String v = getSetInput("input", null);
1.170 assertEquals("Kukuc", v, "Value is really kukuc: " + v);
1.171 -
1.172 +
1.173 Timer t = new Timer("Set to Jardo");
1.174 t.schedule(new TimerTask() {
1.175 @Override
1.176 @@ -398,15 +398,100 @@
1.177 }
1.178 }, 1);
1.179 }
1.180 -
1.181 +
1.182 String v = getSetInput("input", null);
1.183 if (!"Jardo".equals(v)) {
1.184 throw new InterruptedException();
1.185 }
1.186 -
1.187 +
1.188 Utils.exposeHTML(KnockoutTest.class, "");
1.189 }
1.190 -
1.191 +
1.192 + @Model(className = "ConstantModel", targetId = "", builder = "assign", properties = {
1.193 + @Property(name = "doubleValue", mutable = false, type = double.class),
1.194 + @Property(name = "longValue", mutable = false, type = long.class),
1.195 + @Property(name = "stringValue", mutable = false, type = String.class),
1.196 + @Property(name = "boolValue", mutable = false, type = boolean.class),
1.197 + @Property(name = "intArray", mutable = false, type = int.class, array = true),
1.198 + })
1.199 + static class ConstantCntrl {
1.200 + }
1.201 +
1.202 + @KOTest public void nonMutableDouble() throws Exception {
1.203 + Utils.exposeHTML(KnockoutTest.class,
1.204 + "Type: <input id='input' data-bind=\"value: typeof doubleValue\"></input>\n"
1.205 + );
1.206 +
1.207 + ConstantModel model = Models.bind(new ConstantModel(), newContext());
1.208 + model.assignStringValue("Hello").assignDoubleValue(10.0);
1.209 + model.applyBindings();
1.210 +
1.211 + String v = getSetInput("input", null);
1.212 + assertEquals(v, "number", "Right type found: " + v);
1.213 +
1.214 + Utils.exposeHTML(KnockoutTest.class, "");
1.215 + }
1.216 +
1.217 + @KOTest public void nonMutableString() throws Exception {
1.218 + Utils.exposeHTML(KnockoutTest.class,
1.219 + "Type: <input id='input' data-bind=\"value: typeof stringValue\"></input>\n"
1.220 + );
1.221 +
1.222 + ConstantModel model = Models.bind(new ConstantModel(), newContext());
1.223 + model.assignStringValue("Hello").assignDoubleValue(10.0);
1.224 + model.applyBindings();
1.225 +
1.226 + String v = getSetInput("input", null);
1.227 + assertEquals(v, "string", "Right type found: " + v);
1.228 +
1.229 + Utils.exposeHTML(KnockoutTest.class, "");
1.230 + }
1.231 +
1.232 + @KOTest public void nonMutableBoolean() throws Exception {
1.233 + Utils.exposeHTML(KnockoutTest.class,
1.234 + "Type: <input id='input' data-bind=\"value: typeof boolValue\"></input>\n"
1.235 + );
1.236 +
1.237 + ConstantModel model = Models.bind(new ConstantModel(), newContext());
1.238 + model.assignStringValue("Hello").assignBoolValue(true);
1.239 + model.applyBindings();
1.240 +
1.241 + String v = getSetInput("input", null);
1.242 + assertEquals(v, "boolean", "Right type found: " + v);
1.243 +
1.244 + Utils.exposeHTML(KnockoutTest.class, "");
1.245 + }
1.246 +
1.247 + @KOTest public void nonMutableLong() throws Exception {
1.248 + Utils.exposeHTML(KnockoutTest.class,
1.249 + "Type: <input id='input' data-bind=\"value: typeof longValue\"></input>\n"
1.250 + );
1.251 +
1.252 + ConstantModel model = Models.bind(new ConstantModel(), newContext());
1.253 + model.assignStringValue("Hello").assignLongValue(Long.MAX_VALUE);
1.254 + model.applyBindings();
1.255 +
1.256 + String v = getSetInput("input", null);
1.257 + assertEquals(v, "number", "Right type found: " + v);
1.258 +
1.259 + Utils.exposeHTML(KnockoutTest.class, "");
1.260 + }
1.261 +
1.262 + @KOTest public void nonMutableIntArray() throws Exception {
1.263 + Utils.exposeHTML(KnockoutTest.class,
1.264 + "Type: <input id='input' data-bind=\"value: typeof intArray\"></input>\n"
1.265 + );
1.266 +
1.267 + ConstantModel model = Models.bind(new ConstantModel(), newContext());
1.268 + model.assignStringValue("Hello").assignLongValue(Long.MAX_VALUE).assignIntArray(1, 2, 3, 4);
1.269 + model.applyBindings();
1.270 +
1.271 + String v = getSetInput("input", null);
1.272 + assertEquals(v, "object", "Right type found: " + v);
1.273 +
1.274 + Utils.exposeHTML(KnockoutTest.class, "");
1.275 + }
1.276 +
1.277 private static String getSetInput(String id, String value) throws Exception {
1.278 String s = "var value = arguments[0];\n"
1.279 + "var n = window.document.getElementById(arguments[1]); \n "
1.280 @@ -429,7 +514,7 @@
1.281 );
1.282 return Boolean.TRUE.equals(ret);
1.283 }
1.284 -
1.285 +
1.286 public static void triggerEvent(String id, String ev) throws Exception {
1.287 Utils.executeScript(
1.288 KnockoutTest.class,
1.289 @@ -437,9 +522,9 @@
1.290 id, ev
1.291 );
1.292 }
1.293 -
1.294 +
1.295 @KOTest public void displayContentOfArray() throws Exception {
1.296 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.297 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.298 "<ul id='ul' data-bind='foreach: results'>\n"
1.299 + " <li data-bind='text: $data, click: $root.call'/>\n"
1.300 + "</ul>\n"
1.301 @@ -465,10 +550,10 @@
1.302 Utils.exposeHTML(KnockoutTest.class, "");
1.303 }
1.304 }
1.305 -
1.306 +
1.307 @KOTest public void displayContentOfAsyncArray() throws Exception {
1.308 if (js == null) {
1.309 - Utils.exposeHTML(KnockoutTest.class,
1.310 + Utils.exposeHTML(KnockoutTest.class,
1.311 "<ul id='ul' data-bind='foreach: results'>\n"
1.312 + " <li data-bind='text: $data, click: $root.call'/>\n"
1.313 + "</ul>\n"
1.314 @@ -479,7 +564,7 @@
1.315
1.316 int cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.317 assertEquals(cnt, 1, "One child, but was " + cnt);
1.318 -
1.319 +
1.320 Timer t = new Timer("add to array");
1.321 t.schedule(new TimerTask() {
1.322 @Override
1.323 @@ -504,9 +589,9 @@
1.324 Utils.exposeHTML(KnockoutTest.class, "");
1.325 }
1.326 }
1.327 -
1.328 +
1.329 @KOTest public void displayContentOfComputedArray() throws Exception {
1.330 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.331 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.332 "<ul id='ul' data-bind='foreach: bothNames'>\n"
1.333 + " <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
1.334 + "</ul>\n"
1.335 @@ -521,23 +606,23 @@
1.336 triggerChildClick("ul", 1);
1.337
1.338 assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
1.339 -
1.340 +
1.341 m.setLastName("Verylast");
1.342
1.343 cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.344 assertEquals(cnt, 2, "Two children now, but was " + cnt);
1.345 -
1.346 +
1.347 triggerChildClick("ul", 1);
1.348
1.349 assertEquals("Verylast", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
1.350 -
1.351 +
1.352 } finally {
1.353 Utils.exposeHTML(KnockoutTest.class, "");
1.354 }
1.355 }
1.356 -
1.357 +
1.358 @KOTest public void displayContentOfComputedArrayOnASubpair() throws Exception {
1.359 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.360 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.361 "<div data-bind='with: next'>\n"
1.362 + "<ul id='ul' data-bind='foreach: bothNames'>\n"
1.363 + " <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
1.364 @@ -553,7 +638,7 @@
1.365 assertEquals(cnt, 2, "Two children now, but was " + cnt);
1.366
1.367 triggerChildClick("ul", 1);
1.368 -
1.369 +
1.370 assertEquals(PairModel.ctx, ctx, "Context remains the same");
1.371
1.372 assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
1.373 @@ -561,9 +646,9 @@
1.374 Utils.exposeHTML(KnockoutTest.class, "");
1.375 }
1.376 }
1.377 -
1.378 +
1.379 @KOTest public void displayContentOfComputedArrayOnComputedASubpair() throws Exception {
1.380 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.381 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.382 "<div data-bind='with: nextOne'>\n"
1.383 + "<ul id='ul' data-bind='foreach: bothNames'>\n"
1.384 + " <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
1.385 @@ -586,7 +671,7 @@
1.386 }
1.387
1.388 @KOTest public void checkBoxToBooleanBinding() throws Exception {
1.389 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.390 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.391 "<input type='checkbox' id='b' data-bind='checked: enabled'></input>\n"
1.392 );
1.393 try {
1.394 @@ -602,11 +687,11 @@
1.395 Utils.exposeHTML(KnockoutTest.class, "");
1.396 }
1.397 }
1.398 -
1.399 -
1.400 -
1.401 +
1.402 +
1.403 +
1.404 @KOTest public void displayContentOfDerivedArray() throws Exception {
1.405 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.406 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.407 "<ul id='ul' data-bind='foreach: cmpResults'>\n"
1.408 + " <li><b data-bind='text: $data'></b></li>\n"
1.409 + "</ul>\n"
1.410 @@ -627,9 +712,9 @@
1.411 Utils.exposeHTML(KnockoutTest.class, "");
1.412 }
1.413 }
1.414 -
1.415 +
1.416 @KOTest public void displayContentOfArrayOfPeople() throws Exception {
1.417 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.418 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.419 "<ul id='ul' data-bind='foreach: people'>\n"
1.420 + " <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
1.421 + "</ul>\n"
1.422 @@ -672,14 +757,14 @@
1.423 Utils.exposeHTML(KnockoutTest.class, "");
1.424 }
1.425 }
1.426 -
1.427 +
1.428 @ComputedProperty
1.429 static Person firstPerson(List<Person> people) {
1.430 return people.isEmpty() ? null : people.get(0);
1.431 }
1.432 -
1.433 +
1.434 @KOTest public void accessFirstPersonWithOnFunction() throws Exception {
1.435 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.436 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.437 "<p id='ul' data-bind='with: firstPerson'>\n"
1.438 + " <span data-bind='text: firstName, click: changeSex'></span>\n"
1.439 + "</p>\n"
1.440 @@ -690,9 +775,9 @@
1.441 Utils.exposeHTML(KnockoutTest.class, "");
1.442 }
1.443 }
1.444 -
1.445 +
1.446 @KOTest public void onPersonFunction() throws Exception {
1.447 - Object exp = Utils.exposeHTML(KnockoutTest.class,
1.448 + Object exp = Utils.exposeHTML(KnockoutTest.class,
1.449 "<ul id='ul' data-bind='foreach: people'>\n"
1.450 + " <li data-bind='text: $data.firstName, click: changeSex'></li>\n"
1.451 + "</ul>\n"
1.452 @@ -703,7 +788,7 @@
1.453 Utils.exposeHTML(KnockoutTest.class, "");
1.454 }
1.455 }
1.456 -
1.457 +
1.458 private void trasfertToFemale() throws Exception {
1.459 KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
1.460
1.461 @@ -723,7 +808,7 @@
1.462
1.463 assertEquals(first.getSex(), Sex.FEMALE, "Transverted to female: " + first.getSex());
1.464 }
1.465 -
1.466 +
1.467 @KOTest public void stringArrayModificationVisible() throws Exception {
1.468 Object exp = Utils.exposeHTML(KnockoutTest.class,
1.469 "<div>\n"
1.470 @@ -737,19 +822,19 @@
1.471 m.getResults().add("Ahoj");
1.472 m.getResults().add("Hello");
1.473 m.applyBindings();
1.474 -
1.475 +
1.476 int cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.477 assertEquals(cnt, 2, "Two children " + cnt);
1.478 -
1.479 +
1.480 Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
1.481 assertTrue(arr instanceof Object[], "Got back an array: " + arr);
1.482 final int len = ((Object[])arr).length;
1.483 -
1.484 +
1.485 assertEquals(len, 3, "Three elements in the array " + len);
1.486 -
1.487 +
1.488 int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
1.489 assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
1.490 -
1.491 +
1.492 assertEquals(m.getResults().size(), 3, "Three java strings: " + m.getResults());
1.493 } finally {
1.494 Utils.exposeHTML(KnockoutTest.class, "");
1.495 @@ -769,19 +854,19 @@
1.496 m.getNumbers().add(1);
1.497 m.getNumbers().add(31);
1.498 m.applyBindings();
1.499 -
1.500 +
1.501 int cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.502 assertEquals(cnt, 2, "Two children " + cnt);
1.503 -
1.504 +
1.505 Object arr = Utils.addChildren(KnockoutTest.class, "ul", "numbers", 42);
1.506 assertTrue(arr instanceof Object[], "Got back an array: " + arr);
1.507 final int len = ((Object[])arr).length;
1.508 -
1.509 +
1.510 assertEquals(len, 3, "Three elements in the array " + len);
1.511 -
1.512 +
1.513 int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
1.514 assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
1.515 -
1.516 +
1.517 assertEquals(m.getNumbers().size(), 3, "Three java ints: " + m.getNumbers());
1.518 assertEquals(m.getNumbers().get(2), 42, "Meaning of world: " + m.getNumbers());
1.519 } finally {
1.520 @@ -802,26 +887,26 @@
1.521 m.getResults().add("Ahoj");
1.522 m.getResults().add("Hello");
1.523 m.applyBindings();
1.524 -
1.525 +
1.526 int cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.527 assertEquals(cnt, 2, "Two children " + cnt);
1.528 -
1.529 +
1.530 Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
1.531 assertTrue(arr instanceof Object[], "Got back an array: " + arr);
1.532 final int len = ((Object[])arr).length;
1.533 -
1.534 +
1.535 assertEquals(len, 3, "Three elements in the array " + len);
1.536 -
1.537 +
1.538 int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
1.539 assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
1.540 -
1.541 +
1.542 assertEquals(m.getResultLengths().size(), 3, "Three java ints: " + m.getResultLengths());
1.543 assertEquals(m.getResultLengths().get(2), 2, "Size is two: " + m.getResultLengths());
1.544 } finally {
1.545 Utils.exposeHTML(KnockoutTest.class, "");
1.546 }
1.547 }
1.548 -
1.549 +
1.550 @KOTest public void archetypeArrayModificationVisible() throws Exception {
1.551 Object exp = Utils.exposeHTML(KnockoutTest.class,
1.552 "<div>\n"
1.553 @@ -833,19 +918,19 @@
1.554 try {
1.555 KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
1.556 m.applyBindings();
1.557 -
1.558 +
1.559 int cnt = Utils.countChildren(KnockoutTest.class, "ul");
1.560 assertEquals(cnt, 0, "No children " + cnt);
1.561 -
1.562 +
1.563 Object arr = Utils.addChildren(KnockoutTest.class, "ul", "archetypes", new ArchetypeData("aid", "gid", "v", "n", "d", "u"));
1.564 assertTrue(arr instanceof Object[], "Got back an array: " + arr);
1.565 final int len = ((Object[])arr).length;
1.566 -
1.567 +
1.568 assertEquals(len, 1, "One element in the array " + len);
1.569 -
1.570 +
1.571 int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
1.572 assertEquals(newCnt, 1, "One child in the DOM: " + newCnt);
1.573 -
1.574 +
1.575 assertEquals(m.getArchetypes().size(), 1, "One archetype: " + m.getArchetypes());
1.576 assertNotNull(m.getArchetypes().get(0), "Not null: " + m.getArchetypes());
1.577 assertEquals(m.getArchetypes().get(0).getArtifactId(), "aid", "'aid' == " + m.getArchetypes());
1.578 @@ -865,18 +950,18 @@
1.579 model.setCallbackCount(model.getCallbackCount() + 1);
1.580 model.getPeople().remove(data);
1.581 }
1.582 -
1.583 -
1.584 +
1.585 +
1.586 @ComputedProperty
1.587 static String helloMessage(String name) {
1.588 return "Hello " + name + "!";
1.589 }
1.590 -
1.591 +
1.592 @ComputedProperty
1.593 static List<String> cmpResults(List<String> results) {
1.594 return results;
1.595 }
1.596 -
1.597 +
1.598 private static void triggerClick(String id) throws Exception {
1.599 String s = "var id = arguments[0];"
1.600 + "var e = window.document.getElementById(id);\n "
1.601 @@ -893,17 +978,17 @@
1.602 s, id);
1.603 }
1.604 private static void triggerChildClick(String id, int pos) throws Exception {
1.605 - String s =
1.606 + String s =
1.607 "var id = arguments[0]; var pos = arguments[1];\n" +
1.608 "var e = window.document.getElementById(id);\n " +
1.609 "var ev = window.document.createEvent('MouseEvents');\n " +
1.610 "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n " +
1.611 "var list = e.childNodes;\n" +
1.612 - "var cnt = -1;\n" +
1.613 - "for (var i = 0; i < list.length; i++) {\n" +
1.614 - " if (list[i].nodeType == 1) cnt++;\n" +
1.615 - " if (cnt == pos) return list[i].dispatchEvent(ev);\n" +
1.616 - "}\n" +
1.617 + "var cnt = -1;\n" +
1.618 + "for (var i = 0; i < list.length; i++) {\n" +
1.619 + " if (list[i].nodeType == 1) cnt++;\n" +
1.620 + " if (cnt == pos) return list[i].dispatchEvent(ev);\n" +
1.621 + "}\n" +
1.622 "return null;\n";
1.623 Utils.executeScript(
1.624 KnockoutTest.class,
1.625 @@ -911,15 +996,15 @@
1.626 }
1.627
1.628 private static String childText(String id, int pos) throws Exception {
1.629 - String s =
1.630 + String s =
1.631 "var id = arguments[0]; var pos = arguments[1];" +
1.632 "var e = window.document.getElementById(id);\n" +
1.633 "var list = e.childNodes;\n" +
1.634 - "var cnt = -1;\n" +
1.635 - "for (var i = 0; i < list.length; i++) {\n" +
1.636 - " if (list[i].nodeType == 1) cnt++;\n" +
1.637 - " if (cnt == pos) return list[i].innerHTML;\n" +
1.638 - "}\n" +
1.639 + "var cnt = -1;\n" +
1.640 + "for (var i = 0; i < list.length; i++) {\n" +
1.641 + " if (list[i].nodeType == 1) cnt++;\n" +
1.642 + " if (cnt == pos) return list[i].innerHTML;\n" +
1.643 + "}\n" +
1.644 "return null;\n";
1.645 return (String)Utils.executeScript(
1.646 KnockoutTest.class,
2.1 --- a/json/src/main/java/net/java/html/json/Property.java Mon Feb 15 05:27:28 2016 +0100
2.2 +++ b/json/src/main/java/net/java/html/json/Property.java Mon Feb 29 05:33:31 2016 +0100
2.3 @@ -46,6 +46,8 @@
2.4 import java.lang.annotation.RetentionPolicy;
2.5 import java.lang.annotation.Target;
2.6 import java.util.List;
2.7 +import org.netbeans.html.context.spi.Contexts;
2.8 +import org.netbeans.html.json.spi.Technology;
2.9
2.10 /** Represents a property in a class defined with {@link Model} annotation.
2.11 *
2.12 @@ -76,4 +78,21 @@
2.13 * @return true, if this property is supposed to represent an array of values
2.14 */
2.15 boolean array() default false;
2.16 +
2.17 + /** Can the value of the property be mutated without restriction or not.
2.18 + * If a property is defined as <em>not mutable</em>, it defines
2.19 + * semi-immutable value that can only be changed in construction time
2.20 + * before the object is passed to underlying {@link Technology}.
2.21 + * Attempts to modify the object later yield {@link IllegalStateException}.
2.22 + *
2.23 + * Technologies may decide to represent such non-mutable
2.24 + * property in more effective way - for
2.25 + * example Knockout Java Bindings technology (with {@link Contexts.Id id} "ko4j")
2.26 + * uses plain JavaScript value (number, string, array, boolean) rather
2.27 + * than classical observable.
2.28 + *
2.29 + * @return false if the value cannot change after its <em>first use</em>
2.30 + * @since 1.3
2.31 + */
2.32 + boolean mutable() default true;
2.33 }
3.1 --- a/json/src/main/java/org/netbeans/html/json/impl/Bindings.java Mon Feb 15 05:27:28 2016 +0100
3.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java Mon Feb 29 05:33:31 2016 +0100
3.3 @@ -64,8 +64,8 @@
3.4 this.bp = bp;
3.5 }
3.6
3.7 - public <M> PropertyBinding registerProperty(String propName, int index, M model, Proto.Type<M> access, boolean readOnly) {
3.8 - return PropertyBindingAccessor.create(access, this, propName, index, model, readOnly);
3.9 + public <M> PropertyBinding registerProperty(String propName, int index, M model, Proto.Type<M> access, byte propertyType) {
3.10 + return PropertyBindingAccessor.create(access, this, propName, index, model, propertyType);
3.11 }
3.12
3.13 public static Bindings<?> apply(BrwsrCtx c) {
4.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Mon Feb 15 05:27:28 2016 +0100
4.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Mon Feb 29 05:33:31 2016 +0100
4.3 @@ -88,6 +88,7 @@
4.4
4.5 @Override
4.6 public boolean add(T e) {
4.7 + prepareChange();
4.8 boolean ret = super.add(e);
4.9 notifyChange();
4.10 return ret;
4.11 @@ -95,6 +96,7 @@
4.12
4.13 @Override
4.14 public boolean addAll(Collection<? extends T> c) {
4.15 + prepareChange();
4.16 boolean ret = super.addAll(c);
4.17 notifyChange();
4.18 return ret;
4.19 @@ -102,12 +104,14 @@
4.20
4.21 @Override
4.22 public boolean addAll(int index, Collection<? extends T> c) {
4.23 + prepareChange();
4.24 boolean ret = super.addAll(index, c);
4.25 notifyChange();
4.26 return ret;
4.27 }
4.28
4.29 public void fastReplace(Collection<? extends T> c) {
4.30 + prepareChange();
4.31 super.clear();
4.32 super.addAll(c);
4.33 notifyChange();
4.34 @@ -115,6 +119,7 @@
4.35
4.36 @Override
4.37 public boolean remove(Object o) {
4.38 + prepareChange();
4.39 boolean ret = super.remove(o);
4.40 notifyChange();
4.41 return ret;
4.42 @@ -122,12 +127,14 @@
4.43
4.44 @Override
4.45 public void clear() {
4.46 + prepareChange();
4.47 super.clear();
4.48 notifyChange();
4.49 }
4.50
4.51 @Override
4.52 public boolean removeAll(Collection<?> c) {
4.53 + prepareChange();
4.54 boolean ret = super.removeAll(c);
4.55 notifyChange();
4.56 return ret;
4.57 @@ -135,6 +142,7 @@
4.58
4.59 @Override
4.60 public boolean retainAll(Collection<?> c) {
4.61 + prepareChange();
4.62 boolean ret = super.retainAll(c);
4.63 notifyChange();
4.64 return ret;
4.65 @@ -142,6 +150,7 @@
4.66
4.67 @Override
4.68 public T set(int index, T element) {
4.69 + prepareChange();
4.70 T ret = super.set(index, element);
4.71 notifyChange();
4.72 return ret;
4.73 @@ -149,12 +158,14 @@
4.74
4.75 @Override
4.76 public void add(int index, T element) {
4.77 + prepareChange();
4.78 super.add(index, element);
4.79 notifyChange();
4.80 }
4.81
4.82 @Override
4.83 public T remove(int index) {
4.84 + prepareChange();
4.85 T ret = super.remove(index);
4.86 notifyChange();
4.87 return ret;
4.88 @@ -179,6 +190,16 @@
4.89 return sb.toString();
4.90 }
4.91
4.92 + private void prepareChange() {
4.93 + if (index == Integer.MIN_VALUE) {
4.94 + try {
4.95 + proto.initTo(null, null);
4.96 + } catch (IllegalStateException ex) {
4.97 + throw new UnsupportedOperationException();
4.98 + }
4.99 + }
4.100 + }
4.101 +
4.102 private void notifyChange() {
4.103 proto.getContext().execute(new Runnable() {
4.104 @Override
5.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Mon Feb 15 05:27:28 2016 +0100
5.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Mon Feb 29 05:33:31 2016 +0100
5.3 @@ -263,11 +263,15 @@
5.4 String[] gs = toGetSet(p.name(), tn, p.array());
5.5 w.write(" this.prop_" + p.name() + " = proto.createList(\""
5.6 + p.name() + "\"");
5.7 - if (functionDeps.containsKey(p.name())) {
5.8 - int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
5.9 - w.write(", " + index);
5.10 + if (p.mutable()) {
5.11 + if (functionDeps.containsKey(p.name())) {
5.12 + int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
5.13 + w.write(", " + index);
5.14 + } else {
5.15 + w.write(", -1");
5.16 + }
5.17 } else {
5.18 - w.write(", -1");
5.19 + w.write(", java.lang.Integer.MIN_VALUE");
5.20 }
5.21 Collection<String[]> dependants = propsDeps.get(p.name());
5.22 if (dependants != null) {
5.23 @@ -355,7 +359,7 @@
5.24 {
5.25 for (int i = 0; i < propsGetSet.size(); i++) {
5.26 w.append(" registerProperty(\"").append(propsGetSet.get(i).name).append("\", ");
5.27 - w.append((i) + ", " + propsGetSet.get(i).readOnly + ");\n");
5.28 + w.append((i) + ", " + propsGetSet.get(i).readOnly + ", " + propsGetSet.get(i).constant + ");\n");
5.29 }
5.30 }
5.31 {
5.32 @@ -672,6 +676,9 @@
5.33 w.write(" return (" + tn + ")prop_" + p.name() + ";\n");
5.34 w.write(" }\n");
5.35 w.write(" public void " + gs[1] + "(" + tn + " v) {\n");
5.36 + if (!p.mutable()) {
5.37 + w.write(" proto.initTo(null, null);\n");
5.38 + }
5.39 w.write(" proto.verifyUnlocked();\n");
5.40 w.write(" Object o = prop_" + p.name() + ";\n");
5.41 if (isModel[0]) {
5.42 @@ -721,7 +728,8 @@
5.43 gs[0],
5.44 gs[1],
5.45 tn,
5.46 - gs[3] == null && !p.array()
5.47 + gs[3] == null && !p.array(),
5.48 + !p.mutable()
5.49 ));
5.50 }
5.51 return ok;
5.52 @@ -857,7 +865,8 @@
5.53 gs[0],
5.54 null,
5.55 tn,
5.56 - true
5.57 + true,
5.58 + false
5.59 ));
5.60 } else {
5.61 w.write(" public void " + gs[4] + "(" + write.getParameters().get(1).asType());
5.62 @@ -870,6 +879,7 @@
5.63 gs[0],
5.64 gs[4],
5.65 tn,
5.66 + false,
5.67 false
5.68 ));
5.69 }
5.70 @@ -2001,6 +2011,10 @@
5.71 return p.array();
5.72 }
5.73
5.74 + boolean mutable() {
5.75 + return p.mutable();
5.76 + }
5.77 +
5.78 String typeName(ProcessingEnvironment env) {
5.79 RuntimeException ex;
5.80 try {
5.81 @@ -2058,12 +2072,15 @@
5.82 final String setter;
5.83 final String type;
5.84 final boolean readOnly;
5.85 - GetSet(String name, String getter, String setter, String type, boolean readOnly) {
5.86 + final boolean constant;
5.87 +
5.88 + GetSet(String name, String getter, String setter, String type, boolean readOnly, boolean constant) {
5.89 this.name = name;
5.90 this.getter = getter;
5.91 this.setter = setter;
5.92 this.type = type;
5.93 this.readOnly = readOnly;
5.94 + this.constant = constant;
5.95 }
5.96 }
5.97
6.1 --- a/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java Mon Feb 15 05:27:28 2016 +0100
6.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java Mon Feb 29 05:33:31 2016 +0100
6.3 @@ -64,8 +64,7 @@
6.4 }
6.5
6.6 protected abstract <M> PropertyBinding newBinding(
6.7 - Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, boolean readOnly
6.8 - );
6.9 + Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, byte propertyType);
6.10 protected abstract JSONCall newCall(
6.11 BrwsrCtx ctx, RcvrJSON callback,
6.12 String headers, String urlBefore, String urlAfter,
6.13 @@ -87,9 +86,9 @@
6.14 }
6.15
6.16 static <M> PropertyBinding create(
6.17 - Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model , boolean readOnly
6.18 + Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model , byte propertyType
6.19 ) {
6.20 - return DEFAULT.newBinding(access, bindings, name, index, model, readOnly);
6.21 + return DEFAULT.newBinding(access, bindings, name, index, model, propertyType);
6.22 }
6.23 public static JSONCall createCall(
6.24 BrwsrCtx ctx, RcvrJSON callback,
7.1 --- a/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java Mon Feb 15 05:27:28 2016 +0100
7.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java Mon Feb 29 05:33:31 2016 +0100
7.3 @@ -45,6 +45,7 @@
7.4 import java.lang.ref.Reference;
7.5 import java.lang.ref.WeakReference;
7.6 import net.java.html.BrwsrCtx;
7.7 +import net.java.html.json.ComputedProperty;
7.8 import org.netbeans.html.json.impl.Bindings;
7.9 import org.netbeans.html.json.impl.JSON;
7.10 import org.netbeans.html.json.impl.PropertyBindingAccessor;
7.11 @@ -93,10 +94,8 @@
7.12
7.13 @Override
7.14 protected <M> PropertyBinding newBinding(
7.15 - Proto.Type<M> access, Bindings<?> bindings, String name,
7.16 - int index, M model, boolean readOnly
7.17 - ) {
7.18 - return new Impl(model, bindings, name, index, access, readOnly);
7.19 + Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, byte propertyType) {
7.20 + return new Impl(model, bindings, name, index, access, propertyType);
7.21 }
7.22 };
7.23 }
7.24 @@ -121,12 +120,22 @@
7.25 */
7.26 public abstract Object getValue();
7.27
7.28 - /** Is this property read only? Or can one call {@link #setValue(java.lang.Object)}?
7.29 + /** Is this property read only?. Or can one call {@link #setValue(java.lang.Object)}?
7.30 + * The property can still change, but only as a result of other
7.31 + * properties being changed, just like {@link ComputedProperty} can.
7.32 *
7.33 * @return true, if this property is read only
7.34 */
7.35 public abstract boolean isReadOnly();
7.36
7.37 + /** Is this property constant?. If a property is constant, than its
7.38 + * value cannot changed after it is read.
7.39 + *
7.40 + * @return true, if this property is constant
7.41 + * @since 1.3
7.42 + */
7.43 + public abstract boolean isConstant();
7.44 +
7.45 /** Returns identical version of the binding, but one that holds on the
7.46 * original model object via weak reference.
7.47 *
7.48 @@ -137,17 +146,17 @@
7.49
7.50 private static abstract class AImpl<M> extends PropertyBinding {
7.51 public final String name;
7.52 - public final boolean readOnly;
7.53 + public final byte propertyType;
7.54 final Proto.Type<M> access;
7.55 final Bindings<?> bindings;
7.56 final int index;
7.57
7.58 - public AImpl(Bindings<?> bindings, String name, int index, Proto.Type<M> access, boolean readOnly) {
7.59 + public AImpl(Bindings<?> bindings, String name, int index, Proto.Type<M> access, byte propertyType) {
7.60 this.bindings = bindings;
7.61 this.name = name;
7.62 this.index = index;
7.63 this.access = access;
7.64 - this.readOnly = readOnly;
7.65 + this.propertyType = propertyType;
7.66 }
7.67
7.68 protected abstract M model();
7.69 @@ -174,7 +183,12 @@
7.70
7.71 @Override
7.72 public boolean isReadOnly() {
7.73 - return readOnly;
7.74 + return (propertyType & 1) != 0;
7.75 + }
7.76 +
7.77 + @Override
7.78 + public boolean isConstant() {
7.79 + return (propertyType & 2) != 0;
7.80 }
7.81
7.82 @Override
7.83 @@ -186,8 +200,8 @@
7.84 private static final class Impl<M> extends AImpl<M> {
7.85 private final M model;
7.86
7.87 - public Impl(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, boolean readOnly) {
7.88 - super(bindings, name, index, access, readOnly);
7.89 + public Impl(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, byte propertyType) {
7.90 + super(bindings, name, index, access, propertyType);
7.91 this.model = model;
7.92 }
7.93
7.94 @@ -198,14 +212,14 @@
7.95
7.96 @Override
7.97 public PropertyBinding weak() {
7.98 - return new Weak(model, bindings, name, index, access, readOnly);
7.99 + return new Weak(model, bindings, name, index, access, propertyType);
7.100 }
7.101 }
7.102
7.103 private static final class Weak<M> extends AImpl<M> {
7.104 private final Reference<M> ref;
7.105 - public Weak(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, boolean readOnly) {
7.106 - super(bindings, name, index, access, readOnly);
7.107 + public Weak(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, byte propertyType) {
7.108 + super(bindings, name, index, access, propertyType);
7.109 this.ref = new WeakReference<M>(model);
7.110 }
7.111
8.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Proto.java Mon Feb 15 05:27:28 2016 +0100
8.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java Mon Feb 29 05:33:31 2016 +0100
8.3 @@ -48,6 +48,7 @@
8.4 import net.java.html.BrwsrCtx;
8.5 import net.java.html.json.ComputedProperty;
8.6 import net.java.html.json.Model;
8.7 +import net.java.html.json.Property;
8.8 import org.netbeans.html.json.impl.Bindings;
8.9 import org.netbeans.html.json.impl.JSON;
8.10 import org.netbeans.html.json.impl.JSON.WS;
8.11 @@ -463,7 +464,10 @@
8.12 * @param <T> the type of the list elements
8.13 * @param propName name of a property this list is associated with
8.14 * @param onChange index of the property to use when the list is modified
8.15 - * during callback to {@link Type#onChange(java.lang.Object, int)}
8.16 + * during callback to {@link Type#onChange(java.lang.Object, int)}.
8.17 + * If the value is {@link Integer#MIN_VALUE}, then the list is
8.18 + * not fully {@link Property#mutable()} and throws {@link UnsupportedOperationException}
8.19 + * on such attempts.
8.20 * @param dependingProps the array of {@link ComputedProperty derived properties}
8.21 * that depend on the value of the list
8.22 * @return new, empty list associated with this proto-object and its model
8.23 @@ -508,7 +512,7 @@
8.24 PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
8.25 for (int i = 0; i < pb.length; i++) {
8.26 pb[i] = b.registerProperty(
8.27 - type.propertyNames[i], i, obj, type, type.propertyReadOnly[i]
8.28 + type.propertyNames[i], i, obj, type, type.propertyType[i]
8.29 );
8.30 }
8.31 FunctionBinding[] fb = new FunctionBinding[type.functions.length];
8.32 @@ -547,7 +551,7 @@
8.33 public static abstract class Type<Model> {
8.34 private final Class<Model> clazz;
8.35 private final String[] propertyNames;
8.36 - private final boolean[] propertyReadOnly;
8.37 + private final byte[] propertyType;
8.38 private final String[] functions;
8.39
8.40 /** Constructor for subclasses generated by the annotation processor
8.41 @@ -569,7 +573,7 @@
8.42 }
8.43 this.clazz = clazz;
8.44 this.propertyNames = new String[properties];
8.45 - this.propertyReadOnly = new boolean[properties];
8.46 + this.propertyType = new byte[properties];
8.47 this.functions = new String[functions];
8.48 JSON.register(clazz, this);
8.49 }
8.50 @@ -584,7 +588,29 @@
8.51 protected final void registerProperty(String name, int index, boolean readOnly) {
8.52 assert propertyNames[index] == null;
8.53 propertyNames[index] = name;
8.54 - propertyReadOnly[index] = readOnly;
8.55 + propertyType[index] = (byte) (readOnly ? 1 : 0);
8.56 + }
8.57 +
8.58 + /** Registers property for the type. It is expected each index
8.59 + * is initialized only once. The difference between <code>readOnly</code>
8.60 + * and <code>constant</code> is: The <code>constant</code> value is
8.61 + * assigned only at the beginning and never changed then - like the
8.62 + * {@link Property#mutable() non-mutable} property. On the other
8.63 + * hand, a <code>readOnly</code> property can change its value,
8.64 + * but not via a setter - just like {@link ComputedProperty}.
8.65 + *
8.66 + * @param name name of the property
8.67 + * @param index index of the property
8.68 + * @param readOnly is the property read only?
8.69 + * @param constant is the property assigned once and never changed again?
8.70 + * @since 1.3
8.71 + */
8.72 + protected final void registerProperty(
8.73 + String name, int index, boolean readOnly, boolean constant
8.74 + ) {
8.75 + assert propertyNames[index] == null;
8.76 + propertyNames[index] = name;
8.77 + propertyType[index] = (byte) ((readOnly ? 1 : 0) | (constant ? 2 : 0));
8.78 }
8.79
8.80 /** Registers function of given name at given index.
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/json/src/test/java/net/java/html/json/MapModelNotMutableTest.java Mon Feb 29 05:33:31 2016 +0100
9.3 @@ -0,0 +1,227 @@
9.4 +/**
9.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
9.6 + *
9.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
9.8 + *
9.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
9.10 + * Other names may be trademarks of their respective owners.
9.11 + *
9.12 + * The contents of this file are subject to the terms of either the GNU
9.13 + * General Public License Version 2 only ("GPL") or the Common
9.14 + * Development and Distribution License("CDDL") (collectively, the
9.15 + * "License"). You may not use this file except in compliance with the
9.16 + * License. You can obtain a copy of the License at
9.17 + * http://www.netbeans.org/cddl-gplv2.html
9.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
9.19 + * specific language governing permissions and limitations under the
9.20 + * License. When distributing the software, include this License Header
9.21 + * Notice in each file and include the License file at
9.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
9.23 + * particular file as subject to the "Classpath" exception as provided
9.24 + * by Oracle in the GPL Version 2 section of the License file that
9.25 + * accompanied this code. If applicable, add the following below the
9.26 + * License Header, with the fields enclosed by brackets [] replaced by
9.27 + * your own identifying information:
9.28 + * "Portions Copyrighted [year] [name of copyright owner]"
9.29 + *
9.30 + * Contributor(s):
9.31 + *
9.32 + * The Original Software is NetBeans. The Initial Developer of the Original
9.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
9.34 + *
9.35 + * If you wish your version of this file to be governed by only the CDDL
9.36 + * or only the GPL Version 2, indicate your decision by adding
9.37 + * "[Contributor] elects to include this software in this distribution
9.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
9.39 + * single choice of license, a recipient has the option to distribute
9.40 + * your version of this file under either the CDDL, the GPL Version 2 or
9.41 + * to extend the choice of license to its licensees as provided above.
9.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
9.43 + * Version 2 license, then the option applies only if the new code is
9.44 + * made subject to such option by the copyright holder.
9.45 + */
9.46 +package net.java.html.json;
9.47 +
9.48 +import java.util.Map;
9.49 +import net.java.html.BrwsrCtx;
9.50 +import org.netbeans.html.context.spi.Contexts;
9.51 +import org.netbeans.html.json.spi.Technology;
9.52 +import org.netbeans.html.json.spi.Transfer;
9.53 +import static org.testng.Assert.assertEquals;
9.54 +import static org.testng.Assert.assertFalse;
9.55 +import static org.testng.Assert.assertNotNull;
9.56 +import static org.testng.Assert.fail;
9.57 +import org.testng.annotations.BeforeMethod;
9.58 +import org.testng.annotations.Test;
9.59 +
9.60 +@Model(className = "ConstantValues", properties = {
9.61 + @Property(name = "byteNumber", type = byte.class, mutable = false),
9.62 + @Property(name = "shortNumber", type = short.class, mutable = false),
9.63 + @Property(name = "intNumber", type = int.class, mutable = false),
9.64 + @Property(name = "longNumber", type = long.class, mutable = false),
9.65 + @Property(name = "floatNumber", type = float.class, mutable = false),
9.66 + @Property(name = "doubleNumber", type = double.class, mutable = false),
9.67 + @Property(name = "stringValue", type = String.class, mutable = false),
9.68 + @Property(name = "byteArray", type = byte.class, mutable = false, array = true),
9.69 + @Property(name = "shortArray", type = short.class, mutable = false, array = true),
9.70 + @Property(name = "intArray", type = int.class, mutable = false, array = true),
9.71 + @Property(name = "longArray", type = long.class, mutable = false, array = true),
9.72 + @Property(name = "floatArray", type = float.class, mutable = false, array = true),
9.73 + @Property(name = "doubleArray", type = double.class, mutable = false, array = true),
9.74 + @Property(name = "stringArray", type = String.class, mutable = false, array = true),
9.75 +})
9.76 +public class MapModelNotMutableTest {
9.77 + private BrwsrCtx c;
9.78 +
9.79 + @BeforeMethod
9.80 + public void initTechnology() {
9.81 + MapModelTest.MapTechnology t = new MapModelTest.MapTechnology();
9.82 + c = Contexts.newBuilder().register(Technology.class, t, 1).
9.83 + register(Transfer.class, t, 1).build();
9.84 + }
9.85 +
9.86 + @Test
9.87 + public void byteConstant() throws Exception {
9.88 + ConstantValues value = Models.bind(new ConstantValues(), c);
9.89 + value.setByteNumber((byte)13);
9.90 +
9.91 + Map m = (Map) Models.toRaw(value);
9.92 + Object v = m.get("byteNumber");
9.93 + assertNotNull(v, "Value should be in the map");
9.94 + assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
9.95 + MapModelTest.One o = (MapModelTest.One) v;
9.96 + assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
9.97 + assertEquals((byte)13, o.get());
9.98 +
9.99 + try {
9.100 + value.setByteNumber((byte)15);
9.101 + fail("Changing value shouldn't succeed!");
9.102 + } catch (IllegalStateException ex) {
9.103 + // OK
9.104 + }
9.105 + assertEquals(o.get(), (byte)13, "Old value should still be in the map");
9.106 + assertEquals(o.changes, 0, "No change");
9.107 + assertFalse(o.pb.isReadOnly(), "Mutable property");
9.108 + }
9.109 +
9.110 + @Test
9.111 + public void shortConstant() throws Exception {
9.112 + ConstantValues value = Models.bind(new ConstantValues(), c);
9.113 + value.setShortNumber((short)13);
9.114 +
9.115 + Map m = (Map) Models.toRaw(value);
9.116 + Object v = m.get("shortNumber");
9.117 + assertNotNull(v, "Value should be in the map");
9.118 + assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
9.119 + MapModelTest.One o = (MapModelTest.One) v;
9.120 + assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
9.121 + assertEquals((short)13, o.get());
9.122 +
9.123 + try {
9.124 + value.setShortNumber((short)15);
9.125 + fail("Changing value shouldn't succeed!");
9.126 + } catch (IllegalStateException ex) {
9.127 + // OK
9.128 + }
9.129 + assertEquals(o.get(), (short)13, "Old value should still be in the map");
9.130 + assertEquals(o.changes, 0, "No change");
9.131 + assertFalse(o.pb.isReadOnly(), "Mutable property");
9.132 + }
9.133 +
9.134 + @Test
9.135 + public void intConstant() throws Exception {
9.136 + ConstantValues value = Models.bind(new ConstantValues(), c);
9.137 + value.setIntNumber(13);
9.138 +
9.139 + Map m = (Map) Models.toRaw(value);
9.140 + Object v = m.get("intNumber");
9.141 + assertNotNull(v, "Value should be in the map");
9.142 + assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
9.143 + MapModelTest.One o = (MapModelTest.One) v;
9.144 + assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
9.145 + assertEquals(13, o.get());
9.146 +
9.147 + try {
9.148 + value.setIntNumber(15);
9.149 + fail("Changing value shouldn't succeed!");
9.150 + } catch (IllegalStateException ex) {
9.151 + // OK
9.152 + }
9.153 + assertEquals(o.get(), 13, "Old value should still be in the map");
9.154 + assertEquals(o.changes, 0, "No change");
9.155 + assertFalse(o.pb.isReadOnly(), "Mutable property");
9.156 + }
9.157 +
9.158 + @Test
9.159 + public void doubleConstant() throws Exception {
9.160 + ConstantValues value = Models.bind(new ConstantValues(), c);
9.161 + value.setDoubleNumber(13);
9.162 +
9.163 + Map m = (Map) Models.toRaw(value);
9.164 + Object v = m.get("doubleNumber");
9.165 + assertNotNull(v, "Value should be in the map");
9.166 + assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
9.167 + MapModelTest.One o = (MapModelTest.One) v;
9.168 + assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
9.169 + assertEquals(13.0, o.get());
9.170 +
9.171 + try {
9.172 + value.setDoubleNumber(15);
9.173 + fail("Changing value shouldn't succeed!");
9.174 + } catch (IllegalStateException ex) {
9.175 + // OK
9.176 + }
9.177 + assertEquals(o.get(), 13.0, "Old value should still be in the map");
9.178 + assertEquals(o.changes, 0, "No change");
9.179 + assertFalse(o.pb.isReadOnly(), "Mutable property");
9.180 + }
9.181 +
9.182 + @Test
9.183 + public void stringConstant() throws Exception {
9.184 + ConstantValues value = Models.bind(new ConstantValues(), c);
9.185 + value.setStringValue("Hi");
9.186 +
9.187 + Map m = (Map) Models.toRaw(value);
9.188 + Object v = m.get("stringValue");
9.189 + assertNotNull(v, "Value should be in the map");
9.190 + assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
9.191 + MapModelTest.One o = (MapModelTest.One) v;
9.192 + assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
9.193 + assertEquals("Hi", o.get());
9.194 +
9.195 + try {
9.196 + value.setStringValue("Hello");
9.197 + fail("Changing value shouldn't succeed!");
9.198 + } catch (IllegalStateException ex) {
9.199 + // OK
9.200 + }
9.201 + assertEquals(o.get(), "Hi", "Old value should still be in the map");
9.202 + assertEquals(o.changes, 0, "No change");
9.203 + assertFalse(o.pb.isReadOnly(), "Mutable property");
9.204 + }
9.205 +
9.206 + @Test
9.207 + public void stringArray() throws Exception {
9.208 + ConstantValues value = Models.bind(new ConstantValues(), c);
9.209 + value.getStringArray().add("Hi");
9.210 +
9.211 + Map m = (Map) Models.toRaw(value);
9.212 + Object v = m.get("stringArray");
9.213 + assertNotNull(v, "Value should be in the map");
9.214 + assertEquals(v.getClass(), MapModelTest.One.class, "It is instance of One");
9.215 + MapModelTest.One o = (MapModelTest.One) v;
9.216 + assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
9.217 + assertEquals(o.get(), new String[] { "Hi" }, "One element");
9.218 +
9.219 + try {
9.220 + value.getStringArray().add("Hello");
9.221 + fail("Changing value shouldn't succeed!");
9.222 + } catch (UnsupportedOperationException ex) {
9.223 + // OK
9.224 + }
9.225 + assertEquals(o.get(), new String[] { "Hi" }, "Old value should still be in the map");
9.226 + assertEquals(o.changes, 0, "No change");
9.227 + assertFalse(o.pb.isReadOnly(), "Mutable property");
9.228 + }
9.229 +
9.230 +}
10.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java Mon Feb 15 05:27:28 2016 +0100
10.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java Mon Feb 29 05:33:31 2016 +0100
10.3 @@ -72,10 +72,12 @@
10.4 final Object createKO(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr, Knockout[] ko) {
10.5 String[] propNames = new String[propArr.length];
10.6 Boolean[] propReadOnly = new Boolean[propArr.length];
10.7 + Boolean[] propConstant = new Boolean[propArr.length];
10.8 Object[] propValues = new Object[propArr.length];
10.9 for (int i = 0; i < propNames.length; i++) {
10.10 propNames[i] = propArr[i].getPropertyName();
10.11 propReadOnly[i] = propArr[i].isReadOnly();
10.12 + propConstant[i] = propArr[i].isConstant();
10.13 Object value = propArr[i].getValue();
10.14 if (value instanceof Enum) {
10.15 value = value.toString();
10.16 @@ -93,7 +95,7 @@
10.17 }
10.18 newKO.wrapModel(
10.19 ret, copyFrom,
10.20 - propNames, propReadOnly, propValues,
10.21 + propNames, propReadOnly, propConstant, propValues,
10.22 funcNames
10.23 );
10.24 return ret;
11.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Mon Feb 15 05:27:28 2016 +0100
11.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Mon Feb 29 05:33:31 2016 +0100
11.3 @@ -168,7 +168,7 @@
11.4 javacall = true,
11.5 keepAlive = false,
11.6 wait4js = false,
11.7 - args = { "ret", "copyFrom", "propNames", "propReadOnly", "propValues", "funcNames" },
11.8 + args = { "ret", "copyFrom", "propNames", "propReadOnly", "propConstant", "propValues", "funcNames" },
11.9 body =
11.10 "Object.defineProperty(ret, 'ko4j', { value : this });\n"
11.11 + "function koComputed(index, name, readOnly, value) {\n"
11.12 @@ -225,7 +225,11 @@
11.13 + " ret[name] = cmpt;\n"
11.14 + "}\n"
11.15 + "for (var i = 0; i < propNames.length; i++) {\n"
11.16 - + " koComputed(i, propNames[i], propReadOnly[i], propValues[i]);\n"
11.17 + + " if (propConstant[i]) {\n"
11.18 + + " ret[propNames[i]] = propValues[i];\n"
11.19 + + " } else {\n"
11.20 + + " koComputed(i, propNames[i], propReadOnly[i], propValues[i]);\n"
11.21 + + " }\n"
11.22 + "}\n"
11.23 + "function koExpose(index, name) {\n"
11.24 + " ret[name] = function(data, ev) {\n"
11.25 @@ -240,7 +244,8 @@
11.26 )
11.27 native void wrapModel(
11.28 Object ret, Object copyFrom,
11.29 - String[] propNames, Boolean[] propReadOnly, Object propValues,
11.30 + String[] propNames, Boolean[] propReadOnly, Boolean[] propConstant,
11.31 + Object propValues,
11.32 String[] funcNames
11.33 );
11.34
12.1 --- a/src/main/javadoc/overview.html Mon Feb 15 05:27:28 2016 +0100
12.2 +++ b/src/main/javadoc/overview.html Mon Feb 29 05:33:31 2016 +0100
12.3 @@ -101,7 +101,11 @@
12.4 {@link net.java.html.json.Model Model classes} can generate
12.5 builder-like construction methods if builder
12.6 {@link net.java.html.json.Model#builder() prefix} is specified.
12.7 - The <em>JavaFX</em> presenter can be executed in headless mode -
12.8 + {@link net.java.html.json.Property#mutable} can be <code>false</code>
12.9 + to define a non-mutable (almost constant) property. That
12.10 + in case of <em>Knockout</em> bindings means: the property is
12.11 + represented by a plain value rather than an observable in the JavaScript
12.12 + object. The <em>JavaFX</em> presenter can be executed in headless mode -
12.13 just specify <code>-Dfxpresenter.headless=true</code> when launching
12.14 its virtual machine and no window will be shown. This is particularly
12.15 useful for testing. Configure your <em>surefire</em> or <em>failsafe</em>