@OnReceive annotation can obtain and process single JSON object model
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 05 Apr 2013 12:43:17 +0200
branchmodel
changeset 93419b4ddc302a6
parent 933 0cb657a2b888
child 935 2cd6f67472c4
@OnReceive annotation can obtain and process single JSON object
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java
javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java
javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java
     1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java	Fri Apr 05 10:41:07 2013 +0200
     1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java	Fri Apr 05 12:43:17 2013 +0200
     1.3 @@ -73,4 +73,26 @@
     1.4      private static Object getProperty(Object object, String property) {
     1.5          return null;
     1.6      }
     1.7 +
     1.8 +    @JavaScriptBody(args = { "url", "arr", "callback" }, body = ""
     1.9 +        + "var request = new XMLHttpRequest();\n"
    1.10 +        + "request.open('GET', url, true);\n"
    1.11 +        + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
    1.12 +        + "request.onreadystatechange = function() {\n"
    1.13 +        + "  if (this.readyState!==4) return;\n"
    1.14 +        + "  arr[0] = eval('(' + this.response + ')');\n"
    1.15 +        + "  callback.run__V();\n"
    1.16 +        + "};"
    1.17 +        + "request.send();"
    1.18 +    )
    1.19 +    public static native void loadJSON(
    1.20 +        String url, Object[] jsonResult, Runnable whenDone
    1.21 +    );
    1.22 +    
    1.23 +    public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
    1.24 +        for (int i = 0; i < props.length; i++) {
    1.25 +            values[i] = getProperty(jsonObject, props[i]);
    1.26 +        }
    1.27 +    }
    1.28 +    
    1.29  }
     2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Fri Apr 05 10:41:07 2013 +0200
     2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Fri Apr 05 12:43:17 2013 +0200
     2.3 @@ -34,6 +34,7 @@
     2.4  import javax.annotation.processing.AbstractProcessor;
     2.5  import javax.annotation.processing.Completion;
     2.6  import javax.annotation.processing.Completions;
     2.7 +import javax.annotation.processing.Messager;
     2.8  import javax.annotation.processing.Processor;
     2.9  import javax.annotation.processing.RoundEnvironment;
    2.10  import javax.annotation.processing.SupportedAnnotationTypes;
    2.11 @@ -57,6 +58,7 @@
    2.12  import org.apidesign.bck2brwsr.htmlpage.api.Model;
    2.13  import org.apidesign.bck2brwsr.htmlpage.api.On;
    2.14  import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
    2.15 +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
    2.16  import org.apidesign.bck2brwsr.htmlpage.api.Page;
    2.17  import org.apidesign.bck2brwsr.htmlpage.api.Property;
    2.18  import org.openide.util.lookup.ServiceProvider;
    2.19 @@ -71,6 +73,7 @@
    2.20      "org.apidesign.bck2brwsr.htmlpage.api.Model",
    2.21      "org.apidesign.bck2brwsr.htmlpage.api.Page",
    2.22      "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
    2.23 +    "org.apidesign.bck2brwsr.htmlpage.api.OnReceive",
    2.24      "org.apidesign.bck2brwsr.htmlpage.api.On"
    2.25  })
    2.26  public final class PageProcessor extends AbstractProcessor {
    2.27 @@ -103,6 +106,10 @@
    2.28              return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream();
    2.29          }
    2.30      }
    2.31 +
    2.32 +    private  Messager err() {
    2.33 +        return processingEnv.getMessager();
    2.34 +    }
    2.35      
    2.36      private boolean processModel(Element e) {
    2.37          boolean ok = true;
    2.38 @@ -135,18 +142,64 @@
    2.39                  w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
    2.40                  w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n");
    2.41                  w.append("final class ").append(className).append(" implements Cloneable {\n");
    2.42 -                w.append("  private Object json;\n");
    2.43                  w.append("  private boolean locked;\n");
    2.44                  w.append("  private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
    2.45                  w.append(body.toString());
    2.46                  w.append("  private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
    2.47                  w.append("  public ").append(className).append("() {\n");
    2.48 +                w.append("    intKnckt();\n");
    2.49 +                w.append("  };\n");
    2.50 +                w.append("  private void intKnckt() {\n");
    2.51                  w.append("    ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, ");
    2.52                  writeStringArray(propsGetSet, w);
    2.53                  w.append(", ");
    2.54                  writeStringArray(functions, w);
    2.55                  w.append("    );\n");
    2.56                  w.append("  };\n");
    2.57 +                w.append("  ").append(className).append("(Object json) {\n");
    2.58 +                int values = 0;
    2.59 +                for (int i = 0; i < propsGetSet.size(); i += 4) {
    2.60 +                    if (propsGetSet.get(i + 2) == null) {
    2.61 +                        continue;
    2.62 +                    }
    2.63 +                    values++;
    2.64 +                }
    2.65 +                w.append("    Object[] ret = new Object[" + values + "];\n");
    2.66 +                w.append("    org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n");
    2.67 +                for (int i = 0; i < propsGetSet.size(); i += 4) {
    2.68 +                    if (propsGetSet.get(i + 2) == null) {
    2.69 +                        continue;
    2.70 +                    }
    2.71 +                    w.append("      \"").append(propsGetSet.get(i)).append("\",\n");
    2.72 +                }
    2.73 +                w.append("    }, ret);\n");
    2.74 +                for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) {
    2.75 +                    if (propsGetSet.get(i + 2) == null) {
    2.76 +                        continue;
    2.77 +                    }
    2.78 +                    boolean[] isModel = { false };
    2.79 +                    boolean[] isEnum = { false };
    2.80 +                    String type = checkType(m.properties()[prop++], isModel, isEnum);
    2.81 +                    w.append("    this.prop_").append(propsGetSet.get(i)).append(" = ");
    2.82 +                    boolean close = false;
    2.83 +                    if (isEnum[0]) {
    2.84 +//                        w.append(type).append(".valueOf((String)");
    2.85 +//                        close = true;
    2.86 +                        w.append("null;\n");
    2.87 +                        continue;
    2.88 +                    } else {
    2.89 +                        w.append('(').append(type).append(')');
    2.90 +                    }
    2.91 +                    w.append("ret[" + cnt++ + "]");
    2.92 +                    if (close) {
    2.93 +                        w.append(");\n");
    2.94 +                    } else {
    2.95 +                        w.append(";\n");
    2.96 +                    }
    2.97 +                    
    2.98 +                }
    2.99 +                w.append("    intKnckt();\n");
   2.100 +                w.append("  };\n");
   2.101                  writeToString(m.properties(), w);
   2.102                  writeClone(className, m.properties(), w);
   2.103                  w.append("}\n");
   2.104 @@ -154,7 +207,7 @@
   2.105                  w.close();
   2.106              }
   2.107          } catch (IOException ex) {
   2.108 -            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   2.109 +            err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   2.110              return false;
   2.111          }
   2.112          return ok;
   2.113 @@ -173,7 +226,7 @@
   2.114              pp = ProcessPage.readPage(is);
   2.115              is.close();
   2.116          } catch (IOException iOException) {
   2.117 -            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
   2.118 +            err().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
   2.119              ok = false;
   2.120              pp = null;
   2.121          }
   2.122 @@ -197,6 +250,9 @@
   2.123              if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   2.124                  ok = false;
   2.125              }
   2.126 +            if (!generateReceive(e, body, className, e.getEnclosedElements(), functions)) {
   2.127 +                ok = false;
   2.128 +            }
   2.129              
   2.130              FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
   2.131              w = new OutputStreamWriter(java.openOutputStream());
   2.132 @@ -237,7 +293,7 @@
   2.133                  w.close();
   2.134              }
   2.135          } catch (IOException ex) {
   2.136 -            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   2.137 +            err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   2.138              return false;
   2.139          }
   2.140          return ok;
   2.141 @@ -283,24 +339,24 @@
   2.142                  if (oc != null) {
   2.143                      for (String id : oc.id()) {
   2.144                          if (pp == null) {
   2.145 -                            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
   2.146 +                            err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
   2.147                              ok = false;
   2.148                              continue;
   2.149                          }
   2.150                          if (pp.tagNameForId(id) == null) {
   2.151 -                            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
   2.152 +                            err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
   2.153                              ok = false;
   2.154                              continue;
   2.155                          }
   2.156                          ExecutableElement ee = (ExecutableElement)method;
   2.157                          CharSequence params = wrapParams(ee, id, className, "ev", null);
   2.158                          if (!ee.getModifiers().contains(Modifier.STATIC)) {
   2.159 -                            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
   2.160 +                            err().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
   2.161                              ok = false;
   2.162                              continue;
   2.163                          }
   2.164                          if (ee.getModifiers().contains(Modifier.PRIVATE)) {
   2.165 -                            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
   2.166 +                            err().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
   2.167                              ok = false;
   2.168                              continue;
   2.169                          }
   2.170 @@ -553,7 +609,7 @@
   2.171          if (!isModel[0] && !"java.lang.String".equals(ret) && !isEnum[0]) {
   2.172              String bt = findBoxedType(ret);
   2.173              if (bt == null) {
   2.174 -                processingEnv.getMessager().printMessage(
   2.175 +                err().printMessage(
   2.176                      Diagnostic.Kind.ERROR, 
   2.177                      "Only primitive types supported in the mapping. Not " + ret,
   2.178                      where
   2.179 @@ -604,7 +660,7 @@
   2.180              sb.append('"');
   2.181              sep = ", ";
   2.182          }
   2.183 -        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
   2.184 +        err().printMessage(Diagnostic.Kind.ERROR,
   2.185              propName + " is not one of known properties: " + sb
   2.186              , e
   2.187          );
   2.188 @@ -634,19 +690,19 @@
   2.189                  continue;
   2.190              }
   2.191              if (!e.getModifiers().contains(Modifier.STATIC)) {
   2.192 -                processingEnv.getMessager().printMessage(
   2.193 +                err().printMessage(
   2.194                      Diagnostic.Kind.ERROR, "@OnFunction method needs to be static", e
   2.195                  );
   2.196                  return false;
   2.197              }
   2.198              if (e.getModifiers().contains(Modifier.PRIVATE)) {
   2.199 -                processingEnv.getMessager().printMessage(
   2.200 +                err().printMessage(
   2.201                      Diagnostic.Kind.ERROR, "@OnFunction method cannot be private", e
   2.202                  );
   2.203                  return false;
   2.204              }
   2.205              if (e.getReturnType().getKind() != TypeKind.VOID) {
   2.206 -                processingEnv.getMessager().printMessage(
   2.207 +                err().printMessage(
   2.208                      Diagnostic.Kind.ERROR, "@OnFunction method should return void", e
   2.209                  );
   2.210                  return false;
   2.211 @@ -664,6 +720,100 @@
   2.212          return true;
   2.213      }
   2.214  
   2.215 +    private boolean generateReceive(
   2.216 +        Element clazz, StringWriter body, String className, 
   2.217 +        List<? extends Element> enclosedElements, List<String> functions
   2.218 +    ) {
   2.219 +        for (Element m : enclosedElements) {
   2.220 +            if (m.getKind() != ElementKind.METHOD) {
   2.221 +                continue;
   2.222 +            }
   2.223 +            ExecutableElement e = (ExecutableElement)m;
   2.224 +            OnReceive onR = e.getAnnotation(OnReceive.class);
   2.225 +            if (onR == null) {
   2.226 +                continue;
   2.227 +            }
   2.228 +            if (!e.getModifiers().contains(Modifier.STATIC)) {
   2.229 +                err().printMessage(
   2.230 +                    Diagnostic.Kind.ERROR, "@OnReceive method needs to be static", e
   2.231 +                );
   2.232 +                return false;
   2.233 +            }
   2.234 +            if (e.getModifiers().contains(Modifier.PRIVATE)) {
   2.235 +                err().printMessage(
   2.236 +                    Diagnostic.Kind.ERROR, "@OnReceive method cannot be private", e
   2.237 +                );
   2.238 +                return false;
   2.239 +            }
   2.240 +            if (e.getReturnType().getKind() != TypeKind.VOID) {
   2.241 +                err().printMessage(
   2.242 +                    Diagnostic.Kind.ERROR, "@OnReceive method should return void", e
   2.243 +                );
   2.244 +                return false;
   2.245 +            }
   2.246 +            String modelClass = null;
   2.247 +            List<String> args = new ArrayList<>();
   2.248 +            {
   2.249 +                for (VariableElement ve : e.getParameters()) {
   2.250 +                    if (ve.asType().toString().equals(className)) {
   2.251 +                        args.add(className + ".this");
   2.252 +                    } else if (isModel(ve.asType())) {
   2.253 +                        if (modelClass != null) {
   2.254 +                            err().printMessage(Diagnostic.Kind.ERROR, "There can be only one model class among arguments", e);
   2.255 +                        } else {
   2.256 +                            modelClass = ve.asType().toString();
   2.257 +                            args.add("new " + modelClass + "(value)");
   2.258 +                        }
   2.259 +                    }
   2.260 +                }
   2.261 +            }
   2.262 +            String n = e.getSimpleName().toString();
   2.263 +            body.append("public void ").append(n).append("(");
   2.264 +            StringBuilder assembleURL = new StringBuilder();
   2.265 +            {
   2.266 +                String sep = "";
   2.267 +                for (String p : findParamNames(e, onR.url(), assembleURL)) {
   2.268 +                    body.append(sep);
   2.269 +                    body.append("String ").append(p);
   2.270 +                    sep = ", ";
   2.271 +                }
   2.272 +            }
   2.273 +            body.append(") {\n");
   2.274 +            body.append("  final Object[] result = { null };\n");
   2.275 +            body.append(
   2.276 +                "  class ProcessResult implements Runnable {\n" +
   2.277 +                "    @Override\n" +
   2.278 +                "    public void run() {\n" +
   2.279 +                "      Object value = result[0];\n" +
   2.280 +                "      if (value instanceof Object[]) {\n" +
   2.281 +                "        throw new IllegalStateException(\"Array value: \" + value);\n" +
   2.282 +                "      } else {\n        ");
   2.283 +            {
   2.284 +                body.append(clazz.getSimpleName()).append(".").append(n).append("(");
   2.285 +                String sep = "";
   2.286 +                for (String arg : args) {
   2.287 +                    body.append(sep);
   2.288 +                    body.append(arg);
   2.289 +                    sep = ", ";
   2.290 +                }
   2.291 +                body.append(");\n");
   2.292 +            }
   2.293 +            body.append(
   2.294 +                "      }\n" +
   2.295 +                "    }\n" +
   2.296 +                "  }\n"
   2.297 +            );
   2.298 +            body.append("  org.apidesign.bck2brwsr.htmlpage.ConvertTypes.loadJSON(\n      ");
   2.299 +            body.append(assembleURL);
   2.300 +            body.append(", result, new ProcessResult()\n  );\n");
   2.301 +//            body.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   2.302 +//            body.append(wrapParams(e, null, className, "ev", "data"));
   2.303 +//            body.append(");\n");
   2.304 +            body.append("}\n");
   2.305 +        }
   2.306 +        return true;
   2.307 +    }
   2.308 +
   2.309      private CharSequence wrapParams(
   2.310          ExecutableElement ee, String id, String className, String evName, String dataName
   2.311      ) {
   2.312 @@ -716,7 +866,7 @@
   2.313                  params.append(className).append(".this");
   2.314                  continue;
   2.315              }
   2.316 -            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, 
   2.317 +            err().printMessage(Diagnostic.Kind.ERROR, 
   2.318                  "@On method can only accept String named 'id' or " + className + " arguments",
   2.319                  ee
   2.320              );
   2.321 @@ -835,4 +985,29 @@
   2.322          }
   2.323          return ret;
   2.324      }
   2.325 +
   2.326 +    private Iterable<String> findParamNames(Element e, String url, StringBuilder assembleURL) {
   2.327 +        List<String> params = new ArrayList<>();
   2.328 +
   2.329 +        for (int pos = 0; ;) {
   2.330 +            int next = url.indexOf('{', pos);
   2.331 +            if (next == -1) {
   2.332 +                assembleURL.append('"')
   2.333 +                    .append(url.substring(pos))
   2.334 +                    .append('"');
   2.335 +                return params;
   2.336 +            }
   2.337 +            int close = url.indexOf('}', next);
   2.338 +            if (close == -1) {
   2.339 +                err().printMessage(Diagnostic.Kind.ERROR, "Unbalanced '{' and '}' in " + url, e);
   2.340 +                return params;
   2.341 +            }
   2.342 +            final String paramName = url.substring(next + 1, close);
   2.343 +            params.add(paramName);
   2.344 +            assembleURL.append('"')
   2.345 +                .append(url.substring(pos, next))
   2.346 +                .append("\" + ").append(paramName).append(" + ");
   2.347 +            pos = close + 1;
   2.348 +        }
   2.349 +    }
   2.350  }
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java	Fri Apr 05 12:43:17 2013 +0200
     3.3 @@ -0,0 +1,41 @@
     3.4 +/**
     3.5 + * Back 2 Browser Bytecode Translator
     3.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, version 2 of the License.
    3.11 + *
    3.12 + * This program is distributed in the hope that it will be useful,
    3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 + * GNU General Public License for more details.
    3.16 + *
    3.17 + * You should have received a copy of the GNU General Public License
    3.18 + * along with this program. Look for COPYING file in the top folder.
    3.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    3.20 + */
    3.21 +package org.apidesign.bck2brwsr.htmlpage.api;
    3.22 +
    3.23 +import java.lang.annotation.ElementType;
    3.24 +import java.lang.annotation.Retention;
    3.25 +import java.lang.annotation.RetentionPolicy;
    3.26 +import java.lang.annotation.Target;
    3.27 +
    3.28 +/** Static methods in classes annotated by {@link Model} or {@link Page}
    3.29 + * can be marked by this annotation establish a JSON communication point.
    3.30 + * The associated model page then gets new method to invoke a network
    3.31 + * connection 
    3.32 + * 
    3.33 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.34 + */
    3.35 +@Retention(RetentionPolicy.SOURCE)
    3.36 +@Target(ElementType.METHOD)
    3.37 +public @interface OnReceive {
    3.38 +    /** The URL to connect to. Can contain variable names surrounded by '{' and '}'.
    3.39 +     * Those parameters will then become variables of the associated method.
    3.40 +     * 
    3.41 +     * @return the (possibly parametrized) url to connect to
    3.42 +     */
    3.43 +    String url();
    3.44 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java	Fri Apr 05 12:43:17 2013 +0200
     4.3 @@ -0,0 +1,52 @@
     4.4 +/**
     4.5 + * Back 2 Browser Bytecode Translator
     4.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4.7 + *
     4.8 + * This program is free software: you can redistribute it and/or modify
     4.9 + * it under the terms of the GNU General Public License as published by
    4.10 + * the Free Software Foundation, version 2 of the License.
    4.11 + *
    4.12 + * This program is distributed in the hope that it will be useful,
    4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.15 + * GNU General Public License for more details.
    4.16 + *
    4.17 + * You should have received a copy of the GNU General Public License
    4.18 + * along with this program. Look for COPYING file in the top folder.
    4.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    4.20 + */
    4.21 +package org.apidesign.bck2brwsr.htmlpage;
    4.22 +
    4.23 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
    4.24 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
    4.25 +import org.apidesign.bck2brwsr.vmtest.VMTest;
    4.26 +import org.testng.annotations.Factory;
    4.27 +
    4.28 +/**
    4.29 + *
    4.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.31 + */
    4.32 +public class ConvertTypesTest {
    4.33 +    @JavaScriptBody(args = {  }, body = "var json = new Object();"
    4.34 +        + "json.firstName = 'son';\n"
    4.35 +        + "json.lastName = 'dj';\n"
    4.36 +        + "json.sex = 'MALE';\n"
    4.37 +        + "return json;"
    4.38 +    )
    4.39 +    private static native Object createJSON();
    4.40 +    
    4.41 +    @BrwsrTest
    4.42 +    public void testConvertToPeople() {
    4.43 +        final Object o = createJSON();
    4.44 +        
    4.45 +        Person p = new Person(o);
    4.46 +        
    4.47 +        assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
    4.48 +        assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
    4.49 +//        assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex();
    4.50 +    }
    4.51 +    
    4.52 +    @Factory public static Object[] create() {
    4.53 +        return VMTest.create(ConvertTypesTest.class);
    4.54 +    }
    4.55 +}
    4.56 \ No newline at end of file
     5.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java	Fri Apr 05 10:41:07 2013 +0200
     5.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java	Fri Apr 05 12:43:17 2013 +0200
     5.3 @@ -18,16 +18,26 @@
     5.4  package org.apidesign.bck2brwsr.htmlpage;
     5.5  
     5.6  import java.util.Iterator;
     5.7 +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
     5.8 +import org.apidesign.bck2brwsr.htmlpage.api.Page;
     5.9 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
    5.10 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
    5.11 +import org.apidesign.bck2brwsr.vmtest.Http;
    5.12 +import org.apidesign.bck2brwsr.vmtest.VMTest;
    5.13  import org.json.JSONException;
    5.14  import org.json.JSONObject;
    5.15  import org.json.JSONTokener;
    5.16  import org.testng.annotations.Test;
    5.17  import static org.testng.Assert.*;
    5.18 +import org.testng.annotations.Factory;
    5.19  
    5.20  /** Need to verify that models produce reasonable JSON objects.
    5.21   *
    5.22   * @author Jaroslav Tulach <jtulach@netbeans.org>
    5.23   */
    5.24 +@Page(xhtml = "Empty.html", className = "JSONik", properties = {
    5.25 +    @Property(name = "fetched", type = PersonImpl.class)
    5.26 +})
    5.27  public class JSONTest {
    5.28      
    5.29      @Test public void personToString() throws JSONException {
    5.30 @@ -108,4 +118,60 @@
    5.31          assertEquals(o.getJSONArray("nicknames").getString(1), n2);
    5.32          assertEquals(o.getJSONArray("age").getInt(1), 73);
    5.33      }
    5.34 +    
    5.35 +    
    5.36 +    @OnReceive(url="/{url}")
    5.37 +    static void fetch(Person p, JSONik model) {
    5.38 +        model.setFetched(p);
    5.39 +        throw new IllegalStateException("Got him: " + p);
    5.40 +    }
    5.41 +    
    5.42 +    @Http(@Http.Resource(
    5.43 +        content = "{'firstName': 'Sitar', 'sex': 'MALE'}", 
    5.44 +        path="/person.json", 
    5.45 +        mimeType = "application/json"
    5.46 +    ))
    5.47 +    @BrwsrTest public void loadAndParseJSON() {
    5.48 +        JSONik js = new JSONik();
    5.49 +        js.applyBindings();
    5.50 +        
    5.51 +        js.fetch("person.json");
    5.52 +        
    5.53 +        Person p = null;
    5.54 +        for (int i = 0; i < 10000000; i++) {
    5.55 +            if (js.getFetched() != null) {
    5.56 +                p = js.getFetched();
    5.57 +            }
    5.58 +        }
    5.59 +        assert p != null : "We should get our person back: " + p;
    5.60 +        assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
    5.61 +        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    5.62 +    }
    5.63 +    
    5.64 +    @Http(@Http.Resource(
    5.65 +        content = "[{'firstName': 'Sitar', 'sex': 'MALE'}]", 
    5.66 +        path="/person.json", 
    5.67 +        mimeType = "application/json"
    5.68 +    ))
    5.69 +    @BrwsrTest public void loadAndParseJSONArray() {
    5.70 +        JSONik js = new JSONik();
    5.71 +        js.applyBindings();
    5.72 +        
    5.73 +        js.fetch("person.json");
    5.74 +        
    5.75 +        Person p = null;
    5.76 +        for (int i = 0; i < 10000000; i++) {
    5.77 +            if (js.getFetched() != null) {
    5.78 +                p = js.getFetched();
    5.79 +            }
    5.80 +        }
    5.81 +        assert p != null : "We should get our person back: " + p;
    5.82 +        assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
    5.83 +        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    5.84 +    }
    5.85 +    
    5.86 +    @Factory public static Object[] create() {
    5.87 +        return VMTest.create(JSONTest.class);
    5.88 +    }
    5.89 +    
    5.90  }