Is model branch in good shape for merging? I think so.
1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Wed Apr 03 13:43:22 2013 +0200
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Mon Apr 08 19:33:08 2013 +0200
1.3 @@ -50,6 +50,22 @@
1.4 throw new IllegalStateException("Value " + ret + " is not of type " + modelClass);
1.5 }
1.6
1.7 + public static String toJSON(Object value) {
1.8 + if (value == null) {
1.9 + return "null";
1.10 + }
1.11 + if (value instanceof String) {
1.12 + return '"' +
1.13 + ((String)value).
1.14 + replace("\"", "\\\"").
1.15 + replace("\n", "\\n").
1.16 + replace("\r", "\\r").
1.17 + replace("\t", "\\t")
1.18 + + '"';
1.19 + }
1.20 + return value.toString();
1.21 + }
1.22 +
1.23 @JavaScriptBody(args = { "object", "property" },
1.24 body = "if (property === null) return object;\n"
1.25 + "var p = object[property]; return p ? p : null;"
1.26 @@ -57,4 +73,78 @@
1.27 private static Object getProperty(Object object, String property) {
1.28 return null;
1.29 }
1.30 +
1.31 + public static String createJSONP(Object[] jsonResult, Runnable whenDone) {
1.32 + int h = whenDone.hashCode();
1.33 + String name;
1.34 + for (;;) {
1.35 + name = "jsonp" + Integer.toHexString(h);
1.36 + if (defineIfUnused(name, jsonResult, whenDone)) {
1.37 + return name;
1.38 + }
1.39 + h++;
1.40 + }
1.41 + }
1.42 +
1.43 + @JavaScriptBody(args = { "name", "arr", "run" }, body =
1.44 + "if (window[name]) return false;\n "
1.45 + + "window[name] = function(data) {\n "
1.46 + + " arr[0] = data;\n"
1.47 + + " run.run__V();\n"
1.48 + + " delete window[name];\n"
1.49 + + "};"
1.50 + + "return true;\n"
1.51 + )
1.52 + private static boolean defineIfUnused(String name, Object[] arr, Runnable run) {
1.53 + return true;
1.54 + }
1.55 +
1.56 + @JavaScriptBody(args = { "url", "arr", "callback" }, body = ""
1.57 + + "var request = new XMLHttpRequest();\n"
1.58 + + "request.open('GET', url, true);\n"
1.59 + + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
1.60 + + "request.onreadystatechange = function() {\n"
1.61 + + " if (this.readyState!==4) return;\n"
1.62 + + " try {\n"
1.63 + + " arr[0] = eval('(' + this.response + ')');\n"
1.64 + + " } catch (error) {;\n"
1.65 + + " throw 'Cannot parse' + error + ':' + this.response;\n"
1.66 + + " };\n"
1.67 + + " callback.run__V();\n"
1.68 + + "};"
1.69 + + "request.send();"
1.70 + )
1.71 + private static void loadJSON(
1.72 + String url, Object[] jsonResult, Runnable whenDone
1.73 + ) {
1.74 + }
1.75 +
1.76 + public static void loadJSON(
1.77 + String url, Object[] jsonResult, Runnable whenDone, String jsonp
1.78 + ) {
1.79 + if (jsonp == null) {
1.80 + loadJSON(url, jsonResult, whenDone);
1.81 + } else {
1.82 + loadJSONP(url, jsonp);
1.83 + }
1.84 + }
1.85 +
1.86 + @JavaScriptBody(args = { "url", "jsonp" }, body =
1.87 + "var scrpt = window.document.createElement('script');\n "
1.88 + + "scrpt.setAttribute('src', url);\n "
1.89 + + "scrpt.setAttribute('id', jsonp);\n "
1.90 + + "scrpt.setAttribute('type', 'text/javascript');\n "
1.91 + + "var body = document.getElementsByTagName('body')[0];\n "
1.92 + + "body.appendChild(scrpt);\n"
1.93 + )
1.94 + private static void loadJSONP(String url, String jsonp) {
1.95 +
1.96 + }
1.97 +
1.98 + public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
1.99 + for (int i = 0; i < props.length; i++) {
1.100 + values[i] = getProperty(jsonObject, props[i]);
1.101 + }
1.102 + }
1.103 +
1.104 }
2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/KOList.java Wed Apr 03 13:43:22 2013 +0200
2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/KOList.java Mon Apr 08 19:33:08 2013 +0200
2.3 @@ -19,6 +19,7 @@
2.4
2.5 import java.util.ArrayList;
2.6 import java.util.Collection;
2.7 +import java.util.Iterator;
2.8 import org.apidesign.bck2brwsr.core.JavaScriptOnly;
2.9
2.10 /**
2.11 @@ -29,6 +30,7 @@
2.12 private final String name;
2.13 private final String[] deps;
2.14 private Knockout model;
2.15 + private Runnable onchange;
2.16
2.17 public KOList(String name, String... deps) {
2.18 this.name = name;
2.19 @@ -36,7 +38,18 @@
2.20 }
2.21
2.22 public void assign(Knockout model) {
2.23 - this.model = model;
2.24 + if (this.model != model) {
2.25 + this.model = model;
2.26 + notifyChange();
2.27 + }
2.28 + }
2.29 +
2.30 + public KOList<T> onChange(Runnable r) {
2.31 + if (this.onchange != null) {
2.32 + throw new IllegalStateException();
2.33 + }
2.34 + this.onchange = r;
2.35 + return this;
2.36 }
2.37
2.38 @Override
2.39 @@ -47,6 +60,20 @@
2.40 }
2.41
2.42 @Override
2.43 + public boolean addAll(Collection<? extends T> c) {
2.44 + boolean ret = super.addAll(c);
2.45 + notifyChange();
2.46 + return ret;
2.47 + }
2.48 +
2.49 + @Override
2.50 + public boolean addAll(int index, Collection<? extends T> c) {
2.51 + boolean ret = super.addAll(index, c);
2.52 + notifyChange();
2.53 + return ret;
2.54 + }
2.55 +
2.56 + @Override
2.57 public boolean remove(Object o) {
2.58 boolean ret = super.remove(o);
2.59 notifyChange();
2.60 @@ -92,7 +119,25 @@
2.61 notifyChange();
2.62 return ret;
2.63 }
2.64 -
2.65 +
2.66 + @Override
2.67 + public String toString() {
2.68 + Iterator<T> it = iterator();
2.69 + if (!it.hasNext()) {
2.70 + return "[]";
2.71 + }
2.72 + String sep = "";
2.73 + StringBuilder sb = new StringBuilder();
2.74 + sb.append('[');
2.75 + while (it.hasNext()) {
2.76 + T t = it.next();
2.77 + sb.append(sep);
2.78 + sb.append(ConvertTypes.toJSON(t));
2.79 + sep = ",";
2.80 + }
2.81 + sb.append(']');
2.82 + return sb.toString();
2.83 + }
2.84
2.85
2.86 @JavaScriptOnly(name = "koArray", value = "function() { return this.toArray___3Ljava_lang_Object_2(); }")
2.87 @@ -100,14 +145,23 @@
2.88
2.89 private void notifyChange() {
2.90 Knockout m = model;
2.91 - if (m == null) {
2.92 - return;
2.93 + if (m != null) {
2.94 + m.valueHasMutated(name);
2.95 + for (String dependant : deps) {
2.96 + m.valueHasMutated(dependant);
2.97 + }
2.98 }
2.99 - m.valueHasMutated(name);
2.100 - for (String dependant : deps) {
2.101 - m.valueHasMutated(dependant);
2.102 + Runnable r = onchange;
2.103 + if (r != null) {
2.104 + r.run();
2.105 }
2.106 }
2.107 -
2.108 +
2.109 + @Override
2.110 + public KOList clone() {
2.111 + KOList ko = (KOList)super.clone();
2.112 + ko.model = null;
2.113 + return ko;
2.114 + }
2.115
2.116 }
3.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Wed Apr 03 13:43:22 2013 +0200
3.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Mon Apr 08 19:33:08 2013 +0200
3.3 @@ -26,6 +26,7 @@
3.4 import java.util.Collection;
3.5 import java.util.Collections;
3.6 import java.util.HashMap;
3.7 +import java.util.HashSet;
3.8 import java.util.LinkedHashSet;
3.9 import java.util.List;
3.10 import java.util.Map;
3.11 @@ -34,6 +35,7 @@
3.12 import javax.annotation.processing.AbstractProcessor;
3.13 import javax.annotation.processing.Completion;
3.14 import javax.annotation.processing.Completions;
3.15 +import javax.annotation.processing.Messager;
3.16 import javax.annotation.processing.Processor;
3.17 import javax.annotation.processing.RoundEnvironment;
3.18 import javax.annotation.processing.SupportedAnnotationTypes;
3.19 @@ -45,9 +47,11 @@
3.20 import javax.lang.model.element.PackageElement;
3.21 import javax.lang.model.element.TypeElement;
3.22 import javax.lang.model.element.VariableElement;
3.23 +import javax.lang.model.type.ArrayType;
3.24 import javax.lang.model.type.MirroredTypeException;
3.25 import javax.lang.model.type.TypeKind;
3.26 import javax.lang.model.type.TypeMirror;
3.27 +import javax.lang.model.util.Elements;
3.28 import javax.lang.model.util.Types;
3.29 import javax.tools.Diagnostic;
3.30 import javax.tools.FileObject;
3.31 @@ -56,6 +60,8 @@
3.32 import org.apidesign.bck2brwsr.htmlpage.api.Model;
3.33 import org.apidesign.bck2brwsr.htmlpage.api.On;
3.34 import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
3.35 +import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange;
3.36 +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
3.37 import org.apidesign.bck2brwsr.htmlpage.api.Page;
3.38 import org.apidesign.bck2brwsr.htmlpage.api.Property;
3.39 import org.openide.util.lookup.ServiceProvider;
3.40 @@ -70,6 +76,8 @@
3.41 "org.apidesign.bck2brwsr.htmlpage.api.Model",
3.42 "org.apidesign.bck2brwsr.htmlpage.api.Page",
3.43 "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
3.44 + "org.apidesign.bck2brwsr.htmlpage.api.OnReceive",
3.45 + "org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange",
3.46 "org.apidesign.bck2brwsr.htmlpage.api.On"
3.47 })
3.48 public final class PageProcessor extends AbstractProcessor {
3.49 @@ -102,6 +110,10 @@
3.50 return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream();
3.51 }
3.52 }
3.53 +
3.54 + private Messager err() {
3.55 + return processingEnv.getMessager();
3.56 + }
3.57
3.58 private boolean processModel(Element e) {
3.59 boolean ok = true;
3.60 @@ -112,15 +124,20 @@
3.61 String pkg = findPkgName(e);
3.62 Writer w;
3.63 String className = m.className();
3.64 + models.put(e, className);
3.65 try {
3.66 StringWriter body = new StringWriter();
3.67 List<String> propsGetSet = new ArrayList<>();
3.68 List<String> functions = new ArrayList<>();
3.69 Map<String, Collection<String>> propsDeps = new HashMap<>();
3.70 + Map<String, Collection<String>> functionDeps = new HashMap<>();
3.71 if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
3.72 ok = false;
3.73 }
3.74 - if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps)) {
3.75 + if (!generateOnChange(e, propsDeps, m.properties(), className, functionDeps)) {
3.76 + ok = false;
3.77 + }
3.78 + if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps, functionDeps)) {
3.79 ok = false;
3.80 }
3.81 if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
3.82 @@ -133,25 +150,94 @@
3.83 w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
3.84 w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
3.85 w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n");
3.86 - w.append("final class ").append(className).append(" {\n");
3.87 - w.append(" private Object json;\n");
3.88 + w.append("final class ").append(className).append(" implements Cloneable {\n");
3.89 w.append(" private boolean locked;\n");
3.90 w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
3.91 w.append(body.toString());
3.92 - w.append(" private static Class<" + e.getSimpleName() + "> modelFor() { return null; }\n");
3.93 + w.append(" private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
3.94 w.append(" public ").append(className).append("() {\n");
3.95 + w.append(" intKnckt();\n");
3.96 + w.append(" };\n");
3.97 + w.append(" private void intKnckt() {\n");
3.98 w.append(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, ");
3.99 writeStringArray(propsGetSet, w);
3.100 w.append(", ");
3.101 writeStringArray(functions, w);
3.102 w.append(" );\n");
3.103 w.append(" };\n");
3.104 + w.append(" ").append(className).append("(Object json) {\n");
3.105 + int values = 0;
3.106 + for (int i = 0; i < propsGetSet.size(); i += 4) {
3.107 + Property p = findProperty(m.properties(), propsGetSet.get(i));
3.108 + if (p == null) {
3.109 + continue;
3.110 + }
3.111 + values++;
3.112 + }
3.113 + w.append(" Object[] ret = new Object[" + values + "];\n");
3.114 + w.append(" org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n");
3.115 + for (int i = 0; i < propsGetSet.size(); i += 4) {
3.116 + Property p = findProperty(m.properties(), propsGetSet.get(i));
3.117 + if (p == null) {
3.118 + continue;
3.119 + }
3.120 + w.append(" \"").append(propsGetSet.get(i)).append("\",\n");
3.121 + }
3.122 + w.append(" }, ret);\n");
3.123 + for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) {
3.124 + final String pn = propsGetSet.get(i);
3.125 + Property p = findProperty(m.properties(), pn);
3.126 + if (p == null) {
3.127 + continue;
3.128 + }
3.129 + boolean[] isModel = { false };
3.130 + boolean[] isEnum = { false };
3.131 + String type = checkType(m.properties()[prop++], isModel, isEnum);
3.132 + if (isEnum[0]) {
3.133 +// w.append(type).append(".valueOf((String)");
3.134 +// close = true;
3.135 + w.append(" this.prop_").append(pn);
3.136 + w.append(" = null;\n");
3.137 + } else if (p.array()) {
3.138 + w.append("if (ret[" + cnt + "] instanceof Object[]) {\n");
3.139 + w.append(" for (Object e : ((Object[])ret[" + cnt + "])) {\n");
3.140 + if (isModel[0]) {
3.141 + w.append(" this.prop_").append(pn).append(".add(new ");
3.142 + w.append(type).append("(e));\n");
3.143 + } else {
3.144 + if (isPrimitive(type)) {
3.145 + w.append(" this.prop_").append(pn).append(".add(((Number)e).");
3.146 + w.append(type).append("Value());\n");
3.147 + } else {
3.148 + w.append(" this.prop_").append(pn).append(".add((");
3.149 + w.append(type).append(")e);\n");
3.150 + }
3.151 + }
3.152 + w.append(" }\n");
3.153 + w.append("}\n");
3.154 + } else {
3.155 + if (isPrimitive(type)) {
3.156 + w.append(" this.prop_").append(pn);
3.157 + w.append(" = ((Number)").append("ret[" + cnt + "]).");
3.158 + w.append(type).append("Value();\n");
3.159 + } else {
3.160 + w.append(" this.prop_").append(pn);
3.161 + w.append(" = (").append(type).append(')');
3.162 + w.append("ret[" + cnt + "];\n");
3.163 + }
3.164 + }
3.165 + cnt++;
3.166 + }
3.167 + w.append(" intKnckt();\n");
3.168 + w.append(" };\n");
3.169 + writeToString(m.properties(), w);
3.170 + writeClone(className, m.properties(), w);
3.171 w.append("}\n");
3.172 } finally {
3.173 w.close();
3.174 }
3.175 } catch (IOException ex) {
3.176 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
3.177 + err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
3.178 return false;
3.179 }
3.180 return ok;
3.181 @@ -170,7 +256,7 @@
3.182 pp = ProcessPage.readPage(is);
3.183 is.close();
3.184 } catch (IOException iOException) {
3.185 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e);
3.186 + err().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
3.187 ok = false;
3.188 pp = null;
3.189 }
3.190 @@ -185,15 +271,22 @@
3.191 List<String> propsGetSet = new ArrayList<>();
3.192 List<String> functions = new ArrayList<>();
3.193 Map<String, Collection<String>> propsDeps = new HashMap<>();
3.194 + Map<String, Collection<String>> functionDeps = new HashMap<>();
3.195 if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
3.196 ok = false;
3.197 }
3.198 - if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps)) {
3.199 + if (!generateOnChange(e, propsDeps, p.properties(), className, functionDeps)) {
3.200 + ok = false;
3.201 + }
3.202 + if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps, functionDeps)) {
3.203 ok = false;
3.204 }
3.205 if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
3.206 ok = false;
3.207 }
3.208 + if (!generateReceive(e, body, className, e.getEnclosedElements(), functions)) {
3.209 + ok = false;
3.210 + }
3.211
3.212 FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
3.213 w = new OutputStreamWriter(java.openOutputStream());
3.214 @@ -206,7 +299,7 @@
3.215 if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
3.216 ok = false;
3.217 } else {
3.218 - for (String id : pp.ids()) {
3.219 + if (pp != null) for (String id : pp.ids()) {
3.220 String tag = pp.tagNameForId(id);
3.221 String type = type(tag);
3.222 w.append(" ").append("public final ").
3.223 @@ -234,7 +327,7 @@
3.224 w.close();
3.225 }
3.226 } catch (IOException ex) {
3.227 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
3.228 + err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
3.229 return false;
3.230 }
3.231 return ok;
3.232 @@ -280,24 +373,24 @@
3.233 if (oc != null) {
3.234 for (String id : oc.id()) {
3.235 if (pp == null) {
3.236 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
3.237 + err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
3.238 ok = false;
3.239 continue;
3.240 }
3.241 if (pp.tagNameForId(id) == null) {
3.242 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
3.243 + err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
3.244 ok = false;
3.245 continue;
3.246 }
3.247 ExecutableElement ee = (ExecutableElement)method;
3.248 CharSequence params = wrapParams(ee, id, className, "ev", null);
3.249 if (!ee.getModifiers().contains(Modifier.STATIC)) {
3.250 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
3.251 + err().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
3.252 ok = false;
3.253 continue;
3.254 }
3.255 if (ee.getModifiers().contains(Modifier.PRIVATE)) {
3.256 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
3.257 + err().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
3.258 ok = false;
3.259 continue;
3.260 }
3.261 @@ -378,7 +471,9 @@
3.262 private boolean generateProperties(
3.263 Element where,
3.264 Writer w, Property[] properties,
3.265 - Collection<String> props, Map<String,Collection<String>> deps
3.266 + Collection<String> props,
3.267 + Map<String,Collection<String>> deps,
3.268 + Map<String,Collection<String>> functionDeps
3.269 ) throws IOException {
3.270 boolean ok = true;
3.271 for (Property p : properties) {
3.272 @@ -389,7 +484,7 @@
3.273 if (p.array()) {
3.274 w.write("private KOList<" + tn + "> prop_" + p.name() + " = new KOList<" + tn + ">(\""
3.275 + p.name() + "\"");
3.276 - final Collection<String> dependants = deps.get(p.name());
3.277 + Collection<String> dependants = deps.get(p.name());
3.278 if (dependants != null) {
3.279 for (String depProp : dependants) {
3.280 w.write(", ");
3.281 @@ -398,7 +493,18 @@
3.282 w.write('\"');
3.283 }
3.284 }
3.285 - w.write(");\n");
3.286 + w.write(")");
3.287 +
3.288 + dependants = functionDeps.get(p.name());
3.289 + if (dependants != null) {
3.290 + w.write(".onChange(new Runnable() { public void run() {\n");
3.291 + for (String call : dependants) {
3.292 + w.append(call);
3.293 + }
3.294 + w.write("}})");
3.295 + }
3.296 + w.write(";\n");
3.297 +
3.298 w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n");
3.299 w.write(" if (locked) throw new IllegalStateException();\n");
3.300 w.write(" prop_" + p.name() + ".assign(ko);\n");
3.301 @@ -415,13 +521,19 @@
3.302 w.write(" prop_" + p.name() + " = v;\n");
3.303 w.write(" if (ko != null) {\n");
3.304 w.write(" ko.valueHasMutated(\"" + p.name() + "\");\n");
3.305 - final Collection<String> dependants = deps.get(p.name());
3.306 + Collection<String> dependants = deps.get(p.name());
3.307 if (dependants != null) {
3.308 for (String depProp : dependants) {
3.309 w.write(" ko.valueHasMutated(\"" + depProp + "\");\n");
3.310 }
3.311 }
3.312 w.write(" }\n");
3.313 + dependants = functionDeps.get(p.name());
3.314 + if (dependants != null) {
3.315 + for (String call : dependants) {
3.316 + w.append(call);
3.317 + }
3.318 + }
3.319 w.write("}\n");
3.320 }
3.321
3.322 @@ -450,7 +562,7 @@
3.323 final TypeMirror rt = ee.getReturnType();
3.324 final Types tu = processingEnv.getTypeUtils();
3.325 TypeMirror ert = tu.erasure(rt);
3.326 - String tn = ert.toString();
3.327 + String tn = fqn(ert, ee);
3.328 boolean array = false;
3.329 if (tn.equals("java.util.List")) {
3.330 array = true;
3.331 @@ -469,7 +581,7 @@
3.332 ok = false;
3.333 }
3.334
3.335 - final String dt = pe.asType().toString();
3.336 + final String dt = fqn(pe.asType(), ee);
3.337 String[] call = toGetSet(dn, dt, false);
3.338 w.write(" " + dt + " arg" + (++arg) + " = ");
3.339 w.write(call[0] + "();\n");
3.340 @@ -483,7 +595,7 @@
3.341 }
3.342 w.write(" try {\n");
3.343 w.write(" locked = true;\n");
3.344 - w.write(" return " + e.getEnclosingElement().getSimpleName() + '.' + e.getSimpleName() + "(");
3.345 + w.write(" return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
3.346 String sep = "";
3.347 for (int i = 1; i <= arg; i++) {
3.348 w.write(sep);
3.349 @@ -538,35 +650,19 @@
3.350
3.351 private String typeName(Element where, Property p) {
3.352 String ret;
3.353 - boolean isModel = false;
3.354 - boolean isEnum = false;
3.355 - try {
3.356 - ret = p.type().getName();
3.357 - } catch (MirroredTypeException ex) {
3.358 - TypeMirror tm = processingEnv.getTypeUtils().erasure(ex.getTypeMirror());
3.359 - final Element e = processingEnv.getTypeUtils().asElement(tm);
3.360 - final Model m = e == null ? null : e.getAnnotation(Model.class);
3.361 - if (m != null) {
3.362 - ret = findPkgName(e) + '.' + m.className();
3.363 - isModel = true;
3.364 - models.put(e, m.className());
3.365 - } else {
3.366 - ret = tm.toString();
3.367 - }
3.368 - TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
3.369 - enm = processingEnv.getTypeUtils().erasure(enm);
3.370 - isEnum = processingEnv.getTypeUtils().isSubtype(tm, enm);
3.371 - }
3.372 + boolean[] isModel = { false };
3.373 + boolean[] isEnum = { false };
3.374 + ret = checkType(p, isModel, isEnum);
3.375 if (p.array()) {
3.376 String bt = findBoxedType(ret);
3.377 if (bt != null) {
3.378 return bt;
3.379 }
3.380 }
3.381 - if (!isModel && !"java.lang.String".equals(ret) && !isEnum) {
3.382 + if (!isModel[0] && !"java.lang.String".equals(ret) && !isEnum[0]) {
3.383 String bt = findBoxedType(ret);
3.384 if (bt == null) {
3.385 - processingEnv.getMessager().printMessage(
3.386 + err().printMessage(
3.387 Diagnostic.Kind.ERROR,
3.388 "Only primitive types supported in the mapping. Not " + ret,
3.389 where
3.390 @@ -617,7 +713,7 @@
3.391 sb.append('"');
3.392 sep = ", ";
3.393 }
3.394 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
3.395 + err().printMessage(Diagnostic.Kind.ERROR,
3.396 propName + " is not one of known properties: " + sb
3.397 , e
3.398 );
3.399 @@ -647,25 +743,25 @@
3.400 continue;
3.401 }
3.402 if (!e.getModifiers().contains(Modifier.STATIC)) {
3.403 - processingEnv.getMessager().printMessage(
3.404 + err().printMessage(
3.405 Diagnostic.Kind.ERROR, "@OnFunction method needs to be static", e
3.406 );
3.407 return false;
3.408 }
3.409 if (e.getModifiers().contains(Modifier.PRIVATE)) {
3.410 - processingEnv.getMessager().printMessage(
3.411 + err().printMessage(
3.412 Diagnostic.Kind.ERROR, "@OnFunction method cannot be private", e
3.413 );
3.414 return false;
3.415 }
3.416 if (e.getReturnType().getKind() != TypeKind.VOID) {
3.417 - processingEnv.getMessager().printMessage(
3.418 + err().printMessage(
3.419 Diagnostic.Kind.ERROR, "@OnFunction method should return void", e
3.420 );
3.421 return false;
3.422 }
3.423 String n = e.getSimpleName().toString();
3.424 - body.append("void ").append(n).append("(Object data, Object ev) {\n");
3.425 + body.append("private void ").append(n).append("(Object data, Object ev) {\n");
3.426 body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
3.427 body.append(wrapParams(e, null, className, "ev", "data"));
3.428 body.append(");\n");
3.429 @@ -677,6 +773,205 @@
3.430 return true;
3.431 }
3.432
3.433 + private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
3.434 + Property[] properties, String className,
3.435 + Map<String, Collection<String>> functionDeps
3.436 + ) {
3.437 + for (Element m : clazz.getEnclosedElements()) {
3.438 + if (m.getKind() != ElementKind.METHOD) {
3.439 + continue;
3.440 + }
3.441 + ExecutableElement e = (ExecutableElement) m;
3.442 + OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
3.443 + if (onPC == null) {
3.444 + continue;
3.445 + }
3.446 + for (String pn : onPC.value()) {
3.447 + if (findProperty(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
3.448 + err().printMessage(Diagnostic.Kind.ERROR, "No property named '" + pn + "' in the model");
3.449 + return false;
3.450 + }
3.451 + }
3.452 + if (!e.getModifiers().contains(Modifier.STATIC)) {
3.453 + err().printMessage(
3.454 + Diagnostic.Kind.ERROR, "@OnPropertyChange method needs to be static", e);
3.455 + return false;
3.456 + }
3.457 + if (e.getModifiers().contains(Modifier.PRIVATE)) {
3.458 + err().printMessage(
3.459 + Diagnostic.Kind.ERROR, "@OnPropertyChange method cannot be private", e);
3.460 + return false;
3.461 + }
3.462 + if (e.getReturnType().getKind() != TypeKind.VOID) {
3.463 + err().printMessage(
3.464 + Diagnostic.Kind.ERROR, "@OnPropertyChange method should return void", e);
3.465 + return false;
3.466 + }
3.467 + String n = e.getSimpleName().toString();
3.468 +
3.469 +
3.470 + for (String pn : onPC.value()) {
3.471 + StringBuilder call = new StringBuilder();
3.472 + call.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
3.473 + call.append(wrapPropName(e, className, "name", pn));
3.474 + call.append(");\n");
3.475 +
3.476 + Collection<String> change = functionDeps.get(pn);
3.477 + if (change == null) {
3.478 + change = new ArrayList<>();
3.479 + functionDeps.put(pn, change);
3.480 + }
3.481 + change.add(call.toString());
3.482 + for (String dpn : findDerivedFrom(propDeps, pn)) {
3.483 + change = functionDeps.get(dpn);
3.484 + if (change == null) {
3.485 + change = new ArrayList<>();
3.486 + functionDeps.put(dpn, change);
3.487 + }
3.488 + change.add(call.toString());
3.489 + }
3.490 + }
3.491 + }
3.492 + return true;
3.493 + }
3.494 +
3.495 + private boolean generateReceive(
3.496 + Element clazz, StringWriter body, String className,
3.497 + List<? extends Element> enclosedElements, List<String> functions
3.498 + ) {
3.499 + for (Element m : enclosedElements) {
3.500 + if (m.getKind() != ElementKind.METHOD) {
3.501 + continue;
3.502 + }
3.503 + ExecutableElement e = (ExecutableElement)m;
3.504 + OnReceive onR = e.getAnnotation(OnReceive.class);
3.505 + if (onR == null) {
3.506 + continue;
3.507 + }
3.508 + if (!e.getModifiers().contains(Modifier.STATIC)) {
3.509 + err().printMessage(
3.510 + Diagnostic.Kind.ERROR, "@OnReceive method needs to be static", e
3.511 + );
3.512 + return false;
3.513 + }
3.514 + if (e.getModifiers().contains(Modifier.PRIVATE)) {
3.515 + err().printMessage(
3.516 + Diagnostic.Kind.ERROR, "@OnReceive method cannot be private", e
3.517 + );
3.518 + return false;
3.519 + }
3.520 + if (e.getReturnType().getKind() != TypeKind.VOID) {
3.521 + err().printMessage(
3.522 + Diagnostic.Kind.ERROR, "@OnReceive method should return void", e
3.523 + );
3.524 + return false;
3.525 + }
3.526 + String modelClass = null;
3.527 + boolean expectsList = false;
3.528 + List<String> args = new ArrayList<>();
3.529 + {
3.530 + for (VariableElement ve : e.getParameters()) {
3.531 + TypeMirror modelType = null;
3.532 + if (ve.asType().toString().equals(className)) {
3.533 + args.add(className + ".this");
3.534 + } else if (isModel(ve.asType())) {
3.535 + modelType = ve.asType();
3.536 + } else if (ve.asType().getKind() == TypeKind.ARRAY) {
3.537 + modelType = ((ArrayType)ve.asType()).getComponentType();
3.538 + expectsList = true;
3.539 + }
3.540 + if (modelType != null) {
3.541 + if (modelClass != null) {
3.542 + err().printMessage(Diagnostic.Kind.ERROR, "There can be only one model class among arguments", e);
3.543 + } else {
3.544 + modelClass = modelType.toString();
3.545 + if (expectsList) {
3.546 + args.add("arr");
3.547 + } else {
3.548 + args.add("arr[0]");
3.549 + }
3.550 + }
3.551 + }
3.552 + }
3.553 + }
3.554 + if (modelClass == null) {
3.555 + err().printMessage(Diagnostic.Kind.ERROR, "The method needs to have one @Model class as parameter", e);
3.556 + }
3.557 + String n = e.getSimpleName().toString();
3.558 + body.append("public void ").append(n).append("(");
3.559 + StringBuilder assembleURL = new StringBuilder();
3.560 + String jsonpVarName = null;
3.561 + {
3.562 + String sep = "";
3.563 + boolean skipJSONP = onR.jsonp().isEmpty();
3.564 + for (String p : findParamNames(e, onR.url(), assembleURL)) {
3.565 + if (!skipJSONP && p.equals(onR.jsonp())) {
3.566 + skipJSONP = true;
3.567 + jsonpVarName = p;
3.568 + continue;
3.569 + }
3.570 + body.append(sep);
3.571 + body.append("String ").append(p);
3.572 + sep = ", ";
3.573 + }
3.574 + if (!skipJSONP) {
3.575 + err().printMessage(Diagnostic.Kind.ERROR,
3.576 + "Name of jsonp attribute ('" + onR.jsonp() +
3.577 + "') is not used in url attribute '" + onR.url() + "'"
3.578 + );
3.579 + }
3.580 + }
3.581 + body.append(") {\n");
3.582 + body.append(" final Object[] result = { null };\n");
3.583 + body.append(
3.584 + " class ProcessResult implements Runnable {\n" +
3.585 + " @Override\n" +
3.586 + " public void run() {\n" +
3.587 + " Object value = result[0];\n");
3.588 + body.append(
3.589 + " " + modelClass + "[] arr;\n");
3.590 + body.append(
3.591 + " if (value instanceof Object[]) {\n" +
3.592 + " Object[] data = ((Object[])value);\n" +
3.593 + " arr = new " + modelClass + "[data.length];\n" +
3.594 + " for (int i = 0; i < data.length; i++) {\n" +
3.595 + " arr[i] = new " + modelClass + "(data[i]);\n" +
3.596 + " }\n" +
3.597 + " } else {\n" +
3.598 + " arr = new " + modelClass + "[1];\n" +
3.599 + " arr[0] = new " + modelClass + "(value);\n" +
3.600 + " }\n"
3.601 + );
3.602 + {
3.603 + body.append(clazz.getSimpleName()).append(".").append(n).append("(");
3.604 + String sep = "";
3.605 + for (String arg : args) {
3.606 + body.append(sep);
3.607 + body.append(arg);
3.608 + sep = ", ";
3.609 + }
3.610 + body.append(");\n");
3.611 + }
3.612 + body.append(
3.613 + " }\n" +
3.614 + " }\n"
3.615 + );
3.616 + body.append(" ProcessResult pr = new ProcessResult();\n");
3.617 + if (jsonpVarName != null) {
3.618 + body.append(" String ").append(jsonpVarName).
3.619 + append(" = org.apidesign.bck2brwsr.htmlpage.ConvertTypes.createJSONP(result, pr);\n");
3.620 + }
3.621 + body.append(" org.apidesign.bck2brwsr.htmlpage.ConvertTypes.loadJSON(\n ");
3.622 + body.append(assembleURL);
3.623 + body.append(", result, pr, ").append(jsonpVarName).append("\n );\n");
3.624 +// body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
3.625 +// body.append(wrapParams(e, null, className, "ev", "data"));
3.626 +// body.append(");\n");
3.627 + body.append("}\n");
3.628 + }
3.629 + return true;
3.630 + }
3.631 +
3.632 private CharSequence wrapParams(
3.633 ExecutableElement ee, String id, String className, String evName, String dataName
3.634 ) {
3.635 @@ -712,6 +1007,14 @@
3.636 params.append(dataName);
3.637 params.append(", null");
3.638 } else {
3.639 + if (evName == null) {
3.640 + final StringBuilder sb = new StringBuilder();
3.641 + sb.append("Unexpected string parameter name.");
3.642 + if (dataName != null) {
3.643 + sb.append(" Try \"").append(dataName).append("\"");
3.644 + }
3.645 + err().printMessage(Diagnostic.Kind.ERROR, sb.toString(), ee);
3.646 + }
3.647 params.append(evName);
3.648 params.append(", \"");
3.649 params.append(ve.getSimpleName().toString());
3.650 @@ -720,7 +1023,7 @@
3.651 params.append(")");
3.652 continue;
3.653 }
3.654 - String rn = ve.asType().toString();
3.655 + String rn = fqn(ve.asType(), ee);
3.656 int last = rn.lastIndexOf('.');
3.657 if (last >= 0) {
3.658 rn = rn.substring(last + 1);
3.659 @@ -729,7 +1032,7 @@
3.660 params.append(className).append(".this");
3.661 continue;
3.662 }
3.663 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
3.664 + err().printMessage(Diagnostic.Kind.ERROR,
3.665 "@On method can only accept String named 'id' or " + className + " arguments",
3.666 ee
3.667 );
3.668 @@ -737,6 +1040,42 @@
3.669 return params;
3.670 }
3.671
3.672 +
3.673 + private CharSequence wrapPropName(
3.674 + ExecutableElement ee, String className, String propName, String propValue
3.675 + ) {
3.676 + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
3.677 + StringBuilder params = new StringBuilder();
3.678 + boolean first = true;
3.679 + for (VariableElement ve : ee.getParameters()) {
3.680 + if (!first) {
3.681 + params.append(", ");
3.682 + }
3.683 + first = false;
3.684 + if (ve.asType() == stringType) {
3.685 + if (propName != null && ve.getSimpleName().contentEquals(propName)) {
3.686 + params.append('"').append(propValue).append('"');
3.687 + } else {
3.688 + err().printMessage(Diagnostic.Kind.ERROR, "Unexpected string parameter name. Try \"" + propName + "\".");
3.689 + }
3.690 + continue;
3.691 + }
3.692 + String rn = fqn(ve.asType(), ee);
3.693 + int last = rn.lastIndexOf('.');
3.694 + if (last >= 0) {
3.695 + rn = rn.substring(last + 1);
3.696 + }
3.697 + if (rn.equals(className)) {
3.698 + params.append(className).append(".this");
3.699 + continue;
3.700 + }
3.701 + err().printMessage(Diagnostic.Kind.ERROR,
3.702 + "@OnPropertyChange method can only accept String or " + className + " arguments",
3.703 + ee);
3.704 + }
3.705 + return params;
3.706 + }
3.707 +
3.708 private boolean isModel(TypeMirror tm) {
3.709 final Element e = processingEnv.getTypeUtils().asElement(tm);
3.710 if (e == null) {
3.711 @@ -767,4 +1106,141 @@
3.712 }
3.713 w.write("\n }");
3.714 }
3.715 +
3.716 + private void writeToString(Property[] props, Writer w) throws IOException {
3.717 + w.write(" public String toString() {\n");
3.718 + w.write(" StringBuilder sb = new StringBuilder();\n");
3.719 + w.write(" sb.append('{');\n");
3.720 + String sep = "";
3.721 + for (Property p : props) {
3.722 + w.write(sep);
3.723 + w.append(" sb.append(\"" + p.name() + ": \");\n");
3.724 + w.append(" sb.append(org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toJSON(prop_");
3.725 + w.append(p.name()).append("));\n");
3.726 + sep = " sb.append(',');\n";
3.727 + }
3.728 + w.write(" sb.append('}');\n");
3.729 + w.write(" return sb.toString();\n");
3.730 + w.write(" }\n");
3.731 + }
3.732 + private void writeClone(String className, Property[] props, Writer w) throws IOException {
3.733 + w.write(" public " + className + " clone() {\n");
3.734 + w.write(" " + className + " ret = new " + className + "();\n");
3.735 + for (Property p : props) {
3.736 + if (!p.array()) {
3.737 + boolean isModel[] = { false };
3.738 + boolean isEnum[] = { false };
3.739 + checkType(p, isModel, isEnum);
3.740 + if (!isModel[0]) {
3.741 + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
3.742 + continue;
3.743 + }
3.744 + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
3.745 + } else {
3.746 + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
3.747 + }
3.748 + }
3.749 +
3.750 + w.write(" return ret;\n");
3.751 + w.write(" }\n");
3.752 + }
3.753 +
3.754 + private String inPckName(Element e) {
3.755 + StringBuilder sb = new StringBuilder();
3.756 + while (e.getKind() != ElementKind.PACKAGE) {
3.757 + if (sb.length() == 0) {
3.758 + sb.append(e.getSimpleName());
3.759 + } else {
3.760 + sb.insert(0, '.');
3.761 + sb.insert(0, e.getSimpleName());
3.762 + }
3.763 + e = e.getEnclosingElement();
3.764 + }
3.765 + return sb.toString();
3.766 + }
3.767 +
3.768 + private String fqn(TypeMirror pt, Element relative) {
3.769 + if (pt.getKind() == TypeKind.ERROR) {
3.770 + final Elements eu = processingEnv.getElementUtils();
3.771 + PackageElement pckg = eu.getPackageOf(relative);
3.772 + return pckg.getQualifiedName() + "." + pt.toString();
3.773 + }
3.774 + return pt.toString();
3.775 + }
3.776 +
3.777 + private String checkType(Property p, boolean[] isModel, boolean[] isEnum) {
3.778 + String ret;
3.779 + try {
3.780 + ret = p.type().getName();
3.781 + } catch (MirroredTypeException ex) {
3.782 + TypeMirror tm = processingEnv.getTypeUtils().erasure(ex.getTypeMirror());
3.783 + final Element e = processingEnv.getTypeUtils().asElement(tm);
3.784 + final Model m = e == null ? null : e.getAnnotation(Model.class);
3.785 + if (m != null) {
3.786 + ret = findPkgName(e) + '.' + m.className();
3.787 + isModel[0] = true;
3.788 + models.put(e, m.className());
3.789 + } else {
3.790 + ret = tm.toString();
3.791 + }
3.792 + TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
3.793 + enm = processingEnv.getTypeUtils().erasure(enm);
3.794 + isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
3.795 + }
3.796 + return ret;
3.797 + }
3.798 +
3.799 + private Iterable<String> findParamNames(Element e, String url, StringBuilder assembleURL) {
3.800 + List<String> params = new ArrayList<>();
3.801 +
3.802 + for (int pos = 0; ;) {
3.803 + int next = url.indexOf('{', pos);
3.804 + if (next == -1) {
3.805 + assembleURL.append('"')
3.806 + .append(url.substring(pos))
3.807 + .append('"');
3.808 + return params;
3.809 + }
3.810 + int close = url.indexOf('}', next);
3.811 + if (close == -1) {
3.812 + err().printMessage(Diagnostic.Kind.ERROR, "Unbalanced '{' and '}' in " + url, e);
3.813 + return params;
3.814 + }
3.815 + final String paramName = url.substring(next + 1, close);
3.816 + params.add(paramName);
3.817 + assembleURL.append('"')
3.818 + .append(url.substring(pos, next))
3.819 + .append("\" + ").append(paramName).append(" + ");
3.820 + pos = close + 1;
3.821 + }
3.822 + }
3.823 +
3.824 + private static Property findProperty(Property[] properties, String propName) {
3.825 + for (Property p : properties) {
3.826 + if (propName.equals(p.name())) {
3.827 + return p;
3.828 + }
3.829 + }
3.830 + return null;
3.831 + }
3.832 +
3.833 + private boolean isPrimitive(String type) {
3.834 + return
3.835 + "int".equals(type) ||
3.836 + "double".equals(type) ||
3.837 + "long".equals(type) ||
3.838 + "short".equals(type) ||
3.839 + "byte".equals(type) ||
3.840 + "float".equals(type);
3.841 + }
3.842 +
3.843 + private static Collection<String> findDerivedFrom(Map<String, Collection<String>> propsDeps, String derivedProp) {
3.844 + Set<String> names = new HashSet<>();
3.845 + for (Map.Entry<String, Collection<String>> e : propsDeps.entrySet()) {
3.846 + if (e.getValue().contains(derivedProp)) {
3.847 + names.add(e.getKey());
3.848 + }
3.849 + }
3.850 + return names;
3.851 + }
3.852 }
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnPropertyChange.java Mon Apr 08 19:33:08 2013 +0200
4.3 @@ -0,0 +1,38 @@
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.api;
4.22 +
4.23 +import java.lang.annotation.ElementType;
4.24 +import java.lang.annotation.Retention;
4.25 +import java.lang.annotation.RetentionPolicy;
4.26 +import java.lang.annotation.Target;
4.27 +
4.28 +/** Represents a property. Either in a generated model of an HTML
4.29 + * {@link Page} or in a class defined by {@link Model}.
4.30 + *
4.31 + * @author Jaroslav Tulach <jtulach@netbeans.org>
4.32 + */
4.33 +@Retention(RetentionPolicy.SOURCE)
4.34 +@Target(ElementType.METHOD)
4.35 +public @interface OnPropertyChange {
4.36 + /** Name(s) of the properties. One wishes to observe.
4.37 + *
4.38 + * @return valid java identifier
4.39 + */
4.40 + String[] value();
4.41 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java Mon Apr 08 19:33:08 2013 +0200
5.3 @@ -0,0 +1,54 @@
5.4 +/**
5.5 + * Back 2 Browser Bytecode Translator
5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5.7 + *
5.8 + * This program is free software: you can redistribute it and/or modify
5.9 + * it under the terms of the GNU General Public License as published by
5.10 + * the Free Software Foundation, version 2 of the License.
5.11 + *
5.12 + * This program is distributed in the hope that it will be useful,
5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.15 + * GNU General Public License for more details.
5.16 + *
5.17 + * You should have received a copy of the GNU General Public License
5.18 + * along with this program. Look for COPYING file in the top folder.
5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
5.20 + */
5.21 +package org.apidesign.bck2brwsr.htmlpage.api;
5.22 +
5.23 +import java.lang.annotation.ElementType;
5.24 +import java.lang.annotation.Retention;
5.25 +import java.lang.annotation.RetentionPolicy;
5.26 +import java.lang.annotation.Target;
5.27 +
5.28 +/** Static methods in classes annotated by {@link Model} or {@link Page}
5.29 + * can be marked by this annotation establish a JSON communication point.
5.30 + * The associated model page then gets new method to invoke a network
5.31 + * connection
5.32 + *
5.33 + * @author Jaroslav Tulach <jtulach@netbeans.org>
5.34 + */
5.35 +@Retention(RetentionPolicy.SOURCE)
5.36 +@Target(ElementType.METHOD)
5.37 +public @interface OnReceive {
5.38 + /** The URL to connect to. Can contain variable names surrounded by '{' and '}'.
5.39 + * Those parameters will then become variables of the associated method.
5.40 + *
5.41 + * @return the (possibly parametrized) url to connect to
5.42 + */
5.43 + String url();
5.44 +
5.45 + /** Support for <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> requires
5.46 + * a callback from the server generated page to a function defined in the
5.47 + * system. The name of such function is usually specified as a property
5.48 + * (of possibly different names). By defining the <code>jsonp</code> attribute
5.49 + * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
5.50 + * transmission and specifies the name of the property. The property should
5.51 + * also be used in the {@link #url()} attribute on appropriate place.
5.52 + *
5.53 + * @return name of a property to carry the name of <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
5.54 + * callback function.
5.55 + */
5.56 + String jsonp() default "";
5.57 +}
6.1 --- a/javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Wed Apr 03 13:43:22 2013 +0200
6.2 +++ b/javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Mon Apr 08 19:33:08 2013 +0200
6.3 @@ -2193,7 +2193,14 @@
6.4 else
6.5 element[attrName] = attrValue;
6.6 } else if (!toRemove) {
6.7 - element.setAttribute(attrName, attrValue.toString());
6.8 + try {
6.9 + element.setAttribute(attrName, attrValue.toString());
6.10 + } catch (err) {
6.11 + // ignore for now
6.12 + if (console) {
6.13 + console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
6.14 + }
6.15 + }
6.16 }
6.17
6.18 // Treat "name" specially - although you can think of it as an attribute, it also needs
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java Mon Apr 08 19:33:08 2013 +0200
7.3 @@ -0,0 +1,52 @@
7.4 +/**
7.5 + * Back 2 Browser Bytecode Translator
7.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
7.7 + *
7.8 + * This program is free software: you can redistribute it and/or modify
7.9 + * it under the terms of the GNU General Public License as published by
7.10 + * the Free Software Foundation, version 2 of the License.
7.11 + *
7.12 + * This program is distributed in the hope that it will be useful,
7.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
7.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.15 + * GNU General Public License for more details.
7.16 + *
7.17 + * You should have received a copy of the GNU General Public License
7.18 + * along with this program. Look for COPYING file in the top folder.
7.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
7.20 + */
7.21 +package org.apidesign.bck2brwsr.htmlpage;
7.22 +
7.23 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
7.24 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
7.25 +import org.apidesign.bck2brwsr.vmtest.VMTest;
7.26 +import org.testng.annotations.Factory;
7.27 +
7.28 +/**
7.29 + *
7.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
7.31 + */
7.32 +public class ConvertTypesTest {
7.33 + @JavaScriptBody(args = { }, body = "var json = new Object();"
7.34 + + "json.firstName = 'son';\n"
7.35 + + "json.lastName = 'dj';\n"
7.36 + + "json.sex = 'MALE';\n"
7.37 + + "return json;"
7.38 + )
7.39 + private static native Object createJSON();
7.40 +
7.41 + @BrwsrTest
7.42 + public void testConvertToPeople() {
7.43 + final Object o = createJSON();
7.44 +
7.45 + Person p = new Person(o);
7.46 +
7.47 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
7.48 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
7.49 +// assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex();
7.50 + }
7.51 +
7.52 + @Factory public static Object[] create() {
7.53 + return VMTest.create(ConvertTypesTest.class);
7.54 + }
7.55 +}
7.56 \ No newline at end of file
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java Mon Apr 08 19:33:08 2013 +0200
8.3 @@ -0,0 +1,325 @@
8.4 +/**
8.5 + * Back 2 Browser Bytecode Translator
8.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
8.7 + *
8.8 + * This program is free software: you can redistribute it and/or modify
8.9 + * it under the terms of the GNU General Public License as published by
8.10 + * the Free Software Foundation, version 2 of the License.
8.11 + *
8.12 + * This program is distributed in the hope that it will be useful,
8.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
8.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.15 + * GNU General Public License for more details.
8.16 + *
8.17 + * You should have received a copy of the GNU General Public License
8.18 + * along with this program. Look for COPYING file in the top folder.
8.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
8.20 + */
8.21 +package org.apidesign.bck2brwsr.htmlpage;
8.22 +
8.23 +import java.util.Arrays;
8.24 +import java.util.Iterator;
8.25 +import java.util.List;
8.26 +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
8.27 +import org.apidesign.bck2brwsr.htmlpage.api.Page;
8.28 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
8.29 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
8.30 +import org.apidesign.bck2brwsr.vmtest.Http;
8.31 +import org.apidesign.bck2brwsr.vmtest.VMTest;
8.32 +import org.json.JSONException;
8.33 +import org.json.JSONObject;
8.34 +import org.json.JSONTokener;
8.35 +import org.testng.annotations.Test;
8.36 +import static org.testng.Assert.*;
8.37 +import org.testng.annotations.Factory;
8.38 +
8.39 +/** Need to verify that models produce reasonable JSON objects.
8.40 + *
8.41 + * @author Jaroslav Tulach <jtulach@netbeans.org>
8.42 + */
8.43 +@Page(xhtml = "Empty.html", className = "JSONik", properties = {
8.44 + @Property(name = "fetched", type = PersonImpl.class),
8.45 + @Property(name = "fetchedCount", type = int.class)
8.46 +})
8.47 +public class JSONTest {
8.48 + private JSONik js;
8.49 +
8.50 + @Test public void personToString() throws JSONException {
8.51 + Person p = new Person();
8.52 + p.setSex(Sex.MALE);
8.53 + p.setFirstName("Jarda");
8.54 + p.setLastName("Tulach");
8.55 +
8.56 + JSONTokener t = new JSONTokener(p.toString());
8.57 + JSONObject o;
8.58 + try {
8.59 + o = new JSONObject(t);
8.60 + } catch (JSONException ex) {
8.61 + throw new AssertionError("Can't parse " + p.toString(), ex);
8.62 + }
8.63 +
8.64 + Iterator it = o.sortedKeys();
8.65 + assertEquals(it.next(), "firstName");
8.66 + assertEquals(it.next(), "lastName");
8.67 + assertEquals(it.next(), "sex");
8.68 +
8.69 + assertEquals(o.getString("firstName"), "Jarda");
8.70 + assertEquals(o.getString("lastName"), "Tulach");
8.71 + assertEquals(o.getString("sex"), "MALE");
8.72 + }
8.73 +
8.74 + @Test public void personWithWildCharactersAndNulls() throws JSONException {
8.75 + Person p = new Person();
8.76 + p.setFirstName("'\"\n");
8.77 + p.setLastName("\t\r\u0002");
8.78 +
8.79 + JSONTokener t = new JSONTokener(p.toString());
8.80 + JSONObject o;
8.81 + try {
8.82 + o = new JSONObject(t);
8.83 + } catch (JSONException ex) {
8.84 + throw new AssertionError("Can't parse " + p.toString(), ex);
8.85 + }
8.86 +
8.87 + Iterator it = o.sortedKeys();
8.88 + assertEquals(it.next(), "firstName");
8.89 + assertEquals(it.next(), "lastName");
8.90 + assertEquals(it.next(), "sex");
8.91 +
8.92 + assertEquals(o.getString("firstName"), p.getFirstName());
8.93 + assertEquals(o.getString("lastName"), p.getLastName());
8.94 + assertEquals(o.get("sex"), JSONObject.NULL);
8.95 + }
8.96 +
8.97 + @Test public void personsInArray() throws JSONException {
8.98 + Person p1 = new Person();
8.99 + p1.setFirstName("One");
8.100 +
8.101 + Person p2 = new Person();
8.102 + p2.setFirstName("Two");
8.103 +
8.104 + People arr = new People();
8.105 + arr.getInfo().add(p1);
8.106 + arr.getInfo().add(p2);
8.107 + arr.getNicknames().add("Prvn\u00ed k\u016f\u0148");
8.108 + final String n2 = "Druh\u00fd hlem\u00fd\u017e\u010f, star\u0161\u00ed";
8.109 + arr.getNicknames().add(n2);
8.110 + arr.getAge().add(33);
8.111 + arr.getAge().add(73);
8.112 +
8.113 +
8.114 + final String json = arr.toString();
8.115 +
8.116 + JSONTokener t = new JSONTokener(json);
8.117 + JSONObject o;
8.118 + try {
8.119 + o = new JSONObject(t);
8.120 + } catch (JSONException ex) {
8.121 + throw new AssertionError("Can't parse " + json, ex);
8.122 + }
8.123 +
8.124 + assertEquals(o.getJSONArray("info").getJSONObject(0).getString("firstName"), "One");
8.125 + assertEquals(o.getJSONArray("nicknames").getString(1), n2);
8.126 + assertEquals(o.getJSONArray("age").getInt(1), 73);
8.127 + }
8.128 +
8.129 +
8.130 + @OnReceive(url="/{url}")
8.131 + static void fetch(Person p, JSONik model) {
8.132 + model.setFetched(p);
8.133 + }
8.134 +
8.135 + @OnReceive(url="/{url}")
8.136 + static void fetchArray(Person[] p, JSONik model) {
8.137 + model.setFetchedCount(p.length);
8.138 + model.setFetched(p[0]);
8.139 + }
8.140 +
8.141 + @OnReceive(url="/{url}")
8.142 + static void fetchPeople(People p, JSONik model) {
8.143 + model.setFetchedCount(p.getInfo().size());
8.144 + model.setFetched(p.getInfo().get(0));
8.145 + }
8.146 +
8.147 + @OnReceive(url="/{url}")
8.148 + static void fetchPeopleAge(People p, JSONik model) {
8.149 + int sum = 0;
8.150 + for (int a : p.getAge()) {
8.151 + sum += a;
8.152 + }
8.153 + model.setFetchedCount(sum);
8.154 + }
8.155 +
8.156 + @Http(@Http.Resource(
8.157 + content = "{'firstName': 'Sitar', 'sex': 'MALE'}",
8.158 + path="/person.json",
8.159 + mimeType = "application/json"
8.160 + ))
8.161 + @BrwsrTest public void loadAndParseJSON() throws InterruptedException {
8.162 + if (js == null) {
8.163 + js = new JSONik();
8.164 + js.applyBindings();
8.165 +
8.166 + js.fetch("person.json");
8.167 + }
8.168 +
8.169 + Person p = js.getFetched();
8.170 + if (p == null) {
8.171 + throw new InterruptedException();
8.172 + }
8.173 +
8.174 + assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
8.175 + // assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
8.176 + }
8.177 +
8.178 + @OnReceive(url="/{url}?callme={me}", jsonp = "me")
8.179 + static void fetchViaJSONP(Person p, JSONik model) {
8.180 + model.setFetched(p);
8.181 + }
8.182 +
8.183 + @Http(@Http.Resource(
8.184 + content = "$0({'firstName': 'Mitar', 'sex': 'MALE'})",
8.185 + path="/person.json",
8.186 + mimeType = "application/javascript",
8.187 + parameters = { "callme" }
8.188 + ))
8.189 + @BrwsrTest public void loadAndParseJSONP() throws InterruptedException {
8.190 + if (js == null) {
8.191 + js = new JSONik();
8.192 + js.applyBindings();
8.193 +
8.194 + js.fetchViaJSONP("person.json");
8.195 + }
8.196 +
8.197 + Person p = js.getFetched();
8.198 + if (p == null) {
8.199 + throw new InterruptedException();
8.200 + }
8.201 +
8.202 + assert "Mitar".equals(p.getFirstName()) : "Unexpected: " + p.getFirstName();
8.203 + // assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
8.204 + }
8.205 +
8.206 + @Http(@Http.Resource(
8.207 + content = "{'firstName': 'Sitar', 'sex': 'MALE'}",
8.208 + path="/person.json",
8.209 + mimeType = "application/json"
8.210 + ))
8.211 + @BrwsrTest public void loadAndParseJSONSentToArray() throws InterruptedException {
8.212 + if (js == null) {
8.213 + js = new JSONik();
8.214 + js.applyBindings();
8.215 +
8.216 + js.fetchArray("person.json");
8.217 + }
8.218 +
8.219 + Person p = js.getFetched();
8.220 + if (p == null) {
8.221 + throw new InterruptedException();
8.222 + }
8.223 +
8.224 + assert p != null : "We should get our person back: " + p;
8.225 + assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
8.226 +// assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
8.227 + }
8.228 +
8.229 + @Http(@Http.Resource(
8.230 + content = "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]",
8.231 + path="/person.json",
8.232 + mimeType = "application/json"
8.233 + ))
8.234 + @BrwsrTest public void loadAndParseJSONArraySingle() throws InterruptedException {
8.235 + if (js == null) {
8.236 + js = new JSONik();
8.237 + js.applyBindings();
8.238 +
8.239 + js.fetch("person.json");
8.240 + }
8.241 +
8.242 + Person p = js.getFetched();
8.243 + if (p == null) {
8.244 + throw new InterruptedException();
8.245 + }
8.246 +
8.247 + assert p != null : "We should get our person back: " + p;
8.248 + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
8.249 +// assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
8.250 + }
8.251 +
8.252 + @Http(@Http.Resource(
8.253 + content = "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
8.254 + path="/people.json",
8.255 + mimeType = "application/json"
8.256 + ))
8.257 + @BrwsrTest public void loadAndParseArrayInPeople() throws InterruptedException {
8.258 + if (js == null) {
8.259 + js = new JSONik();
8.260 + js.applyBindings();
8.261 +
8.262 + js.fetchPeople("people.json");
8.263 + }
8.264 +
8.265 + if (0 == js.getFetchedCount()) {
8.266 + throw new InterruptedException();
8.267 + }
8.268 +
8.269 + assert js.getFetchedCount() == 1 : "One person loaded: " + js.getFetchedCount();
8.270 +
8.271 + Person p = js.getFetched();
8.272 +
8.273 + assert p != null : "We should get our person back: " + p;
8.274 + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
8.275 +// assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
8.276 + }
8.277 +
8.278 + @Http(@Http.Resource(
8.279 + content = "{'age':[1, 2, 3]}",
8.280 + path="/people.json",
8.281 + mimeType = "application/json"
8.282 + ))
8.283 + @BrwsrTest public void loadAndParseArrayOfIntegers() throws InterruptedException {
8.284 + if (js == null) {
8.285 + js = new JSONik();
8.286 + js.applyBindings();
8.287 +
8.288 + js.fetchPeopleAge("people.json");
8.289 + }
8.290 +
8.291 + if (0 == js.getFetchedCount()) {
8.292 + throw new InterruptedException();
8.293 + }
8.294 +
8.295 + assert js.getFetchedCount() == 6 : "1 + 2 + 3 is " + js.getFetchedCount();
8.296 + }
8.297 +
8.298 + @Http(@Http.Resource(
8.299 + content = "[{'firstName': 'Gitar', 'sex': 'FEMALE'},"
8.300 + + "{'firstName': 'Peter', 'sex': 'MALE'}"
8.301 + + "]",
8.302 + path="/person.json",
8.303 + mimeType = "application/json"
8.304 + ))
8.305 + @BrwsrTest public void loadAndParseJSONArray() throws InterruptedException {
8.306 + if (js == null) {
8.307 + js = new JSONik();
8.308 + js.applyBindings();
8.309 + js.fetchArray("person.json");
8.310 + }
8.311 +
8.312 +
8.313 + Person p = js.getFetched();
8.314 + if (p == null) {
8.315 + throw new InterruptedException();
8.316 + }
8.317 +
8.318 + assert js.getFetchedCount() == 2 : "We got two values: " + js.getFetchedCount();
8.319 + assert p != null : "We should get our person back: " + p;
8.320 + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
8.321 +// assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
8.322 + }
8.323 +
8.324 + @Factory public static Object[] create() {
8.325 + return VMTest.create(JSONTest.class);
8.326 + }
8.327 +
8.328 +}
9.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Wed Apr 03 13:43:22 2013 +0200
9.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Mon Apr 08 19:33:08 2013 +0200
9.3 @@ -27,7 +27,9 @@
9.4 import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
9.5 import org.apidesign.bck2brwsr.vmtest.HtmlFragment;
9.6 import org.apidesign.bck2brwsr.vmtest.VMTest;
9.7 +import static org.testng.Assert.assertEquals;
9.8 import org.testng.annotations.Factory;
9.9 +import org.testng.annotations.Test;
9.10
9.11 /**
9.12 *
9.13 @@ -139,30 +141,70 @@
9.14 assert "changed".equals(txt) : "Expecting 'changed': " + txt;
9.15 }
9.16
9.17 + @ComputedProperty
9.18 + static Person firstPerson(List<Person> people) {
9.19 + return people.isEmpty() ? null : people.get(0);
9.20 + }
9.21 +
9.22 + @HtmlFragment(
9.23 + "<p id='ul' data-bind='with: firstPerson'>\n"
9.24 + + " <span data-bind='text: firstName, click: changeSex'></span>\n"
9.25 + + "</p>\n"
9.26 + )
9.27 + @BrwsrTest public void accessFirstPersonWithOnFunction() {
9.28 + trasfertToFemale();
9.29 + }
9.30 +
9.31 @HtmlFragment(
9.32 "<ul id='ul' data-bind='foreach: people'>\n"
9.33 + " <li data-bind='text: $data.firstName, click: changeSex'></li>\n"
9.34 + "</ul>\n"
9.35 )
9.36 @BrwsrTest public void onPersonFunction() {
9.37 + trasfertToFemale();
9.38 + }
9.39 +
9.40 + private void trasfertToFemale() {
9.41 KnockoutModel m = new KnockoutModel();
9.42 -
9.43 +
9.44 final Person first = new Person();
9.45 first.setFirstName("first");
9.46 first.setSex(Sex.MALE);
9.47 m.getPeople().add(first);
9.48 -
9.49 -
9.50 +
9.51 +
9.52 m.applyBindings();
9.53 -
9.54 +
9.55 int cnt = countChildren("ul");
9.56 assert cnt == 1 : "One child, but was " + cnt;
9.57 -
9.58 -
9.59 +
9.60 +
9.61 triggerChildClick("ul", 0);
9.62 -
9.63 +
9.64 assert first.getSex() == Sex.FEMALE : "Transverted to female: " + first.getSex();
9.65 }
9.66 +
9.67 + @Test public void cloneModel() {
9.68 + Person model = new Person();
9.69 +
9.70 + model.setFirstName("first");
9.71 + Person snd = model.clone();
9.72 + snd.setFirstName("clone");
9.73 + assertEquals("first", model.getFirstName(), "Value has not changed");
9.74 + assertEquals("clone", snd.getFirstName(), "Value has changed in clone");
9.75 + }
9.76 +
9.77 +
9.78 + @Test public void deepCopyOnClone() {
9.79 + People model = new People();
9.80 + model.getNicknames().add("Jarda");
9.81 + assertEquals(model.getNicknames().size(), 1, "One element");
9.82 + People snd = model.clone();
9.83 + snd.getNicknames().clear();
9.84 + assertEquals(snd.getNicknames().size(), 0, "Clone is empty");
9.85 + assertEquals(model.getNicknames().size(), 1, "Still one element");
9.86 + }
9.87 +
9.88
9.89 @OnFunction
9.90 static void call(KnockoutModel m, String data) {
10.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Wed Apr 03 13:43:22 2013 +0200
10.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Mon Apr 08 19:33:08 2013 +0200
10.3 @@ -24,6 +24,7 @@
10.4 import java.util.ListIterator;
10.5 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
10.6 import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
10.7 +import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange;
10.8 import org.apidesign.bck2brwsr.htmlpage.api.Page;
10.9 import org.apidesign.bck2brwsr.htmlpage.api.Property;
10.10 import static org.testng.Assert.*;
10.11 @@ -40,7 +41,8 @@
10.12 @Property(name = "unrelated", type = long.class),
10.13 @Property(name = "names", type = String.class, array = true),
10.14 @Property(name = "values", type = int.class, array = true),
10.15 - @Property(name = "people", type = PersonImpl.class, array = true)
10.16 + @Property(name = "people", type = PersonImpl.class, array = true),
10.17 + @Property(name = "changedProperty", type=String.class)
10.18 })
10.19 public class ModelTest {
10.20 private Modelik model;
10.21 @@ -138,6 +140,9 @@
10.22
10.23 model.setValue(33);
10.24
10.25 + // not interested in change of this property
10.26 + my.mutated.remove("changedProperty");
10.27 +
10.28 assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
10.29 assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
10.30 assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated);
10.31 @@ -145,7 +150,11 @@
10.32 my.mutated.clear();
10.33
10.34 model.setUnrelated(44);
10.35 - assertEquals(my.mutated.size(), 1, "One property changed");
10.36 +
10.37 +
10.38 + // not interested in change of this property
10.39 + my.mutated.remove("changedProperty");
10.40 + assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated);
10.41 assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated");
10.42 }
10.43
10.44 @@ -178,6 +187,34 @@
10.45 return value * value;
10.46 }
10.47
10.48 + @OnPropertyChange({ "powerValue", "unrelated" })
10.49 + static void aPropertyChanged(Modelik m, String name) {
10.50 + m.setChangedProperty(name);
10.51 + }
10.52 +
10.53 + @OnPropertyChange({ "values" })
10.54 + static void anArrayPropertyChanged(String name, Modelik m) {
10.55 + m.setChangedProperty(name);
10.56 + }
10.57 +
10.58 + @Test public void changeAnything() {
10.59 + model.setCount(44);
10.60 + assertNull(model.getChangedProperty(), "No observed value change");
10.61 + }
10.62 + @Test public void changeValue() {
10.63 + model.setValue(33);
10.64 + assertEquals(model.getChangedProperty(), "powerValue", "power property changed");
10.65 + }
10.66 + @Test public void changeUnrelated() {
10.67 + model.setUnrelated(333);
10.68 + assertEquals(model.getChangedProperty(), "unrelated", "unrelated changed");
10.69 + }
10.70 +
10.71 + @Test public void changeInArray() {
10.72 + model.getValues().add(10);
10.73 + assertEquals(model.getChangedProperty(), "values", "Something added into the array");
10.74 + }
10.75 +
10.76 @ComputedProperty
10.77 static String notAllowedRead() {
10.78 return "Not allowed callback: " + leakedModel.getUnrelated();
11.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Wed Apr 03 13:43:22 2013 +0200
11.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Mon Apr 08 19:33:08 2013 +0200
11.3 @@ -50,4 +50,11 @@
11.4 p.setSex(Sex.MALE);
11.5 }
11.6 }
11.7 +
11.8 + @Model(className = "People", properties = {
11.9 + @Property(array = true, name = "info", type = PersonImpl.class),
11.10 + @Property(array = true, name = "nicknames", type = String.class),
11.11 + @Property(array = true, name = "age", type = int.class),})
11.12 + public class PeopleImpl {
11.13 + }
11.14 }
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/javaquery/demo-twitter/bck2brwsr-assembly.xml Mon Apr 08 19:33:08 2013 +0200
12.3 @@ -0,0 +1,62 @@
12.4 +<?xml version="1.0"?>
12.5 +<!--
12.6 +
12.7 + Back 2 Browser Bytecode Translator
12.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
12.9 +
12.10 + This program is free software: you can redistribute it and/or modify
12.11 + it under the terms of the GNU General Public License as published by
12.12 + the Free Software Foundation, version 2 of the License.
12.13 +
12.14 + This program is distributed in the hope that it will be useful,
12.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
12.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12.17 + GNU General Public License for more details.
12.18 +
12.19 + You should have received a copy of the GNU General Public License
12.20 + along with this program. Look for COPYING file in the top folder.
12.21 + If not, see http://opensource.org/licenses/GPL-2.0.
12.22 +
12.23 +-->
12.24 +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
12.25 + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
12.26 +
12.27 + <id>bck2brwsr</id>
12.28 + <formats>
12.29 + <format>zip</format>
12.30 + </formats>
12.31 + <baseDirectory>public_html</baseDirectory>
12.32 + <dependencySets>
12.33 + <dependencySet>
12.34 + <useProjectArtifact>false</useProjectArtifact>
12.35 + <scope>runtime</scope>
12.36 + <outputDirectory>lib</outputDirectory>
12.37 + <includes>
12.38 + <include>*:jar</include>
12.39 + <include>*:rt</include>
12.40 + </includes>
12.41 + </dependencySet>
12.42 + </dependencySets>
12.43 + <fileSets>
12.44 + <fileSet>
12.45 + <directory>${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/twitter/</directory>
12.46 + <includes>
12.47 + <include>**/*</include>
12.48 + </includes>
12.49 + <excludes>
12.50 + <exclude>**/*.class</exclude>
12.51 + </excludes>
12.52 + <outputDirectory>/</outputDirectory>
12.53 + </fileSet>
12.54 + </fileSets>
12.55 + <files>
12.56 + <file>
12.57 + <source>${project.build.directory}/${project.build.finalName}.jar</source>
12.58 + <outputDirectory>/</outputDirectory>
12.59 + </file>
12.60 + <file>
12.61 + <source>${project.build.directory}/bck2brwsr.js</source>
12.62 + <outputDirectory>/</outputDirectory>
12.63 + </file>
12.64 + </files>
12.65 +</assembly>
12.66 \ No newline at end of file
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/javaquery/demo-twitter/nb-configuration.xml Mon Apr 08 19:33:08 2013 +0200
13.3 @@ -0,0 +1,37 @@
13.4 +<?xml version="1.0" encoding="UTF-8"?>
13.5 +<!--
13.6 +
13.7 + Back 2 Browser Bytecode Translator
13.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
13.9 +
13.10 + This program is free software: you can redistribute it and/or modify
13.11 + it under the terms of the GNU General Public License as published by
13.12 + the Free Software Foundation, version 2 of the License.
13.13 +
13.14 + This program is distributed in the hope that it will be useful,
13.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
13.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13.17 + GNU General Public License for more details.
13.18 +
13.19 + You should have received a copy of the GNU General Public License
13.20 + along with this program. Look for COPYING file in the top folder.
13.21 + If not, see http://opensource.org/licenses/GPL-2.0.
13.22 +
13.23 +-->
13.24 +<project-shared-configuration>
13.25 + <!--
13.26 +This file contains additional configuration written by modules in the NetBeans IDE.
13.27 +The configuration is intended to be shared among all the users of project and
13.28 +therefore it is assumed to be part of version control checkout.
13.29 +Without this configuration present, some functionality in the IDE may be limited or fail altogether.
13.30 +-->
13.31 + <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
13.32 + <!--
13.33 +Properties that influence various parts of the IDE, especially code formatting and the like.
13.34 +You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
13.35 +That way multiple projects can share the same settings (useful for formatting rules for example).
13.36 +Any value defined here will override the pom.xml file value but is only applicable to the current project.
13.37 +-->
13.38 + <netbeans.compile.on.save>none</netbeans.compile.on.save>
13.39 + </properties>
13.40 +</project-shared-configuration>
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/javaquery/demo-twitter/nbactions.xml Mon Apr 08 19:33:08 2013 +0200
14.3 @@ -0,0 +1,29 @@
14.4 +<?xml version="1.0" encoding="UTF-8"?>
14.5 +<!--
14.6 +
14.7 + Back 2 Browser Bytecode Translator
14.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
14.9 +
14.10 + This program is free software: you can redistribute it and/or modify
14.11 + it under the terms of the GNU General Public License as published by
14.12 + the Free Software Foundation, version 2 of the License.
14.13 +
14.14 + This program is distributed in the hope that it will be useful,
14.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
14.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14.17 + GNU General Public License for more details.
14.18 +
14.19 + You should have received a copy of the GNU General Public License
14.20 + along with this program. Look for COPYING file in the top folder.
14.21 + If not, see http://opensource.org/licenses/GPL-2.0.
14.22 +
14.23 +-->
14.24 +<actions>
14.25 + <action>
14.26 + <actionName>run</actionName>
14.27 + <goals>
14.28 + <goal>process-classes</goal>
14.29 + <goal>org.apidesign.bck2brwsr:mojo:0.6-SNAPSHOT:brwsr</goal>
14.30 + </goals>
14.31 + </action>
14.32 +</actions>
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/javaquery/demo-twitter/pom.xml Mon Apr 08 19:33:08 2013 +0200
15.3 @@ -0,0 +1,137 @@
15.4 +<?xml version="1.0" encoding="UTF-8"?>
15.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
15.6 + <modelVersion>4.0.0</modelVersion>
15.7 + <parent>
15.8 + <artifactId>javaquery</artifactId>
15.9 + <groupId>org.apidesign.bck2brwsr</groupId>
15.10 + <version>0.6-SNAPSHOT</version>
15.11 + </parent>
15.12 +
15.13 + <groupId>org.apidesign.bck2brwsr</groupId>
15.14 + <artifactId>demo-twitter</artifactId>
15.15 + <version>0.6-SNAPSHOT</version>
15.16 + <packaging>jar</packaging>
15.17 +
15.18 + <name>Bck2Brwsr's Twttr</name>
15.19 + <description>
15.20 + Rewrite of knockoutjs example to use model written in Java and
15.21 + execute using Bck2Brwsr virtual machine.
15.22 + </description>
15.23 +
15.24 + <repositories>
15.25 + <repository>
15.26 + <id>java.net</id>
15.27 + <name>Java.net</name>
15.28 + <url>https://maven.java.net/content/repositories/releases/</url>
15.29 + <snapshots>
15.30 + </snapshots>
15.31 + </repository>
15.32 + <repository>
15.33 + <id>netbeans</id>
15.34 + <name>NetBeans</name>
15.35 + <url>http://bits.netbeans.org/maven2/</url>
15.36 + </repository>
15.37 + </repositories>
15.38 + <pluginRepositories>
15.39 + <pluginRepository>
15.40 + <id>java.net</id>
15.41 + <name>Java.net</name>
15.42 + <url>https://maven.java.net/content/repositories/releases/</url>
15.43 + <snapshots>
15.44 + </snapshots>
15.45 + </pluginRepository>
15.46 + </pluginRepositories>
15.47 +
15.48 + <properties>
15.49 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15.50 + <bck2brwsr.obfuscationlevel>FULL</bck2brwsr.obfuscationlevel>
15.51 + </properties>
15.52 + <build>
15.53 + <plugins>
15.54 + <plugin>
15.55 + <groupId>org.apidesign.bck2brwsr</groupId>
15.56 + <artifactId>mojo</artifactId>
15.57 + <version>0.6-SNAPSHOT</version>
15.58 + <executions>
15.59 + <execution>
15.60 + <goals>
15.61 + <goal>brwsr</goal>
15.62 + <goal>j2js</goal>
15.63 + </goals>
15.64 + </execution>
15.65 + </executions>
15.66 + <configuration>
15.67 + <startpage>org/apidesign/bck2brwsr/demo/twitter/index.html</startpage>
15.68 + <javascript>${project.build.directory}/bck2brwsr.js</javascript>
15.69 + <obfuscation>${bck2brwsr.obfuscationlevel}</obfuscation>
15.70 + </configuration>
15.71 + </plugin>
15.72 + <plugin>
15.73 + <groupId>org.apache.maven.plugins</groupId>
15.74 + <artifactId>maven-compiler-plugin</artifactId>
15.75 + <version>2.3.2</version>
15.76 + <configuration>
15.77 + <source>1.7</source>
15.78 + <target>1.7</target>
15.79 + </configuration>
15.80 + </plugin>
15.81 + <plugin>
15.82 + <groupId>org.apache.maven.plugins</groupId>
15.83 + <artifactId>maven-jar-plugin</artifactId>
15.84 + <version>2.4</version>
15.85 + <configuration>
15.86 + <archive>
15.87 + <manifest>
15.88 + <addClasspath>true</addClasspath>
15.89 + <classpathPrefix>lib/</classpathPrefix>
15.90 + </manifest>
15.91 + </archive>
15.92 + </configuration>
15.93 + </plugin>
15.94 + <plugin>
15.95 + <artifactId>maven-assembly-plugin</artifactId>
15.96 + <version>2.4</version>
15.97 + <executions>
15.98 + <execution>
15.99 + <id>distro-assembly</id>
15.100 + <phase>package</phase>
15.101 + <goals>
15.102 + <goal>single</goal>
15.103 + </goals>
15.104 + <configuration>
15.105 + <descriptors>
15.106 + <descriptor>bck2brwsr-assembly.xml</descriptor>
15.107 + </descriptors>
15.108 + </configuration>
15.109 + </execution>
15.110 + </executions>
15.111 + </plugin>
15.112 + </plugins>
15.113 + </build>
15.114 +
15.115 + <dependencies>
15.116 + <dependency>
15.117 + <groupId>org.apidesign.bck2brwsr</groupId>
15.118 + <artifactId>emul</artifactId>
15.119 + <version>0.6-SNAPSHOT</version>
15.120 + <classifier>rt</classifier>
15.121 + </dependency>
15.122 + <dependency>
15.123 + <groupId>org.apidesign.bck2brwsr</groupId>
15.124 + <artifactId>javaquery.api</artifactId>
15.125 + <version>0.6-SNAPSHOT</version>
15.126 + </dependency>
15.127 + <dependency>
15.128 + <groupId>org.testng</groupId>
15.129 + <artifactId>testng</artifactId>
15.130 + <version>6.5.2</version>
15.131 + <scope>test</scope>
15.132 + </dependency>
15.133 + <dependency>
15.134 + <groupId>org.apidesign.bck2brwsr</groupId>
15.135 + <artifactId>vmtest</artifactId>
15.136 + <version>0.6-SNAPSHOT</version>
15.137 + <scope>test</scope>
15.138 + </dependency>
15.139 + </dependencies>
15.140 +</project>
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java Mon Apr 08 19:33:08 2013 +0200
16.3 @@ -0,0 +1,194 @@
16.4 +/**
16.5 + * Back 2 Browser Bytecode Translator
16.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
16.7 + *
16.8 + * This program is free software: you can redistribute it and/or modify
16.9 + * it under the terms of the GNU General Public License as published by
16.10 + * the Free Software Foundation, version 2 of the License.
16.11 + *
16.12 + * This program is distributed in the hope that it will be useful,
16.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
16.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16.15 + * GNU General Public License for more details.
16.16 + *
16.17 + * You should have received a copy of the GNU General Public License
16.18 + * along with this program. Look for COPYING file in the top folder.
16.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
16.20 + */
16.21 +package org.apidesign.bck2brwsr.demo.twitter;
16.22 +
16.23 +import java.util.Arrays;
16.24 +import java.util.List;
16.25 +import org.apidesign.bck2brwsr.htmlpage.api.*;
16.26 +import org.apidesign.bck2brwsr.htmlpage.api.Page;
16.27 +import org.apidesign.bck2brwsr.htmlpage.api.Property;
16.28 +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
16.29 +
16.30 +/** Controller class for access to Twitter.
16.31 + *
16.32 + * @author Jaroslav Tulach
16.33 + */
16.34 +@Page(xhtml="index.html", className="TwitterModel", properties={
16.35 + @Property(name="savedLists", type=TwitterClient.Twttrs.class, array = true),
16.36 + @Property(name="activeTweetersName", type=String.class),
16.37 + @Property(name="activeTweeters", type=String.class, array = true),
16.38 + @Property(name="userNameToAdd", type=String.class),
16.39 + @Property(name="currentTweets", type=TwitterClient.Twt.class, array = true)
16.40 +})
16.41 +public class TwitterClient {
16.42 + @Model(className = "Tweeters", properties = {
16.43 + @Property(name="name", type = String.class),
16.44 + @Property(name="userNames", type = String.class, array = true)
16.45 + })
16.46 + static class Twttrs {
16.47 + }
16.48 + @Model(className = "Tweet", properties = {
16.49 + @Property(name = "from_user", type = String.class),
16.50 + @Property(name = "from_user_id", type = int.class),
16.51 + @Property(name = "profile_image_url", type = String.class),
16.52 + @Property(name = "text", type = String.class),
16.53 + @Property(name = "created_at", type = String.class),
16.54 + })
16.55 + static final class Twt {
16.56 + @ComputedProperty static String html(String text) {
16.57 + StringBuilder sb = new StringBuilder(320);
16.58 + for (int pos = 0;;) {
16.59 + int http = text.indexOf("http", pos);
16.60 + if (http == -1) {
16.61 + sb.append(text.substring(pos));
16.62 + return sb.toString();
16.63 + }
16.64 + int spc = text.indexOf(' ', http);
16.65 + if (spc == -1) {
16.66 + spc = text.length();
16.67 + }
16.68 + sb.append(text.substring(pos, http));
16.69 + String url = text.substring(http, spc);
16.70 + sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
16.71 + pos = spc;
16.72 + }
16.73 + }
16.74 +
16.75 + @ComputedProperty static String userUrl(String from_user) {
16.76 + return "http://twitter.com/" + from_user;
16.77 + }
16.78 + }
16.79 + @Model(className = "TwitterQuery", properties = {
16.80 + @Property(array = true, name = "results", type = Twt.class)
16.81 + })
16.82 + public static final class TwttrQr {
16.83 + }
16.84 +
16.85 + @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
16.86 + static void queryTweets(TwitterModel page, TwitterQuery q) {
16.87 + page.getCurrentTweets().clear();
16.88 + page.getCurrentTweets().addAll(q.getResults());
16.89 + }
16.90 +
16.91 + @OnPropertyChange("activeTweetersName")
16.92 + static void changeTweetersList(TwitterModel model) {
16.93 + Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());
16.94 + model.getActiveTweeters().clear();
16.95 + model.getActiveTweeters().addAll(people.getUserNames());
16.96 + }
16.97 +
16.98 + @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
16.99 + static void refreshTweets(TwitterModel model) {
16.100 + StringBuilder sb = new StringBuilder();
16.101 + sb.append("rpp=25&q=");
16.102 + String sep = "";
16.103 + for (String p : model.getActiveTweeters()) {
16.104 + sb.append(sep);
16.105 + sb.append("from:");
16.106 + sb.append(p);
16.107 + sep = " OR ";
16.108 + }
16.109 + model.queryTweets("http://search.twitter.com", sb.toString());
16.110 + }
16.111 +
16.112 + static {
16.113 + final TwitterModel model = new TwitterModel();
16.114 + final List<Tweeters> svdLst = model.getSavedLists();
16.115 + svdLst.add(newTweeters("API Design", "JaroslavTulach"));
16.116 + svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
16.117 + svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
16.118 + svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
16.119 + svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
16.120 +
16.121 + model.setActiveTweetersName("NetBeans");
16.122 +
16.123 + model.applyBindings();
16.124 + }
16.125 +
16.126 + @ComputedProperty
16.127 + static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
16.128 + Tweeters tw = findByName(savedLists, activeTweetersName);
16.129 + if (activeTweeters == null) {
16.130 + return false;
16.131 + }
16.132 + return !tw.getUserNames().equals(activeTweeters);
16.133 + }
16.134 +
16.135 + @ComputedProperty
16.136 + static int activeTweetersCount(List<String> activeTweeters) {
16.137 + return activeTweeters.size();
16.138 + }
16.139 +
16.140 + @ComputedProperty
16.141 + static boolean userNameToAddIsValid(
16.142 + String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
16.143 + ) {
16.144 + return userNameToAdd != null &&
16.145 + userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
16.146 + !activeTweeters.contains(userNameToAdd);
16.147 + }
16.148 +
16.149 + @OnFunction
16.150 + static void deleteList(TwitterModel model) {
16.151 + final List<Tweeters> sl = model.getSavedLists();
16.152 + sl.remove(findByName(sl, model.getActiveTweetersName()));
16.153 + if (sl.isEmpty()) {
16.154 + final Tweeters t = new Tweeters();
16.155 + t.setName("New");
16.156 + sl.add(t);
16.157 + }
16.158 + model.setActiveTweetersName(sl.get(0).getName());
16.159 + }
16.160 +
16.161 + @OnFunction
16.162 + static void saveChanges(TwitterModel model) {
16.163 + Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
16.164 + int indx = model.getSavedLists().indexOf(t);
16.165 + if (indx != -1) {
16.166 + t.setName(model.getActiveTweetersName());
16.167 + t.getUserNames().clear();
16.168 + t.getUserNames().addAll(model.getActiveTweeters());
16.169 + }
16.170 + }
16.171 +
16.172 + @OnFunction
16.173 + static void addUser(TwitterModel model) {
16.174 + String n = model.getUserNameToAdd();
16.175 + model.getActiveTweeters().add(n);
16.176 + }
16.177 + @OnFunction
16.178 + static void removeUser(String data, TwitterModel model) {
16.179 + model.getActiveTweeters().remove(data);
16.180 + }
16.181 +
16.182 + private static Tweeters findByName(List<Tweeters> list, String name) {
16.183 + for (Tweeters l : list) {
16.184 + if (l.getName() != null && l.getName().equals(name)) {
16.185 + return l;
16.186 + }
16.187 + }
16.188 + return list.isEmpty() ? new Tweeters() : list.get(0);
16.189 + }
16.190 +
16.191 + private static Tweeters newTweeters(String listName, String... userNames) {
16.192 + Tweeters t = new Tweeters();
16.193 + t.setName(listName);
16.194 + t.getUserNames().addAll(Arrays.asList(userNames));
16.195 + return t;
16.196 + }
16.197 +}
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
17.2 +++ b/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/index.html Mon Apr 08 19:33:08 2013 +0200
17.3 @@ -0,0 +1,99 @@
17.4 +<?xml version="1.0" encoding="UTF-8"?>
17.5 +<!--
17.6 +
17.7 + Back 2 Browser Bytecode Translator
17.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
17.9 +
17.10 + This program is free software: you can redistribute it and/or modify
17.11 + it under the terms of the GNU General Public License as published by
17.12 + the Free Software Foundation, version 2 of the License.
17.13 +
17.14 + This program is distributed in the hope that it will be useful,
17.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
17.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17.17 + GNU General Public License for more details.
17.18 +
17.19 + You should have received a copy of the GNU General Public License
17.20 + along with this program. Look for COPYING file in the top folder.
17.21 + If not, see http://opensource.org/licenses/GPL-2.0.
17.22 +
17.23 +-->
17.24 +
17.25 +<!--
17.26 + Copied from knockout.js Twitter example:
17.27 + http://knockoutjs.com/examples/twitter.html
17.28 +-->
17.29 +
17.30 +<!DOCTYPE html>
17.31 +<html xmlns="http://www.w3.org/1999/xhtml">
17.32 + <head>
17.33 + <title>Bck2Brwsr's Twitter</title>
17.34 + </head>
17.35 + <body>
17.36 + <link href='twitterExample.css' rel='Stylesheet' ></link>
17.37 +
17.38 + <style type='text/css'>
17.39 + .liveExample select { height: 1.7em; }
17.40 + .liveExample button { height: 2em; }
17.41 + </style>
17.42 +
17.43 +
17.44 + <h2>Bck2Brwsr's Twitter</h2>
17.45 +
17.46 + <p>
17.47 + This code based on original <a href="http://knockoutjs.com/examples/twitter.html">knockout.js Twitter example</a> and
17.48 + uses almost unmodified HTML code. It just changes the model. It
17.49 + is written in Java language and it is executed using <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a>
17.50 + virtual machine. The Java source code has about 190 lines and is available
17.51 + <a href="http://source.apidesign.org/hg/bck2brwsr/file/7fc6b7e9c982/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java">here</a>
17.52 + - in fact it may even be more dense than the original JavaScript model.
17.53 + </p>
17.54 +
17.55 + <div class='liveExample'>
17.56 + <div class='configuration'>
17.57 + <div class='listChooser'>
17.58 + <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
17.59 + <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button>
17.60 + <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
17.61 + </div>
17.62 +
17.63 + <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
17.64 + <div class='currentUsers' >
17.65 + <ul data-bind='foreach: activeTweeters'>
17.66 + <li>
17.67 + <button data-bind='click: $root.removeUser'>Remove</button>
17.68 + <div data-bind='text: $data'> </div>
17.69 + </li>
17.70 + </ul>
17.71 + </div>
17.72 +
17.73 + <form data-bind='submit: addUser'>
17.74 + <label>Add user:</label>
17.75 + <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
17.76 + <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
17.77 + </form>
17.78 + </div>
17.79 + <div class='tweets'>
17.80 + <div class='loadingIndicator'>Loading...</div>
17.81 + <table data-bind='foreach: currentTweets' width='100%'>
17.82 + <tr>
17.83 + <td><img data-bind='attr: { src: profile_image_url }' /></td>
17.84 + <td>
17.85 + <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
17.86 + <span data-bind='html: html'> </span>
17.87 + <div class='tweetInfo' data-bind='text: created_at'> </div>
17.88 + </td>
17.89 + </tr>
17.90 + </table>
17.91 + </div>
17.92 + </div>
17.93 +
17.94 + <script src="bck2brwsr.js"></script>
17.95 + <script type="text/javascript">
17.96 + var vm = bck2brwsr('demo-twitter-0.6-SNAPSHOT.jar');
17.97 + vm.loadClass('org.apidesign.bck2brwsr.demo.twitter.TwitterClient');
17.98 + </script>
17.99 +
17.100 +
17.101 + </body>
17.102 +</html>
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
18.2 +++ b/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/twitterExample.css Mon Apr 08 19:33:08 2013 +0200
18.3 @@ -0,0 +1,50 @@
18.4 +/**
18.5 + * Back 2 Browser Bytecode Translator
18.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
18.7 + *
18.8 + * This program is free software: you can redistribute it and/or modify
18.9 + * it under the terms of the GNU General Public License as published by
18.10 + * the Free Software Foundation, version 2 of the License.
18.11 + *
18.12 + * This program is distributed in the hope that it will be useful,
18.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
18.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18.15 + * GNU General Public License for more details.
18.16 + *
18.17 + * You should have received a copy of the GNU General Public License
18.18 + * along with this program. Look for COPYING file in the top folder.
18.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
18.20 + */
18.21 +
18.22 +/*
18.23 + Copied from knockout.js Twitter example:
18.24 + http://knockoutjs.com/examples/twitter.html
18.25 +*/
18.26 +
18.27 +.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
18.28 +.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
18.29 +.tweets { width: 55%; border: 2px solid gray; height: 40em; overflow: scroll; overflow-x: hidden; background-color: Black; color: White; padding: 0.5em; position: relative; }
18.30 +.tweets table { border-width: 0;}
18.31 +.tweets tr { vertical-align: top; }
18.32 +.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
18.33 +.tweets img { width: 4em; }
18.34 +.tweetInfo { color: Gray; font-size: 0.9em; }
18.35 +.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
18.36 +input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
18.37 +
18.38 +.listChooser select, .listChooser button { vertical-align:top; }
18.39 +.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
18.40 +.listChooser button { width: 19%; height:1.68em; float:right; }
18.41 +
18.42 +.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
18.43 +.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
18.44 +.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
18.45 +.currentUsers li { height: 2.4em; font-size: 1.2em; background-color: #A7D0E3; border: 1px solid gray; margin-bottom: 0.3em; -webkit-border-radius: 5px; -moz-border-radius: 5px; -webkit-box-shadow: 0 0.2em 0.5em gray; -moz-box-shadow: 0 0.2em 0.5em gray; }
18.46 +.currentUsers li div { padding: 0.6em; }
18.47 +.currentUsers li:hover { background-color: #EEC; }
18.48 +
18.49 +.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
18.50 +.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
18.51 +.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
18.52 +
18.53 +.loadingIndicator { position: absolute; top: 0.1em; left: 0.1em; font: 0.8em Arial; background-color: #229; color: White; padding: 0.2em 0.5em 0.2em 0.5em; display: none; }
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
19.2 +++ b/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClientTest.java Mon Apr 08 19:33:08 2013 +0200
19.3 @@ -0,0 +1,67 @@
19.4 +/**
19.5 + * Back 2 Browser Bytecode Translator
19.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
19.7 + *
19.8 + * This program is free software: you can redistribute it and/or modify
19.9 + * it under the terms of the GNU General Public License as published by
19.10 + * the Free Software Foundation, version 2 of the License.
19.11 + *
19.12 + * This program is distributed in the hope that it will be useful,
19.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
19.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19.15 + * GNU General Public License for more details.
19.16 + *
19.17 + * You should have received a copy of the GNU General Public License
19.18 + * along with this program. Look for COPYING file in the top folder.
19.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
19.20 + */
19.21 +package org.apidesign.bck2brwsr.demo.twitter;
19.22 +
19.23 +import java.util.List;
19.24 +import static org.testng.Assert.*;
19.25 +import org.testng.annotations.BeforeMethod;
19.26 +import org.testng.annotations.Test;
19.27 +
19.28 +/** We can unit test the TwitterModel smoothly.
19.29 + *
19.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
19.31 + */
19.32 +public class TwitterClientTest {
19.33 + private TwitterModel model;
19.34 +
19.35 +
19.36 + @BeforeMethod
19.37 + public void initModel() {
19.38 + model = new TwitterModel().applyBindings();
19.39 + }
19.40 +
19.41 + @Test public void testIsValidToAdd() {
19.42 + model.setUserNameToAdd("Joe");
19.43 + Tweeters t = new Tweeters();
19.44 + t.setName("test");
19.45 + model.getSavedLists().add(t);
19.46 + model.setActiveTweetersName("test");
19.47 +
19.48 + assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
19.49 + TwitterClient.addUser(model);
19.50 + assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
19.51 + assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
19.52 +
19.53 + List<String> mod = model.getActiveTweeters();
19.54 + assertTrue(model.isHasUnsavedChanges(), "We have modifications");
19.55 + assertEquals(mod.size(), 1, "One element in the list");
19.56 + assertEquals(mod.get(0), "Joe", "Its name is Joe");
19.57 +
19.58 + assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
19.59 +
19.60 + TwitterClient.saveChanges(model);
19.61 + assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
19.62 +
19.63 + assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
19.64 + }
19.65 +
19.66 + @Test public void httpAtTheEnd() {
19.67 + String res = TwitterClient.Twt.html("Ahoj http://kuk");
19.68 + assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
19.69 + }
19.70 +}
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
20.2 +++ b/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterProtocolTest.java Mon Apr 08 19:33:08 2013 +0200
20.3 @@ -0,0 +1,94 @@
20.4 +/**
20.5 + * Back 2 Browser Bytecode Translator
20.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
20.7 + *
20.8 + * This program is free software: you can redistribute it and/or modify
20.9 + * it under the terms of the GNU General Public License as published by
20.10 + * the Free Software Foundation, version 2 of the License.
20.11 + *
20.12 + * This program is distributed in the hope that it will be useful,
20.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
20.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20.15 + * GNU General Public License for more details.
20.16 + *
20.17 + * You should have received a copy of the GNU General Public License
20.18 + * along with this program. Look for COPYING file in the top folder.
20.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
20.20 + */
20.21 +package org.apidesign.bck2brwsr.demo.twitter;
20.22 +
20.23 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
20.24 +import org.apidesign.bck2brwsr.vmtest.Http;
20.25 +import org.apidesign.bck2brwsr.vmtest.VMTest;
20.26 +import org.testng.annotations.Factory;
20.27 +
20.28 +/**
20.29 + *
20.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
20.31 + */
20.32 +public class TwitterProtocolTest {
20.33 + private TwitterModel page;
20.34 + @Http(@Http.Resource(
20.35 + path = "/search.json",
20.36 + mimeType = "application/json",
20.37 + parameters = {"callback"},
20.38 + content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\""
20.39 + + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":"
20.40 + + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\","
20.41 + + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\","
20.42 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
20.43 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344,"
20.44 + + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
20.45 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.46 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.47 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
20.48 + + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + "
20.49 + + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747,"
20.50 + + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872,"
20.51 + + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\","
20.52 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
20.53 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913,"
20.54 + + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
20.55 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.56 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.57 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
20.58 + + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"},"
20.59 + + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\","
20.60 + + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\","
20.61 + + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\","
20.62 + + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":"
20.63 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.64 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.65 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
20.66 + + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"},"
20.67 + + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,"
20.68 + + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,"
20.69 + + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":"
20.70 + + "{\"result_type\":\"recent\"},\"profile_image_url\":"
20.71 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.72 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
20.73 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
20.74 + + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100,"
20.75 + + "\"since_id\":0,\"since_id_str\":\"0\"})"
20.76 + ))
20.77 + @BrwsrTest public void readFromTwttr() throws InterruptedException {
20.78 + if (page == null) {
20.79 + page = new TwitterModel();
20.80 + page.applyBindings();
20.81 + page.queryTweets("", "q=xyz");
20.82 + }
20.83 +
20.84 + if (page.getCurrentTweets().isEmpty()) {
20.85 + throw new InterruptedException();
20.86 + }
20.87 +
20.88 + assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets();
20.89 +
20.90 + String firstDate = page.getCurrentTweets().get(0).getCreated_at();
20.91 + assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate;
20.92 + }
20.93 +
20.94 + @Factory public static Object[] create() {
20.95 + return VMTest.create(TwitterProtocolTest.class);
20.96 + }
20.97 +}
21.1 --- a/javaquery/pom.xml Wed Apr 03 13:43:22 2013 +0200
21.2 +++ b/javaquery/pom.xml Mon Apr 08 19:33:08 2013 +0200
21.3 @@ -15,5 +15,6 @@
21.4 <module>api</module>
21.5 <module>demo-calculator</module>
21.6 <module>demo-calculator-dynamic</module>
21.7 - </modules>
21.8 -</project>
21.9 + <module>demo-twitter</module>
21.10 + </modules>
21.11 +</project>
21.12 \ No newline at end of file
22.1 --- a/rt/emul/mini/src/main/java/java/lang/Class.java Wed Apr 03 13:43:22 2013 +0200
22.2 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Mon Apr 08 19:33:08 2013 +0200
22.3 @@ -1252,6 +1252,7 @@
22.4 }
22.5
22.6 @JavaScriptBody(args = { "sig" }, body =
22.7 + "if (!sig) sig = '[Ljava/lang/Object;';\n" +
22.8 "var c = Array[sig];\n" +
22.9 "if (c) return c;\n" +
22.10 "c = vm.java_lang_Class(true);\n" +
23.1 --- a/rt/emul/mini/src/main/java/java/lang/String.java Wed Apr 03 13:43:22 2013 +0200
23.2 +++ b/rt/emul/mini/src/main/java/java/lang/String.java Mon Apr 08 19:33:08 2013 +0200
23.3 @@ -2220,9 +2220,19 @@
23.4 * <code>replacement</code> is <code>null</code>.
23.5 * @since 1.5
23.6 */
23.7 - public String replace(CharSequence target, CharSequence replacement) {
23.8 - throw new UnsupportedOperationException("This one should be supported, but without dep on rest of regexp");
23.9 - }
23.10 + @JavaScriptBody(args = { "target", "replacement" }, body =
23.11 + "var s = this.toString();\n"
23.12 + + "target = target.toString();\n"
23.13 + + "replacement = replacement.toString();\n"
23.14 + + "for (;;) {\n"
23.15 + + " var ret = s.replace(target, replacement);\n"
23.16 + + " if (ret === s) {\n"
23.17 + + " return ret;\n"
23.18 + + " }\n"
23.19 + + " s = ret;\n"
23.20 + + "}"
23.21 + )
23.22 + public native String replace(CharSequence target, CharSequence replacement);
23.23
23.24 /**
23.25 * Splits this string around matches of the given
24.1 --- a/rt/emul/mini/src/main/java/java/lang/reflect/Method.java Wed Apr 03 13:43:22 2013 +0200
24.2 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Method.java Mon Apr 08 19:33:08 2013 +0200
24.3 @@ -501,8 +501,8 @@
24.4 throws IllegalAccessException, IllegalArgumentException,
24.5 InvocationTargetException
24.6 {
24.7 - final boolean isStatic = (getModifiers() & Modifier.STATIC) == 0;
24.8 - if (isStatic && obj == null) {
24.9 + final boolean nonStatic = (getModifiers() & Modifier.STATIC) == 0;
24.10 + if (nonStatic && obj == null) {
24.11 throw new NullPointerException();
24.12 }
24.13 Class[] types = getParameterTypes();
24.14 @@ -517,7 +517,7 @@
24.15 }
24.16 }
24.17 }
24.18 - Object res = invoke0(isStatic, this, obj, args);
24.19 + Object res = invokeTry(nonStatic, this, obj, args);
24.20 if (getReturnType().isPrimitive()) {
24.21 res = fromPrimitive(getReturnType(), res);
24.22 }
24.23 @@ -536,6 +536,15 @@
24.24 + "return method._data().apply(self, p);\n"
24.25 )
24.26 private static native Object invoke0(boolean isStatic, Method m, Object self, Object[] args);
24.27 +
24.28 + private static Object invokeTry(boolean isStatic, Method m, Object self, Object[] args)
24.29 + throws InvocationTargetException {
24.30 + try {
24.31 + return invoke0(isStatic, m, self, args);
24.32 + } catch (Throwable ex) {
24.33 + throw new InvocationTargetException(ex, ex.getMessage());
24.34 + }
24.35 + }
24.36
24.37 static Object fromPrimitive(Class<?> type, Object o) {
24.38 if (type == Integer.TYPE) {
25.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Wed Apr 03 13:43:22 2013 +0200
25.2 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Mon Apr 08 19:33:08 2013 +0200
25.3 @@ -98,9 +98,9 @@
25.4 }
25.5 HttpServer s = initServer(".", true);
25.6 int last = startpage.lastIndexOf('/');
25.7 + String prefix = startpage.substring(0, last);
25.8 String simpleName = startpage.substring(last);
25.9 - s.getServerConfiguration().addHttpHandler(new Page(resources, startpage), simpleName);
25.10 - s.getServerConfiguration().addHttpHandler(new Page(resources, null), "/");
25.11 + s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/");
25.12 try {
25.13 launchServerAndBrwsr(s, simpleName);
25.14 } catch (URISyntaxException | InterruptedException ex) {
25.15 @@ -177,7 +177,16 @@
25.16 if (r.httpPath.equals(request.getRequestURI())) {
25.17 LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI());
25.18 response.setContentType(r.httpType);
25.19 - copyStream(r.httpContent, response.getOutputStream(), null);
25.20 + r.httpContent.reset();
25.21 + String[] params = null;
25.22 + if (r.parameters.length != 0) {
25.23 + params = new String[r.parameters.length];
25.24 + for (int i = 0; i < r.parameters.length; i++) {
25.25 + params[i] = request.getParameter(r.parameters[i]);
25.26 + }
25.27 + }
25.28 +
25.29 + copyStream(r.httpContent, response.getOutputStream(), null, params);
25.30 }
25.31 }
25.32 }
25.33 @@ -315,7 +324,7 @@
25.34 }
25.35 if (ch == '$' && params.length > 0) {
25.36 int cnt = is.read() - '0';
25.37 - if (cnt == 'U' - '0') {
25.38 + if (baseURL != null && cnt == 'U' - '0') {
25.39 os.write(baseURL.getBytes("UTF-8"));
25.40 } else {
25.41 if (cnt >= 0 && cnt < params.length) {
25.42 @@ -454,7 +463,7 @@
25.43 }
25.44
25.45 private static class Page extends HttpHandler {
25.46 - private final String resource;
25.47 + final String resource;
25.48 private final String[] args;
25.49 private final Res res;
25.50
25.51 @@ -466,10 +475,7 @@
25.52
25.53 @Override
25.54 public void service(Request request, Response response) throws Exception {
25.55 - String r = resource;
25.56 - if (r == null) {
25.57 - r = request.getHttpHandlerPath();
25.58 - }
25.59 + String r = computePage(request);
25.60 if (r.startsWith("/")) {
25.61 r = r.substring(1);
25.62 }
25.63 @@ -493,6 +499,28 @@
25.64 response.setStatus(404);
25.65 }
25.66 }
25.67 +
25.68 + protected String computePage(Request request) {
25.69 + String r = resource;
25.70 + if (r == null) {
25.71 + r = request.getHttpHandlerPath();
25.72 + }
25.73 + return r;
25.74 + }
25.75 + }
25.76 +
25.77 + private static class SubTree extends Page {
25.78 +
25.79 + public SubTree(Res res, String resource, String... args) {
25.80 + super(res, resource, args);
25.81 + }
25.82 +
25.83 + @Override
25.84 + protected String computePage(Request request) {
25.85 + return resource + request.getHttpHandlerPath();
25.86 + }
25.87 +
25.88 +
25.89 }
25.90
25.91 private static class VM extends HttpHandler {
26.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Wed Apr 03 13:43:22 2013 +0200
26.2 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Mon Apr 08 19:33:08 2013 +0200
26.3 @@ -55,11 +55,11 @@
26.4 /** HTTP resource to be available during execution. An invocation may
26.5 * perform an HTTP query and obtain a resource relative to the page.
26.6 */
26.7 - public void addHttpResource(String relativePath, String mimeType, InputStream content) {
26.8 - if (relativePath == null || mimeType == null || content == null) {
26.9 + public void addHttpResource(String relativePath, String mimeType, String[] parameters, InputStream content) {
26.10 + if (relativePath == null || mimeType == null || content == null || parameters == null) {
26.11 throw new NullPointerException();
26.12 }
26.13 - resources.add(new Resource(content, mimeType, relativePath));
26.14 + resources.add(new Resource(content, mimeType, relativePath, parameters));
26.15 }
26.16
26.17 /** Invokes the associated method.
26.18 @@ -100,11 +100,16 @@
26.19 final InputStream httpContent;
26.20 final String httpType;
26.21 final String httpPath;
26.22 + final String[] parameters;
26.23
26.24 - Resource(InputStream httpContent, String httpType, String httpPath) {
26.25 + Resource(InputStream httpContent, String httpType, String httpPath,
26.26 + String[] parameters
26.27 + ) {
26.28 + httpContent.mark(Integer.MAX_VALUE);
26.29 this.httpContent = httpContent;
26.30 this.httpType = httpType;
26.31 this.httpPath = httpPath;
26.32 + this.parameters = parameters;
26.33 }
26.34 }
26.35 }
27.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Wed Apr 03 13:43:22 2013 +0200
27.2 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Mon Apr 08 19:33:08 2013 +0200
27.3 @@ -113,13 +113,6 @@
27.4 )
27.5 private static native void beginTest(String test, Case c, Object[] arr);
27.6
27.7 - public static void execute() throws Exception {
27.8 - String clazz = (String) getAttr("clazz", "value");
27.9 - String method = (String) getAttr("method", "value");
27.10 - Object res = invokeMethod(clazz, method);
27.11 - setAttr("bck2brwsr.result", "value", res);
27.12 - }
27.13 -
27.14 @JavaScriptBody(args = { "url", "callback", "arr" }, body = ""
27.15 + "var request = new XMLHttpRequest();\n"
27.16 + "request.open('GET', url, true);\n"
27.17 @@ -141,39 +134,53 @@
27.18 private static class Request implements Runnable {
27.19 private final String[] arr = { null };
27.20 private final String url;
27.21 + private Case c;
27.22 + private int retries;
27.23
27.24 private Request(String url) throws IOException {
27.25 this.url = url;
27.26 loadText(url, this, arr);
27.27 }
27.28 + private Request(String url, String u) throws IOException {
27.29 + this.url = url;
27.30 + loadText(u, this, arr);
27.31 + }
27.32
27.33 @Override
27.34 public void run() {
27.35 try {
27.36 - String data = arr[0];
27.37 - log("\nGot \"" + data + "\"");
27.38 + if (c == null) {
27.39 + String data = arr[0];
27.40 +
27.41 + if (data == null) {
27.42 + log("Some error exiting");
27.43 + closeWindow();
27.44 + return;
27.45 + }
27.46 +
27.47 + if (data.isEmpty()) {
27.48 + log("No data, exiting");
27.49 + closeWindow();
27.50 + return;
27.51 + }
27.52 +
27.53 + c = Case.parseData(data);
27.54 + beginTest(c);
27.55 + log("Got \"" + data + "\"");
27.56 + } else {
27.57 + log("Processing \"" + arr[0] + "\" for " + retries + " time");
27.58 + }
27.59 + Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest();
27.60 + finishTest(c, result);
27.61
27.62 - if (data == null) {
27.63 - log("Some error exiting");
27.64 - closeWindow();
27.65 + String u = url + "?request=" + c.getRequestId() + "&result=" + result;
27.66 + new Request(url, u);
27.67 + } catch (Exception ex) {
27.68 + if (ex instanceof InterruptedException) {
27.69 + log("Re-scheduling in 100ms");
27.70 + schedule(this, 100);
27.71 return;
27.72 }
27.73 -
27.74 - if (data.isEmpty()) {
27.75 - log("No data, exiting");
27.76 - closeWindow();
27.77 - return;
27.78 - }
27.79 -
27.80 - Case c = Case.parseData(data);
27.81 - beginTest(c);
27.82 - Object result = c.runTest();
27.83 - finishTest(c, result);
27.84 - String u = url + "?request=" + c.getRequestId() + "&result=" + result;
27.85 -
27.86 - loadText(u, this, arr);
27.87 -
27.88 - } catch (Exception ex) {
27.89 log(ex.getClass().getName() + ":" + ex.getMessage());
27.90 }
27.91 }
27.92 @@ -199,8 +206,10 @@
27.93 return sb.toString();
27.94 }
27.95
27.96 - static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
27.97 - final Object r = invokeMethod(clazz, method);
27.98 + static String invoke(String clazz, String method) throws
27.99 + ClassNotFoundException, InvocationTargetException, IllegalAccessException,
27.100 + InstantiationException, InterruptedException {
27.101 + final Object r = new Case(null).invokeMethod(clazz, method);
27.102 return r == null ? "null" : r.toString().toString();
27.103 }
27.104
27.105 @@ -235,40 +244,17 @@
27.106 }
27.107 }
27.108
27.109 - private static Object invokeMethod(String clazz, String method)
27.110 - throws ClassNotFoundException, InvocationTargetException,
27.111 - SecurityException, IllegalAccessException, IllegalArgumentException,
27.112 - InstantiationException {
27.113 - Method found = null;
27.114 - Class<?> c = Class.forName(clazz);
27.115 - for (Method m : c.getMethods()) {
27.116 - if (m.getName().equals(method)) {
27.117 - found = m;
27.118 - }
27.119 - }
27.120 - Object res;
27.121 - if (found != null) {
27.122 - try {
27.123 - if ((found.getModifiers() & Modifier.STATIC) != 0) {
27.124 - res = found.invoke(null);
27.125 - } else {
27.126 - res = found.invoke(c.newInstance());
27.127 - }
27.128 - } catch (Throwable ex) {
27.129 - res = ex.getClass().getName() + ":" + ex.getMessage();
27.130 - }
27.131 - } else {
27.132 - res = "Can't find method " + method + " in " + clazz;
27.133 - }
27.134 - return res;
27.135 - }
27.136 -
27.137 @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;")
27.138 private static void turnAssetionStatusOn() {
27.139 }
27.140 +
27.141 + @JavaScriptBody(args = {"r", "time"}, body =
27.142 + "return window.setTimeout(function() { r.run__V(); }, time);")
27.143 + private static native Object schedule(Runnable r, int time);
27.144
27.145 private static final class Case {
27.146 private final Object data;
27.147 + private Object inst;
27.148
27.149 private Case(Object data) {
27.150 this.data = data;
27.151 @@ -305,7 +291,9 @@
27.152 }
27.153 }
27.154
27.155 - private Object runTest() throws IllegalAccessException, IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException, InvocationTargetException, InstantiationException, SecurityException {
27.156 + private Object runTest() throws IllegalAccessException,
27.157 + IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException,
27.158 + InvocationTargetException, InstantiationException, InterruptedException {
27.159 if (this.getHtmlFragment() != null) {
27.160 setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment());
27.161 }
27.162 @@ -317,6 +305,43 @@
27.163 log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
27.164 return result;
27.165 }
27.166 +
27.167 + private Object invokeMethod(String clazz, String method)
27.168 + throws ClassNotFoundException, InvocationTargetException,
27.169 + InterruptedException, IllegalAccessException, IllegalArgumentException,
27.170 + InstantiationException {
27.171 + Method found = null;
27.172 + Class<?> c = Class.forName(clazz);
27.173 + for (Method m : c.getMethods()) {
27.174 + if (m.getName().equals(method)) {
27.175 + found = m;
27.176 + }
27.177 + }
27.178 + Object res;
27.179 + if (found != null) {
27.180 + try {
27.181 + if ((found.getModifiers() & Modifier.STATIC) != 0) {
27.182 + res = found.invoke(null);
27.183 + } else {
27.184 + if (inst == null) {
27.185 + inst = c.newInstance();
27.186 + }
27.187 + res = found.invoke(inst);
27.188 + }
27.189 + } catch (Throwable ex) {
27.190 + if (ex instanceof InvocationTargetException) {
27.191 + ex = ((InvocationTargetException) ex).getTargetException();
27.192 + }
27.193 + if (ex instanceof InterruptedException) {
27.194 + throw (InterruptedException)ex;
27.195 + }
27.196 + res = ex.getClass().getName() + ":" + ex.getMessage();
27.197 + }
27.198 + } else {
27.199 + res = "Can't find method " + method + " in " + clazz;
27.200 + }
27.201 + return res;
27.202 + }
27.203
27.204 @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');")
27.205 private static native Object toJSON(String s);
28.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Wed Apr 03 13:43:22 2013 +0200
28.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Mon Apr 08 19:33:08 2013 +0200
28.3 @@ -18,6 +18,7 @@
28.4 package org.apidesign.vm4brwsr;
28.5
28.6 import java.io.UnsupportedEncodingException;
28.7 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
28.8
28.9 /**
28.10 *
28.11 @@ -129,4 +130,20 @@
28.12 public String toString() {
28.13 return HELLO + cnt;
28.14 }
28.15 +
28.16 + @JavaScriptBody(args = {}, body = "return [1, 2];")
28.17 + private static native Object crtarr();
28.18 + @JavaScriptBody(args = { "o" }, body = "return o.toString();")
28.19 + private static native String toStrng(Object o);
28.20 +
28.21 + public static String toStringArray(boolean fakeArr, boolean toString) {
28.22 + final Object arr = fakeArr ? crtarr() : new Object[2];
28.23 + final String whole = toString ? arr.toString() : toStrng(arr);
28.24 + int zav = whole.indexOf('@');
28.25 + if (zav <= 0) {
28.26 + zav = whole.length();
28.27 + }
28.28 + return whole.substring(0, zav).toString().toString();
28.29 + }
28.30 +
28.31 }
29.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Wed Apr 03 13:43:22 2013 +0200
29.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Mon Apr 08 19:33:08 2013 +0200
29.3 @@ -194,6 +194,34 @@
29.4
29.5 }
29.6
29.7 + @Test public void toStringOnJSArray() throws Exception {
29.8 + String exp = StringSample.toStringArray(false, true);
29.9 +
29.10 + assertExec(
29.11 + "Treated as Java Object array",
29.12 + StringSample.class, "toStringArray__Ljava_lang_String_2ZZ",
29.13 + exp, true, true
29.14 + );
29.15 + }
29.16 +
29.17 + @Test public void toStringOnRealArray() throws Exception {
29.18 + String exp = StringSample.toStringArray(false, true);
29.19 +
29.20 + assertExec(
29.21 + "Is Java Object array",
29.22 + StringSample.class, "toStringArray__Ljava_lang_String_2ZZ",
29.23 + exp, false, true
29.24 + );
29.25 + }
29.26 +
29.27 + @Test public void valueOfOnJSArray() throws Exception {
29.28 + assertExec(
29.29 + "Treated as classical JavaScript array",
29.30 + StringSample.class, "toStringArray__Ljava_lang_String_2ZZ",
29.31 + "1,2", true, false
29.32 + );
29.33 + }
29.34 +
29.35 private static TestVM code;
29.36
29.37 @BeforeClass
30.1 --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/BrwsrTest.java Wed Apr 03 13:43:22 2013 +0200
30.2 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/BrwsrTest.java Mon Apr 08 19:33:08 2013 +0200
30.3 @@ -29,6 +29,10 @@
30.4 * The browser to is by default executed via {@link java.awt.Desktop#browse(java.net.URI)},
30.5 * but one can change that by specifying <code>-Dvmtest.brwsrs=firefox,google-chrome</code>
30.6 * property.
30.7 + * <p>
30.8 + * If the annotated method throws {@link InterruptedException}, it will return
30.9 + * the processing to the browser and after 100ms, called again. This is useful
30.10 + * for testing asynchronous communication, etc.
30.11 *
30.12 * @author Jaroslav Tulach <jtulach@netbeans.org>
30.13 */
31.1 --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java Wed Apr 03 13:43:22 2013 +0200
31.2 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java Mon Apr 08 19:33:08 2013 +0200
31.3 @@ -53,5 +53,10 @@
31.4 String resource() default "";
31.5 /** mime type of the resource */
31.6 String mimeType();
31.7 + /** query parameters. Can be referenced from the {@link #content} as
31.8 + * <code>$0</code>, <code>$1</code>, etc. The values will be extracted
31.9 + * from URL parameters of the request.
31.10 + */
31.11 + String[] parameters() default {};
31.12 }
31.13 }
32.1 --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Wed Apr 03 13:43:22 2013 +0200
32.2 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Mon Apr 08 19:33:08 2013 +0200
32.3 @@ -65,17 +65,17 @@
32.4 for (Http.Resource r : http) {
32.5 if (!r.content().isEmpty()) {
32.6 InputStream is = new ByteArrayInputStream(r.content().getBytes("UTF-8"));
32.7 - c.addHttpResource(r.path(), r.mimeType(), is);
32.8 + c.addHttpResource(r.path(), r.mimeType(), r.parameters(), is);
32.9 } else {
32.10 InputStream is = m.getDeclaringClass().getResourceAsStream(r.resource());
32.11 - c.addHttpResource(r.path(), r.mimeType(), is);
32.12 + c.addHttpResource(r.path(), r.mimeType(), r.parameters(), is);
32.13 }
32.14 }
32.15 }
32.16 String res = c.invoke();
32.17 value = res;
32.18 if (fail) {
32.19 - int idx = res.indexOf(':');
32.20 + int idx = res == null ? -1 : res.indexOf(':');
32.21 if (idx >= 0) {
32.22 Class<? extends Throwable> thrwbl = null;
32.23 try {
33.1 --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Wed Apr 03 13:43:22 2013 +0200
33.2 +++ b/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Mon Apr 08 19:33:08 2013 +0200
33.3 @@ -149,6 +149,18 @@
33.4 public int stringToBytesLenght() throws UnsupportedEncodingException {
33.5 return "\u017dlu\u0165ou\u010dk\u00fd k\u016f\u0148".getBytes("utf8").length;
33.6 }
33.7 +
33.8 + @Compare public String replaceSeq() {
33.9 + return "Hello World.".replace(".", "!");
33.10 + }
33.11 + @Compare public String replaceSeqAll() {
33.12 + return "Hello World! Hello World.".replace("World", "Jarda");
33.13 + }
33.14 + @Compare public String replaceSeqAA() {
33.15 + String res = "aaa".replace("aa", "b");
33.16 + assert res.equals("ba") : "Expecting ba: " + res;
33.17 + return res;
33.18 + }
33.19
33.20 @Factory
33.21 public static Object[] create() {
34.1 --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java Wed Apr 03 13:43:22 2013 +0200
34.2 +++ b/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java Mon Apr 08 19:33:08 2013 +0200
34.3 @@ -18,6 +18,8 @@
34.4 package org.apidesign.bck2brwsr.tck;
34.5
34.6 import java.lang.reflect.Array;
34.7 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
34.8 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
34.9 import org.apidesign.bck2brwsr.vmtest.Compare;
34.10 import org.apidesign.bck2brwsr.vmtest.VMTest;
34.11 import org.testng.annotations.Factory;
34.12 @@ -127,6 +129,30 @@
34.13 return Array.newInstance(int.class, 3, 3, 3).getClass().getName();
34.14 }
34.15
34.16 + @JavaScriptBody(args = {}, body = "return [1, 2];")
34.17 + private static native Object crtarr();
34.18 +
34.19 + @JavaScriptBody(args = {}, body = "return new Object();")
34.20 + private static native Object newobj();
34.21 +
34.22 + @BrwsrTest
34.23 + public static void toStringArray() {
34.24 + final Object arr = crtarr();
34.25 + final Object real = new Object[2];
34.26 + assert arr instanceof Object[] : "Any array is Java array: " + arr;
34.27 + assert arr.getClass() == real.getClass() : "Same classes " + arr + " and " + real.getClass();
34.28 + final String str = arr.toString();
34.29 + assert str != null;
34.30 + assert str.startsWith("[Ljava.lang.Object;@") : str;
34.31 + }
34.32 +
34.33 + @BrwsrTest
34.34 + public static void objectToString() {
34.35 + String s = newobj().toString();
34.36 + assert s != null : "Some string computed";
34.37 + assert s.startsWith("java.lang.Object@") : "Regular object toString(): " + s;
34.38 + }
34.39 +
34.40
34.41 @Factory
34.42 public static Object[] create() {
35.1 --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Wed Apr 03 13:43:22 2013 +0200
35.2 +++ b/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Mon Apr 08 19:33:08 2013 +0200
35.3 @@ -104,6 +104,11 @@
35.4 return "should not happen";
35.5 }
35.6
35.7 + @Compare public String methodThatThrowsException() throws Exception {
35.8 + StaticUse.class.getMethod("instanceMethod").invoke(new StaticUse());
35.9 + return "should not happen";
35.10 + }
35.11 +
35.12 @Compare public Object voidReturnType() throws Exception {
35.13 return StaticUse.class.getMethod("instanceMethod").getReturnType();
35.14 }
36.1 --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java Wed Apr 03 13:43:22 2013 +0200
36.2 +++ b/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java Mon Apr 08 19:33:08 2013 +0200
36.3 @@ -30,6 +30,7 @@
36.4 }
36.5
36.6 public void instanceMethod() {
36.7 + throw new IllegalStateException();
36.8 }
36.9
36.10 public static int plus(int a, int b) {
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
37.2 +++ b/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CallMeTwiceTest.java Mon Apr 08 19:33:08 2013 +0200
37.3 @@ -0,0 +1,43 @@
37.4 +/**
37.5 + * Back 2 Browser Bytecode Translator
37.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
37.7 + *
37.8 + * This program is free software: you can redistribute it and/or modify
37.9 + * it under the terms of the GNU General Public License as published by
37.10 + * the Free Software Foundation, version 2 of the License.
37.11 + *
37.12 + * This program is distributed in the hope that it will be useful,
37.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
37.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37.15 + * GNU General Public License for more details.
37.16 + *
37.17 + * You should have received a copy of the GNU General Public License
37.18 + * along with this program. Look for COPYING file in the top folder.
37.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
37.20 + */
37.21 +package org.apidesign.bck2brwsr.vmtest.impl;
37.22 +
37.23 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
37.24 +import org.apidesign.bck2brwsr.vmtest.VMTest;
37.25 +import org.testng.annotations.Factory;
37.26 +
37.27 +/**
37.28 + *
37.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
37.30 + */
37.31 +public class CallMeTwiceTest {
37.32 + int cnt;
37.33 +
37.34 + @BrwsrTest public void callMeTwice() throws InterruptedException {
37.35 + if (cnt++ == 0) {
37.36 + throw new InterruptedException();
37.37 + }
37.38 + int prevCnt = cnt;
37.39 + cnt = 0;
37.40 + assert prevCnt == 2 : "We need to receive two calls " + prevCnt;
37.41 + }
37.42 +
37.43 + @Factory public static Object[] create() {
37.44 + return VMTest.create(CallMeTwiceTest.class);
37.45 + }
37.46 +}