#257039: @Model.instance() to allow storage of private (and non-JSON like) state in a model
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
32 * If you wish your version of this file to be governed by only the CDDL
33 * or only the GPL Version 2, indicate your decision by adding
34 * "[Contributor] elects to include this software in this distribution
35 * under the [CDDL or GPL Version 2] license." If you do not indicate a
36 * single choice of license, a recipient has the option to distribute
37 * your version of this file under either the CDDL, the GPL Version 2 or
38 * to extend the choice of license to its licensees as provided above.
39 * However, if you add GPL Version 2 code and therefore, elected the GPL
40 * Version 2 license, then the option applies only if the new code is
41 * made subject to such option by the copyright holder.
43 package net.java.html.json;
45 import java.io.IOException;
46 import java.util.Locale;
47 import javax.tools.Diagnostic;
48 import javax.tools.JavaFileObject;
49 import static org.testng.Assert.*;
50 import org.testng.annotations.Test;
52 /** Verify errors emitted by the processor.
54 * @author Jaroslav Tulach
56 public class ModelProcessorTest {
57 @Test public void verifyWrongType() throws IOException {
58 String html = "<html><body>"
60 String code = "package x.y.z;\n"
61 + "import net.java.html.json.Model;\n"
62 + "import net.java.html.json.Property;\n"
63 + "@Model(className=\"XModel\", properties={\n"
64 + " @Property(name=\"prop\", type=Runnable.class)\n"
69 Compile c = Compile.create(html, code);
70 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
72 StringBuilder msgs = new StringBuilder();
73 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
74 String msg = e.getMessage(Locale.ENGLISH);
75 if (msg.contains("Runnable")) {
78 msgs.append("\n").append(msg);
81 fail("Should contain warning about Runnable:" + msgs);
85 @Test public void verifyWrongTypeInInnerClass() throws IOException {
86 String html = "<html><body>"
88 String code = "package x.y.z;\n"
89 + "import net.java.html.json.Model;\n"
90 + "import net.java.html.json.Property;\n"
92 + " @Model(className=\"XModel\", properties={\n"
93 + " @Property(name=\"prop\", type=Runnable.class)\n"
95 + " static class Inner {\n"
99 Compile c = Compile.create(html, code);
100 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
102 StringBuilder msgs = new StringBuilder();
103 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
104 String msg = e.getMessage(Locale.ENGLISH);
105 if (msg.contains("Runnable")) {
108 msgs.append("\n").append(msg);
111 fail("Should contain warning about Runnable:" + msgs);
115 @Test public void warnOnNonStatic() throws IOException {
116 String html = "<html><body>"
118 String code = "package x.y.z;\n"
119 + "import net.java.html.json.Model;\n"
120 + "import net.java.html.json.Property;\n"
121 + "import net.java.html.json.ComputedProperty;\n"
122 + "@Model(className=\"XModel\", properties={\n"
123 + " @Property(name=\"prop\", type=int.class)\n"
126 + " @ComputedProperty int y(int prop) {\n"
131 Compile c = Compile.create(html, code);
132 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
134 StringBuilder msgs = new StringBuilder();
135 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
136 String msg = e.getMessage(Locale.ENGLISH);
137 if (msg.contains("y has to be static")) {
140 msgs.append("\n").append(msg);
143 fail("Should contain warning about non-static method:" + msgs);
147 @Test public void tooManyProperties() throws IOException {
148 manyProperties(255, false, 0);
151 @Test public void tooManyArrayPropertiesIsOK() throws IOException {
152 manyProperties(0, true, 300);
155 @Test public void justEnoughProperties() throws IOException {
156 manyProperties(254, true, 0);
159 @Test public void justEnoughPropertiesWithArrayOne() throws IOException {
160 manyProperties(253, true, 300);
163 @Test public void justEnoughPropertiesButOneArrayOne() throws IOException {
164 manyProperties(254, false, 300);
167 private void manyProperties(
168 int cnt, boolean constructorWithParams, int arrayCnt
169 ) throws IOException {
170 String html = "<html><body>"
172 StringBuilder code = new StringBuilder();
173 code.append("package x.y.z;\n"
174 + "import net.java.html.json.Model;\n"
175 + "import net.java.html.json.Property;\n"
176 + "@Model(className=\"XModel\", properties={\n"
178 for (int i = 1; i <= cnt; i++) {
179 code.append(" @Property(name=\"prop").append(i).append("\", ");
180 code.append("type=int.class),\n");
182 for (int i = 1; i <= arrayCnt; i++) {
183 code.append(" @Property(name=\"array").append(i).append("\", ");
184 code.append("array=true, ");
185 code.append("type=int.class),\n");
194 if (constructorWithParams) {
196 for (int i = 1; i < cnt; i++) {
197 code.append(",\n").append(i);
205 Compile c = Compile.create(html, code.toString());
206 assertTrue(c.getErrors().isEmpty(), "Compiles OK: " + c.getErrors());
209 @Test public void writeableComputedPropertyMissingWrite() throws IOException {
210 String html = "<html><body>"
212 String code = "package x.y.z;\n"
213 + "import net.java.html.json.Model;\n"
214 + "import net.java.html.json.Property;\n"
215 + "import net.java.html.json.ComputedProperty;\n"
216 + "@Model(className=\"XModel\", properties={\n"
217 + " @Property(name=\"prop\", type=int.class)\n"
220 + " static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
225 Compile c = Compile.create(html, code);
226 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
228 StringBuilder msgs = new StringBuilder();
229 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
230 String msg = e.getMessage(Locale.ENGLISH);
231 if (msg.contains("Cannot find setY")) {
234 msgs.append("\n").append(msg);
237 fail("Should contain warning about non-static method:" + msgs);
241 @Test public void writeableComputedPropertyWrongWriteType() throws IOException {
242 String html = "<html><body>"
244 String code = "package x.y.z;\n"
245 + "import net.java.html.json.Model;\n"
246 + "import net.java.html.json.Property;\n"
247 + "import net.java.html.json.ComputedProperty;\n"
248 + "@Model(className=\"XModel\", properties={\n"
249 + " @Property(name=\"prop\", type=int.class)\n"
252 + " static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
255 + " static void setY(XModel model, String prop) {\n"
259 Compile c = Compile.create(html, code);
260 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
262 StringBuilder msgs = new StringBuilder();
263 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
264 String msg = e.getMessage(Locale.ENGLISH);
265 if (msg.contains("Write method first argument needs to be XModel and second int or Object")) {
268 msgs.append("\n").append(msg);
271 fail("Should contain warning about non-static method:" + msgs);
275 @Test public void writeableComputedPropertyReturnsVoid() throws IOException {
276 String html = "<html><body>"
278 String code = "package x.y.z;\n"
279 + "import net.java.html.json.Model;\n"
280 + "import net.java.html.json.Property;\n"
281 + "import net.java.html.json.ComputedProperty;\n"
282 + "@Model(className=\"XModel\", properties={\n"
283 + " @Property(name=\"prop\", type=int.class)\n"
286 + " static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
289 + " static Number setY(XModel model, int prop) {\n"
293 Compile c = Compile.create(html, code);
294 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
296 StringBuilder msgs = new StringBuilder();
297 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
298 String msg = e.getMessage(Locale.ENGLISH);
299 if (msg.contains("Write method has to return void")) {
302 msgs.append("\n").append(msg);
305 fail("Should contain warning about non-static method:" + msgs);
309 @Test public void computedCantReturnVoid() throws IOException {
310 String html = "<html><body>"
312 String code = "package x.y.z;\n"
313 + "import net.java.html.json.Model;\n"
314 + "import net.java.html.json.Property;\n"
315 + "import net.java.html.json.ComputedProperty;\n"
316 + "@Model(className=\"XModel\", properties={\n"
317 + " @Property(name=\"prop\", type=int.class)\n"
320 + " @ComputedProperty static void y(int prop) {\n"
324 Compile c = Compile.create(html, code);
325 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
327 StringBuilder msgs = new StringBuilder();
328 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
329 String msg = e.getMessage(Locale.ENGLISH);
330 if (msg.contains("y cannot return void")) {
333 msgs.append("\n").append(msg);
336 fail("Should contain warning about non-static method:" + msgs);
340 @Test public void computedCantReturnRunnable() throws IOException {
341 String html = "<html><body>"
343 String code = "package x.y.z;\n"
344 + "import net.java.html.json.Model;\n"
345 + "import net.java.html.json.Property;\n"
346 + "import net.java.html.json.ComputedProperty;\n"
347 + "@Model(className=\"XModel\", properties={\n"
348 + " @Property(name=\"prop\", type=int.class)\n"
351 + " @ComputedProperty static Runnable y(int prop) {\n"
356 Compile c = Compile.create(html, code);
357 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
359 StringBuilder msgs = new StringBuilder();
360 for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
361 String msg = e.getMessage(Locale.ENGLISH);
362 if (msg.contains("y cannot return java.lang.Runnable")) {
365 msgs.append("\n").append(msg);
368 fail("Should contain warning about non-static method:" + msgs);
372 @Test public void canWeCompileWithJDK1_5SourceLevel() throws IOException {
373 String html = "<html><body>"
375 String code = "package x.y.z;\n"
376 + "import net.java.html.json.Model;\n"
377 + "import net.java.html.json.Property;\n"
378 + "import net.java.html.json.ComputedProperty;\n"
379 + "@Model(className=\"XModel\", properties={\n"
380 + " @Property(name=\"prop\", type=long.class)\n"
383 + " @ComputedProperty static double derived(long prop) { return prop; }"
386 Compile c = Compile.create(html, code, "1.5");
387 assertTrue(c.getErrors().isEmpty(), "No errors: " + c.getErrors());
390 @Test public void instanceNeedsDefaultConstructor() throws IOException {
391 String html = "<html><body>"
393 String code = "package x.y.z;\n"
394 + "import net.java.html.json.Model;\n"
395 + "import net.java.html.json.Property;\n"
396 + "import net.java.html.json.ComputedProperty;\n"
397 + "@Model(className=\"XModel\", instance=true, properties={\n"
398 + " @Property(name=\"prop\", type=long.class)\n"
404 Compile c = Compile.create(html, code);
405 c.assertError("Needs non-private default constructor when instance=true");
408 @Test public void instanceNeedsNonPrivateConstructor() throws IOException {
409 String html = "<html><body>"
411 String code = "package x.y.z;\n"
412 + "import net.java.html.json.Model;\n"
413 + "import net.java.html.json.Property;\n"
414 + "import net.java.html.json.ComputedProperty;\n"
415 + "@Model(className=\"XModel\", instance=true, properties={\n"
416 + " @Property(name=\"prop\", type=long.class)\n"
419 + " private X() {}\n"
422 Compile c = Compile.create(html, code);
423 c.assertError("Needs non-private default constructor when instance=true");
426 @Test public void instanceNoConstructorIsOK() throws IOException {
427 String html = "<html><body>"
429 String code = "package x.y.z;\n"
430 + "import net.java.html.json.Model;\n"
431 + "import net.java.html.json.Property;\n"
432 + "import net.java.html.json.ComputedProperty;\n"
433 + "@Model(className=\"XModel\", instance=true, properties={\n"
434 + " @Property(name=\"prop\", type=long.class)\n"
439 Compile c = Compile.create(html, code);
443 @Test public void putNeedsDataArgument() throws Exception {
447 @Test public void postNeedsDataArgument() throws Exception {
451 private void needsAnArg(String method) throws Exception {
452 String html = "<html><body>"
454 String code = "package x.y.z;\n"
455 + "import net.java.html.json.Model;\n"
456 + "import net.java.html.json.Property;\n"
457 + "import net.java.html.json.OnReceive;\n"
458 + "@Model(className=\"XModel\", properties={\n"
459 + " @Property(name=\"prop\", type=long.class)\n"
462 + " @Model(className=\"PQ\", properties={})\n"
465 + " @OnReceive(method=\"" + method + "\", url=\"whereever\")\n"
466 + " static void obtained(XModel m, PQ p) { }\n"
469 Compile c = Compile.create(html, code);
470 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
471 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
472 String msg = diagnostic.getMessage(Locale.ENGLISH);
473 if (msg.contains("specify a data()")) {
477 fail("Needs an error message about missing data():\n" + c.getErrors());
482 @Test public void jsonNeedsToUseGet () throws Exception {
483 String html = "<html><body>"
485 String code = "package x.y.z;\n"
486 + "import net.java.html.json.Model;\n"
487 + "import net.java.html.json.Property;\n"
488 + "import net.java.html.json.OnReceive;\n"
489 + "@Model(className=\"XModel\", properties={\n"
490 + " @Property(name=\"prop\", type=long.class)\n"
493 + " @Model(className=\"PQ\", properties={})\n"
496 + " @OnReceive(method=\"POST\", jsonp=\"callback\", url=\"whereever\")\n"
497 + " static void obtained(XModel m, PQ p) { }\n"
500 Compile c = Compile.create(html, code);
501 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
502 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
503 String msg = diagnostic.getMessage(Locale.ENGLISH);
504 if (msg.contains("JSONP works only with GET")) {
508 fail("Needs an error message about wrong method:\n" + c.getErrors());
512 @Test public void noHeadersForWebSockets() throws Exception {
513 String html = "<html><body>"
515 String code = "package x.y.z;\n"
516 + "import net.java.html.json.Model;\n"
517 + "import net.java.html.json.Property;\n"
518 + "import net.java.html.json.OnReceive;\n"
519 + "@Model(className=\"XModel\", properties={\n"
520 + " @Property(name=\"prop\", type=long.class)\n"
523 + " @Model(className=\"PQ\", properties={})\n"
526 + " @OnReceive(method=\"WebSocket\", data = PQ.class, headers=\"SomeHeader: {some}\", url=\"whereever\")\n"
527 + " static void obtained(XModel m, PQ p) { }\n"
530 Compile c = Compile.create(html, code);
531 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
532 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
533 String msg = diagnostic.getMessage(Locale.ENGLISH);
534 if (msg.contains("WebSocket spec does not support headers")) {
538 fail("Needs an error message about headers:\n" + c.getErrors());
542 @Test public void webSocketsWithoutDataIsError() throws Exception {
543 String html = "<html><body>"
545 String code = "package x.y.z;\n"
546 + "import net.java.html.json.Model;\n"
547 + "import net.java.html.json.Property;\n"
548 + "import net.java.html.json.OnReceive;\n"
549 + "@Model(className=\"XModel\", properties={\n"
550 + " @Property(name=\"prop\", type=long.class)\n"
553 + " @Model(className=\"PQ\", properties={})\n"
556 + " @OnReceive(method=\"WebSocket\", url=\"whereever\")\n"
557 + " static void obtained(XModel m, PQ p) { }\n"
560 Compile c = Compile.create(html, code);
561 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
562 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
563 String msg = diagnostic.getMessage(Locale.ENGLISH);
564 if (msg.contains("eeds to specify a data()")) {
568 fail("Needs data attribute :\n" + c.getErrors());
571 @Test public void noNewLinesInHeaderLines() throws Exception {
572 String html = "<html><body>"
574 String code = "package x.y.z;\n"
575 + "import net.java.html.json.Model;\n"
576 + "import net.java.html.json.Property;\n"
577 + "import net.java.html.json.OnReceive;\n"
578 + "@Model(className=\"XModel\", properties={\n"
579 + " @Property(name=\"prop\", type=long.class)\n"
582 + " @Model(className=\"PQ\", properties={})\n"
585 + " @OnReceive(headers=\"SomeHeader\\n: {some}\", url=\"whereever\")\n"
586 + " static void obtained(XModel m, PQ p) { }\n"
589 Compile c = Compile.create(html, code);
590 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
591 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
592 String msg = diagnostic.getMessage(Locale.ENGLISH);
593 if (msg.contains("Header line cannot contain line separator")) {
597 fail("Needs an error message about headers:\n" + c.getErrors());
601 @Test public void noReturnInHeaderLines() throws Exception {
602 String html = "<html><body>"
604 String code = "package x.y.z;\n"
605 + "import net.java.html.json.Model;\n"
606 + "import net.java.html.json.Property;\n"
607 + "import net.java.html.json.OnReceive;\n"
608 + "@Model(className=\"XModel\", properties={\n"
609 + " @Property(name=\"prop\", type=long.class)\n"
612 + " @Model(className=\"PQ\", properties={})\n"
615 + " @OnReceive(headers=\"Some\\rHeader: {some}\", url=\"whereever\")\n"
616 + " static void obtained(XModel m, PQ p) { }\n"
619 Compile c = Compile.create(html, code);
620 assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
621 for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
622 String msg = diagnostic.getMessage(Locale.ENGLISH);
623 if (msg.contains("Header line cannot contain line separator")) {
627 fail("Needs an error message about headers:\n" + c.getErrors());
631 @Test public void onErrorHasToExist() throws IOException {
632 Compile res = Compile.create("", "package x;\n"
633 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
634 + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
636 + "class UseOnReceive {\n"
637 + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"doesNotExist\")\n"
638 + " static void onMessage(MyModel model, String value) {\n"
643 res.assertError("not find doesNotExist");
646 @Test public void usingListIsOK() throws IOException {
647 Compile res = Compile.create("", "package x;\n"
648 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
649 + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
651 + "class UseOnReceive {\n"
652 + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\")\n"
653 + " static void onMessage(MyModel model, java.util.List<MyData> value) {\n"
656 + " @net.java.html.json.Model(className=\"MyData\", properties={\n"
658 + " static class MyDataModel {\n"
662 res.assertNoErrors();
665 @Test public void functionAndPropertyCollide() throws IOException {
666 Compile res = Compile.create("", "package x;\n"
667 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
668 + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
670 + "class Collision {\n"
671 + " @net.java.html.json.Function\n"
672 + " static void x(MyModel model, String value) {\n"
677 res.assertError("cannot have the name");
680 @Test public void twoPropertiesCollide() throws IOException {
681 Compile res = Compile.create("", "package x;\n"
682 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
683 + " @net.java.html.json.Property(name=\"x\", type=String.class),\n"
684 + " @net.java.html.json.Property(name=\"x\", type=int.class)\n"
686 + "class Collision {\n"
690 res.assertError("Cannot have the name");
693 @Test public void propertyAndComputedOneCollide() throws IOException {
694 Compile res = Compile.create("", "package x;\n"
695 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
696 + " @net.java.html.json.Property(name=\"x\", type=String.class),\n"
698 + "class Collision {\n"
699 + " @net.java.html.json.ComputedProperty static int x(String x) {\n"
700 + " return x.length();\n"
705 res.assertError("Cannot have the name");
708 @Test public void onWebSocketJustTwoArgs() throws IOException {
709 Compile res = Compile.create("", "package x;\n"
710 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
711 + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
713 + "class UseOnReceive {\n"
714 + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", method=\"WebSocket\", data=String.class)\n"
715 + " static void onMessage(MyModel model, String value, int arg) {\n"
720 res.assertError("only have two arg");
723 @Test public void onErrorWouldHaveToBeStatic() throws IOException {
724 Compile res = Compile.create("", "package x;\n"
725 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
726 + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
728 + "class UseOnReceive {\n"
729 + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"notStatic\")\n"
730 + " static void onMessage(MyModel model, String value) {\n"
732 + " void notStatic(Exception e) {}\n"
736 res.assertError("have to be static");
739 @Test public void onErrorMustAcceptExceptionArgument() throws IOException {
740 Compile res = Compile.create("", "package x;\n"
741 + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
742 + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
744 + "class UseOnReceive {\n"
745 + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"subclass\")\n"
746 + " static void onMessage(MyModel model, String value) {\n"
748 + " static void subclass(java.io.IOException e) {}\n"
752 res.assertError("Error method first argument needs to be MyModel and second Exception");