# HG changeset patch # User Jaroslav Tulach # Date 1367479102 -7200 # Node ID 3800d11c0bdb0e504f731538de3206b2a0d32d05 # Parent 146ae7b52b646c70706a5514c03024e82f8c0fe2# Parent 2894b9a9dbfc5d68b89d6c53bb7014be37305cfd Merging with latest state - e.g. 0.7 version diff -r 146ae7b52b64 -r 3800d11c0bdb .hgtags --- a/.hgtags Tue Apr 02 15:40:51 2013 +0200 +++ b/.hgtags Thu May 02 09:18:22 2013 +0200 @@ -1,3 +1,10 @@ 0a115f1c6f3c70458fc479ae82b4d7fcdeb7e95a jdk7-b147_base 7367a296a9ec4a88e0292a41244c96283818e563 bck2brwsr-0.3 caf1e66268fd4100d57922d973ae09a6bf3be847 release-${releaseVersion} +30e9ac29654fba6f67d0921e7e3aa21133442592 release-0.5 +caf1e66268fd4100d57922d973ae09a6bf3be847 release-0.4 +caf1e66268fd4100d57922d973ae09a6bf3be847 release-${releaseVersion} +0000000000000000000000000000000000000000 release-${releaseVersion} +52a4a5f868bccc67d50ad17f793b9ebabdf75d88 release-0.6 +6792dc0bafb9c76a099e45bfc9e967d6a2823827 release-0.7 +623816269b75e53fffb4b19960df7040a3c20056 release-0.7 diff -r 146ae7b52b64 -r 3800d11c0bdb benchmarks/matrix-multiplication/pom.xml --- a/benchmarks/matrix-multiplication/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/benchmarks/matrix-multiplication/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,12 +4,12 @@ org.apidesign.bck2brwsr matrix.multiplication - 0.5-SNAPSHOT + 0.8-SNAPSHOT jar benchmarks org.apidesign.bck2brwsr - 0.5-SNAPSHOT + 0.8-SNAPSHOT Matrix multiplication @@ -74,7 +74,7 @@ org.apidesign.bck2brwsr emul.mini - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.testng @@ -91,7 +91,13 @@ org.apidesign.bck2brwsr vmtest - 0.5-SNAPSHOT + 0.8-SNAPSHOT + test + + + org.apidesign.bck2brwsr + launcher.http + 0.8-SNAPSHOT test diff -r 146ae7b52b64 -r 3800d11c0bdb benchmarks/pom.xml --- a/benchmarks/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/benchmarks/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ bck2brwsr org.apidesign - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr benchmarks - 0.5-SNAPSHOT + 0.8-SNAPSHOT pom Performance benchmarks diff -r 146ae7b52b64 -r 3800d11c0bdb dew/pom.xml --- a/dew/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/dew/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ org.apidesign bck2brwsr - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr dew - 0.5-SNAPSHOT + 0.8-SNAPSHOT Development Environment for Web http://maven.apache.org diff -r 146ae7b52b64 -r 3800d11c0bdb ide/editor/pom.xml --- a/ide/editor/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/ide/editor/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,19 +4,18 @@ ide org.apidesign.bck2brwsr - 0.5-SNAPSHOT + 0.8-SNAPSHOT - org.apidesign.bck2brwsr.ide.editor + org.apidesign.bck2brwsr.ide editor - 0.5-SNAPSHOT + 0.8-SNAPSHOT nbm Editor Support for Bck2Brwsr UTF-8 - RELEASE73 ${project.build.directory}/endorsed @@ -40,71 +39,59 @@ org.netbeans.api org-netbeans-api-annotations-common - ${netbeans.version} org.netbeans.api org-netbeans-modules-java-source - ${netbeans.version} org.netbeans.api org-netbeans-libs-javacapi - ${netbeans.version} org.netbeans.api org-netbeans-spi-java-hints - ${netbeans.version} org.netbeans.api org-netbeans-modules-parsing-api - ${netbeans.version} org.netbeans.api org-netbeans-spi-editor-hints - ${netbeans.version} org.netbeans.api org-openide-util - ${netbeans.version} org.netbeans.api org-netbeans-modules-java-lexer - ${netbeans.version} org.netbeans.api org-netbeans-modules-lexer - ${netbeans.version} org.apidesign.bck2brwsr core - 0.5-SNAPSHOT + 0.8-SNAPSHOT jar test org.netbeans.api org-netbeans-modules-java-hints-test - ${netbeans.version} test org.netbeans.api org-netbeans-libs-junit4 - ${netbeans.version} test org.netbeans.modules org-netbeans-lib-nbjavac - ${netbeans.version} test diff -r 146ae7b52b64 -r 3800d11c0bdb ide/pom.xml --- a/ide/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/ide/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,14 +4,26 @@ bck2brwsr org.apidesign - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr ide - 0.5-SNAPSHOT + 0.8-SNAPSHOT pom IDE Support editor + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/pom.xml --- a/javaquery/api/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ org.apidesign.bck2brwsr javaquery - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr javaquery.api - 0.5-SNAPSHOT + 0.8-SNAPSHOT JavaQuery API http://maven.apache.org @@ -18,8 +18,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.7 + 1.7 @@ -74,6 +74,12 @@ test + ${project.groupId} + launcher.http + ${project.version} + test + + org.netbeans.modules org-netbeans-modules-html-parser diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Attributes.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Attributes.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.htmlpage; - -import java.util.HashMap; -import java.util.Map; - -/** - * Temporary storing the type of attributes here. This should be implemented in HTML 5 model - * - * @author Jan Horvath - */ -public class Attributes { - - static final Map TYPES = new HashMap() { - { - // HTML Global Attributes - // id attribute is already defined in Element, don't add it again - put("accesskey", "String"); - put("class", "String"); - put("contenteditable", "Boolean"); - put("contextmenu", "String"); - put("dir", "String"); - put("draggable", "Boolean"); - put("dropzone", "String"); - put("hidden", "Boolean"); - put("lang", "String"); - put("spellcheck", "Boolean"); - put("style", "String"); - put("tabindex", "String"); - put("title", "String"); - put("translate", "Boolean"); - put("width", "Integer"); - put("height", "Integer"); - - put("value", "String"); - put("disabled", "Boolean"); - -// put("text", "String"); 'text' field is used to set innerHTML of element - } - }; -} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Thu May 02 09:18:22 2013 +0200 @@ -41,11 +41,115 @@ Object ret = getProperty(object, property); return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE; } + + public static T toModel(Class modelClass, Object object, String property) { + Object ret = getProperty(object, property); + if (ret == null || modelClass.isInstance(ret)) { + return modelClass.cast(ret); + } + throw new IllegalStateException("Value " + ret + " is not of type " + modelClass); + } + + public static String toJSON(Object value) { + if (value == null) { + return "null"; + } + if (value instanceof Enum) { + value = value.toString(); + } + if (value instanceof String) { + return '"' + + ((String)value). + replace("\"", "\\\""). + replace("\n", "\\n"). + replace("\r", "\\r"). + replace("\t", "\\t") + + '"'; + } + return value.toString(); + } @JavaScriptBody(args = { "object", "property" }, - body = "var p = object[property]; return p ? p : null;" + body = "if (property === null) return object;\n" + + "var p = object[property]; return p ? p : null;" ) private static Object getProperty(Object object, String property) { return null; } + + public static String createJSONP(Object[] jsonResult, Runnable whenDone) { + int h = whenDone.hashCode(); + String name; + for (;;) { + name = "jsonp" + Integer.toHexString(h); + if (defineIfUnused(name, jsonResult, whenDone)) { + return name; + } + h++; + } + } + + @JavaScriptBody(args = { "name", "arr", "run" }, body = + "if (window[name]) return false;\n " + + "window[name] = function(data) {\n " + + " delete window[name];\n" + + " var el = window.document.getElementById(name);\n" + + " el.parentNode.removeChild(el);\n" + + " arr[0] = data;\n" + + " run.run__V();\n" + + "};\n" + + "return true;\n" + ) + private static boolean defineIfUnused(String name, Object[] arr, Runnable run) { + return true; + } + + @JavaScriptBody(args = { "url", "arr", "callback" }, body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, true);\n" + + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n" + + "request.onreadystatechange = function() {\n" + + " if (this.readyState!==4) return;\n" + + " try {\n" + + " arr[0] = eval('(' + this.response + ')');\n" + + " } catch (error) {;\n" + + " throw 'Cannot parse' + error + ':' + this.response;\n" + + " };\n" + + " callback.run__V();\n" + + "};" + + "request.send();" + ) + private static void loadJSON( + String url, Object[] jsonResult, Runnable whenDone + ) { + } + + public static void loadJSON( + String url, Object[] jsonResult, Runnable whenDone, String jsonp + ) { + if (jsonp == null) { + loadJSON(url, jsonResult, whenDone); + } else { + loadJSONP(url, jsonp); + } + } + + @JavaScriptBody(args = { "url", "jsonp" }, body = + "var scrpt = window.document.createElement('script');\n " + + "scrpt.setAttribute('src', url);\n " + + "scrpt.setAttribute('id', jsonp);\n " + + "scrpt.setAttribute('type', 'text/javascript');\n " + + "var body = document.getElementsByTagName('body')[0];\n " + + "body.appendChild(scrpt);\n" + ) + private static void loadJSONP(String url, String jsonp) { + + } + + public static void extractJSON(Object jsonObject, String[] props, Object[] values) { + for (int i = 0; i < props.length; i++) { + values[i] = getProperty(jsonObject, props[i]); + } + } + } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ElementGenerator.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ElementGenerator.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,175 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.htmlpage; - -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.ServiceLoader; -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.tools.Diagnostic; -import javax.tools.FileObject; -import org.netbeans.modules.html.editor.lib.api.HtmlVersion; -import org.netbeans.modules.html.editor.lib.api.model.HtmlModel; -import org.netbeans.modules.html.editor.lib.api.model.HtmlModelProvider; -import org.netbeans.modules.html.editor.lib.api.model.HtmlTag; -import org.netbeans.modules.html.editor.lib.api.model.HtmlTagAttribute; - -/** - * - * @author Jan Horvath - */ -public class ElementGenerator { - - static final Map NAMING_EXCEPTIONS = new HashMap() { - { - put("img", "Image"); - put("class", "Clazz"); - } - }; - - static final String javaKeywords[] = { - "abstract", "assert", "boolean", "break", "byte", "case", - "catch", "char", "class", "const", "continue", "default", - "do", "double", "else", "extends", "false", "final", "finally", - "float", "for", "goto", "if", "implements", "import", - "instanceof", "int", "interface", "long", "native", "new", - "null", "package", "private", "protected", "public", - "return", "short", "static", "strictfp", "super", - "switch", "synchronized", "this", "throw", "throws", - "transient", "true", "try", "void", "volatile", "while" - }; - - private static Map elements = new HashMap(); - private final ProcessingEnvironment processingEnv; - private HtmlModel model = null; - - ElementGenerator(ProcessingEnvironment processingEnv) { - this.processingEnv = processingEnv; - } - - String getType(String pkg, String tag, Element e) { - String className = elements.get(tag); - if (className == null) { - className = createClass(pkg, tag, e); - elements.put(tag, className); - } - return className; - } - - private String createClass(String pkg, String tag, Element e) { - String className = className(tag); - Writer w; - try { - FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e); - w = new OutputStreamWriter(java.openOutputStream()); - try { - w.append("package " + pkg + ";\n\n"); - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); - PredefinedFields.appendImports(w, tag); - w.append("\n"); - - w.append("class ").append(className).append(" extends Element {\n\n"); - w.append(" public ").append(className).append("(String id) {\n"); - w.append(" super(id);\n"); - w.append(" }\n\n"); - for (Entry entry : getAttributes(tag).entrySet()) { - String attrName = entry.getKey(); - String attrType = entry.getValue(); - // getter - w.append(" public ").append(attrType).append(" ") - .append("get").append(className(attrName)).append("() {\n"); - w.append(" return (").append(attrType).append(")getAttribute(\"") - .append(attrName).append("\");\n"); - w.append(" }\n\n"); - // setter - w.append(" public void ") - .append("set").append(className(attrName)).append("(") - .append(attrType).append(" ").append(attributeName(attrName)).append(") {\n"); - w.append(" setAttribute(\"").append(attrName).append("\", ").append(attributeName(attrName)).append(");\n"); - w.append(" }\n\n"); - } - PredefinedFields.appendFields(w, tag); - w.append("}\n"); - } finally { - w.close(); - } - } catch (IOException ex) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e); - return null; - } - return className; - } - - Map getAttributes(String tagName) { - Map result = new HashMap(); - - if (model == null) { - // HtmlModelProvider modelProvider = Lookup.getDefault().lookup(HtmlModelProvider.class); - ServiceLoader hmpLoader = - ServiceLoader.load(HtmlModelProvider.class, this.getClass().getClassLoader()); - for (HtmlModelProvider htmlModelProvider : hmpLoader) { - model = htmlModelProvider.getModel(HtmlVersion.HTML5); - if (model != null) { - break; - } - } - } - - if (model == null) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "HTML 5 model provider was not found on classpath"); - return Collections.emptyMap(); - } - HtmlTag tag = model.getTag(tagName); - for (HtmlTagAttribute attr : tag.getAttributes()) { - String name = attr.getName(); - String type = Attributes.TYPES.get(name); - if (type != null) { - result.put(name, type); - } - } - - return result; - } - - private String className(String s) { - if (s.length() == 0) { - return s; - } - String name = NAMING_EXCEPTIONS.get(s.toLowerCase()); - if (name == null) { - name = s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase(); - } - return name; - } - - private String attributeName(String s) { - if (Arrays.binarySearch(javaKeywords, s) >= 0) { - return String.format("%sAttr", s); - } - return s; - } - -} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/KOList.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/KOList.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,167 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import org.apidesign.bck2brwsr.core.JavaScriptOnly; + +/** + * + * @author Jaroslav Tulach + */ +public final class KOList extends ArrayList { + private final String name; + private final String[] deps; + private Knockout model; + private Runnable onchange; + + public KOList(String name, String... deps) { + this.name = name; + this.deps = deps; + } + + public void assign(Knockout model) { + if (this.model != model) { + this.model = model; + notifyChange(); + } + } + + public KOList onChange(Runnable r) { + if (this.onchange != null) { + throw new IllegalStateException(); + } + this.onchange = r; + return this; + } + + @Override + public boolean add(T e) { + boolean ret = super.add(e); + notifyChange(); + return ret; + } + + @Override + public boolean addAll(Collection c) { + boolean ret = super.addAll(c); + notifyChange(); + return ret; + } + + @Override + public boolean addAll(int index, Collection c) { + boolean ret = super.addAll(index, c); + notifyChange(); + return ret; + } + + @Override + public boolean remove(Object o) { + boolean ret = super.remove(o); + notifyChange(); + return ret; + } + + @Override + public void clear() { + super.clear(); + notifyChange(); + } + + @Override + public boolean removeAll(Collection c) { + boolean ret = super.removeAll(c); + notifyChange(); + return ret; + } + + @Override + public boolean retainAll(Collection c) { + boolean ret = super.retainAll(c); + notifyChange(); + return ret; + } + + @Override + public T set(int index, T element) { + T ret = super.set(index, element); + notifyChange(); + return ret; + } + + @Override + public void add(int index, T element) { + super.add(index, element); + notifyChange(); + } + + @Override + public T remove(int index) { + T ret = super.remove(index); + notifyChange(); + return ret; + } + + @Override + public String toString() { + Iterator it = iterator(); + if (!it.hasNext()) { + return "[]"; + } + String sep = ""; + StringBuilder sb = new StringBuilder(); + sb.append('['); + while (it.hasNext()) { + T t = it.next(); + sb.append(sep); + sb.append(ConvertTypes.toJSON(t)); + sep = ","; + } + sb.append(']'); + return sb.toString(); + } + + + @JavaScriptOnly(name = "koArray", value = "function() { return this.toArray___3Ljava_lang_Object_2(); }") + private static native int koArray(); + + private void notifyChange() { + Knockout m = model; + if (m != null) { + m.valueHasMutated(name); + for (String dependant : deps) { + m.valueHasMutated(dependant); + } + } + Runnable r = onchange; + if (r != null) { + r.run(); + } + } + + @Override + public KOList clone() { + KOList ko = (KOList)super.clone(); + ko.model = null; + return ko; + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Thu May 02 09:18:22 2013 +0200 @@ -18,6 +18,7 @@ package org.apidesign.bck2brwsr.htmlpage; import java.lang.reflect.Method; +import java.util.List; import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -29,38 +30,40 @@ public class Knockout { /** used by tests */ static Knockout next; - - Knockout() { + private final Object model; + + Knockout(Object model) { + this.model = model == null ? this : model; } public static Knockout applyBindings( - Class modelClass, M model, String[] propsGettersAndSetters + Object model, String[] propsGettersAndSetters, + String[] methodsAndSignatures + ) { + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures); + return new Knockout(model); + } + public static Knockout applyBindings( + Class modelClass, M model, String[] propsGettersAndSetters, + String[] methodsAndSignatures ) { Knockout bindings = next; next = null; if (bindings == null) { - bindings = new Knockout(); + bindings = new Knockout(null); } - for (int i = 0; i < propsGettersAndSetters.length; i += 4) { - try { - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]); - bind(bindings, model, propsGettersAndSetters[i], - propsGettersAndSetters[i + 1], - propsGettersAndSetters[i + 2], - getter.getReturnType().isPrimitive() - ); - } catch (NoSuchMethodException ex) { - throw new IllegalStateException(ex.getMessage()); - } - } + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures); applyBindings(bindings); return bindings; } - @JavaScriptBody(args = { "prop" }, body = - "this[prop].valueHasMutated();" + public void valueHasMutated(String prop) { + valueHasMutated(model, prop); + } + @JavaScriptBody(args = { "self", "prop" }, body = + "self[prop].valueHasMutated();" ) - public void valueHasMutated(String prop) { + public void valueHasMutated(Object self, String prop) { } @@ -68,26 +71,60 @@ public static void triggerEvent(String id, String ev) { } - @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive" }, body = + @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body = "var bnd = {\n" - + " read: function() {\n" + + " 'read': function() {\n" + " var v = model[getter]();\n" + + " if (array) v = v.koArray();\n" + " return v;\n" + " },\n" - + " owner: bindings\n" + + " 'owner': bindings\n" + "};\n" + "if (setter != null) {\n" - + " bnd.write = function(val) {\n" + + " bnd['write'] = function(val) {\n" + " model[setter](primitive ? new Number(val) : val);\n" + " };\n" + "}\n" - + "bindings[prop] = ko.computed(bnd);" + + "bindings[prop] = ko['computed'](bnd);" ) private static void bind( - Object bindings, Object model, String prop, String getter, String setter, boolean primitive + Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array + ) { + } + + @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body = + "bindings[prop] = function(data, ev) { model[sig](data, ev); };" + ) + private static void expose( + Object bindings, Object model, String prop, String sig ) { } @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);") private static void applyBindings(Object bindings) {} + + private static void applyImpl( + String[] propsGettersAndSetters, + Class modelClass, + Object bindings, + Object model, + String[] methodsAndSignatures + ) throws IllegalStateException, SecurityException { + for (int i = 0; i < propsGettersAndSetters.length; i += 4) { + try { + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]); + bind(bindings, model, propsGettersAndSetters[i], + propsGettersAndSetters[i + 1], + propsGettersAndSetters[i + 2], + getter.getReturnType().isPrimitive(), + List.class.isAssignableFrom(getter.getReturnType())); + } catch (NoSuchMethodException ex) { + throw new IllegalStateException(ex.getMessage()); + } + } + for (int i = 0; i < methodsAndSignatures.length; i += 2) { + expose( + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]); + } + } } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Thu May 02 09:18:22 2013 +0200 @@ -20,23 +20,30 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; +import java.io.StringWriter; import java.io.Writer; +import java.lang.annotation.AnnotationTypeMismatchException; +import java.lang.annotation.IncompleteAnnotationException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Completion; import javax.annotation.processing.Completions; +import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -44,14 +51,21 @@ import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.StandardLocation; import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; +import org.apidesign.bck2brwsr.htmlpage.api.Model; import org.apidesign.bck2brwsr.htmlpage.api.On; +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction; +import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange; +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive; import org.apidesign.bck2brwsr.htmlpage.api.Page; import org.apidesign.bck2brwsr.htmlpage.api.Property; import org.openide.util.lookup.ServiceProvider; @@ -63,90 +77,70 @@ */ @ServiceProvider(service=Processor.class) @SupportedAnnotationTypes({ + "org.apidesign.bck2brwsr.htmlpage.api.Model", "org.apidesign.bck2brwsr.htmlpage.api.Page", + "org.apidesign.bck2brwsr.htmlpage.api.OnFunction", + "org.apidesign.bck2brwsr.htmlpage.api.OnReceive", + "org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange", + "org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty", "org.apidesign.bck2brwsr.htmlpage.api.On" }) public final class PageProcessor extends AbstractProcessor { + private final Map models = new WeakHashMap<>(); + private final Map verify = new WeakHashMap<>(); @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) { - Page p = e.getAnnotation(Page.class); - if (p == null) { - continue; - } - PackageElement pe = (PackageElement)e.getEnclosingElement(); - String pkg = pe.getQualifiedName().toString(); - - ProcessPage pp; - ElementGenerator eGen = new ElementGenerator(processingEnv); - try { - InputStream is = openStream(pkg, p.xhtml()); - pp = ProcessPage.readPage(is); - is.close(); - } catch (IOException iOException) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e); - return false; - } - Writer w; - String className = p.className(); - if (className.isEmpty()) { - int indx = p.xhtml().indexOf('.'); - className = p.xhtml().substring(0, indx); - } - try { - FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e); - w = new OutputStreamWriter(java.openOutputStream()); - try { - w.append("package " + pkg + ";\n"); - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); - w.append("final class ").append(className).append(" {\n"); - w.append(" private boolean locked;\n"); - if (!initializeOnClick(className, (TypeElement) e, w, pp)) { - return false; - } - for (String id : pp.ids()) { - String tag = pp.tagNameForId(id); - String type = eGen.getType(pkg, tag, e); - w.append(" ").append("public final "). - append(type).append(' ').append(cnstnt(id)).append(" = new "). - append(type).append("(\"").append(id).append("\");\n"); - } - List propsGetSet = new ArrayList(); - Map> propsDeps = new HashMap>(); - generateComputedProperties(w, e.getEnclosedElements(), propsGetSet, propsDeps); - generateProperties(w, p.properties(), propsGetSet, propsDeps); - w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n"); - if (!propsGetSet.isEmpty()) { - w.write("public " + className + " applyBindings() {\n"); - w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings("); - w.write(className + ".class, this, "); - w.write("new String[] {\n"); - String sep = ""; - for (String n : propsGetSet) { - w.write(sep); - if (n == null) { - w.write(" null"); - } else { - w.write(" \"" + n + "\""); - } - sep = ",\n"; - } - w.write("\n });\n return this;\n}\n"); - - w.write("public void triggerEvent(Element e, OnEvent ev) {\n"); - w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n"); - w.write("}\n"); - } - w.append("}\n"); - } finally { - w.close(); - } - } catch (IOException ex) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e); - return false; + boolean ok = true; + for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) { + if (!processModel(e)) { + ok = false; } } - return true; + for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) { + if (!processPage(e)) { + ok = false; + } + } + if (roundEnv.processingOver()) { + models.clear(); + for (Map.Entry entry : verify.entrySet()) { + TypeElement te = (TypeElement)entry.getKey(); + String fqn = processingEnv.getElementUtils().getBinaryName(te).toString(); + Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn); + if (finalElem == null) { + continue; + } + Prprt[] props; + Model m = finalElem.getAnnotation(Model.class); + if (m != null) { + props = Prprt.wrap(processingEnv, finalElem, m.properties()); + } else { + Page p = finalElem.getAnnotation(Page.class); + props = Prprt.wrap(processingEnv, finalElem, p.properties()); + } + for (Prprt p : props) { + boolean[] isModel = { false }; + boolean[] isEnum = { false }; + boolean[] isPrimitive = { false }; + String t = checkType(p, isModel, isEnum, isPrimitive); + if (isEnum[0]) { + continue; + } + if (isPrimitive[0]) { + continue; + } + if (isModel[0]) { + continue; + } + if ("java.lang.String".equals(t)) { + continue; + } + error("The type " + t + " should be defined by @Model annotation", entry.getKey()); + } + } + verify.clear(); + } + return ok; } private InputStream openStream(String pkg, String name) throws IOException { @@ -159,13 +153,263 @@ } } + private void error(String msg, Element e) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e); + } + + private boolean processModel(Element e) { + boolean ok = true; + Model m = e.getAnnotation(Model.class); + if (m == null) { + return true; + } + String pkg = findPkgName(e); + Writer w; + String className = m.className(); + models.put(e, className); + try { + StringWriter body = new StringWriter(); + List propsGetSet = new ArrayList<>(); + List functions = new ArrayList<>(); + Map> propsDeps = new HashMap<>(); + Map> functionDeps = new HashMap<>(); + Prprt[] props = createProps(e, m.properties()); + + if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) { + ok = false; + } + if (!generateOnChange(e, propsDeps, props, className, functionDeps)) { + ok = false; + } + if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) { + ok = false; + } + if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) { + ok = false; + } + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e); + w = new OutputStreamWriter(java.openOutputStream()); + try { + w.append("package " + pkg + ";\n"); + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n"); + w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n"); + w.append("final class ").append(className).append(" implements Cloneable {\n"); + w.append(" private boolean locked;\n"); + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n"); + w.append(body.toString()); + w.append(" private static Class<" + inPckName(e) + "> modelFor() { return null; }\n"); + w.append(" public ").append(className).append("() {\n"); + w.append(" intKnckt();\n"); + w.append(" };\n"); + w.append(" private void intKnckt() {\n"); + w.append(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, "); + writeStringArray(propsGetSet, w); + w.append(", "); + writeStringArray(functions, w); + w.append(" );\n"); + w.append(" };\n"); + w.append(" ").append(className).append("(Object json) {\n"); + int values = 0; + for (int i = 0; i < propsGetSet.size(); i += 4) { + Prprt p = findPrprt(props, propsGetSet.get(i)); + if (p == null) { + continue; + } + values++; + } + w.append(" Object[] ret = new Object[" + values + "];\n"); + w.append(" org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n"); + for (int i = 0; i < propsGetSet.size(); i += 4) { + Prprt p = findPrprt(props, propsGetSet.get(i)); + if (p == null) { + continue; + } + w.append(" \"").append(propsGetSet.get(i)).append("\",\n"); + } + w.append(" }, ret);\n"); + for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) { + final String pn = propsGetSet.get(i); + Prprt p = findPrprt(props, pn); + if (p == null) { + continue; + } + boolean[] isModel = { false }; + boolean[] isEnum = { false }; + boolean isPrimitive[] = { false }; + String type = checkType(props[prop++], isModel, isEnum, isPrimitive); + if (p.array()) { + w.append("if (ret[" + cnt + "] instanceof Object[]) {\n"); + w.append(" for (Object e : ((Object[])ret[" + cnt + "])) {\n"); + if (isModel[0]) { + w.append(" this.prop_").append(pn).append(".add(new "); + w.append(type).append("(e));\n"); + } else if (isEnum[0]) { + w.append(" this.prop_").append(pn); + w.append(".add(e == null ? null : "); + w.append(type).append(".valueOf((String)e));\n"); + } else { + if (isPrimitive(type)) { + w.append(" this.prop_").append(pn).append(".add(((Number)e)."); + w.append(type).append("Value());\n"); + } else { + w.append(" this.prop_").append(pn).append(".add(("); + w.append(type).append(")e);\n"); + } + } + w.append(" }\n"); + w.append("}\n"); + } else { + if (isEnum[0]) { + w.append(" this.prop_").append(pn); + w.append(" = ret[" + cnt + "] == null ? null : "); + w.append(type).append(".valueOf((String)ret[" + cnt + "]);\n"); + } else if (isPrimitive(type)) { + w.append(" this.prop_").append(pn); + w.append(" = ((Number)").append("ret[" + cnt + "])."); + w.append(type).append("Value();\n"); + } else { + w.append(" this.prop_").append(pn); + w.append(" = (").append(type).append(')'); + w.append("ret[" + cnt + "];\n"); + } + } + cnt++; + } + w.append(" intKnckt();\n"); + w.append(" };\n"); + writeToString(props, w); + writeClone(className, props, w); + w.append("}\n"); + } finally { + w.close(); + } + } catch (IOException ex) { + error("Can't create " + className + ".java", e); + return false; + } + return ok; + } + + private boolean processPage(Element e) { + boolean ok = true; + Page p = e.getAnnotation(Page.class); + if (p == null) { + return true; + } + String pkg = findPkgName(e); + + ProcessPage pp; + try (InputStream is = openStream(pkg, p.xhtml())) { + pp = ProcessPage.readPage(is); + is.close(); + } catch (IOException iOException) { + error("Can't read " + p.xhtml() + " as " + iOException.getMessage(), e); + ok = false; + pp = null; + } + Writer w; + String className = p.className(); + if (className.isEmpty()) { + int indx = p.xhtml().indexOf('.'); + className = p.xhtml().substring(0, indx); + } + try { + StringWriter body = new StringWriter(); + List propsGetSet = new ArrayList<>(); + List functions = new ArrayList<>(); + Map> propsDeps = new HashMap<>(); + Map> functionDeps = new HashMap<>(); + + Prprt[] props = createProps(e, p.properties()); + if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) { + ok = false; + } + if (!generateOnChange(e, propsDeps, props, className, functionDeps)) { + ok = false; + } + if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) { + ok = false; + } + if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) { + ok = false; + } + if (!generateReceive(e, body, className, e.getEnclosedElements(), functions)) { + ok = false; + } + + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e); + w = new OutputStreamWriter(java.openOutputStream()); + try { + w.append("package " + pkg + ";\n"); + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n"); + w.append("final class ").append(className).append(" {\n"); + w.append(" private boolean locked;\n"); + if (!initializeOnClick(className, (TypeElement) e, w, pp)) { + ok = false; + } else { + if (pp != null) for (String id : pp.ids()) { + String tag = pp.tagNameForId(id); + String type = type(tag); + w.append(" ").append("public final "). + append(type).append(' ').append(cnstnt(id)).append(" = new "). + append(type).append("(\"").append(id).append("\");\n"); + } + } + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n"); + w.append(body.toString()); + if (!propsGetSet.isEmpty()) { + w.write("public " + className + " applyBindings() {\n"); + w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings("); + w.write(className + ".class, this, "); + writeStringArray(propsGetSet, w); + w.append(", "); + writeStringArray(functions, w); + w.write(");\n return this;\n}\n"); + + w.write("public void triggerEvent(Element e, OnEvent ev) {\n"); + w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n"); + w.write("}\n"); + } + w.append("}\n"); + } finally { + w.close(); + } + } catch (IOException ex) { + error("Can't create " + className + ".java", e); + return false; + } + return ok; + } + + private static String type(String tag) { + if (tag.equals("title")) { + return "Title"; + } + if (tag.equals("button")) { + return "Button"; + } + if (tag.equals("input")) { + return "Input"; + } + if (tag.equals("canvas")) { + return "Canvas"; + } + if (tag.equals("img")) { + return "Image"; + } + return "Element"; + } + private static String cnstnt(String id) { - return id.toUpperCase(Locale.ENGLISH).replace('.', '_').replace('-', '_'); + return id.replace('.', '_').replace('-', '_'); } private boolean initializeOnClick( String className, TypeElement type, Writer w, ProcessPage pp ) throws IOException { + boolean ok = true; TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType(); { //for (Element clazz : pe.getEnclosedElements()) { // if (clazz.getKind() != ElementKind.CLASS) { @@ -178,58 +422,27 @@ On oc = method.getAnnotation(On.class); if (oc != null) { for (String id : oc.id()) { + if (pp == null) { + error("id = " + id + " not found in HTML page.", method); + ok = false; + continue; + } if (pp.tagNameForId(id) == null) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method); - return false; + error("id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method); + ok = false; + continue; } ExecutableElement ee = (ExecutableElement)method; - StringBuilder params = new StringBuilder(); - { - boolean first = true; - for (VariableElement ve : ee.getParameters()) { - if (!first) { - params.append(", "); - } - first = false; - if (ve.asType() == stringType) { - if (ve.getSimpleName().contentEquals("id")) { - params.append('"').append(id).append('"'); - continue; - } - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(ev, \""); - params.append(ve.getSimpleName().toString()); - params.append("\")"); - continue; - } - if (processingEnv.getTypeUtils().getPrimitiveType(TypeKind.DOUBLE) == ve.asType()) { - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(ev, \""); - params.append(ve.getSimpleName().toString()); - params.append("\")"); - continue; - } - String rn = ve.asType().toString(); - int last = rn.lastIndexOf('.'); - if (last >= 0) { - rn = rn.substring(last + 1); - } - if (rn.equals(className)) { - params.append(className).append(".this"); - continue; - } - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "@On method can only accept String named 'id' or " + className + " arguments", - ee - ); - return false; - } - } + CharSequence params = wrapParams(ee, id, className, "ev", null); if (!ee.getModifiers().contains(Modifier.STATIC)) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee); - return false; + error("@On method has to be static", ee); + ok = false; + continue; } if (ee.getModifiers().contains(Modifier.PRIVATE)) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee); - return false; + error("@On method can't be private", ee); + ok = false; + continue; } w.append(" OnEvent." + oc.event()).append(".of(").append(cnstnt(id)). append(").perform(new OnDispatch(" + dispatchCnt + "));\n"); @@ -260,7 +473,7 @@ } - return true; + return ok; } @Override @@ -274,8 +487,7 @@ Element cls = findClass(element); Page p = cls.getAnnotation(Page.class); - PackageElement pe = (PackageElement) cls.getEnclosingElement(); - String pkg = pe.getQualifiedName().toString(); + String pkg = findPkgName(cls); ProcessPage pp; try { InputStream is = openStream(pkg, p.xhtml()); @@ -285,7 +497,7 @@ return Collections.emptyList(); } - List cc = new ArrayList(); + List cc = new ArrayList<>(); userText = userText.substring(1); for (String id : pp.ids()) { if (id.startsWith(userText)) { @@ -306,44 +518,89 @@ return e.getEnclosingElement(); } - private static void generateProperties( - Writer w, Property[] properties, Collection props, - Map> deps + private boolean generateProperties( + Element where, + Writer w, Prprt[] properties, + Collection props, + Map> deps, + Map> functionDeps ) throws IOException { - for (Property p : properties) { - final String tn = typeName(p); - String[] gs = toGetSet(p.name(), tn); + boolean ok = true; + for (Prprt p : properties) { + final String tn; + tn = typeName(where, p); + String[] gs = toGetSet(p.name(), tn, p.array()); - w.write("private " + tn + " prop_" + p.name() + ";\n"); - w.write("public " + tn + " " + gs[0] + "() {\n"); - w.write(" if (locked) throw new IllegalStateException();\n"); - w.write(" return prop_" + p.name() + ";\n"); - w.write("}\n"); - w.write("public void " + gs[1] + "(" + tn + " v) {\n"); - w.write(" if (locked) throw new IllegalStateException();\n"); - w.write(" prop_" + p.name() + " = v;\n"); - w.write(" if (ko != null) {\n"); - w.write(" ko.valueHasMutated(\"" + p.name() + "\");\n"); - final Collection dependants = deps.get(p.name()); - if (dependants != null) { - for (String depProp : dependants) { - w.write(" ko.valueHasMutated(\"" + depProp + "\");\n"); + if (p.array()) { + w.write("private KOList<" + tn + "> prop_" + p.name() + " = new KOList<" + tn + ">(\"" + + p.name() + "\""); + Collection dependants = deps.get(p.name()); + if (dependants != null) { + for (String depProp : dependants) { + w.write(", "); + w.write('\"'); + w.write(depProp); + w.write('\"'); + } } + w.write(")"); + + dependants = functionDeps.get(p.name()); + if (dependants != null) { + w.write(".onChange(new Runnable() { public void run() {\n"); + for (String call : dependants) { + w.append(call); + } + w.write("}})"); + } + w.write(";\n"); + + w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n"); + w.write(" if (locked) throw new IllegalStateException();\n"); + w.write(" prop_" + p.name() + ".assign(ko);\n"); + w.write(" return prop_" + p.name() + ";\n"); + w.write("}\n"); + } else { + w.write("private " + tn + " prop_" + p.name() + ";\n"); + w.write("public " + tn + " " + gs[0] + "() {\n"); + w.write(" if (locked) throw new IllegalStateException();\n"); + w.write(" return prop_" + p.name() + ";\n"); + w.write("}\n"); + w.write("public void " + gs[1] + "(" + tn + " v) {\n"); + w.write(" if (locked) throw new IllegalStateException();\n"); + w.write(" prop_" + p.name() + " = v;\n"); + w.write(" if (ko != null) {\n"); + w.write(" ko.valueHasMutated(\"" + p.name() + "\");\n"); + Collection dependants = deps.get(p.name()); + if (dependants != null) { + for (String depProp : dependants) { + w.write(" ko.valueHasMutated(\"" + depProp + "\");\n"); + } + } + w.write(" }\n"); + dependants = functionDeps.get(p.name()); + if (dependants != null) { + for (String call : dependants) { + w.append(call); + } + } + w.write("}\n"); } - w.write(" }\n"); - w.write("}\n"); props.add(p.name()); props.add(gs[2]); props.add(gs[3]); props.add(gs[0]); } + return ok; } private boolean generateComputedProperties( - Writer w, Collection arr, Collection props, + Writer w, Prprt[] fixedProps, + Collection arr, Collection props, Map> deps ) throws IOException { + boolean ok = true; for (Element e : arr) { if (e.getKind() != ElementKind.METHOD) { continue; @@ -352,30 +609,43 @@ continue; } ExecutableElement ee = (ExecutableElement)e; - final String tn = ee.getReturnType().toString(); + final TypeMirror rt = ee.getReturnType(); + final Types tu = processingEnv.getTypeUtils(); + TypeMirror ert = tu.erasure(rt); + String tn = fqn(ert, ee); + boolean array = false; + if (tn.equals("java.util.List")) { + array = true; + } + final String sn = ee.getSimpleName().toString(); - String[] gs = toGetSet(sn, tn); + String[] gs = toGetSet(sn, tn, array); w.write("public " + tn + " " + gs[0] + "() {\n"); w.write(" if (locked) throw new IllegalStateException();\n"); int arg = 0; for (VariableElement pe : ee.getParameters()) { final String dn = pe.getSimpleName().toString(); - final String dt = pe.asType().toString(); - String[] call = toGetSet(dn, dt); + + if (!verifyPropName(pe, dn, fixedProps)) { + ok = false; + } + + final String dt = fqn(pe.asType(), ee); + String[] call = toGetSet(dn, dt, false); w.write(" " + dt + " arg" + (++arg) + " = "); w.write(call[0] + "();\n"); Collection depends = deps.get(dn); if (depends == null) { - depends = new LinkedHashSet(); + depends = new LinkedHashSet<>(); deps.put(dn, depends); } depends.add(sn); } w.write(" try {\n"); w.write(" locked = true;\n"); - w.write(" return " + e.getEnclosingElement().getSimpleName() + '.' + e.getSimpleName() + "("); + w.write(" return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "("); String sep = ""; for (int i = 1; i <= arg; i++) { w.write(sep); @@ -387,17 +657,17 @@ w.write(" locked = false;\n"); w.write(" }\n"); w.write("}\n"); - + props.add(e.getSimpleName().toString()); props.add(gs[2]); props.add(null); props.add(gs[0]); } - return true; + return ok; } - private static String[] toGetSet(String name, String type) { + private static String[] toGetSet(String name, String type, boolean array) { String n = Character.toUpperCase(name.charAt(0)) + name.substring(1); String bck2brwsrType = "L" + type.replace('.', '_') + "_2"; if ("int".equals(type)) { @@ -412,6 +682,14 @@ bck2brwsrType = "Z"; } final String nu = n.replace('.', '_'); + if (array) { + return new String[] { + "get" + n, + null, + "get" + nu + "__Ljava_util_List_2", + null + }; + } return new String[]{ pref + n, "set" + n, @@ -420,11 +698,702 @@ }; } - private static String typeName(Property p) { - try { - return p.type().getName(); - } catch (MirroredTypeException ex) { - return ex.getTypeMirror().toString(); + private String typeName(Element where, Prprt p) { + String ret; + boolean[] isModel = { false }; + boolean[] isEnum = { false }; + boolean isPrimitive[] = { false }; + ret = checkType(p, isModel, isEnum, isPrimitive); + if (p.array()) { + String bt = findBoxedType(ret); + if (bt != null) { + return bt; + } + } + return ret; + } + + private static String findBoxedType(String ret) { + if (ret.equals("boolean")) { + return Boolean.class.getName(); + } + if (ret.equals("byte")) { + return Byte.class.getName(); + } + if (ret.equals("short")) { + return Short.class.getName(); + } + if (ret.equals("char")) { + return Character.class.getName(); + } + if (ret.equals("int")) { + return Integer.class.getName(); + } + if (ret.equals("long")) { + return Long.class.getName(); + } + if (ret.equals("float")) { + return Float.class.getName(); + } + if (ret.equals("double")) { + return Double.class.getName(); + } + return null; + } + + private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) { + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (Prprt Prprt : existingProps) { + if (Prprt.name().equals(propName)) { + return true; + } + sb.append(sep); + sb.append('"'); + sb.append(Prprt.name()); + sb.append('"'); + sep = ", "; + } + error( + propName + " is not one of known properties: " + sb + , e + ); + return false; + } + + private static String findPkgName(Element e) { + for (;;) { + if (e.getKind() == ElementKind.PACKAGE) { + return ((PackageElement)e).getQualifiedName().toString(); + } + e = e.getEnclosingElement(); } } + + private boolean generateFunctions( + Element clazz, StringWriter body, String className, + List enclosedElements, List functions + ) { + for (Element m : enclosedElements) { + if (m.getKind() != ElementKind.METHOD) { + continue; + } + ExecutableElement e = (ExecutableElement)m; + OnFunction onF = e.getAnnotation(OnFunction.class); + if (onF == null) { + continue; + } + if (!e.getModifiers().contains(Modifier.STATIC)) { + error("@OnFunction method needs to be static", e); + return false; + } + if (e.getModifiers().contains(Modifier.PRIVATE)) { + error("@OnFunction method cannot be private", e); + return false; + } + if (e.getReturnType().getKind() != TypeKind.VOID) { + error("@OnFunction method should return void", e); + return false; + } + String n = e.getSimpleName().toString(); + body.append("private void ").append(n).append("(Object data, Object ev) {\n"); + body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("("); + body.append(wrapParams(e, null, className, "ev", "data")); + body.append(");\n"); + body.append("}\n"); + + functions.add(n); + functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2"); + } + return true; + } + + private boolean generateOnChange(Element clazz, Map> propDeps, + Prprt[] properties, String className, + Map> functionDeps + ) { + for (Element m : clazz.getEnclosedElements()) { + if (m.getKind() != ElementKind.METHOD) { + continue; + } + ExecutableElement e = (ExecutableElement) m; + OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class); + if (onPC == null) { + continue; + } + for (String pn : onPC.value()) { + if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) { + error("No Prprt named '" + pn + "' in the model", clazz); + return false; + } + } + if (!e.getModifiers().contains(Modifier.STATIC)) { + error("@OnPrprtChange method needs to be static", e); + return false; + } + if (e.getModifiers().contains(Modifier.PRIVATE)) { + error("@OnPrprtChange method cannot be private", e); + return false; + } + if (e.getReturnType().getKind() != TypeKind.VOID) { + error("@OnPrprtChange method should return void", e); + return false; + } + String n = e.getSimpleName().toString(); + + + for (String pn : onPC.value()) { + StringBuilder call = new StringBuilder(); + call.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("("); + call.append(wrapPropName(e, className, "name", pn)); + call.append(");\n"); + + Collection change = functionDeps.get(pn); + if (change == null) { + change = new ArrayList<>(); + functionDeps.put(pn, change); + } + change.add(call.toString()); + for (String dpn : findDerivedFrom(propDeps, pn)) { + change = functionDeps.get(dpn); + if (change == null) { + change = new ArrayList<>(); + functionDeps.put(dpn, change); + } + change.add(call.toString()); + } + } + } + return true; + } + + private boolean generateReceive( + Element clazz, StringWriter body, String className, + List enclosedElements, List functions + ) { + for (Element m : enclosedElements) { + if (m.getKind() != ElementKind.METHOD) { + continue; + } + ExecutableElement e = (ExecutableElement)m; + OnReceive onR = e.getAnnotation(OnReceive.class); + if (onR == null) { + continue; + } + if (!e.getModifiers().contains(Modifier.STATIC)) { + error("@OnReceive method needs to be static", e); + return false; + } + if (e.getModifiers().contains(Modifier.PRIVATE)) { + error("@OnReceive method cannot be private", e); + return false; + } + if (e.getReturnType().getKind() != TypeKind.VOID) { + error("@OnReceive method should return void", e); + return false; + } + String modelClass = null; + boolean expectsList = false; + List args = new ArrayList<>(); + { + for (VariableElement ve : e.getParameters()) { + TypeMirror modelType = null; + if (ve.asType().toString().equals(className)) { + args.add(className + ".this"); + } else if (isModel(ve.asType())) { + modelType = ve.asType(); + } else if (ve.asType().getKind() == TypeKind.ARRAY) { + modelType = ((ArrayType)ve.asType()).getComponentType(); + expectsList = true; + } + if (modelType != null) { + if (modelClass != null) { + error("There can be only one model class among arguments", e); + } else { + modelClass = modelType.toString(); + if (expectsList) { + args.add("arr"); + } else { + args.add("arr[0]"); + } + } + } + } + } + if (modelClass == null) { + error("The method needs to have one @Model class as parameter", e); + } + String n = e.getSimpleName().toString(); + body.append("public void ").append(n).append("("); + StringBuilder assembleURL = new StringBuilder(); + String jsonpVarName = null; + { + String sep = ""; + boolean skipJSONP = onR.jsonp().isEmpty(); + for (String p : findParamNames(e, onR.url(), assembleURL)) { + if (!skipJSONP && p.equals(onR.jsonp())) { + skipJSONP = true; + jsonpVarName = p; + continue; + } + body.append(sep); + body.append("String ").append(p); + sep = ", "; + } + if (!skipJSONP) { + error( + "Name of jsonp attribute ('" + onR.jsonp() + + "') is not used in url attribute '" + onR.url() + "'", e + ); + } + } + body.append(") {\n"); + body.append(" final Object[] result = { null };\n"); + body.append( + " class ProcessResult implements Runnable {\n" + + " @Override\n" + + " public void run() {\n" + + " Object value = result[0];\n"); + body.append( + " " + modelClass + "[] arr;\n"); + body.append( + " if (value instanceof Object[]) {\n" + + " Object[] data = ((Object[])value);\n" + + " arr = new " + modelClass + "[data.length];\n" + + " for (int i = 0; i < data.length; i++) {\n" + + " arr[i] = new " + modelClass + "(data[i]);\n" + + " }\n" + + " } else {\n" + + " arr = new " + modelClass + "[1];\n" + + " arr[0] = new " + modelClass + "(value);\n" + + " }\n" + ); + { + body.append(clazz.getSimpleName()).append(".").append(n).append("("); + String sep = ""; + for (String arg : args) { + body.append(sep); + body.append(arg); + sep = ", "; + } + body.append(");\n"); + } + body.append( + " }\n" + + " }\n" + ); + body.append(" ProcessResult pr = new ProcessResult();\n"); + if (jsonpVarName != null) { + body.append(" String ").append(jsonpVarName). + append(" = org.apidesign.bck2brwsr.htmlpage.ConvertTypes.createJSONP(result, pr);\n"); + } + body.append(" org.apidesign.bck2brwsr.htmlpage.ConvertTypes.loadJSON(\n "); + body.append(assembleURL); + body.append(", result, pr, ").append(jsonpVarName).append("\n );\n"); +// body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("("); +// body.append(wrapParams(e, null, className, "ev", "data")); +// body.append(");\n"); + body.append("}\n"); + } + return true; + } + + private CharSequence wrapParams( + ExecutableElement ee, String id, String className, String evName, String dataName + ) { + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType(); + StringBuilder params = new StringBuilder(); + boolean first = true; + for (VariableElement ve : ee.getParameters()) { + if (!first) { + params.append(", "); + } + first = false; + String toCall = null; + if (ve.asType() == stringType) { + if (ve.getSimpleName().contentEquals("id")) { + params.append('"').append(id).append('"'); + continue; + } + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString("; + } + if (ve.asType().getKind() == TypeKind.DOUBLE) { + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble("; + } + if (ve.asType().getKind() == TypeKind.INT) { + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt("; + } + if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) { + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toModel(" + ve.asType() + ".class, "; + } + + if (toCall != null) { + params.append(toCall); + if (dataName != null && ve.getSimpleName().contentEquals(dataName)) { + params.append(dataName); + params.append(", null"); + } else { + if (evName == null) { + final StringBuilder sb = new StringBuilder(); + sb.append("Unexpected string parameter name."); + if (dataName != null) { + sb.append(" Try \"").append(dataName).append("\""); + } + error(sb.toString(), ee); + } + params.append(evName); + params.append(", \""); + params.append(ve.getSimpleName().toString()); + params.append("\""); + } + params.append(")"); + continue; + } + String rn = fqn(ve.asType(), ee); + int last = rn.lastIndexOf('.'); + if (last >= 0) { + rn = rn.substring(last + 1); + } + if (rn.equals(className)) { + params.append(className).append(".this"); + continue; + } + error( + "@On method can only accept String named 'id' or " + className + " arguments", + ee + ); + } + return params; + } + + + private CharSequence wrapPropName( + ExecutableElement ee, String className, String propName, String propValue + ) { + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType(); + StringBuilder params = new StringBuilder(); + boolean first = true; + for (VariableElement ve : ee.getParameters()) { + if (!first) { + params.append(", "); + } + first = false; + if (ve.asType() == stringType) { + if (propName != null && ve.getSimpleName().contentEquals(propName)) { + params.append('"').append(propValue).append('"'); + } else { + error("Unexpected string parameter name. Try \"" + propName + "\".", ee); + } + continue; + } + String rn = fqn(ve.asType(), ee); + int last = rn.lastIndexOf('.'); + if (last >= 0) { + rn = rn.substring(last + 1); + } + if (rn.equals(className)) { + params.append(className).append(".this"); + continue; + } + error( + "@OnPrprtChange method can only accept String or " + className + " arguments", + ee); + } + return params; + } + + private boolean isModel(TypeMirror tm) { + final Element e = processingEnv.getTypeUtils().asElement(tm); + if (e == null) { + return false; + } + for (Element ch : e.getEnclosedElements()) { + if (ch.getKind() == ElementKind.METHOD) { + ExecutableElement ee = (ExecutableElement)ch; + if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) { + return true; + } + } + } + return models.values().contains(e.getSimpleName().toString()); + } + + private void writeStringArray(List strings, Writer w) throws IOException { + w.write("new String[] {\n"); + String sep = ""; + for (String n : strings) { + w.write(sep); + if (n == null) { + w.write(" null"); + } else { + w.write(" \"" + n + "\""); + } + sep = ",\n"; + } + w.write("\n }"); + } + + private void writeToString(Prprt[] props, Writer w) throws IOException { + w.write(" public String toString() {\n"); + w.write(" StringBuilder sb = new StringBuilder();\n"); + w.write(" sb.append('{');\n"); + String sep = ""; + for (Prprt p : props) { + w.write(sep); + w.append(" sb.append('\"').append(\"" + p.name() + "\")"); + w.append(".append('\"').append(\":\");\n"); + w.append(" sb.append(org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toJSON(prop_"); + w.append(p.name()).append("));\n"); + sep = " sb.append(',');\n"; + } + w.write(" sb.append('}');\n"); + w.write(" return sb.toString();\n"); + w.write(" }\n"); + } + private void writeClone(String className, Prprt[] props, Writer w) throws IOException { + w.write(" public " + className + " clone() {\n"); + w.write(" " + className + " ret = new " + className + "();\n"); + for (Prprt p : props) { + if (!p.array()) { + boolean isModel[] = { false }; + boolean isEnum[] = { false }; + boolean isPrimitive[] = { false }; + checkType(p, isModel, isEnum, isPrimitive); + if (!isModel[0]) { + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ";\n"); + continue; + } + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n"); + } else { + w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n"); + } + } + + w.write(" return ret;\n"); + w.write(" }\n"); + } + + private String inPckName(Element e) { + StringBuilder sb = new StringBuilder(); + while (e.getKind() != ElementKind.PACKAGE) { + if (sb.length() == 0) { + sb.append(e.getSimpleName()); + } else { + sb.insert(0, '.'); + sb.insert(0, e.getSimpleName()); + } + e = e.getEnclosingElement(); + } + return sb.toString(); + } + + private String fqn(TypeMirror pt, Element relative) { + if (pt.getKind() == TypeKind.ERROR) { + final Elements eu = processingEnv.getElementUtils(); + PackageElement pckg = eu.getPackageOf(relative); + return pckg.getQualifiedName() + "." + pt.toString(); + } + return pt.toString(); + } + + private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) { + TypeMirror tm; + try { + String ret = p.typeName(processingEnv); + TypeElement e = processingEnv.getElementUtils().getTypeElement(ret); + if (e == null) { + isModel[0] = true; + isEnum[0] = false; + isPrimitive[0] = false; + return ret; + } + tm = e.asType(); + } catch (MirroredTypeException ex) { + tm = ex.getTypeMirror(); + } + tm = processingEnv.getTypeUtils().erasure(tm); + isPrimitive[0] = tm.getKind().isPrimitive(); + final Element e = processingEnv.getTypeUtils().asElement(tm); + final Model m = e == null ? null : e.getAnnotation(Model.class); + + String ret; + if (m != null) { + ret = findPkgName(e) + '.' + m.className(); + isModel[0] = true; + models.put(e, m.className()); + } else if (findModelForMthd(e)) { + ret = ((TypeElement)e).getQualifiedName().toString(); + isModel[0] = true; + } else { + ret = tm.toString(); + } + TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType(); + enm = processingEnv.getTypeUtils().erasure(enm); + isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm); + return ret; + } + + private static boolean findModelForMthd(Element clazz) { + if (clazz == null) { + return false; + } + for (Element e : clazz.getEnclosedElements()) { + if (e.getKind() == ElementKind.METHOD) { + ExecutableElement ee = (ExecutableElement)e; + if ( + ee.getSimpleName().contentEquals("modelFor") && + ee.getParameters().isEmpty() + ) { + return true; + } + } + } + return false; + } + + private Iterable findParamNames(Element e, String url, StringBuilder assembleURL) { + List params = new ArrayList<>(); + + for (int pos = 0; ;) { + int next = url.indexOf('{', pos); + if (next == -1) { + assembleURL.append('"') + .append(url.substring(pos)) + .append('"'); + return params; + } + int close = url.indexOf('}', next); + if (close == -1) { + error("Unbalanced '{' and '}' in " + url, e); + return params; + } + final String paramName = url.substring(next + 1, close); + params.add(paramName); + assembleURL.append('"') + .append(url.substring(pos, next)) + .append("\" + ").append(paramName).append(" + "); + pos = close + 1; + } + } + + private static Prprt findPrprt(Prprt[] properties, String propName) { + for (Prprt p : properties) { + if (propName.equals(p.name())) { + return p; + } + } + return null; + } + + private boolean isPrimitive(String type) { + return + "int".equals(type) || + "double".equals(type) || + "long".equals(type) || + "short".equals(type) || + "byte".equals(type) || + "float".equals(type); + } + + private static Collection findDerivedFrom(Map> propsDeps, String derivedProp) { + Set names = new HashSet<>(); + for (Map.Entry> e : propsDeps.entrySet()) { + if (e.getValue().contains(derivedProp)) { + names.add(e.getKey()); + } + } + return names; + } + + private Prprt[] createProps(Element e, Property[] arr) { + Prprt[] ret = Prprt.wrap(processingEnv, e, arr); + Prprt[] prev = verify.put(e, ret); + if (prev != null) { + error("Two sets of properties for ", e); + } + return ret; + } + + private static class Prprt { + private final Element e; + private final AnnotationMirror tm; + private final Property p; + + public Prprt(Element e, AnnotationMirror tm, Property p) { + this.e = e; + this.tm = tm; + this.p = p; + } + + String name() { + return p.name(); + } + + boolean array() { + return p.array(); + } + + String typeName(ProcessingEnvironment env) { + try { + return p.type().getName(); + } catch (IncompleteAnnotationException | AnnotationTypeMismatchException ex) { + for (Object v : getAnnoValues(env)) { + String s = v.toString().replace(" ", ""); + if (s.startsWith("type=") && s.endsWith(".class")) { + return s.substring(5, s.length() - 6); + } + } + throw ex; + } + } + + + static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) { + if (arr.length == 0) { + return new Prprt[0]; + } + + if (e.getKind() != ElementKind.CLASS) { + throw new IllegalStateException("" + e.getKind()); + } + TypeElement te = (TypeElement)e; + List val = null; + for (AnnotationMirror an : te.getAnnotationMirrors()) { + for (Map.Entry entry : an.getElementValues().entrySet()) { + if (entry.getKey().getSimpleName().contentEquals("properties")) { + val = (List)entry.getValue().getValue(); + break; + } + } + } + if (val == null || val.size() != arr.length) { + pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e); + return new Prprt[0]; + } + Prprt[] ret = new Prprt[arr.length]; + BIG: for (int i = 0; i < ret.length; i++) { + AnnotationMirror am = (AnnotationMirror)val.get(i).getValue(); + ret[i] = new Prprt(e, am, arr[i]); + + } + return ret; + } + + private List getAnnoValues(ProcessingEnvironment pe) { + try { + Class trees = Class.forName("com.sun.tools.javac.api.JavacTrees"); + Method m = trees.getMethod("instance", ProcessingEnvironment.class); + Object instance = m.invoke(null, pe); + m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class); + Object path = m.invoke(instance, e, tm); + m = path.getClass().getMethod("getLeaf"); + Object leaf = m.invoke(path); + m = leaf.getClass().getMethod("getArguments"); + return (List)m.invoke(leaf); + } catch (Exception ex) { + return Collections.emptyList(); + } + } + } + } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PredefinedFields.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PredefinedFields.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.htmlpage; - -import java.io.IOException; -import java.io.Writer; -import java.util.HashMap; -import java.util.Map; - -/** - * - * @author Jan Horvath - */ -public class PredefinedFields { - - private static final Map IMPORTS = new HashMap() { - { - put("canvas", "import org.apidesign.bck2brwsr.core.JavaScriptBody;"); - } - }; - - private static final Map FIELDS = new HashMap() { - { - put("canvas", - " @JavaScriptBody(\n" + - " args = {\"el\"},\n" + - " body = \"var e = window.document.getElementById(el._id());\\n\"\n" + - " + \"return e.getContext('2d');\\n\")\n" + - " private native static Object getContextImpl(Canvas el);\n" + - " \n" + - " public GraphicsContext getContext() {\n" + - " return new GraphicsContext(getContextImpl(this));\n" + - " }"); - } - }; - - static void appendImports(Writer w, String tag) throws IOException { - String text = IMPORTS.get(tag.toLowerCase()); - if (text != null) { - w.append(text).append("\n"); - } - } - - static void appendFields(Writer w, String tag) throws IOException { - String text = FIELDS.get(tag.toLowerCase()); - if (text != null) { - w.append(text).append("\n"); - } - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Button.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Button.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +/** + * + * @author Jaroslav Tulach + */ +public final class Button extends Element { + public Button(String id) { + super(id); + } + + @Override + void dontSubclass() { + } + + public void setDisabled(boolean state) { + setAttribute(this, "disabled", state); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Canvas.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Canvas.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,64 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import static org.apidesign.bck2brwsr.htmlpage.api.Element.getAttribute; + +/** + * + * @author Anton Epple + */ +public class Canvas extends Element { + + public Canvas(String id) { + super(id); + } + + public void setHeight(int height) { + setAttribute(this, "height", height); + } + + public int getHeight() { + Object ret = getAttribute(this, "height"); + return (ret instanceof Number) ? ((Number)ret).intValue(): Integer.MIN_VALUE; + } + + public void setWidth(int width) { + setAttribute(this, "width", width); + } + + public int getWidth() { + Object ret = getAttribute(this, "width"); + return (ret instanceof Number) ? ((Number)ret).intValue(): Integer.MIN_VALUE; + } + + @JavaScriptBody( + args = {"el"}, + body = "var e = window.document.getElementById(el._id());\n" + + "return e.getContext('2d');\n") + private native static Object getContextImpl(Canvas el); + + public GraphicsContext getContext() { + return new GraphicsContext(getContextImpl(this)); + } + + @Override + void dontSubclass() { + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/ComputedProperty.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/ComputedProperty.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/ComputedProperty.java Thu May 02 09:18:22 2013 +0200 @@ -22,16 +22,11 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** Can be used in classes annotated with {@link Page} annotation to - * define a derived property. Value of derived property is based on values - * of {@link Property} as enumerated by {@link Page#properties()}. - *

- * The name of the derived property is the name of the method. The arguments - * of the method define the property names (from {@link Page#properties()} list) - * the value of property depends on. - * +/** + * @deprecated Replaced by new {@link net.java.html.json.ComputedProperty net.java.html.json} API. * @author Jaroslav Tulach */ +@Deprecated @Retention(RetentionPolicy.SOURCE) @Target(ElementType.METHOD) public @interface ComputedProperty { diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Thu May 02 09:18:22 2013 +0200 @@ -37,13 +37,7 @@ return id; } - public String getText() { - return (String)getAttribute("innerHTML"); - } - - public void setText(String text) { - setAttribute("innerHTML", text); - } + abstract void dontSubclass(); @JavaScriptBody( args={"el", "property", "value"}, diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java Thu May 02 09:18:22 2013 +0200 @@ -27,7 +27,7 @@ Object context; - public GraphicsContext(Object contextImpl) { + GraphicsContext(Object contextImpl) { this.context = contextImpl; } @@ -113,15 +113,15 @@ @JavaScriptBody(args = {"x", "y"}, body = "this._context().scale(x,y);") public native void scale(double x, double y); - public void drawImage(Element image, double x, double y) { + public void drawImage(Image image, double x, double y) { drawImageImpl(context, Element.getElementById(image), x, y); } - public void drawImage(Element image, double x, double y, double width, double height) { + public void drawImage(Image image, double x, double y, double width, double height) { drawImageImpl(context, Element.getElementById(image), x, y, width, height); } - public void drawImage(Element image, double sx, double sy, double sWidth, double sHeight, double x, double y, double width, double height) { + public void drawImage(Image image, double sx, double sy, double sWidth, double sHeight, double x, double y, double width, double height) { drawImageImpl(context, Element.getElementById(image), sx, sy, sWidth, sHeight, x, y, width, height); } @@ -319,12 +319,12 @@ @JavaScriptBody(args = {"context", "x0", "y0", "x1", "y1"}, body = "return context.createLinearGradient(x0,y0,x1,y1);") private native Object createLinearGradientImpl(Object context, double x0, double y0, double x1, double y1); - public Pattern createPattern(Element image, String repeat) { + public Pattern createPattern(Image image, String repeat) { return new Pattern(createPatternImpl(context, image, repeat)); } @JavaScriptBody(args = {"context", "image", "repeat"}, body = "return context.createPattern(image, repeat);") - private static native Object createPatternImpl(Object context, Element image, String repeat); + private static native Object createPatternImpl(Object context, Image image, String repeat); public RadialGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1) { return new RadialGradient(createRadialGradientImpl(context, x0, y0, r0, x1, y1, r1)); diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Image.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Image.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +/** + * + * @author Anton Epple + */ +public class Image extends Element{ + + public Image(String id) { + super(id); + } + + + + @Override + void dontSubclass() { + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Input.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Input.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,44 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +/** + * + * @author Jaroslav Tulach + */ +public final class Input extends Element { + public Input(String id) { + super(id); + } + + @Override + void dontSubclass() { + } + + public void setAutocomplete(boolean state) { + setAttribute(this, "autocomplete", state); + } + + public final String getValue() { + return (String)getAttribute(this, "value"); + } + + public final void setValue(String txt) { + setAttribute(this, "value", txt); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Model.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Model.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,38 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @deprecated Replaced by new {@link net.java.html.json.Model net.java.html.json} API. + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +@Deprecated +public @interface Model { + /** Name of the model class */ + String className(); + /** List of properties in the model. + */ + Property[] properties(); +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnFunction.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnFunction.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,33 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @deprecated Replaced by new {@link net.java.html.json.Function net.java.html.json} API. + * @author Jaroslav Tulach + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +@Deprecated +public @interface OnFunction { +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnPropertyChange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnPropertyChange.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,38 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @deprecated Replaced by new {@link net.java.html.json.OnPropertyChange net.java.html.json} API. + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +@Deprecated +public @interface OnPropertyChange { + /** Name(s) of the properties. One wishes to observe. + * + * @return valid java identifier + */ + String[] value(); +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,53 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @deprecated Replaced by new {@link net.java.html.json.OnReceive net.java.html.json} API. + * @author Jaroslav Tulach + * @since 0.6 + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +@Deprecated +public @interface OnReceive { + /** The URL to connect to. Can contain variable names surrounded by '{' and '}'. + * Those parameters will then become variables of the associated method. + * + * @return the (possibly parametrized) url to connect to + */ + String url(); + + /** Support for JSONP requires + * a callback from the server generated page to a function defined in the + * system. The name of such function is usually specified as a property + * (of possibly different names). By defining the jsonp attribute + * one turns on the JSONP + * transmission and specifies the name of the property. The property should + * also be used in the {@link #url()} attribute on appropriate place. + * + * @return name of a property to carry the name of JSONP + * callback function. + */ + String jsonp() default ""; +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Property.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Property.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Property.java Thu May 02 09:18:22 2013 +0200 @@ -20,15 +20,36 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.List; -/** Represents a property in a generated model of an HTML - * {@link Page}. - * +/** + * @deprecated Replaced by new {@link net.java.html.json.Property net.java.html.json} API. * @author Jaroslav Tulach */ @Retention(RetentionPolicy.SOURCE) @Target({}) +@Deprecated public @interface Property { + /** Name of the property. Will be used to define proper getter and setter + * in the associated class. + * + * @return valid java identifier + */ String name(); + + /** Type of the property. Can either be primitive type (like int.class, + * double.class, etc.), {@link String} or complex model + * class (defined by {@link Model} property). + * + * @return the class of the property + */ Class type(); + + /** Is this property an array of the {@link #type()} or a single value? + * If the property is an array, only its getter (returning mutable {@link List} of + * the boxed {@link #type()}). + * + * @return true, if this is supposed to be an array of values. + */ + boolean array() default false; } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Title.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Title.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +/** + * + * @author Jaroslav Tulach + */ +public class Title extends Element { + public Title(String id) { + super(id); + } + + @Override + void dontSubclass() { + } + + public final void setText(String text) { + setAttribute(this, "innerHTML", text); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js --- a/javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Thu May 02 09:18:22 2013 +0200 @@ -2193,7 +2193,14 @@ else element[attrName] = attrValue; } else if (!toRemove) { - element.setAttribute(attrName, attrValue.toString()); + try { + element.setAttribute(attrName, attrValue.toString()); + } catch (err) { + // ignore for now + if (console) { + console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err); + } + } } // Treat "name" specially - although you can think of it as an attribute, it also needs diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,216 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticListener; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; + +/** + * + * @author Jaroslav Tulach + */ +final class Compile implements DiagnosticListener { + private final List> errors = new ArrayList<>(); + private final Map classes; + private final String pkg; + private final String cls; + private final String html; + + private Compile(String html, String code) throws IOException { + this.pkg = findPkg(code); + this.cls = findCls(code); + this.html = html; + classes = compile(html, code); + } + + /** Performs compilation of given HTML page and associated Java code + */ + public static Compile create(String html, String code) throws IOException { + return new Compile(html, code); + } + + /** Checks for given class among compiled resources */ + public byte[] get(String res) { + return classes.get(res); + } + + /** Obtains errors created during compilation. + */ + public List> getErrors() { + List> err = new ArrayList<>(); + for (Diagnostic diagnostic : errors) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + err.add(diagnostic); + } + } + return err; + } + + private Map compile(final String html, final String code) throws IOException { + StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null); + + final Map class2BAOS = new HashMap<>(); + + JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return code; + } + }; + final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return html; + } + + @Override + public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(html.getBytes()); + } + }; + + final URI scratch; + try { + scratch = new URI("mem://mem3"); + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + + JavaFileManager jfm = new ForwardingJavaFileManager(sjfm) { + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { + if (kind == Kind.CLASS) { + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + class2BAOS.put(className.replace('.', '/') + ".class", buffer); + return new SimpleJavaFileObject(sibling.toUri(), kind) { + @Override + public OutputStream openOutputStream() throws IOException { + return buffer; + } + }; + } + + if (kind == Kind.SOURCE) { + return new SimpleJavaFileObject(scratch/*sibling.toUri()*/, kind) { + private final ByteArrayOutputStream data = new ByteArrayOutputStream(); + @Override + public OutputStream openOutputStream() throws IOException { + return data; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + data.close(); + return new String(data.toByteArray()); + } + }; + } + + throw new IllegalStateException(); + } + + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + if (location == StandardLocation.SOURCE_PATH) { + if (packageName.equals(pkg)) { + return htmlFile; + } + } + + return null; + } + + @Override + public boolean isSameFile(FileObject a, FileObject b) { + if (a == null || b == null) { + throw new NullPointerException(); + } + if (!(a instanceof SimpleJavaFileObject)) { + throw new IllegalArgumentException("Not supported: " + a); + } + if (!(b instanceof SimpleJavaFileObject)) { + throw new IllegalArgumentException("Not supported: " + b); + } + return a.equals(b); + } + }; + + ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call(); + + Map result = new HashMap<>(); + + for (Map.Entry e : class2BAOS.entrySet()) { + result.put(e.getKey(), e.getValue().toByteArray()); + } + + return result; + } + + + @Override + public void report(Diagnostic diagnostic) { + errors.add(diagnostic); + } + private static String findPkg(String java) throws IOException { + Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE); + Matcher m = p.matcher(java); + if (!m.find()) { + throw new IOException("Can't find package declaration in the java file"); + } + String pkg = m.group(1); + return pkg; + } + private static String findCls(String java) throws IOException { + Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE); + Matcher m = p.matcher(java); + if (!m.find()) { + throw new IOException("Can't find package declaration in the java file"); + } + String cls = m.group(1); + return cls; + } + + String getHtml() { + String fqn = "'" + pkg + '.' + cls + "'"; + return html.replace("'${fqn}'", fqn); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,63 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ConvertTypesTest { + @JavaScriptBody(args = { "includeSex" }, body = "var json = new Object();" + + "json.firstName = 'son';\n" + + "json.lastName = 'dj';\n" + + "if (includeSex) json.sex = 'MALE';\n" + + "return json;" + ) + private static native Object createJSON(boolean includeSex); + + @BrwsrTest + public void testConvertToPeople() throws Exception { + final Object o = createJSON(true); + + Person p = new Person(o); + + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName(); + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName(); + assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex(); + } + + @BrwsrTest + public void testConvertToPeopleWithoutSex() throws Exception { + final Object o = createJSON(false); + + Person p = new Person(o); + + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName(); + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName(); + assert p.getSex() == null : "No sex: " + p.getSex(); + } + + @Factory public static Object[] create() { + return VMTest.create(ConvertTypesTest.class); + } +} \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ElementGeneratorTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ElementGeneratorTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.htmlpage; - -import java.util.Map; -import static org.testng.Assert.*; -import org.testng.annotations.Test; - -/** - * - * @author Jan Horvath - */ -public class ElementGeneratorTest { - - @Test public void testGetAttributes() { - ElementGenerator gen = new ElementGenerator(null); - Map attrs = gen.getAttributes("input"); - assertEquals(attrs.get("width"), "Integer", "Expected type of width attribute is Integer"); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,392 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +import java.util.Arrays; +import java.util.Iterator; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.htmlpage.api.OnReceive; +import org.apidesign.bck2brwsr.htmlpage.api.Page; +import org.apidesign.bck2brwsr.htmlpage.api.Property; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Http; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.testng.annotations.Test; +import static org.testng.Assert.*; +import org.testng.annotations.Factory; + +/** Need to verify that models produce reasonable JSON objects. + * + * @author Jaroslav Tulach + */ +@Page(xhtml = "Empty.html", className = "JSONik", properties = { + @Property(name = "fetched", type = PersonImpl.class), + @Property(name = "fetchedCount", type = int.class), + @Property(name = "fetchedSex", type = Sex.class, array = true) +}) +public class JSONTest { + private JSONik js; + private Integer orig; + + @Test public void personToString() throws JSONException { + Person p = new Person(); + p.setSex(Sex.MALE); + p.setFirstName("Jarda"); + p.setLastName("Tulach"); + + JSONTokener t = new JSONTokener(p.toString()); + JSONObject o; + try { + o = new JSONObject(t); + } catch (JSONException ex) { + throw new AssertionError("Can't parse " + p.toString(), ex); + } + + Iterator it = o.sortedKeys(); + assertEquals(it.next(), "firstName"); + assertEquals(it.next(), "lastName"); + assertEquals(it.next(), "sex"); + + assertEquals(o.getString("firstName"), "Jarda"); + assertEquals(o.getString("lastName"), "Tulach"); + assertEquals(o.getString("sex"), "MALE"); + } + + @BrwsrTest public void toJSONInABrowser() throws Throwable { + Person p = new Person(); + p.setSex(Sex.MALE); + p.setFirstName("Jarda"); + p.setLastName("Tulach"); + + Object json; + try { + json = parseJSON(p.toString()); + } catch (Throwable ex) { + throw new IllegalStateException("Can't parse " + p).initCause(ex); + } + + Person p2 = new Person(json); + + assert p2.getFirstName().equals(p.getFirstName()) : + "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName(); + } + + @Test public void personWithWildCharactersAndNulls() throws JSONException { + Person p = new Person(); + p.setFirstName("'\"\n"); + p.setLastName("\t\r\u0002"); + + JSONTokener t = new JSONTokener(p.toString()); + JSONObject o; + try { + o = new JSONObject(t); + } catch (JSONException ex) { + throw new AssertionError("Can't parse " + p.toString(), ex); + } + + Iterator it = o.sortedKeys(); + assertEquals(it.next(), "firstName"); + assertEquals(it.next(), "lastName"); + assertEquals(it.next(), "sex"); + + assertEquals(o.getString("firstName"), p.getFirstName()); + assertEquals(o.getString("lastName"), p.getLastName()); + assertEquals(o.get("sex"), JSONObject.NULL); + } + + @Test public void personsInArray() throws JSONException { + Person p1 = new Person(); + p1.setFirstName("One"); + + Person p2 = new Person(); + p2.setFirstName("Two"); + + People arr = new People(); + arr.getInfo().add(p1); + arr.getInfo().add(p2); + arr.getNicknames().add("Prvn\u00ed k\u016f\u0148"); + final String n2 = "Druh\u00fd hlem\u00fd\u017e\u010f, star\u0161\u00ed"; + arr.getNicknames().add(n2); + arr.getAge().add(33); + arr.getAge().add(73); + + + final String json = arr.toString(); + + JSONTokener t = new JSONTokener(json); + JSONObject o; + try { + o = new JSONObject(t); + } catch (JSONException ex) { + throw new AssertionError("Can't parse " + json, ex); + } + + assertEquals(o.getJSONArray("info").getJSONObject(0).getString("firstName"), "One"); + assertEquals(o.getJSONArray("nicknames").getString(1), n2); + assertEquals(o.getJSONArray("age").getInt(1), 73); + } + + + @OnReceive(url="/{url}") + static void fetch(Person p, JSONik model) { + model.setFetched(p); + } + + @OnReceive(url="/{url}") + static void fetchArray(Person[] p, JSONik model) { + model.setFetchedCount(p.length); + model.setFetched(p[0]); + } + + @OnReceive(url="/{url}") + static void fetchPeople(People p, JSONik model) { + model.setFetchedCount(p.getInfo().size()); + model.setFetched(p.getInfo().get(0)); + } + + @OnReceive(url="/{url}") + static void fetchPeopleAge(People p, JSONik model) { + int sum = 0; + for (int a : p.getAge()) { + sum += a; + } + model.setFetchedCount(sum); + } + + @Http(@Http.Resource( + content = "{'firstName': 'Sitar', 'sex': 'MALE'}", + path="/person.json", + mimeType = "application/json" + )) + @BrwsrTest public void loadAndParseJSON() throws InterruptedException { + if (js == null) { + js = new JSONik(); + js.applyBindings(); + + js.fetch("person.json"); + } + + Person p = js.getFetched(); + if (p == null) { + throw new InterruptedException(); + } + + assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName(); + assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex(); + } + + @OnReceive(url="/{url}?callme={me}", jsonp = "me") + static void fetchViaJSONP(Person p, JSONik model) { + model.setFetched(p); + } + + @Http(@Http.Resource( + content = "$0({'firstName': 'Mitar', 'sex': 'MALE'})", + path="/person.json", + mimeType = "application/javascript", + parameters = { "callme" } + )) + @BrwsrTest public void loadAndParseJSONP() throws InterruptedException { + + if (js == null) { + orig = scriptElements(); + assert orig > 0 : "There should be some scripts on the page"; + + js = new JSONik(); + js.applyBindings(); + + js.fetchViaJSONP("person.json"); + } + + Person p = js.getFetched(); + if (p == null) { + throw new InterruptedException(); + } + + assert "Mitar".equals(p.getFirstName()) : "Unexpected: " + p.getFirstName(); + assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex(); + + int now = scriptElements(); + + assert orig == now : "The set of elements is unchanged. Delta: " + (now - orig); + } + + @JavaScriptBody(args = { }, body = "return window.document.getElementsByTagName('script').length;") + private static native int scriptElements(); + + @JavaScriptBody(args = { "s" }, body = "return window.JSON.parse(s);") + private static native Object parseJSON(String s); + + @Http(@Http.Resource( + content = "{'firstName': 'Sitar', 'sex': 'MALE'}", + path="/person.json", + mimeType = "application/json" + )) + @BrwsrTest public void loadAndParseJSONSentToArray() throws InterruptedException { + if (js == null) { + js = new JSONik(); + js.applyBindings(); + + js.fetchArray("person.json"); + } + + Person p = js.getFetched(); + if (p == null) { + throw new InterruptedException(); + } + + assert p != null : "We should get our person back: " + p; + assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName(); + assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex(); + } + + @Http(@Http.Resource( + content = "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]", + path="/person.json", + mimeType = "application/json" + )) + @BrwsrTest public void loadAndParseJSONArraySingle() throws InterruptedException { + if (js == null) { + js = new JSONik(); + js.applyBindings(); + + js.fetch("person.json"); + } + + Person p = js.getFetched(); + if (p == null) { + throw new InterruptedException(); + } + + assert p != null : "We should get our person back: " + p; + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName(); + assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex(); + } + + @Http(@Http.Resource( + content = "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}", + path="/people.json", + mimeType = "application/json" + )) + @BrwsrTest public void loadAndParseArrayInPeople() throws InterruptedException { + if (js == null) { + js = new JSONik(); + js.applyBindings(); + + js.fetchPeople("people.json"); + } + + if (0 == js.getFetchedCount()) { + throw new InterruptedException(); + } + + assert js.getFetchedCount() == 1 : "One person loaded: " + js.getFetchedCount(); + + Person p = js.getFetched(); + + assert p != null : "We should get our person back: " + p; + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName(); + assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex(); + } + + @Http(@Http.Resource( + content = "{'age':[1, 2, 3]}", + path="/people.json", + mimeType = "application/json" + )) + @BrwsrTest public void loadAndParseArrayOfIntegers() throws InterruptedException { + if (js == null) { + js = new JSONik(); + js.applyBindings(); + + js.fetchPeopleAge("people.json"); + } + + if (0 == js.getFetchedCount()) { + throw new InterruptedException(); + } + + assert js.getFetchedCount() == 6 : "1 + 2 + 3 is " + js.getFetchedCount(); + } + + @OnReceive(url="/{url}") + static void fetchPeopleSex(People p, JSONik model) { + model.setFetchedCount(1); + model.getFetchedSex().addAll(p.getSex()); + } + + + @Http(@Http.Resource( + content = "{'sex':['FEMALE', 'MALE', 'MALE']}", + path="/people.json", + mimeType = "application/json" + )) + @BrwsrTest public void loadAndParseArrayOfEnums() throws InterruptedException { + if (js == null) { + js = new JSONik(); + js.applyBindings(); + + js.fetchPeopleSex("people.json"); + } + + if (0 == js.getFetchedCount()) { + throw new InterruptedException(); + } + + assert js.getFetchedCount() == 1 : "Loaded"; + + assert js.getFetchedSex().size() == 3 : "Three values " + js.getFetchedSex(); + assert js.getFetchedSex().get(0) == Sex.FEMALE : "Female first " + js.getFetchedSex(); + assert js.getFetchedSex().get(1) == Sex.MALE : "male 2nd " + js.getFetchedSex(); + assert js.getFetchedSex().get(2) == Sex.MALE : "male 3rd " + js.getFetchedSex(); + } + + @Http(@Http.Resource( + content = "[{'firstName': 'Gitar', 'sex': 'FEMALE'}," + + "{'firstName': 'Peter', 'sex': 'MALE'}" + + "]", + path="/person.json", + mimeType = "application/json" + )) + @BrwsrTest public void loadAndParseJSONArray() throws InterruptedException { + if (js == null) { + js = new JSONik(); + js.applyBindings(); + js.fetchArray("person.json"); + } + + + Person p = js.getFetched(); + if (p == null) { + throw new InterruptedException(); + } + + assert js.getFetchedCount() == 2 : "We got two values: " + js.getFetchedCount(); + assert p != null : "We should get our person back: " + p; + assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName(); + assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex(); + } + + @Factory public static Object[] create() { + return VMTest.create(JSONTest.class); + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Thu May 02 09:18:22 2013 +0200 @@ -17,21 +17,29 @@ */ package org.apidesign.bck2brwsr.htmlpage; +import java.util.List; +import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; import org.apidesign.bck2brwsr.htmlpage.api.OnEvent; +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction; import org.apidesign.bck2brwsr.htmlpage.api.Page; import org.apidesign.bck2brwsr.htmlpage.api.Property; import org.apidesign.bck2brwsr.vmtest.BrwsrTest; import org.apidesign.bck2brwsr.vmtest.HtmlFragment; import org.apidesign.bck2brwsr.vmtest.VMTest; +import static org.testng.Assert.assertEquals; import org.testng.annotations.Factory; +import org.testng.annotations.Test; /** * * @author Jaroslav Tulach */ @Page(xhtml="Knockout.xhtml", className="KnockoutModel", properties={ - @Property(name="name", type=String.class) + @Property(name="name", type=String.class), + @Property(name="results", type=String.class, array = true), + @Property(name="callbackCount", type=int.class), + @Property(name="people", type=PersonImpl.class, array = true) }) public class KnockoutTest { @@ -44,19 +52,207 @@ KnockoutModel m = new KnockoutModel(); m.setName("Kukuc"); m.applyBindings(); - assert "Kukuc".equals(m.INPUT.getValue()) : "Value is really kukuc: " + m.INPUT.getValue(); - m.INPUT.setValue("Jardo"); - m.triggerEvent(m.INPUT, OnEvent.CHANGE); + assert "Kukuc".equals(m.input.getValue()) : "Value is really kukuc: " + m.input.getValue(); + m.input.setValue("Jardo"); + m.triggerEvent(m.input, OnEvent.CHANGE); assert "Jardo".equals(m.getName()) : "Name property updated: " + m.getName(); } + @HtmlFragment( + "

    \n" + + "
  • \n" + + "
\n" + ) + @BrwsrTest public void displayContentOfArray() { + KnockoutModel m = new KnockoutModel(); + m.getResults().add("Ahoj"); + m.applyBindings(); + + int cnt = countChildren("ul"); + assert cnt == 1 : "One child, but was " + cnt; + + m.getResults().add("Hi"); + + cnt = countChildren("ul"); + assert cnt == 2 : "Two children now, but was " + cnt; + + triggerChildClick("ul", 1); + + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount(); + assert "Hi".equals(m.getName()) : "We got callback from 2nd child " + m.getName(); + } + + @HtmlFragment( + "
    \n" + + "
  • \n" + + "
\n" + ) + @BrwsrTest public void displayContentOfDerivedArray() { + KnockoutModel m = new KnockoutModel(); + m.getResults().add("Ahoj"); + m.applyBindings(); + + int cnt = countChildren("ul"); + assert cnt == 1 : "One child, but was " + cnt; + + m.getResults().add("hello"); + + cnt = countChildren("ul"); + assert cnt == 2 : "Two children now, but was " + cnt; + } + + @HtmlFragment( + "
    \n" + + "
  • \n" + + "
\n" + ) + @BrwsrTest public void displayContentOfArrayOfPeople() { + KnockoutModel m = new KnockoutModel(); + + final Person first = new Person(); + first.setFirstName("first"); + m.getPeople().add(first); + + m.applyBindings(); + + int cnt = countChildren("ul"); + assert cnt == 1 : "One child, but was " + cnt; + + final Person second = new Person(); + second.setFirstName("second"); + m.getPeople().add(second); + + cnt = countChildren("ul"); + assert cnt == 2 : "Two children now, but was " + cnt; + + triggerChildClick("ul", 1); + + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount(); + + cnt = countChildren("ul"); + assert cnt == 1 : "Again one child, but was " + cnt; + + String txt = childText("ul", 0); + assert "first".equals(txt) : "Expecting 'first': " + txt; + + first.setFirstName("changed"); + + txt = childText("ul", 0); + assert "changed".equals(txt) : "Expecting 'changed': " + txt; + } + + @ComputedProperty + static Person firstPerson(List people) { + return people.isEmpty() ? null : people.get(0); + } + + @HtmlFragment( + "

\n" + + " \n" + + "

\n" + ) + @BrwsrTest public void accessFirstPersonWithOnFunction() { + trasfertToFemale(); + } + + @HtmlFragment( + "
    \n" + + "
  • \n" + + "
\n" + ) + @BrwsrTest public void onPersonFunction() { + trasfertToFemale(); + } + + private void trasfertToFemale() { + KnockoutModel m = new KnockoutModel(); + + final Person first = new Person(); + first.setFirstName("first"); + first.setSex(Sex.MALE); + m.getPeople().add(first); + + + m.applyBindings(); + + int cnt = countChildren("ul"); + assert cnt == 1 : "One child, but was " + cnt; + + + triggerChildClick("ul", 0); + + assert first.getSex() == Sex.FEMALE : "Transverted to female: " + first.getSex(); + } + + @Test public void cloneModel() { + Person model = new Person(); + + model.setFirstName("first"); + Person snd = model.clone(); + snd.setFirstName("clone"); + assertEquals("first", model.getFirstName(), "Value has not changed"); + assertEquals("clone", snd.getFirstName(), "Value has changed in clone"); + } + + + @Test public void deepCopyOnClone() { + People model = new People(); + model.getNicknames().add("Jarda"); + assertEquals(model.getNicknames().size(), 1, "One element"); + People snd = model.clone(); + snd.getNicknames().clear(); + assertEquals(snd.getNicknames().size(), 0, "Clone is empty"); + assertEquals(model.getNicknames().size(), 1, "Still one element"); + } + + + @OnFunction + static void call(KnockoutModel m, String data) { + m.setName(data); + m.setCallbackCount(m.getCallbackCount() + 1); + } + + @OnFunction + static void removePerson(KnockoutModel model, Person data) { + model.setCallbackCount(model.getCallbackCount() + 1); + model.getPeople().remove(data); + } + + @ComputedProperty static String helloMessage(String name) { return "Hello " + name + "!"; } + @ComputedProperty + static List cmpResults(List results) { + return results; + } + @Factory public static Object[] create() { return VMTest.create(KnockoutTest.class); } + + @JavaScriptBody(args = { "id" }, body = + "var e = window.document.getElementById(id);\n " + + "if (typeof e === 'undefined') return -2;\n " + + "return e.children.length;\n " + ) + private static native int countChildren(String id); + + @JavaScriptBody(args = { "id", "pos" }, body = + "var e = window.document.getElementById(id);\n " + + "var ev = window.document.createEvent('MouseEvents');\n " + + "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n " + + "e.children[pos].dispatchEvent(ev);\n " + ) + private static native void triggerChildClick(String id, int pos); + + @JavaScriptBody(args = { "id", "pos" }, body = + "var e = window.document.getElementById(id);\n " + + "var t = e.children[pos].innerHTML;\n " + + "return t ? t : null;" + ) + private static native String childText(String id, int pos); } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Thu May 02 09:18:22 2013 +0200 @@ -18,8 +18,13 @@ package org.apidesign.bck2brwsr.htmlpage; import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction; +import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange; import org.apidesign.bck2brwsr.htmlpage.api.Page; import org.apidesign.bck2brwsr.htmlpage.api.Property; import static org.testng.Assert.*; @@ -30,17 +35,22 @@ * * @author Jaroslav Tulach */ -@Page(xhtml = "Empty.html", className = "Model", properties = { +@Page(xhtml = "Empty.html", className = "Modelik", properties = { @Property(name = "value", type = int.class), - @Property(name = "unrelated", type = long.class) + @Property(name = "count", type = int.class), + @Property(name = "unrelated", type = long.class), + @Property(name = "names", type = String.class, array = true), + @Property(name = "values", type = int.class, array = true), + @Property(name = "people", type = PersonImpl.class, array = true), + @Property(name = "changedProperty", type=String.class) }) public class ModelTest { - private Model model; - private static Model leakedModel; + private Modelik model; + private static Modelik leakedModel; @BeforeMethod public void createModel() { - model = new Model(); + model = new Modelik(); } @Test public void classGeneratedWithSetterGetter() { @@ -53,6 +63,75 @@ assertEquals(16, model.getPowerValue()); } + @Test public void arrayIsMutable() { + assertEquals(model.getNames().size(), 0, "Is empty"); + model.getNames().add("Jarda"); + assertEquals(model.getNames().size(), 1, "One element"); + } + + @Test public void arrayChangesNotified() { + MockKnockout my = new MockKnockout(); + MockKnockout.next = my; + + model.applyBindings(); + + model.getNames().add("Hello"); + + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated); + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated); + + my.mutated.clear(); + + Iterator it = model.getNames().iterator(); + assertEquals(it.next(), "Hello"); + it.remove(); + + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated); + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated); + + my.mutated.clear(); + + ListIterator lit = model.getNames().listIterator(); + lit.add("Jarda"); + + assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated); + assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated); + } + + @Test public void autoboxedArray() { + MockKnockout my = new MockKnockout(); + MockKnockout.next = my; + + model.applyBindings(); + + model.getValues().add(10); + + assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten"); + } + + @Test public void derivedArrayProp() { + MockKnockout my = new MockKnockout(); + MockKnockout.next = my; + + model.applyBindings(); + + model.setCount(10); + + List arr = model.getRepeat(); + assertEquals(arr.size(), 10, "Ten items: " + arr); + + my.mutated.clear(); + + model.setCount(5); + + arr = model.getRepeat(); + assertEquals(arr.size(), 5, "Five items: " + arr); + + assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated); + assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated); + assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated); + } + @Test public void derivedPropertiesAreNotified() { MockKnockout my = new MockKnockout(); MockKnockout.next = my; @@ -61,6 +140,9 @@ model.setValue(33); + // not interested in change of this property + my.mutated.remove("changedProperty"); + assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated); assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated); assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated); @@ -68,7 +150,11 @@ my.mutated.clear(); model.setUnrelated(44); - assertEquals(my.mutated.size(), 1, "One property changed"); + + + // not interested in change of this property + my.mutated.remove("changedProperty"); + assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated); assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated"); } @@ -92,11 +178,43 @@ } } + @OnFunction + static void doSomething() { + } + @ComputedProperty static int powerValue(int value) { return value * value; } + @OnPropertyChange({ "powerValue", "unrelated" }) + static void aPropertyChanged(Modelik m, String name) { + m.setChangedProperty(name); + } + + @OnPropertyChange({ "values" }) + static void anArrayPropertyChanged(String name, Modelik m) { + m.setChangedProperty(name); + } + + @Test public void changeAnything() { + model.setCount(44); + assertNull(model.getChangedProperty(), "No observed value change"); + } + @Test public void changeValue() { + model.setValue(33); + assertEquals(model.getChangedProperty(), "powerValue", "power property changed"); + } + @Test public void changeUnrelated() { + model.setUnrelated(333); + assertEquals(model.getChangedProperty(), "unrelated", "unrelated changed"); + } + + @Test public void changeInArray() { + model.getValues().add(10); + assertEquals(model.getChangedProperty(), "values", "Something added into the array"); + } + @ComputedProperty static String notAllowedRead() { return "Not allowed callback: " + leakedModel.getUnrelated(); @@ -108,12 +226,31 @@ return "Not allowed callback!"; } + @ComputedProperty + static List repeat(int count) { + return Collections.nCopies(count, "Hello"); + } + static class MockKnockout extends Knockout { - List mutated = new ArrayList(); + List mutated = new ArrayList<>(); + + MockKnockout() { + super(null); + } @Override public void valueHasMutated(String prop) { mutated.add(prop); } } + + public @Test void hasPersonPropertyAndComputedFullName() { + List arr = model.getPeople(); + assertEquals(arr.size(), 0, "By default empty"); + Person p = null; + if (p != null) { + String fullNameGenerated = p.getFullName(); + assertNotNull(fullNameGenerated); + } + } } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Thu May 02 09:18:22 2013 +0200 @@ -50,7 +50,7 @@ if (PAGE != ref) { throw new IllegalStateException("Both references should be the same. " + ref + " != " + PAGE); } - ref.PG_TITLE.setText("You want this window to be named " + ref.PG_TEXT.getValue()); + ref.pg_title.setText("You want this window to be named " + ref.pg_text.getValue()); } @On(event = CLICK, id={ "pg.title", "pg.text" }) @@ -58,11 +58,11 @@ if (!id.equals("pg.title")) { throw new IllegalStateException(); } - PAGE.PG_TITLE.setText(id); + PAGE.pg_title.setText(id); } @On(event = CLICK, id={ "pg.canvas" }) static void clickCanvas(String id, double layerX) { - PAGE.PG_CANVAS.setWidth((int) layerX); + PAGE.pg_canvas.setWidth((int) layerX); } } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,53 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +import java.io.IOException; +import java.util.Locale; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +/** Verify errors emitted by the processor. + * + * @author Jaroslav Tulach + */ +public class PageTest { + @Test public void verifyWrongType() throws IOException { + String html = "" + + ""; + String code = "package x.y.z;\n" + + "import org.apidesign.bck2brwsr.htmlpage.api.*;\n" + + "@Page(xhtml=\"index.xhtml\", className=\"Model\", properties={\n" + + " @Property(name=\"prop\", type=Runnable.class)\n" + + "})\n" + + "class X {\n" + + "}\n"; + + Compile c = Compile.create(html, code); + assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors()); + for (Diagnostic e : c.getErrors()) { + String msg = e.getMessage(Locale.ENGLISH); + if (!msg.contains("Runnable")) { + fail("Should contain warning about Runnable: " + msg); + } + } + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,62 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; +import org.apidesign.bck2brwsr.htmlpage.api.Model; +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction; +import org.apidesign.bck2brwsr.htmlpage.api.Property; + +/** + * + * @author Jaroslav Tulach + */ +@Model(className = "Person", properties = { + @Property(name = "firstName", type = String.class), + @Property(name = "lastName", type = String.class), + @Property(name = "sex", type = Sex.class) +}) +final class PersonImpl { + @ComputedProperty + public static String fullName(String firstName, String lastName) { + return firstName + " " + lastName; + } + + @ComputedProperty + public static String sexType(Sex sex) { + return sex == null ? "unknown" : sex.toString(); + } + + @OnFunction + static void changeSex(Person p) { + if (p.getSex() == Sex.MALE) { + p.setSex(Sex.FEMALE); + } else { + p.setSex(Sex.MALE); + } + } + + @Model(className = "People", properties = { + @Property(array = true, name = "info", type = Person.class), + @Property(array = true, name = "nicknames", type = String.class), + @Property(array = true, name = "age", type = int.class), + @Property(array = true, name = "sex", type = Sex.class) + }) + public class PeopleImpl { + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Sex.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Sex.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,26 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +/** + * + * @author Jaroslav Tulach + */ +public enum Sex { + MALE, FEMALE; +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator-dynamic/nbactions.xml --- a/javaquery/demo-calculator-dynamic/nbactions.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator-dynamic/nbactions.xml Thu May 02 09:18:22 2013 +0200 @@ -23,7 +23,7 @@ run process-classes - org.apidesign.bck2brwsr:mojo:0.5-SNAPSHOT:brwsr + bck2brwsr:brwsr diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator-dynamic/pom.xml --- a/javaquery/demo-calculator-dynamic/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator-dynamic/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,7 +4,7 @@ org.apidesign.bck2brwsr demo.calculator - 0.5-SNAPSHOT + 0.8-SNAPSHOT jar JavaQuery Demo - Calculator @@ -17,8 +17,8 @@ org.apidesign.bck2brwsr - mojo - 0.5-SNAPSHOT + bck2brwsr-maven-plugin + ${project.version} @@ -93,13 +93,13 @@ org.apidesign.bck2brwsr emul - 0.5-SNAPSHOT + ${project.version} rt org.apidesign.bck2brwsr javaquery.api - 0.5-SNAPSHOT + ${project.version} org.testng @@ -112,7 +112,7 @@ vm4brwsr js zip - 0.5-SNAPSHOT + ${project.version} provided diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java --- a/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/Calc.java Thu May 02 09:18:22 2013 +0200 @@ -17,9 +17,11 @@ */ package org.apidesign.bck2brwsr.demo.calc; +import java.util.List; import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; import org.apidesign.bck2brwsr.htmlpage.api.On; import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction; import org.apidesign.bck2brwsr.htmlpage.api.Page; import org.apidesign.bck2brwsr.htmlpage.api.Property; @@ -33,11 +35,12 @@ @Property(name = "memory", type = double.class), @Property(name = "display", type = double.class), @Property(name = "operation", type = String.class), - @Property(name = "hover", type = boolean.class) + @Property(name = "hover", type = boolean.class), + @Property(name = "history", type = HistoryImpl.class, array = true) }) public class Calc { static { - new Calculator().applyBindings(); + new Calculator().applyBindings().setOperation("plus"); } @On(event = CLICK, id="clear") @@ -65,14 +68,31 @@ @On(event = CLICK, id="result") static void computeTheValue(Calculator c) { - c.setDisplay(compute( + final double newValue = compute( c.getOperation(), c.getMemory(), c.getDisplay() - )); + ); + c.setDisplay(newValue); + if (!containsValue(c.getHistory(), newValue)) { + History h = new History(); + h.setValue(newValue); + h.setOperation(c.getOperation()); + c.getHistory().add(h); + } c.setMemory(0); } + @OnFunction + static void recoverMemory(Calculator c, History data) { + c.setDisplay(data.getValue()); + } + + @OnFunction + static void removeMemory(Calculator c, History data) { + c.getHistory().remove(data); + } + private static double compute(String op, double memory, double display) { switch (op) { case "plus": return memory + display; @@ -109,4 +129,18 @@ } return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display); } + + @ComputedProperty + static boolean emptyHistory(List history) { + return history.isEmpty(); + } + + private static boolean containsValue(List arr, final double newValue) { + for (History history : arr) { + if (history.getValue() == newValue) { + return true; + } + } + return false; + } } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/HistoryImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/demo/calc/HistoryImpl.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,43 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.demo.calc; + +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; +import org.apidesign.bck2brwsr.htmlpage.api.Model; +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction; +import org.apidesign.bck2brwsr.htmlpage.api.Property; + +/** + * + * @author Jaroslav Tulach + */ +@Model(className = "History", properties = { + @Property(name = "value", type = double.class), + @Property(name = "operation", type = String.class) +}) +public class HistoryImpl { + @ComputedProperty + static String resultOf(String operation) { + return "result of " + operation; + } + + @OnFunction + static void twice(History data) { + data.setValue(2.0 * data.getValue()); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Thu May 02 09:18:22 2013 +0200 @@ -78,82 +78,25 @@
+

Previous Results

+ +
No results yet.
+ +
-
-    package org.apidesign.bck2brwsr.mavenhtml;
-
-    import org.apidesign.bck2brwsr.htmlpage.api.OnClick;
-    import org.apidesign.bck2brwsr.htmlpage.api.Page;
-
-    /** HTML5 & Java demo showing the power of annotation processors
-     * as well as other goodies, including type-safe association between
-     * an XHTML page and Java.
-     * 
-     * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
-     */
-    @Page(xhtml="Calculator.xhtml")
-    public class App {
-        private static double memory;
-        private static String operation;
-
-        @OnClick(id="clear")
-        static void clear() {
-            memory = 0;
-            operation = null;
-            Calculator.DISPLAY.setValue("0");
-        }
-
-        @OnClick(id= { "plus", "minus", "mul", "div" })
-        static void applyOp(String op) {
-            memory = getValue();
-            operation = op;
-            Calculator.DISPLAY.setValue("0");
-        }
-
-        @OnClick(id="result")
-        static void computeTheValue() {
-            switch (operation) {
-                case "plus": setValue(memory + getValue()); break;
-                case "minus": setValue(memory - getValue()); break;
-                case "mul": setValue(memory * getValue()); break;
-                case "div": setValue(memory / getValue()); break;
-                default: throw new IllegalStateException(operation);
-            }
-        }
-
-        @OnClick(id={"n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9"}) 
-        static void addDigit(String digit) {
-            digit = digit.substring(1);
-            String v = Calculator.DISPLAY.getValue();
-            if (getValue() == 0.0) {
-                Calculator.DISPLAY.setValue(digit);
-            } else {
-                Calculator.DISPLAY.setValue(v + digit);
-            }
-        }
-
-        private static void setValue(double v) {
-            StringBuilder sb = new StringBuilder();
-            sb.append(v);
-            Calculator.DISPLAY.setValue(sb.toString());
-        }
-
-        private static double getValue() {
-            try {
-                return Double.parseDouble(Calculator.DISPLAY.getValue());
-            } catch (NumberFormatException ex) {
-                Calculator.DISPLAY.setValue("err");
-                return 0.0;
-            }
-        }
-    }
-
-    
diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator/nbactions.xml --- a/javaquery/demo-calculator/nbactions.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator/nbactions.xml Thu May 02 09:18:22 2013 +0200 @@ -23,7 +23,7 @@ run package - org.apidesign.bck2brwsr:mojo:0.5-SNAPSHOT:brwsr + bck2brwsr:brwsr true diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,7 +4,7 @@ org.apidesign.bck2brwsr demo.static.calculator - 0.5-SNAPSHOT + 0.8-SNAPSHOT jar JavaQuery Demo - Calculator - Static Compilation @@ -12,13 +12,14 @@ UTF-8 + MINIMAL org.apidesign.bck2brwsr - mojo - 0.5-SNAPSHOT + bck2brwsr-maven-plugin + ${project.version} @@ -31,6 +32,7 @@ ${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/ index.xhtml ${project.build.directory}/bck2brwsr.js + ${bck2brwsr.obfuscationlevel} @@ -96,13 +98,13 @@ org.apidesign.bck2brwsr emul - 0.5-SNAPSHOT + ${project.version} rt org.apidesign.bck2brwsr javaquery.api - 0.5-SNAPSHOT + ${project.version} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java --- a/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Thu May 02 09:18:22 2013 +0200 @@ -17,9 +17,11 @@ */ package org.apidesign.bck2brwsr.demo.calc.staticcompilation; +import java.util.List; import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; import org.apidesign.bck2brwsr.htmlpage.api.On; import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction; import org.apidesign.bck2brwsr.htmlpage.api.Page; import org.apidesign.bck2brwsr.htmlpage.api.Property; @@ -33,11 +35,12 @@ @Property(name = "memory", type = double.class), @Property(name = "display", type = double.class), @Property(name = "operation", type = String.class), - @Property(name = "hover", type = boolean.class) + @Property(name = "hover", type = boolean.class), + @Property(name = "history", type = double.class, array = true) }) public class Calc { static { - new Calculator().applyBindings(); + new Calculator().applyBindings().setOperation("plus"); } @On(event = CLICK, id="clear") @@ -65,14 +68,28 @@ @On(event = CLICK, id="result") static void computeTheValue(Calculator c) { - c.setDisplay(compute( + final double newValue = compute( c.getOperation(), c.getMemory(), c.getDisplay() - )); + ); + c.setDisplay(newValue); + if (!c.getHistory().contains(newValue)) { + c.getHistory().add(newValue); + } c.setMemory(0); } + @OnFunction + static void recoverMemory(Calculator c, double data) { + c.setDisplay(data); + } + + @OnFunction + static void removeMemory(Calculator c, double data) { + c.getHistory().remove(data); + } + private static double compute(String op, double memory, double display) { switch (op) { case "plus": return memory + display; @@ -109,4 +126,9 @@ } return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display); } + + @ComputedProperty + static boolean emptyHistory(List history) { + return history.isEmpty(); + } } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Thu May 02 09:18:22 2013 +0200 @@ -76,10 +76,22 @@ + +

Previous Results

+ +
No results yet.
+ +
- diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/bck2brwsr-assembly.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/bck2brwsr-assembly.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,62 @@ + + + + + bck2brwsr + + zip + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + + + ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/twitter/ + + **/* + + + **/*.class + + / + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/bck2brwsr.js + / + + + \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/nb-configuration.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/nb-configuration.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,37 @@ + + + + + + + none + + diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/nbactions.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/nbactions.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,29 @@ + + + + + run + + process-classes + bck2brwsr:brwsr + + + diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,151 @@ + + + 4.0.0 + + javaquery + org.apidesign.bck2brwsr + 0.8-SNAPSHOT + + + org.apidesign.bck2brwsr + demo-twitter + 0.8-SNAPSHOT + jar + + Bck2Brwsr's Twttr + + Rewrite of knockoutjs example to use model written in Java and + execute using Bck2Brwsr virtual machine. + + + + + java.net + Java.net + https://maven.java.net/content/repositories/releases/ + + + + + netbeans + NetBeans + http://bits.netbeans.org/maven2/ + + + + + java.net + Java.net + https://maven.java.net/content/repositories/releases/ + + + + + + + UTF-8 + MINIMAL + + + + + org.apidesign.bck2brwsr + bck2brwsr-maven-plugin + ${project.version} + + + + brwsr + j2js + + + + + org/apidesign/bck2brwsr/demo/twitter/index.html + ${project.build.directory}/bck2brwsr.js + ${bck2brwsr.obfuscationlevel} + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + bck2brwsr-assembly.xml + + + + + + + + + + + org.apidesign.bck2brwsr + emul + ${project.version} + rt + + + org.apidesign.bck2brwsr + javaquery.api + ${project.version} + + + org.testng + testng + 6.5.2 + test + + + org.apidesign.bck2brwsr + vmtest + ${project.version} + test + + + org.apidesign.bck2brwsr + launcher.http + ${project.version} + runtime + + + diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,194 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.demo.twitter; + +import java.util.Arrays; +import java.util.List; +import org.apidesign.bck2brwsr.htmlpage.api.*; +import org.apidesign.bck2brwsr.htmlpage.api.Page; +import org.apidesign.bck2brwsr.htmlpage.api.Property; +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; + +/** Controller class for access to Twitter. + * + * @author Jaroslav Tulach + */ +@Page(xhtml="index.html", className="TwitterModel", properties={ + @Property(name="savedLists", type=Tweeters.class, array = true), + @Property(name="activeTweetersName", type=String.class), + @Property(name="activeTweeters", type=String.class, array = true), + @Property(name="userNameToAdd", type=String.class), + @Property(name="currentTweets", type=Tweet.class, array = true) +}) +public class TwitterClient { + @Model(className = "Tweeters", properties = { + @Property(name="name", type = String.class), + @Property(name="userNames", type = String.class, array = true) + }) + static class Twttrs { + } + @Model(className = "Tweet", properties = { + @Property(name = "from_user", type = String.class), + @Property(name = "from_user_id", type = int.class), + @Property(name = "profile_image_url", type = String.class), + @Property(name = "text", type = String.class), + @Property(name = "created_at", type = String.class), + }) + static final class Twt { + @ComputedProperty static String html(String text) { + StringBuilder sb = new StringBuilder(320); + for (int pos = 0;;) { + int http = text.indexOf("http", pos); + if (http == -1) { + sb.append(text.substring(pos)); + return sb.toString(); + } + int spc = text.indexOf(' ', http); + if (spc == -1) { + spc = text.length(); + } + sb.append(text.substring(pos, http)); + String url = text.substring(http, spc); + sb.append("").append(url).append(""); + pos = spc; + } + } + + @ComputedProperty static String userUrl(String from_user) { + return "http://twitter.com/" + from_user; + } + } + @Model(className = "TwitterQuery", properties = { + @Property(array = true, name = "results", type = Twt.class) + }) + public static final class TwttrQr { + } + + @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me") + static void queryTweets(TwitterModel page, TwitterQuery q) { + page.getCurrentTweets().clear(); + page.getCurrentTweets().addAll(q.getResults()); + } + + @OnPropertyChange("activeTweetersName") + static void changeTweetersList(TwitterModel model) { + Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName()); + model.getActiveTweeters().clear(); + model.getActiveTweeters().addAll(people.getUserNames()); + } + + @OnPropertyChange({ "activeTweeters", "activeTweetersCount" }) + static void refreshTweets(TwitterModel model) { + StringBuilder sb = new StringBuilder(); + sb.append("rpp=25&q="); + String sep = ""; + for (String p : model.getActiveTweeters()) { + sb.append(sep); + sb.append("from:"); + sb.append(p); + sep = " OR "; + } + model.queryTweets("http://search.twitter.com", sb.toString()); + } + + static { + final TwitterModel model = new TwitterModel(); + final List svdLst = model.getSavedLists(); + svdLst.add(newTweeters("API Design", "JaroslavTulach")); + svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson")); + svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu")); + svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka")); + svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror")); + + model.setActiveTweetersName("NetBeans"); + + model.applyBindings(); + } + + @ComputedProperty + static boolean hasUnsavedChanges(List activeTweeters, List savedLists, String activeTweetersName) { + Tweeters tw = findByName(savedLists, activeTweetersName); + if (activeTweeters == null) { + return false; + } + return !tw.getUserNames().equals(activeTweeters); + } + + @ComputedProperty + static int activeTweetersCount(List activeTweeters) { + return activeTweeters.size(); + } + + @ComputedProperty + static boolean userNameToAddIsValid( + String userNameToAdd, String activeTweetersName, List savedLists, List activeTweeters + ) { + return userNameToAdd != null && + userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") && + !activeTweeters.contains(userNameToAdd); + } + + @OnFunction + static void deleteList(TwitterModel model) { + final List sl = model.getSavedLists(); + sl.remove(findByName(sl, model.getActiveTweetersName())); + if (sl.isEmpty()) { + final Tweeters t = new Tweeters(); + t.setName("New"); + sl.add(t); + } + model.setActiveTweetersName(sl.get(0).getName()); + } + + @OnFunction + static void saveChanges(TwitterModel model) { + Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName()); + int indx = model.getSavedLists().indexOf(t); + if (indx != -1) { + t.setName(model.getActiveTweetersName()); + t.getUserNames().clear(); + t.getUserNames().addAll(model.getActiveTweeters()); + } + } + + @OnFunction + static void addUser(TwitterModel model) { + String n = model.getUserNameToAdd(); + model.getActiveTweeters().add(n); + } + @OnFunction + static void removeUser(String data, TwitterModel model) { + model.getActiveTweeters().remove(data); + } + + private static Tweeters findByName(List list, String name) { + for (Tweeters l : list) { + if (l.getName() != null && l.getName().equals(name)) { + return l; + } + } + return list.isEmpty() ? new Tweeters() : list.get(0); + } + + private static Tweeters newTweeters(String listName, String... userNames) { + Tweeters t = new Tweeters(); + t.setName(listName); + t.getUserNames().addAll(Arrays.asList(userNames)); + return t; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/index.html Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,99 @@ + + + + + + + + + Bck2Brwsr's Twitter + + + + + + + +

Bck2Brwsr's Twitter

+ +

+ This code based on original knockout.js Twitter example and + uses almost unmodified HTML code. It just changes the model. It + is written in Java language and it is executed using Bck2Brwsr + virtual machine. The Java source code has about 190 lines and is available + here + - in fact it may even be more dense than the original JavaScript model. +

+ +
+
+
+ + + +
+ +

Currently viewing user(s):

+
+
    +
  • + +
    +
  • +
+
+ +
+ + + +
+
+
+
Loading...
+ + + + + +
+ + +
+
+
+
+ + + + + + + diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/twitterExample.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/src/main/resources/org/apidesign/bck2brwsr/demo/twitter/twitterExample.css Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,50 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ + +/* + Copied from knockout.js Twitter example: + http://knockoutjs.com/examples/twitter.html +*/ + +.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; } +.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; } +.tweets { width: 55%; border: 2px solid gray; height: 40em; overflow: scroll; overflow-x: hidden; background-color: Black; color: White; padding: 0.5em; position: relative; } +.tweets table { border-width: 0;} +.tweets tr { vertical-align: top; } +.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; } +.tweets img { width: 4em; } +.tweetInfo { color: Gray; font-size: 0.9em; } +.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; } +input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; } + +.listChooser select, .listChooser button { vertical-align:top; } +.listChooser select { width: 60%; font-size:1.2em; height:1.4em; } +.listChooser button { width: 19%; height:1.68em; float:right; } + +.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; } +.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; } +.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 } +.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; } +.currentUsers li div { padding: 0.6em; } +.currentUsers li:hover { background-color: #EEC; } + +.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; } +.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; } +.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; } + +.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; } diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClientTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClientTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,67 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.demo.twitter; + +import java.util.List; +import static org.testng.Assert.*; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** We can unit test the TwitterModel smoothly. + * + * @author Jaroslav Tulach + */ +public class TwitterClientTest { + private TwitterModel model; + + + @BeforeMethod + public void initModel() { + model = new TwitterModel().applyBindings(); + } + + @Test public void testIsValidToAdd() { + model.setUserNameToAdd("Joe"); + Tweeters t = new Tweeters(); + t.setName("test"); + model.getSavedLists().add(t); + model.setActiveTweetersName("test"); + + assertTrue(model.isUserNameToAddIsValid(), "Joe is OK"); + TwitterClient.addUser(model); + assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time"); + assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty"); + + List mod = model.getActiveTweeters(); + assertTrue(model.isHasUnsavedChanges(), "We have modifications"); + assertEquals(mod.size(), 1, "One element in the list"); + assertEquals(mod.get(0), "Joe", "Its name is Joe"); + + assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one"); + + TwitterClient.saveChanges(model); + assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save"); + + assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one"); + } + + @Test public void httpAtTheEnd() { + String res = TwitterClient.Twt.html("Ahoj http://kuk"); + assertEquals(res, "Ahoj http://kuk"); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterProtocolTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-twitter/src/test/java/org/apidesign/bck2brwsr/demo/twitter/TwitterProtocolTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,94 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.demo.twitter; + +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Http; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class TwitterProtocolTest { + private TwitterModel page; + @Http(@Http.Resource( + path = "/search.json", + mimeType = "application/json", + parameters = {"callback"}, + content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\"" + + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":" + + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\"," + + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\"," + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":" + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344," + + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":" + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":" + + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + " + + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747," + + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872," + + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\"," + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":" + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913," + + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":" + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":" + + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"}," + + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\"," + + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\"," + + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\"," + + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":" + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":" + + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"}," + + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648," + + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null," + + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":" + + "{\"result_type\":\"recent\"},\"profile_image_url\":" + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\"," + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":" + + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100," + + "\"since_id\":0,\"since_id_str\":\"0\"})" + )) + @BrwsrTest public void readFromTwttr() throws InterruptedException { + if (page == null) { + page = new TwitterModel(); + page.applyBindings(); + page.queryTweets("", "q=xyz"); + } + + if (page.getCurrentTweets().isEmpty()) { + throw new InterruptedException(); + } + + assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets(); + + String firstDate = page.getCurrentTweets().get(0).getCreated_at(); + assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate; + } + + @Factory public static Object[] create() { + return VMTest.create(TwitterProtocolTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb javaquery/pom.xml --- a/javaquery/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/javaquery/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,16 +4,17 @@ bck2brwsr org.apidesign - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr javaquery - 0.5-SNAPSHOT + 0.8-SNAPSHOT pom JavaQuery API and Demo api demo-calculator demo-calculator-dynamic - - + demo-twitter +
+ \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/api/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/api/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,31 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + launcher-pom + 0.8-SNAPSHOT + + org.apidesign.bck2brwsr + launcher + 0.8-SNAPSHOT + Launcher API + http://maven.apache.org + + UTF-8 + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + org.apidesign.bck2brwsr.launcher + false + + + + + + + diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,115 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** Represents individual method invocation, its context and its result. + * + * @author Jaroslav Tulach + */ +public final class InvocationContext { + final CountDownLatch wait = new CountDownLatch(1); + final Class clazz; + final String methodName; + private final Launcher launcher; + private String result; + private Throwable exception; + String html; + final List resources = new ArrayList<>(); + + InvocationContext(Launcher launcher, Class clazz, String methodName) { + this.launcher = launcher; + this.clazz = clazz; + this.methodName = methodName; + } + + /** An HTML fragment to be available for the execution. Useful primarily when + * executing in a browser via {@link Launcher#createBrowser(java.lang.String)}. + * @param html the html fragment + */ + public void setHtmlFragment(String html) { + this.html = html; + } + + /** HTTP resource to be available during execution. An invocation may + * perform an HTTP query and obtain a resource relative to the page. + */ + public void addHttpResource(String relativePath, String mimeType, String[] parameters, InputStream content) { + if (relativePath == null || mimeType == null || content == null || parameters == null) { + throw new NullPointerException(); + } + resources.add(new Resource(content, mimeType, relativePath, parameters)); + } + + /** Invokes the associated method. + * @return the textual result of the invocation + */ + public String invoke() throws IOException { + launcher.runMethod(this); + return toString(); + } + + /** Obtains textual result of the invocation. + * @return text representing the exception or result value + */ + @Override + public String toString() { + if (exception != null) { + return exception.toString(); + } + return result; + } + + /** + * @param timeOut + * @throws InterruptedException + */ + void await(long timeOut) throws InterruptedException { + wait.await(timeOut, TimeUnit.MILLISECONDS); + } + + void result(String r, Throwable e) { + this.result = r; + this.exception = e; + wait.countDown(); + } + + + static final class Resource { + final InputStream httpContent; + final String httpType; + final String httpPath; + final String[] parameters; + + Resource(InputStream httpContent, String httpType, String httpPath, + String[] parameters + ) { + httpContent.mark(Integer.MAX_VALUE); + this.httpContent = httpContent; + this.httpType = httpType; + this.httpPath = httpPath; + this.parameters = parameters; + } + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,166 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; + +/** An abstraction for executing tests in a Bck2Brwsr virtual machine. + * Either in {@link Launcher#createJavaScript JavaScript engine}, + * or in {@link Launcher#createBrowser external browser}. + *

+ * There also are methods to {@link #showDir(java.io.File, java.lang.String) display pages} + * in an external browser served by internal HTTP server. + * + * @author Jaroslav Tulach + */ +public abstract class Launcher { + + Launcher() { + } + + /** Initializes the launcher. This may mean starting a web browser or + * initializing execution engine. + * @throws IOException if something goes wrong + */ + public abstract void initialize() throws IOException; + + /** Shuts down the launcher. + * @throws IOException if something goes wrong + */ + public abstract void shutdown() throws IOException; + + + /** Builds an invocation context. The context can later be customized + * and {@link InvocationContext#invoke() invoked}. + * + * @param clazz the class to execute method from + * @param method the method to execute + * @return the context pointing to the selected method + */ + public InvocationContext createInvocation(Class clazz, String method) { + return new InvocationContext(this, clazz, method); + } + + + /** Creates launcher that uses internal JavaScript engine (Rhino). + * @return the launcher + */ + public static Launcher createJavaScript() { + try { + Class c = loadClass("org.apidesign.bck2brwsr.launcher.JSLauncher"); + return (Launcher) c.newInstance(); + } catch (Exception ex) { + throw new IllegalStateException("Please include org.apidesign.bck2brwsr:launcher.html dependency!", ex); + } + } + + /** Creates launcher that is using external browser. + * + * @param cmd null to use java.awt.Desktop to show the launcher + * or a string to execute in an external process (with a parameter to the URL) + * @return launcher executing in external browser. + */ + public static Launcher createBrowser(String cmd) { + String msg = "Trying to create browser '" + cmd + "'"; + try { + Class c; + if ("fxbrwsr".equals(cmd)) { // NOI18N + msg = "Please include org.apidesign.bck2brwsr:launcher.fx dependency!"; + c = loadClass("org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher"); // NOI18N + } else { + msg = "Please include org.apidesign.bck2brwsr:launcher.html dependency!"; + c = loadClass("org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher"); // NOI18N + if ("bck2brwsr".equals(cmd)) { // NOI18N + // use default executable + cmd = null; + } + } + Constructor cnstr = c.getConstructor(String.class); + return (Launcher) cnstr.newInstance(cmd); + } catch (Exception ex) { + throw new IllegalStateException(msg, ex); + } + } + + /** Starts an HTTP server which provides access to classes and resources + * available in the classes URL and shows a start page + * available as {@link ClassLoader#getResource(java.lang.String)} from the + * provide classloader. Opens a browser with URL showing the start page. + * + * @param classes classloader offering access to classes and resources + * @param startpage page to show in the browser + * @return interface that allows one to stop the server + * @throws IOException if something goes wrong + */ + public static Closeable showURL(ClassLoader classes, String startpage) throws IOException { + return showURL(null, classes, startpage); + } + /** Starts an HTTP server which provides access to classes and resources + * available in the classes URL and shows a start page + * available as {@link ClassLoader#getResource(java.lang.String)} from the + * provide classloader. Opens a browser with URL showing the start page. + * + * @param brwsr name of browser to use or null + * @param classes classloader offering access to classes and resources + * @param startpage page to show in the browser + * @return interface that allows one to stop the server + * @throws IOException if something goes wrong + * @since 0.7 + */ + public static Closeable showURL(String brwsr, ClassLoader classes, String startpage) throws IOException { + Launcher l = createBrowser(brwsr); + l.addClassLoader(classes); + l.showURL(startpage); + return (Closeable) l; + } + /** Starts an HTTP server which provides access to certain directory. + * The startpage should be relative location inside the root + * directory. Opens a browser with URL showing the start page. + * + * @param directory the root directory on disk + * @param startpage relative path from the root to the page + * @exception IOException if something goes wrong. + */ + public static Closeable showDir(File directory, String startpage) throws IOException { + Launcher l = createBrowser(null); + l.showDirectory(directory, startpage); + return (Closeable) l; + } + + abstract InvocationContext runMethod(InvocationContext c) throws IOException; + + + private static Class loadClass(String cn) throws ClassNotFoundException { + return Launcher.class.getClassLoader().loadClass(cn); + } + + void showDirectory(File directory, String startpage) throws IOException { + throw new UnsupportedOperationException(); + } + + void showURL(String startpage) throws IOException { + throw new UnsupportedOperationException(); + } + + void addClassLoader(ClassLoader classes) { + throw new UnsupportedOperationException(); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + launcher-pom + 0.8-SNAPSHOT + + org.apidesign.bck2brwsr + launcher.fx + 0.8-SNAPSHOT + FXBrwsr Launcher + http://maven.apache.org + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-javadoc-plugin + + org.apidesign.bck2brwsr.launcher.fx + false + + + + + + UTF-8 + + + + ${project.groupId} + launcher + ${project.version} + + + org.glassfish.grizzly + grizzly-http-server + 2.2.19 + + + com.oracle + javafx + 2.2 + system + ${java.home}/lib/jfxrt.jar + + + diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,588 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; +import org.glassfish.grizzly.PortRange; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.http.server.NetworkListener; +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.grizzly.http.server.ServerConfiguration; +import org.glassfish.grizzly.http.util.HttpStatus; + +/** + * Lightweight server to launch Bck2Brwsr applications and tests. + * Supports execution in native browser as well as Java's internal + * execution engine. + */ +abstract class BaseHTTPLauncher extends Launcher implements Closeable { + private static final Logger LOG = Logger.getLogger(BaseHTTPLauncher.class.getName()); + private static final InvocationContext END = new InvocationContext(null, null, null); + private final Set loaders = new LinkedHashSet<>(); + private final BlockingQueue methods = new LinkedBlockingQueue<>(); + private long timeOut; + private final Res resources = new Res(); + private final String cmd; + private Object[] brwsr; + private HttpServer server; + private CountDownLatch wait; + + public BaseHTTPLauncher(String cmd) { + this.cmd = cmd; + addClassLoader(BaseHTTPLauncher.class.getClassLoader()); + setTimeout(180000); + } + + @Override + InvocationContext runMethod(InvocationContext c) throws IOException { + loaders.add(c.clazz.getClassLoader()); + methods.add(c); + try { + c.await(timeOut); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + return c; + } + + public void setTimeout(long ms) { + timeOut = ms; + } + + public void addClassLoader(ClassLoader url) { + this.loaders.add(url); + } + + ClassLoader[] loaders() { + return loaders.toArray(new ClassLoader[loaders.size()]); + } + + public void showURL(String startpage) throws IOException { + if (!startpage.startsWith("/")) { + startpage = "/" + startpage; + } + HttpServer s = initServer(".", true); + int last = startpage.lastIndexOf('/'); + String prefix = startpage.substring(0, last); + String simpleName = startpage.substring(last); + s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/"); + try { + launchServerAndBrwsr(s, simpleName); + } catch (URISyntaxException | InterruptedException ex) { + throw new IOException(ex); + } + } + + void showDirectory(File dir, String startpage) throws IOException { + if (!startpage.startsWith("/")) { + startpage = "/" + startpage; + } + HttpServer s = initServer(dir.getPath(), false); + try { + launchServerAndBrwsr(s, startpage); + } catch (URISyntaxException | InterruptedException ex) { + throw new IOException(ex); + } + } + + @Override + public void initialize() throws IOException { + try { + executeInBrowser(); + } catch (InterruptedException ex) { + final InterruptedIOException iio = new InterruptedIOException(ex.getMessage()); + iio.initCause(ex); + throw iio; + } catch (Exception ex) { + if (ex instanceof IOException) { + throw (IOException)ex; + } + if (ex instanceof RuntimeException) { + throw (RuntimeException)ex; + } + throw new IOException(ex); + } + } + + private HttpServer initServer(String path, boolean addClasses) throws IOException { + HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535)); + + final ServerConfiguration conf = s.getServerConfiguration(); + if (addClasses) { + conf.addHttpHandler(new VM(), "/bck2brwsr.js"); + conf.addHttpHandler(new Classes(resources), "/classes/"); + } + return s; + } + + private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException { + wait = new CountDownLatch(1); + server = initServer(".", true); + final ServerConfiguration conf = server.getServerConfiguration(); + + class DynamicResourceHandler extends HttpHandler { + private final InvocationContext ic; + public DynamicResourceHandler(InvocationContext ic) { + if (ic == null || ic.resources.isEmpty()) { + throw new NullPointerException(); + } + this.ic = ic; + for (Resource r : ic.resources) { + conf.addHttpHandler(this, r.httpPath); + } + } + + public void close() { + conf.removeHttpHandler(this); + } + + @Override + public void service(Request request, Response response) throws Exception { + for (Resource r : ic.resources) { + if (r.httpPath.equals(request.getRequestURI())) { + LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); + response.setContentType(r.httpType); + r.httpContent.reset(); + String[] params = null; + if (r.parameters.length != 0) { + params = new String[r.parameters.length]; + for (int i = 0; i < r.parameters.length; i++) { + params[i] = request.getParameter(r.parameters[i]); + } + } + + copyStream(r.httpContent, response.getOutputStream(), null, params); + } + } + } + } + + conf.addHttpHandler(new Page(resources, + "org/apidesign/bck2brwsr/launcher/fximpl/harness.xhtml" + ), "/execute"); + + conf.addHttpHandler(new HttpHandler() { + int cnt; + List cases = new ArrayList<>(); + DynamicResourceHandler prev; + @Override + public void service(Request request, Response response) throws Exception { + String id = request.getParameter("request"); + String value = request.getParameter("result"); + if (value != null && value.indexOf((char)0xC5) != -1) { + value = toUTF8(value); + } + + + InvocationContext mi = null; + int caseNmbr = -1; + + if (id != null && value != null) { + LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); + value = decodeURL(value); + int indx = Integer.parseInt(id); + cases.get(indx).result(value, null); + if (++indx < cases.size()) { + mi = cases.get(indx); + LOG.log(Level.INFO, "Re-executing case {0}", indx); + caseNmbr = indx; + } + } else { + if (!cases.isEmpty()) { + LOG.info("Re-executing test cases"); + mi = cases.get(0); + caseNmbr = 0; + } + } + + if (prev != null) { + prev.close(); + prev = null; + } + + if (mi == null) { + mi = methods.take(); + caseNmbr = cnt++; + } + if (mi == END) { + response.getWriter().write(""); + wait.countDown(); + cnt = 0; + LOG.log(Level.INFO, "End of data reached. Exiting."); + return; + } + + if (!mi.resources.isEmpty()) { + prev = new DynamicResourceHandler(mi); + } + + cases.add(mi); + final String cn = mi.clazz.getName(); + final String mn = mi.methodName; + LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn}); + response.getWriter().write("{" + + "className: '" + cn + "', " + + "methodName: '" + mn + "', " + + "request: " + caseNmbr + ); + if (mi.html != null) { + response.getWriter().write(", html: '"); + response.getWriter().write(encodeJSON(mi.html)); + response.getWriter().write("'"); + } + response.getWriter().write("}"); + } + }, "/data"); + + this.brwsr = launchServerAndBrwsr(server, "/execute"); + } + + private static String encodeJSON(String in) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < in.length(); i++) { + char ch = in.charAt(i); + if (ch < 32 || ch == '\'' || ch == '"') { + sb.append("\\u"); + String hs = "0000" + Integer.toHexString(ch); + hs = hs.substring(hs.length() - 4); + sb.append(hs); + } else { + sb.append(ch); + } + } + return sb.toString(); + } + + @Override + public void shutdown() throws IOException { + methods.offer(END); + for (;;) { + int prev = methods.size(); + try { + if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) { + break; + } + } catch (InterruptedException ex) { + throw new IOException(ex); + } + if (prev == methods.size()) { + LOG.log( + Level.WARNING, + "Timeout and no test has been executed meanwhile (at {0}). Giving up.", + methods.size() + ); + break; + } + LOG.log(Level.INFO, + "Timeout, but tests got from {0} to {1}. Trying again.", + new Object[]{prev, methods.size()} + ); + } + stopServerAndBrwsr(server, brwsr); + } + + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + if (ch == '$' && params.length > 0) { + int cnt = is.read() - '0'; + if (baseURL != null && cnt == 'U' - '0') { + os.write(baseURL.getBytes("UTF-8")); + } else { + if (cnt >= 0 && cnt < params.length) { + os.write(params[cnt].getBytes("UTF-8")); + } else { + os.write('$'); + os.write(cnt + '0'); + } + } + } else { + os.write(ch); + } + } + } + + private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException { + server.start(); + NetworkListener listener = server.getListeners().iterator().next(); + int port = listener.getPort(); + + URI uri = new URI("http://localhost:" + port + page); + return showBrwsr(uri); + } + private static String toUTF8(String value) throws UnsupportedEncodingException { + byte[] arr = new byte[value.length()]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (byte)value.charAt(i); + } + return new String(arr, "UTF-8"); + } + + private static String decodeURL(String s) { + for (;;) { + int pos = s.indexOf('%'); + if (pos == -1) { + return s; + } + int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16); + s = s.substring(0, pos) + (char)i + s.substring(pos + 2); + } + } + + private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException { + if (brwsr == null) { + return; + } + Process process = (Process)brwsr[0]; + + server.stop(); + InputStream stdout = process.getInputStream(); + InputStream stderr = process.getErrorStream(); + drain("StdOut", stdout); + drain("StdErr", stderr); + process.destroy(); + int res; + try { + res = process.waitFor(); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + LOG.log(Level.INFO, "Exit code: {0}", res); + + deleteTree((File)brwsr[1]); + } + + private static void drain(String name, InputStream is) throws IOException { + int av = is.available(); + if (av > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("v== ").append(name).append(" ==v\n"); + while (av-- > 0) { + sb.append((char)is.read()); + } + sb.append("\n^== ").append(name).append(" ==^"); + LOG.log(Level.INFO, sb.toString()); + } + } + + private void deleteTree(File file) { + if (file == null) { + return; + } + File[] arr = file.listFiles(); + if (arr != null) { + for (File s : arr) { + deleteTree(s); + } + } + file.delete(); + } + + @Override + public void close() throws IOException { + shutdown(); + } + + protected Object[] showBrwsr(URI uri) throws IOException { + LOG.log(Level.INFO, "Showing {0}", uri); + if (cmd == null) { + try { + LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] { + System.getProperty("java.vm.name"), + System.getProperty("java.vm.vendor"), + System.getProperty("java.vm.version"), + }); + java.awt.Desktop.getDesktop().browse(uri); + LOG.log(Level.INFO, "Desktop.browse successfully finished"); + return null; + } catch (UnsupportedOperationException ex) { + LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage()); + LOG.log(Level.FINE, null, ex); + } + } + { + String cmdName = cmd == null ? "xdg-open" : cmd; + String[] cmdArr = { + cmdName, uri.toString() + }; + LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); + final Process process = Runtime.getRuntime().exec(cmdArr); + return new Object[] { process, null }; + } + } + + abstract void generateBck2BrwsrJS(StringBuilder sb, Object loader) throws IOException; + + private class Res { + public InputStream get(String resource) throws IOException { + for (ClassLoader l : loaders) { + URL u = null; + Enumeration en = l.getResources(resource); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u != null) { + return u.openStream(); + } + } + throw new IOException("Can't find " + resource); + } + } + + private static class Page extends HttpHandler { + final String resource; + private final String[] args; + private final Res res; + + public Page(Res res, String resource, String... args) { + this.res = res; + this.resource = resource; + this.args = args.length == 0 ? new String[] { "$0" } : args; + } + + @Override + public void service(Request request, Response response) throws Exception { + String r = computePage(request); + if (r.startsWith("/")) { + r = r.substring(1); + } + String[] replace = {}; + if (r.endsWith(".html")) { + response.setContentType("text/html"); + LOG.info("Content type text/html"); + replace = args; + } + if (r.endsWith(".xhtml")) { + response.setContentType("application/xhtml+xml"); + LOG.info("Content type application/xhtml+xml"); + replace = args; + } + OutputStream os = response.getOutputStream(); + try (InputStream is = res.get(r)) { + copyStream(is, os, request.getRequestURL().toString(), replace); + } catch (IOException ex) { + response.setDetailMessage(ex.getLocalizedMessage()); + response.setError(); + response.setStatus(404); + } + } + + protected String computePage(Request request) { + String r = resource; + if (r == null) { + r = request.getHttpHandlerPath(); + } + return r; + } + } + + private static class SubTree extends Page { + + public SubTree(Res res, String resource, String... args) { + super(res, resource, args); + } + + @Override + protected String computePage(Request request) { + return resource + request.getHttpHandlerPath(); + } + + + } + + private class VM extends HttpHandler { + @Override + public void service(Request request, Response response) throws Exception { + response.setCharacterEncoding("UTF-8"); + response.setContentType("text/javascript"); + StringBuilder sb = new StringBuilder(); + generateBck2BrwsrJS(sb, BaseHTTPLauncher.this.resources); + response.getWriter().write(sb.toString()); + } + } + + private static class Classes extends HttpHandler { + private final Res loader; + + public Classes(Res loader) { + this.loader = loader; + } + + @Override + public void service(Request request, Response response) throws Exception { + String res = request.getHttpHandlerPath(); + if (res.startsWith("/")) { + res = res.substring(1); + } + try (InputStream is = loader.get(res)) { + response.setContentType("text/javascript"); + Writer w = response.getWriter(); + w.append("["); + for (int i = 0;; i++) { + int b = is.read(); + if (b == -1) { + break; + } + if (i > 0) { + w.append(", "); + } + if (i % 20 == 0) { + w.write("\n"); + } + if (b > 127) { + b = b - 256; + } + w.append(Integer.toString(b)); + } + w.append("\n]"); + } catch (IOException ex) { + response.setStatus(HttpStatus.NOT_FOUND_404); + response.setError(); + response.setDetailMessage(ex.getMessage()); + } + } + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,129 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import org.apidesign.bck2brwsr.launcher.fximpl.FXBrwsr; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; + +import java.util.concurrent.Executors; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.application.Platform; +import org.apidesign.bck2brwsr.launcher.fximpl.JVMBridge; + +/** + * + * @author Jaroslav Tulach + */ +final class FXBrwsrLauncher extends BaseHTTPLauncher { + private static final Logger LOG = Logger.getLogger(FXBrwsrLauncher.class.getName()); + static { + try { + Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + m.setAccessible(true); + URL l = new URL("file://" + System.getProperty("java.home") + "/lib/jfxrt.jar"); + LOG.log(Level.INFO, "url : {0}", l); + m.invoke(ClassLoader.getSystemClassLoader(), l); + } catch (Exception ex) { + throw new LinkageError("Can't add jfxrt.jar on the classpath", ex); + } + } + + public FXBrwsrLauncher(String ignore) { + super(null); + } + + @Override + protected Object[] showBrwsr(final URI url) throws IOException { + try { + LOG.log(Level.INFO, "showBrwsr for {0}", url); + JVMBridge.registerClassLoaders(loaders()); + LOG.info("About to launch WebView"); + Executors.newSingleThreadExecutor().submit(new Runnable() { + @Override + public void run() { + LOG.log(Level.INFO, "In FX thread. Launching!"); + try { + FXBrwsr.launch(FXBrwsr.class, url.toString()); + LOG.log(Level.INFO, "Launcher is back. Closing"); + close(); + } catch (Throwable ex) { + LOG.log(Level.WARNING, "Error launching Web View", ex); + } + } + }); + } catch (Throwable ex) { + LOG.log(Level.WARNING, "Can't open WebView", ex); + } + return null; + } + + @Override + void generateBck2BrwsrJS(StringBuilder sb, Object loader) throws IOException { + sb.append("(function() {\n" + + " var impl = this.bck2brwsr;\n" + + " this.bck2brwsr = function() { return impl; };\n" + + "})(window);\n" + ); + JVMBridge.onBck2BrwsrLoad(); + } + + + + @Override + public void close() throws IOException { + super.close(); + Platform.exit(); + } + + public static void main(String... args) throws IOException { + String startPage = null; + + final ClassLoader cl = FXBrwsrLauncher.class.getClassLoader(); + startPage = findStartPage(cl, startPage); + if (startPage == null) { + throw new NullPointerException("Can't find StartPage tag in manifests!"); + } + + Launcher.showURL("fxbrwsr", cl, startPage); + } + + private static String findStartPage(final ClassLoader cl, String startPage) throws IOException { + Enumeration en = cl.getResources("META-INF/MANIFEST.MF"); + while (en.hasMoreElements()) { + URL url = en.nextElement(); + Manifest mf; + try (InputStream is = url.openStream()) { + mf = new Manifest(is); + } + String sp = mf.getMainAttributes().getValue("StartPage"); + if (sp != null) { + startPage = sp; + break; + } + } + return startPage; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fx/LauncherFX.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fx/LauncherFX.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,32 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.fx; + +import org.apidesign.bck2brwsr.launcher.Launcher; + +/** This is a launcher for the Bck2Brwsr + * project that is using JavaFX's WebView to display the browser inside + * real Java virtual machine. Use {@link Launcher} methods to access this + * functionality via public, supported methods. + * + * @author Jaroslav Tulach + */ +public final class LauncherFX { + private LauncherFX() { + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,395 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.fximpl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.Enumeration; +import javafx.scene.web.WebEngine; +import netscape.javascript.JSObject; + +/** + * + * @author Jaroslav Tulach + */ +public final class Console { + public Console() { + } + + private static Object getAttr(Object elem, String attr) { + return InvokeJS.CObject.call("getAttr", elem, attr); + } + + private static void setAttr(String id, String attr, Object value) { + InvokeJS.CObject.call("setAttrId", id, attr, value); + } + private static void setAttr(Object id, String attr, Object value) { + InvokeJS.CObject.call("setAttr", id, attr, value); + } + + private static void closeWindow() {} + + private static Object textArea; + private static Object statusArea; + + private static void log(String newText) { + if (textArea == null) { + return; + } + String attr = "value"; + setAttr(textArea, attr, getAttr(textArea, attr) + "\n" + newText); + setAttr(textArea, "scrollTop", getAttr(textArea, "scrollHeight")); + } + + private static void beginTest(Case c) { + Object[] arr = new Object[2]; + beginTest(c.getClassName() + "." + c.getMethodName(), c, arr); + textArea = arr[0]; + statusArea = arr[1]; + } + + private static void finishTest(Case c, Object res) { + if ("null".equals(res)) { + setAttr(statusArea, "innerHTML", "Success"); + } else { + setAttr(statusArea, "innerHTML", "Result " + res); + } + statusArea = null; + textArea = null; + } + + private static final String BEGIN_TEST = + "var ul = window.document.getElementById('bck2brwsr.result');\n" + + "var li = window.document.createElement('li');\n" + + "var span = window.document.createElement('span');" + + "span.innerHTML = test + ' - ';\n" + + "var details = window.document.createElement('a');\n" + + "details.innerHTML = 'Details';\n" + + "details.href = '#';\n" + + "var p = window.document.createElement('p');\n" + + "var status = window.document.createElement('a');\n" + + "status.innerHTML = 'running';" + + "details.onclick = function() { li.appendChild(p); li.removeChild(details); status.innerHTML = 'Run Again'; status.href = '#'; };\n" + + "status.onclick = function() { c.again(arr); }\n" + + "var pre = window.document.createElement('textarea');\n" + + "pre.cols = 100;" + + "pre.rows = 10;" + + "li.appendChild(span);\n" + + "li.appendChild(status);\n" + + "var span = window.document.createElement('span');" + + "span.innerHTML = ' ';\n" + + "li.appendChild(span);\n" + + "li.appendChild(details);\n" + + "p.appendChild(pre);\n" + + "ul.appendChild(li);\n" + + "arr[0] = pre;\n" + + "arr[1] = status;\n"; + + private static void beginTest(String test, Case c, Object[] arr) { + InvokeJS.CObject.call("beginTest", test, c, arr); + } + + private static final String LOAD_TEXT = + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, true);\n" + + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n" + + "request.onreadystatechange = function() {\n" + + " if (this.readyState!==4) return;\n" + + " try {" + + " arr[0] = this.responseText;\n" + + " callback.run__V();\n" + + " } catch (e) { alert(e); }" + + "};" + + "request.send();"; + private static void loadText(String url, Runnable callback, String[] arr) throws IOException { + InvokeJS.CObject.call("loadText", url, new Run(callback), arr); + } + + public static void runHarness(String url) throws IOException { + new Console().harness(url); + } + + public void harness(String url) throws IOException { + log("Connecting to " + url); + Request r = new Request(url); + } + + private static class Request implements Runnable { + private final String[] arr = { null }; + private final String url; + private Case c; + private int retries; + + private Request(String url) throws IOException { + this.url = url; + loadText(url, this, arr); + } + private Request(String url, String u) throws IOException { + this.url = url; + loadText(u, this, arr); + } + + @Override + public void run() { + try { + if (c == null) { + String data = arr[0]; + + if (data == null) { + log("Some error exiting"); + closeWindow(); + return; + } + + if (data.isEmpty()) { + log("No data, exiting"); + closeWindow(); + return; + } + + c = Case.parseData(data); + beginTest(c); + log("Got \"" + data + "\""); + } else { + log("Processing \"" + arr[0] + "\" for " + retries + " time"); + } + Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest(); + finishTest(c, result); + + String u = url + "?request=" + c.getRequestId() + "&result=" + result; + new Request(url, u); + } catch (Exception ex) { + if (ex instanceof InterruptedException) { + log("Re-scheduling in 100ms"); + schedule(this, 100); + return; + } + log(ex.getClass().getName() + ":" + ex.getMessage()); + } + } + } + + private static String encodeURL(String r) throws UnsupportedEncodingException { + final String SPECIAL = "%$&+,/:;=?@"; + StringBuilder sb = new StringBuilder(); + byte[] utf8 = r.getBytes("UTF-8"); + for (int i = 0; i < utf8.length; i++) { + int ch = utf8[i] & 0xff; + if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) { + final String numbers = "0" + Integer.toHexString(ch); + sb.append("%").append(numbers.substring(numbers.length() - 2)); + } else { + if (ch == 32) { + sb.append("+"); + } else { + sb.append((char)ch); + } + } + } + return sb.toString(); + } + + static String invoke(String clazz, String method) throws + ClassNotFoundException, InvocationTargetException, IllegalAccessException, + InstantiationException, InterruptedException { + final Object r = new Case(null).invokeMethod(clazz, method); + return r == null ? "null" : r.toString().toString(); + } + + /** Helper method that inspects the classpath and loads given resource + * (usually a class file). Used while running tests in Rhino. + * + * @param name resource name to find + * @return the array of bytes in the given resource + * @throws IOException I/O in case something goes wrong + */ + public static byte[] read(String name) throws IOException { + URL u = null; + Enumeration en = Console.class.getClassLoader().getResources(name); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + try (InputStream is = u.openStream()) { + byte[] arr; + arr = new byte[is.available()]; + int offset = 0; + while (offset < arr.length) { + int len = is.read(arr, offset, arr.length - offset); + if (len == -1) { + throw new IOException("Can't read " + name); + } + offset += len; + } + return arr; + } + } + + private static void turnAssetionStatusOn() { + } + + private static Object schedule(Runnable r, int time) { + return InvokeJS.CObject.call("schedule", new Run(r), time); + } + + private static final class Case { + private final Object data; + private Object inst; + + private Case(Object data) { + this.data = data; + } + + public static Case parseData(String s) { + return new Case(toJSON(s)); + } + + public String getMethodName() { + return (String) value("methodName", data); + } + + public String getClassName() { + return (String) value("className", data); + } + + public int getRequestId() { + Object v = value("request", data); + if (v instanceof Number) { + return ((Number)v).intValue(); + } + return Integer.parseInt(v.toString()); + } + + public String getHtmlFragment() { + return (String) value("html", data); + } + + void again(Object[] arr) { + try { + textArea = arr[0]; + statusArea = arr[1]; + setAttr(textArea, "value", ""); + runTest(); + } catch (Exception ex) { + log(ex.getClass().getName() + ":" + ex.getMessage()); + } + } + + private Object runTest() throws IllegalAccessException, + IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException, + InvocationTargetException, InstantiationException, InterruptedException { + if (this.getHtmlFragment() != null) { + setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment()); + } + log("Invoking " + this.getClassName() + '.' + this.getMethodName() + " as request: " + this.getRequestId()); + Object result = invokeMethod(this.getClassName(), this.getMethodName()); + setAttr("bck2brwsr.fragment", "innerHTML", ""); + log("Result: " + result); + result = encodeURL("" + result); + log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result); + return result; + } + + private Object invokeMethod(String clazz, String method) + throws ClassNotFoundException, InvocationTargetException, + InterruptedException, IllegalAccessException, IllegalArgumentException, + InstantiationException { + Method found = null; + Class c = Class.forName(clazz); + for (Method m : c.getMethods()) { + if (m.getName().equals(method)) { + found = m; + } + } + Object res; + if (found != null) { + try { + if ((found.getModifiers() & Modifier.STATIC) != 0) { + res = found.invoke(null); + } else { + if (inst == null) { + inst = c.newInstance(); + } + res = found.invoke(inst); + } + } catch (Throwable ex) { + if (ex instanceof InvocationTargetException) { + ex = ((InvocationTargetException) ex).getTargetException(); + } + if (ex instanceof InterruptedException) { + throw (InterruptedException)ex; + } + res = ex.getClass().getName() + ":" + ex.getMessage(); + } + } else { + res = "Can't find method " + method + " in " + clazz; + } + return res; + } + + private static Object toJSON(String s) { + return InvokeJS.CObject.call("toJSON", s); + } + + private static Object value(String p, Object d) { + return ((JSObject)d).getMember(p); + } + } + + private static String safe(String txt) { + return "try {" + txt + "} catch (err) { alert(err); }"; + } + + static { + turnAssetionStatusOn(); + } + + private static final class InvokeJS { + static final JSObject CObject = initJS(); + + private static JSObject initJS() { + WebEngine web = (WebEngine) System.getProperties().get("webEngine"); + return (JSObject) web.executeScript("(function() {" + + "var CObject = {};" + + + "CObject.getAttr = function(elem, attr) { return elem[attr].toString(); };" + + + "CObject.setAttrId = function(id, attr, value) { window.document.getElementById(id)[attr] = value; };" + + "CObject.setAttr = function(elem, attr, value) { elem[attr] = value; };" + + + "CObject.beginTest = function(test, c, arr) {" + safe(BEGIN_TEST) + "};" + + + "CObject.loadText = function(url, callback, arr) {" + safe(LOAD_TEXT.replace("run__V", "run")) + "};" + + + "CObject.schedule = function(r, time) { return window.setTimeout(function() { r.run(); }, time); };" + + + "CObject.toJSON = function(s) { return eval('(' + s + ')'); };" + + + "return CObject;" + + "})(this)"); + } + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,184 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.fximpl; + +import java.util.List; +import java.util.TooManyListenersException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.geometry.VPos; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebEvent; +import javafx.scene.web.WebView; +import javafx.stage.Modality; +import javafx.stage.Stage; +import netscape.javascript.JSObject; + +/** + * Demonstrates a WebView object accessing a web page. + * + * @see javafx.scene.web.WebView + * @see javafx.scene.web.WebEngine + */ +public class FXBrwsr extends Application { + private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName()); + + @Override + public void start(Stage primaryStage) throws Exception { + Pane root = new WebViewPane(getParameters().getUnnamed()); + primaryStage.setScene(new Scene(root, 1024, 768)); + LOG.info("Showing the stage"); + primaryStage.show(); + LOG.log(Level.INFO, "State shown: {0}", primaryStage.isShowing()); + } + + /** + * Create a resizable WebView pane + */ + private class WebViewPane extends Pane { + private final JVMBridge bridge = new JVMBridge(); + + public WebViewPane(List params) { + LOG.log(Level.INFO, "Initializing WebView with {0}", params); + VBox.setVgrow(this, Priority.ALWAYS); + setMaxWidth(Double.MAX_VALUE); + setMaxHeight(Double.MAX_VALUE); + WebView view = new WebView(); + view.setMinSize(500, 400); + view.setPrefSize(500, 400); + final WebEngine eng = view.getEngine(); + try { + JVMBridge.addBck2BrwsrLoad(new InitBck2Brwsr(eng)); + } catch (TooManyListenersException ex) { + LOG.log(Level.SEVERE, null, ex); + } + + if (params.size() > 0) { + LOG.log(Level.INFO, "loading page {0}", params.get(0)); + eng.load(params.get(0)); + LOG.fine("back from load"); + } + eng.setOnAlert(new EventHandler>() { + @Override + public void handle(WebEvent t) { + final Stage dialogStage = new Stage(); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.setTitle("Warning"); + final Button button = new Button("Close"); + final Text text = new Text(t.getData()); + + VBox box = new VBox(); + box.setAlignment(Pos.CENTER); + box.setSpacing(10); + box.setPadding(new Insets(10)); + box.getChildren().addAll(text, button); + + dialogStage.setScene(new Scene(box)); + + button.setCancelButton(true); + button.setOnAction(new EventHandler() { + @Override + public void handle(ActionEvent t) { + dialogStage.close(); + } + }); + + dialogStage.centerOnScreen(); + dialogStage.showAndWait(); + } + }); + GridPane grid = new GridPane(); + grid.setVgap(5); + grid.setHgap(5); + GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS); + grid.getColumnConstraints().addAll(new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true), new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true)); + grid.getChildren().addAll(view); + getChildren().add(grid); + } + + boolean initBck2Brwsr(WebEngine webEngine) { + JSObject jsobj = (JSObject) webEngine.executeScript("window"); + LOG.log(Level.FINE, "window: {0}", jsobj); + Object prev = jsobj.getMember("bck2brwsr"); + if ("undefined".equals(prev)) { + System.getProperties().put("webEngine", webEngine); + jsobj.setMember("bck2brwsr", bridge); + return true; + } + return false; + } + + @Override + protected void layoutChildren() { + List managed = getManagedChildren(); + double width = getWidth(); + double height = getHeight(); + double top = getInsets().getTop(); + double right = getInsets().getRight(); + double left = getInsets().getLeft(); + double bottom = getInsets().getBottom(); + for (int i = 0; i < managed.size(); i++) { + Node child = managed.get(i); + layoutInArea(child, left, top, width - left - right, height - top - bottom, 0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER); + } + } + + private class InitBck2Brwsr implements ChangeListener, Runnable { + private final WebEngine eng; + + public InitBck2Brwsr(WebEngine eng) { + this.eng = eng; + } + + @Override + public synchronized void changed(ObservableValue ov, Void t, Void t1) { + Platform.runLater(this); + try { + wait(); + } catch (InterruptedException ex) { + LOG.log(Level.SEVERE, null, ex); + } + } + + @Override + public synchronized void run() { + initBck2Brwsr(eng); + notifyAll(); + } + } + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,64 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.fximpl; + +import java.util.TooManyListenersException; +import javafx.beans.value.ChangeListener; + +/** + * + * @author Jaroslav Tulach + */ +public final class JVMBridge { + private static ClassLoader[] ldrs; + private static ChangeListener onBck2BrwsrLoad; + + public static void registerClassLoaders(ClassLoader[] loaders) { + ldrs = loaders.clone(); + } + + public static void addBck2BrwsrLoad(ChangeListener l) throws TooManyListenersException { + if (onBck2BrwsrLoad != null) { + throw new TooManyListenersException(); + } + onBck2BrwsrLoad = l; + } + + public static void onBck2BrwsrLoad() { + ChangeListener l = onBck2BrwsrLoad; + if (l != null) { + l.changed(null, null, null); + } + } + + public Class loadClass(String name) throws ClassNotFoundException { + System.err.println("trying to load " + name); + ClassNotFoundException ex = null; + if (ldrs != null) for (ClassLoader l : ldrs) { + try { + return Class.forName(name, true, l); + } catch (ClassNotFoundException ex2) { + ex = ex2; + } + } + if (ex == null) { + ex = new ClassNotFoundException("No loaders"); + } + throw ex; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Run.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Run.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,35 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ + +package org.apidesign.bck2brwsr.launcher.fximpl; + +/** + * + * @author Jaroslav Tulach + */ +public final class Run implements Runnable { + private final Runnable r; + Run(Runnable r) { + this.r = r; + } + + @Override + public void run() { + r.run(); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/fx/src/main/resources/org/apidesign/bck2brwsr/launcher/fximpl/harness.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/fx/src/main/resources/org/apidesign/bck2brwsr/launcher/fximpl/harness.xhtml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,52 @@ + + + + + + Bck2Brwsr Harness + + + + + +

Bck2Brwsr Execution Harness

+ +
    +
+ +
+ + + + diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/http/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,55 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + launcher-pom + 0.8-SNAPSHOT + + org.apidesign.bck2brwsr + launcher.http + 0.8-SNAPSHOT + Bck2Brwsr Launcher + http://maven.apache.org + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-javadoc-plugin + + org.apidesign.bck2brwsr.launcher.b2b + false + + + + + + UTF-8 + + + + ${project.groupId} + launcher + ${project.version} + + + org.glassfish.grizzly + grizzly-http-server + 2.2.19 + + + ${project.groupId} + vm4brwsr + ${project.version} + + + diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,604 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; +import org.apidesign.vm4brwsr.Bck2Brwsr; +import org.glassfish.grizzly.PortRange; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.http.server.NetworkListener; +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.grizzly.http.server.ServerConfiguration; +import org.glassfish.grizzly.http.util.HttpStatus; + +/** + * Lightweight server to launch Bck2Brwsr applications and tests. + * Supports execution in native browser as well as Java's internal + * execution engine. + */ +final class Bck2BrwsrLauncher extends Launcher implements Closeable { + private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName()); + private static final InvocationContext END = new InvocationContext(null, null, null); + private final Set loaders = new LinkedHashSet<>(); + private final BlockingQueue methods = new LinkedBlockingQueue<>(); + private long timeOut; + private final Res resources = new Res(); + private final String cmd; + private Object[] brwsr; + private HttpServer server; + private CountDownLatch wait; + + public Bck2BrwsrLauncher(String cmd) { + this.cmd = cmd; + addClassLoader(Bck2Brwsr.class.getClassLoader()); + setTimeout(180000); + } + + @Override + InvocationContext runMethod(InvocationContext c) throws IOException { + loaders.add(c.clazz.getClassLoader()); + methods.add(c); + try { + c.await(timeOut); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + return c; + } + + public void setTimeout(long ms) { + timeOut = ms; + } + + public void addClassLoader(ClassLoader url) { + this.loaders.add(url); + } + + public void showURL(String startpage) throws IOException { + if (!startpage.startsWith("/")) { + startpage = "/" + startpage; + } + HttpServer s = initServer(".", true); + int last = startpage.lastIndexOf('/'); + String prefix = startpage.substring(0, last); + String simpleName = startpage.substring(last); + s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/"); + try { + launchServerAndBrwsr(s, simpleName); + } catch (URISyntaxException | InterruptedException ex) { + throw new IOException(ex); + } + } + + void showDirectory(File dir, String startpage) throws IOException { + if (!startpage.startsWith("/")) { + startpage = "/" + startpage; + } + HttpServer s = initServer(dir.getPath(), false); + try { + launchServerAndBrwsr(s, startpage); + } catch (URISyntaxException | InterruptedException ex) { + throw new IOException(ex); + } + } + + @Override + public void initialize() throws IOException { + try { + executeInBrowser(); + } catch (InterruptedException ex) { + final InterruptedIOException iio = new InterruptedIOException(ex.getMessage()); + iio.initCause(ex); + throw iio; + } catch (Exception ex) { + if (ex instanceof IOException) { + throw (IOException)ex; + } + if (ex instanceof RuntimeException) { + throw (RuntimeException)ex; + } + throw new IOException(ex); + } + } + + private HttpServer initServer(String path, boolean addClasses) throws IOException { + HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535)); + + final ServerConfiguration conf = s.getServerConfiguration(); + if (addClasses) { + conf.addHttpHandler(new VM(resources), "/bck2brwsr.js"); + conf.addHttpHandler(new Classes(resources), "/classes/"); + } + return s; + } + + private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException { + wait = new CountDownLatch(1); + server = initServer(".", true); + final ServerConfiguration conf = server.getServerConfiguration(); + + class DynamicResourceHandler extends HttpHandler { + private final InvocationContext ic; + public DynamicResourceHandler(InvocationContext ic) { + if (ic == null || ic.resources.isEmpty()) { + throw new NullPointerException(); + } + this.ic = ic; + for (Resource r : ic.resources) { + conf.addHttpHandler(this, r.httpPath); + } + } + + public void close() { + conf.removeHttpHandler(this); + } + + @Override + public void service(Request request, Response response) throws Exception { + for (Resource r : ic.resources) { + if (r.httpPath.equals(request.getRequestURI())) { + LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); + response.setContentType(r.httpType); + r.httpContent.reset(); + String[] params = null; + if (r.parameters.length != 0) { + params = new String[r.parameters.length]; + for (int i = 0; i < r.parameters.length; i++) { + params[i] = request.getParameter(r.parameters[i]); + } + } + + copyStream(r.httpContent, response.getOutputStream(), null, params); + } + } + } + } + + conf.addHttpHandler(new Page(resources, + "org/apidesign/bck2brwsr/launcher/harness.xhtml" + ), "/execute"); + + conf.addHttpHandler(new HttpHandler() { + int cnt; + List cases = new ArrayList<>(); + DynamicResourceHandler prev; + @Override + public void service(Request request, Response response) throws Exception { + String id = request.getParameter("request"); + String value = request.getParameter("result"); + if (value != null && value.indexOf((char)0xC5) != -1) { + value = toUTF8(value); + } + + + InvocationContext mi = null; + int caseNmbr = -1; + + if (id != null && value != null) { + LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); + value = decodeURL(value); + int indx = Integer.parseInt(id); + cases.get(indx).result(value, null); + if (++indx < cases.size()) { + mi = cases.get(indx); + LOG.log(Level.INFO, "Re-executing case {0}", indx); + caseNmbr = indx; + } + } else { + if (!cases.isEmpty()) { + LOG.info("Re-executing test cases"); + mi = cases.get(0); + caseNmbr = 0; + } + } + + if (prev != null) { + prev.close(); + prev = null; + } + + if (mi == null) { + mi = methods.take(); + caseNmbr = cnt++; + } + if (mi == END) { + response.getWriter().write(""); + wait.countDown(); + cnt = 0; + LOG.log(Level.INFO, "End of data reached. Exiting."); + return; + } + + if (!mi.resources.isEmpty()) { + prev = new DynamicResourceHandler(mi); + } + + cases.add(mi); + final String cn = mi.clazz.getName(); + final String mn = mi.methodName; + LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn}); + response.getWriter().write("{" + + "className: '" + cn + "', " + + "methodName: '" + mn + "', " + + "request: " + caseNmbr + ); + if (mi.html != null) { + response.getWriter().write(", html: '"); + response.getWriter().write(encodeJSON(mi.html)); + response.getWriter().write("'"); + } + response.getWriter().write("}"); + } + }, "/data"); + + this.brwsr = launchServerAndBrwsr(server, "/execute"); + } + + private static String encodeJSON(String in) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < in.length(); i++) { + char ch = in.charAt(i); + if (ch < 32 || ch == '\'' || ch == '"') { + sb.append("\\u"); + String hs = "0000" + Integer.toHexString(ch); + hs = hs.substring(hs.length() - 4); + sb.append(hs); + } else { + sb.append(ch); + } + } + return sb.toString(); + } + + @Override + public void shutdown() throws IOException { + methods.offer(END); + for (;;) { + int prev = methods.size(); + try { + if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) { + break; + } + } catch (InterruptedException ex) { + throw new IOException(ex); + } + if (prev == methods.size()) { + LOG.log( + Level.WARNING, + "Timeout and no test has been executed meanwhile (at {0}). Giving up.", + methods.size() + ); + break; + } + LOG.log(Level.INFO, + "Timeout, but tests got from {0} to {1}. Trying again.", + new Object[]{prev, methods.size()} + ); + } + stopServerAndBrwsr(server, brwsr); + } + + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + if (ch == '$' && params.length > 0) { + int cnt = is.read() - '0'; + if (baseURL != null && cnt == 'U' - '0') { + os.write(baseURL.getBytes("UTF-8")); + } else { + if (cnt >= 0 && cnt < params.length) { + os.write(params[cnt].getBytes("UTF-8")); + } else { + os.write('$'); + os.write(cnt + '0'); + } + } + } else { + os.write(ch); + } + } + } + + private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException { + server.start(); + NetworkListener listener = server.getListeners().iterator().next(); + int port = listener.getPort(); + + URI uri = new URI("http://localhost:" + port + page); + LOG.log(Level.INFO, "Showing {0}", uri); + if (cmd == null) { + try { + LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] { + System.getProperty("java.vm.name"), + System.getProperty("java.vm.vendor"), + System.getProperty("java.vm.version"), + }); + java.awt.Desktop.getDesktop().browse(uri); + LOG.log(Level.INFO, "Desktop.browse successfully finished"); + return null; + } catch (UnsupportedOperationException ex) { + LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage()); + LOG.log(Level.FINE, null, ex); + } + } + { + String cmdName = cmd == null ? "xdg-open" : cmd; + String[] cmdArr = { + cmdName, uri.toString() + }; + LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); + final Process process = Runtime.getRuntime().exec(cmdArr); + return new Object[] { process, null }; + } + } + private static String toUTF8(String value) throws UnsupportedEncodingException { + byte[] arr = new byte[value.length()]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (byte)value.charAt(i); + } + return new String(arr, "UTF-8"); + } + + private static String decodeURL(String s) { + for (;;) { + int pos = s.indexOf('%'); + if (pos == -1) { + return s; + } + int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16); + s = s.substring(0, pos) + (char)i + s.substring(pos + 2); + } + } + + private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException { + if (brwsr == null) { + return; + } + Process process = (Process)brwsr[0]; + + server.stop(); + InputStream stdout = process.getInputStream(); + InputStream stderr = process.getErrorStream(); + drain("StdOut", stdout); + drain("StdErr", stderr); + process.destroy(); + int res; + try { + res = process.waitFor(); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + LOG.log(Level.INFO, "Exit code: {0}", res); + + deleteTree((File)brwsr[1]); + } + + private static void drain(String name, InputStream is) throws IOException { + int av = is.available(); + if (av > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("v== ").append(name).append(" ==v\n"); + while (av-- > 0) { + sb.append((char)is.read()); + } + sb.append("\n^== ").append(name).append(" ==^"); + LOG.log(Level.INFO, sb.toString()); + } + } + + private void deleteTree(File file) { + if (file == null) { + return; + } + File[] arr = file.listFiles(); + if (arr != null) { + for (File s : arr) { + deleteTree(s); + } + } + file.delete(); + } + + @Override + public void close() throws IOException { + shutdown(); + } + + private class Res implements Bck2Brwsr.Resources { + @Override + public InputStream get(String resource) throws IOException { + URL u = null; + for (ClassLoader l : loaders) { + Enumeration en = l.getResources(resource); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + } + if (u != null) { + return u.openStream(); + } + throw new IOException("Can't find " + resource); + } + } + + private static class Page extends HttpHandler { + final String resource; + private final String[] args; + private final Res res; + + public Page(Res res, String resource, String... args) { + this.res = res; + this.resource = resource; + this.args = args.length == 0 ? new String[] { "$0" } : args; + } + + @Override + public void service(Request request, Response response) throws Exception { + String r = computePage(request); + if (r.startsWith("/")) { + r = r.substring(1); + } + String[] replace = {}; + if (r.endsWith(".html")) { + response.setContentType("text/html"); + LOG.info("Content type text/html"); + replace = args; + } + if (r.endsWith(".xhtml")) { + response.setContentType("application/xhtml+xml"); + LOG.info("Content type application/xhtml+xml"); + replace = args; + } + OutputStream os = response.getOutputStream(); + try (InputStream is = res.get(r)) { + copyStream(is, os, request.getRequestURL().toString(), replace); + } catch (IOException ex) { + response.setDetailMessage(ex.getLocalizedMessage()); + response.setError(); + response.setStatus(404); + } + } + + protected String computePage(Request request) { + String r = resource; + if (r == null) { + r = request.getHttpHandlerPath(); + } + return r; + } + } + + private static class SubTree extends Page { + + public SubTree(Res res, String resource, String... args) { + super(res, resource, args); + } + + @Override + protected String computePage(Request request) { + return resource + request.getHttpHandlerPath(); + } + + + } + + private static class VM extends HttpHandler { + private final String bck2brwsr; + + public VM(Res loader) throws IOException { + StringBuilder sb = new StringBuilder(); + Bck2Brwsr.generate(sb, loader); + sb.append( + "(function WrapperVM(global) {" + + " function ldCls(res) {\n" + + " var request = new XMLHttpRequest();\n" + + " request.open('GET', '/classes/' + res, false);\n" + + " request.send();\n" + + " if (request.status !== 200) return null;\n" + + " var arr = eval('(' + request.responseText + ')');\n" + + " return arr;\n" + + " }\n" + + " var prevvm = global.bck2brwsr;\n" + + " global.bck2brwsr = function() {\n" + + " var args = Array.prototype.slice.apply(arguments);\n" + + " args.unshift(ldCls);\n" + + " return prevvm.apply(null, args);\n" + + " };\n" + + "})(this);\n" + ); + this.bck2brwsr = sb.toString(); + } + + @Override + public void service(Request request, Response response) throws Exception { + response.setCharacterEncoding("UTF-8"); + response.setContentType("text/javascript"); + response.getWriter().write(bck2brwsr); + } + } + + private static class Classes extends HttpHandler { + private final Res loader; + + public Classes(Res loader) { + this.loader = loader; + } + + @Override + public void service(Request request, Response response) throws Exception { + String res = request.getHttpHandlerPath(); + if (res.startsWith("/")) { + res = res.substring(1); + } + try (InputStream is = loader.get(res)) { + response.setContentType("text/javascript"); + Writer w = response.getWriter(); + w.append("["); + for (int i = 0;; i++) { + int b = is.read(); + if (b == -1) { + break; + } + if (i > 0) { + w.append(", "); + } + if (i % 20 == 0) { + w.write("\n"); + } + if (b > 127) { + b = b - 256; + } + w.append(Integer.toString(b)); + } + w.append("\n]"); + } catch (IOException ex) { + response.setStatus(HttpStatus.NOT_FOUND_404); + response.setError(); + response.setDetailMessage(ex.getMessage()); + } + } + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,135 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import org.apidesign.bck2brwsr.launcher.impl.Console; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** + * Tests execution in Java's internal scripting engine. + */ +final class JSLauncher extends Launcher { + private static final Logger LOG = Logger.getLogger(JSLauncher.class.getName()); + private Set loaders = new LinkedHashSet<>(); + private final Res resources = new Res(); + private Invocable code; + private StringBuilder codeSeq; + private Object console; + + JSLauncher() { + addClassLoader(Bck2Brwsr.class.getClassLoader()); + } + + @Override InvocationContext runMethod(InvocationContext mi) { + loaders.add(mi.clazz.getClassLoader()); + try { + long time = System.currentTimeMillis(); + LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.clazz.getName(), mi.methodName}); + String res = code.invokeMethod( + console, + "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2", + mi.clazz.getName(), mi.methodName).toString(); + time = System.currentTimeMillis() - time; + LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time}); + mi.result(res, null); + } catch (ScriptException | NoSuchMethodException ex) { + mi.result(null, ex); + } + return mi; + } + + public void addClassLoader(ClassLoader url) { + this.loaders.add(url); + } + + @Override + public void initialize() throws IOException { + try { + initRhino(); + } catch (Exception ex) { + if (ex instanceof IOException) { + throw (IOException)ex; + } + if (ex instanceof RuntimeException) { + throw (RuntimeException)ex; + } + throw new IOException(ex); + } + } + + private void initRhino() throws IOException, ScriptException, NoSuchMethodException { + StringBuilder sb = new StringBuilder(); + Bck2Brwsr.generate(sb, new Res()); + + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine mach = sem.getEngineByExtension("js"); + + sb.append( + "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.impl.Console.read);" + + "\nfunction initVM() { return vm; };" + + "\n"); + + Object res = mach.eval(sb.toString()); + if (!(mach instanceof Invocable)) { + throw new IOException("It is invocable object: " + res); + } + code = (Invocable) mach; + codeSeq = sb; + + Object vm = code.invokeFunction("initVM"); + console = code.invokeMethod(vm, "loadClass", Console.class.getName()); + } + + @Override + public void shutdown() throws IOException { + } + + @Override + public String toString() { + return codeSeq.toString(); + } + + private class Res implements Bck2Brwsr.Resources { + @Override + public InputStream get(String resource) throws IOException { + for (ClassLoader l : loaders) { + URL u = null; + Enumeration en = l.getResources(resource); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u != null) { + return u.openStream(); + } + } + throw new IOException("Can't find " + resource); + } + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/b2b/LauncherBck2Brwsr.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/b2b/LauncherBck2Brwsr.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,33 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.b2b; + +import java.awt.Desktop; +import org.apidesign.bck2brwsr.launcher.Launcher; + +/** This is a launcher for the Bck2Brwsr + * project that is using {@link Desktop} (or {@link Process}) to display the + * external browser in separate process. Use {@link Launcher} methods to access this + * functionality via public, supported methods. + * + * @author Jaroslav Tulach + */ +public final class LauncherBck2Brwsr { + private LauncherBck2Brwsr() { + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,356 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.Enumeration; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author Jaroslav Tulach + */ +public class Console { + private Console() { + } + static { + turnAssetionStatusOn(); + } + + @JavaScriptBody(args = {"id", "attr"}, body = + "return window.document.getElementById(id)[attr].toString();") + private static native Object getAttr(String id, String attr); + @JavaScriptBody(args = {"elem", "attr"}, body = + "return elem[attr].toString();") + private static native Object getAttr(Object elem, String attr); + + @JavaScriptBody(args = {"id", "attr", "value"}, body = + "window.document.getElementById(id)[attr] = value;") + private static native void setAttr(String id, String attr, Object value); + @JavaScriptBody(args = {"elem", "attr", "value"}, body = + "elem[attr] = value;") + private static native void setAttr(Object id, String attr, Object value); + + @JavaScriptBody(args = {}, body = "return; window.close();") + private static native void closeWindow(); + + private static Object textArea; + private static Object statusArea; + + private static void log(String newText) { + if (textArea == null) { + return; + } + String attr = "value"; + setAttr(textArea, attr, getAttr(textArea, attr) + "\n" + newText); + setAttr(textArea, "scrollTop", getAttr(textArea, "scrollHeight")); + } + + private static void beginTest(Case c) { + Object[] arr = new Object[2]; + beginTest(c.getClassName() + "." + c.getMethodName(), c, arr); + textArea = arr[0]; + statusArea = arr[1]; + } + + private static void finishTest(Case c, Object res) { + if ("null".equals(res)) { + setAttr(statusArea, "innerHTML", "Success"); + } else { + setAttr(statusArea, "innerHTML", "Result " + res); + } + statusArea = null; + textArea = null; + } + + @JavaScriptBody(args = { "test", "c", "arr" }, body = + "var ul = window.document.getElementById('bck2brwsr.result');\n" + + "var li = window.document.createElement('li');\n" + + "var span = window.document.createElement('span');" + + "span.innerHTML = test + ' - ';\n" + + "var details = window.document.createElement('a');\n" + + "details.innerHTML = 'Details';\n" + + "details.href = '#';\n" + + "var p = window.document.createElement('p');\n" + + "var status = window.document.createElement('a');\n" + + "status.innerHTML = 'running';" + + "details.onclick = function() { li.appendChild(p); li.removeChild(details); status.innerHTML = 'Run Again'; status.href = '#'; };\n" + + "status.onclick = function() { c.again__V_3Ljava_lang_Object_2(arr); }\n" + + "var pre = window.document.createElement('textarea');\n" + + "pre.cols = 100;" + + "pre.rows = 10;" + + "li.appendChild(span);\n" + + "li.appendChild(status);\n" + + "var span = window.document.createElement('span');" + + "span.innerHTML = ' ';\n" + + "li.appendChild(span);\n" + + "li.appendChild(details);\n" + + "p.appendChild(pre);\n" + + "ul.appendChild(li);\n" + + "arr[0] = pre;\n" + + "arr[1] = status;\n" + ) + private static native void beginTest(String test, Case c, Object[] arr); + + @JavaScriptBody(args = { "url", "callback", "arr" }, body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, true);\n" + + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n" + + "request.onreadystatechange = function() {\n" + + " if (this.readyState!==4) return;\n" + + " arr[0] = this.responseText;\n" + + " callback.run__V();\n" + + "};" + + "request.send();" + ) + private static native void loadText(String url, Runnable callback, String[] arr) throws IOException; + + public static void harness(String url) throws IOException { + log("Connecting to " + url); + Request r = new Request(url); + } + + private static class Request implements Runnable { + private final String[] arr = { null }; + private final String url; + private Case c; + private int retries; + + private Request(String url) throws IOException { + this.url = url; + loadText(url, this, arr); + } + private Request(String url, String u) throws IOException { + this.url = url; + loadText(u, this, arr); + } + + @Override + public void run() { + try { + if (c == null) { + String data = arr[0]; + + if (data == null) { + log("Some error exiting"); + closeWindow(); + return; + } + + if (data.isEmpty()) { + log("No data, exiting"); + closeWindow(); + return; + } + + c = Case.parseData(data); + beginTest(c); + log("Got \"" + data + "\""); + } else { + log("Processing \"" + arr[0] + "\" for " + retries + " time"); + } + Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest(); + finishTest(c, result); + + String u = url + "?request=" + c.getRequestId() + "&result=" + result; + new Request(url, u); + } catch (Exception ex) { + if (ex instanceof InterruptedException) { + log("Re-scheduling in 100ms"); + schedule(this, 100); + return; + } + log(ex.getClass().getName() + ":" + ex.getMessage()); + } + } + } + + private static String encodeURL(String r) throws UnsupportedEncodingException { + final String SPECIAL = "%$&+,/:;=?@"; + StringBuilder sb = new StringBuilder(); + byte[] utf8 = r.getBytes("UTF-8"); + for (int i = 0; i < utf8.length; i++) { + int ch = utf8[i] & 0xff; + if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) { + final String numbers = "0" + Integer.toHexString(ch); + sb.append("%").append(numbers.substring(numbers.length() - 2)); + } else { + if (ch == 32) { + sb.append("+"); + } else { + sb.append((char)ch); + } + } + } + return sb.toString(); + } + + static String invoke(String clazz, String method) throws + ClassNotFoundException, InvocationTargetException, IllegalAccessException, + InstantiationException, InterruptedException { + final Object r = new Case(null).invokeMethod(clazz, method); + return r == null ? "null" : r.toString().toString(); + } + + /** Helper method that inspects the classpath and loads given resource + * (usually a class file). Used while running tests in Rhino. + * + * @param name resource name to find + * @return the array of bytes in the given resource + * @throws IOException I/O in case something goes wrong + */ + public static byte[] read(String name) throws IOException { + URL u = null; + Enumeration en = Console.class.getClassLoader().getResources(name); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + try (InputStream is = u.openStream()) { + byte[] arr; + arr = new byte[is.available()]; + int offset = 0; + while (offset < arr.length) { + int len = is.read(arr, offset, arr.length - offset); + if (len == -1) { + throw new IOException("Can't read " + name); + } + offset += len; + } + return arr; + } + } + + @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") + private static void turnAssetionStatusOn() { + } + + @JavaScriptBody(args = {"r", "time"}, body = + "return window.setTimeout(function() { r.run__V(); }, time);") + private static native Object schedule(Runnable r, int time); + + private static final class Case { + private final Object data; + private Object inst; + + private Case(Object data) { + this.data = data; + } + + public static Case parseData(String s) { + return new Case(toJSON(s)); + } + + public String getMethodName() { + return value("methodName", data); + } + + public String getClassName() { + return value("className", data); + } + + public String getRequestId() { + return value("request", data); + } + + public String getHtmlFragment() { + return value("html", data); + } + + void again(Object[] arr) { + try { + textArea = arr[0]; + statusArea = arr[1]; + setAttr(textArea, "value", ""); + runTest(); + } catch (Exception ex) { + log(ex.getClass().getName() + ":" + ex.getMessage()); + } + } + + private Object runTest() throws IllegalAccessException, + IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException, + InvocationTargetException, InstantiationException, InterruptedException { + if (this.getHtmlFragment() != null) { + setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment()); + } + log("Invoking " + this.getClassName() + '.' + this.getMethodName() + " as request: " + this.getRequestId()); + Object result = invokeMethod(this.getClassName(), this.getMethodName()); + setAttr("bck2brwsr.fragment", "innerHTML", ""); + log("Result: " + result); + result = encodeURL("" + result); + log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result); + return result; + } + + private Object invokeMethod(String clazz, String method) + throws ClassNotFoundException, InvocationTargetException, + InterruptedException, IllegalAccessException, IllegalArgumentException, + InstantiationException { + Method found = null; + Class c = Class.forName(clazz); + for (Method m : c.getMethods()) { + if (m.getName().equals(method)) { + found = m; + } + } + Object res; + if (found != null) { + try { + if ((found.getModifiers() & Modifier.STATIC) != 0) { + res = found.invoke(null); + } else { + if (inst == null) { + inst = c.newInstance(); + } + res = found.invoke(inst); + } + } catch (Throwable ex) { + if (ex instanceof InvocationTargetException) { + ex = ((InvocationTargetException) ex).getTargetException(); + } + if (ex instanceof InterruptedException) { + throw (InterruptedException)ex; + } + res = ex.getClass().getName() + ":" + ex.getMessage(); + } + } else { + res = "Can't find method " + method + " in " + clazz; + } + return res; + } + + @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") + private static native Object toJSON(String s); + + @JavaScriptBody(args = {"p", "d"}, body = + "var v = d[p];\n" + + "if (typeof v === 'undefined') return null;\n" + + "return v.toString();" + ) + private static native String value(String p, Object d); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/http/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,43 @@ + + + + + + Bck2Brwsr Harness + + + + + +

Bck2Brwsr Execution Harness

+ +
    +
+ +
+ + + + diff -r 146ae7b52b64 -r 3800d11c0bdb launcher/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,19 @@ + + + 4.0.0 + + bck2brwsr + org.apidesign + 0.8-SNAPSHOT + + org.apidesign.bck2brwsr + launcher-pom + 0.8-SNAPSHOT + pom + Launchers + + api + http + fx + + \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb pom.xml --- a/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -3,7 +3,7 @@ 4.0.0 org.apidesign bck2brwsr - 0.5-SNAPSHOT + 0.8-SNAPSHOT pom Back 2 Browser @@ -11,11 +11,17 @@ jvnet-parent 3 + + UTF-8 + RELEASE73 + COPYING + dew javaquery benchmarks ide + launcher rt @@ -80,21 +86,24 @@ .*/** rt/emul/*/src/main/** rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java - rt/mojo/src/main/resources/archetype-resources/** - rt/vmtest/src/test/resources/** + rt/archetype/src/main/resources/archetype-resources/** + rt/emul/compact/src/test/resources/** dew/src/main/resources/org/apidesign/bck2brwsr/dew/** javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout*.js - - org.apache.maven.plugins - maven-release-plugin - 2.4 - - release-${releaseVersion} - - + + maven-release-plugin + 2.4 + + forked-path + false + -Pjvnet-release -Pgpg + release-${releaseVersion} + + + @@ -139,26 +148,120 @@ org.netbeans.api org-netbeans-modules-classfile - RELEASE73 + ${netbeans.version} jar org.netbeans.api org-openide-util-lookup - RELEASE73 + ${netbeans.version} compile jar + org.netbeans.api + org-netbeans-api-annotations-common + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-source + ${netbeans.version} + + + org.netbeans.api + org-netbeans-libs-javacapi + ${netbeans.version} + + + org.netbeans.api + org-netbeans-spi-java-hints + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-parsing-api + ${netbeans.version} + + + org.netbeans.api + org-netbeans-spi-editor-hints + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-lexer + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-lexer + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-hints-test + ${netbeans.version} + + + org.netbeans.api + org-netbeans-libs-junit4 + ${netbeans.version} + + + org.netbeans.modules + org-netbeans-lib-nbjavac + ${netbeans.version} + + + org.netbeans.modules + org-netbeans-modules-web-browser-api + ${netbeans.version} + + + org-netbeans-core + org.netbeans.modules + + + org-netbeans-core-multiview + org.netbeans.api + + + org-netbeans-libs-lucene + org.netbeans.api + + + org-netbeans-modules-diff + org.netbeans.api + + + org-netbeans-modules-editor-fold + org.netbeans.api + + + org-netbeans-modules-editor-guards + org.netbeans.api + + + + + org-netbeans-modules-projectapi + org.netbeans.api + jar + ${netbeans.version} + + org.netbeans.modules org-netbeans-modules-html-parser - RELEASE73 + ${netbeans.version} compile jar - - COPYING - - \ No newline at end of file + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,62 @@ + + + 4.0.0 + + rt + org.apidesign.bck2brwsr + 0.8-SNAPSHOT + + org.apidesign.bck2brwsr + bck2brwsr-archetype-html-sample + 0.8-SNAPSHOT + jar + Bck2Brwsr Maven Archetype + + Creates a skeletal HTML page and associated Java controller class. + Runs in any browser (even without Java plugin) with the help of Bck2Brwsr + virtual machine. + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + test + + test + + integration-test + + + ${project.build.directory}/bck2brwsr-archetype-html-sample-${project.version}.jar + + false + + + + + + + + + + org.testng + testng + test + + + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/java/org/apidesign/bck2brwsr/archetype/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/java/org/apidesign/bck2brwsr/archetype/package-info.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,18 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.archetype; diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,55 @@ + + + + + + src/main/java + + **/App.java + + + + src/main/resources + + **/*.xhtml + **/*.html + + + + src/test/java + + **/*Test.java + + + + + + nbactions.xml + + + + + + bck2brwsr-assembly.xml + + + + \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/bck2brwsr-assembly.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/bck2brwsr-assembly.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,61 @@ + + + + + bck2brwsr + + zip + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/classes/${package.replace('.','/')}/index.html + / + index.html + + + + \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/nbactions.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/nbactions.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,10 @@ + + + + run + + process-classes + bck2brwsr:brwsr + + + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,135 @@ + + + 4.0.0 + + ${groupId} + ${artifactId} + ${version} + jar + + ${artifactId} + + + + java.net + Java.net + https://maven.java.net/content/repositories/releases/ + + true + + + + netbeans + NetBeans + http://bits.netbeans.org/maven2/ + + + + + java.net + Java.net + https://maven.java.net/content/repositories/releases/ + + true + + + + + + UTF-8 + + + + + org.apidesign.bck2brwsr + bck2brwsr-maven-plugin + 0.8-SNAPSHOT + + + + brwsr + + + + + ${package.replace('.','/')}/index.html + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + bck2brwsr-assembly.xml + + + + + + + + + + + org.apidesign.bck2brwsr + emul + 0.8-SNAPSHOT + rt + + + org.apidesign.bck2brwsr + javaquery.api + 0.8-SNAPSHOT + + + org.testng + testng + 6.5.2 + test + + + org.apidesign.bck2brwsr + vm4brwsr + js + zip + 0.8-SNAPSHOT + provided + + + org.apidesign.bck2brwsr + vmtest + 0.8-SNAPSHOT + test + + + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/src/main/java/App.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/src/main/java/App.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,88 @@ +package ${package}; + +import java.util.List; +import org.apidesign.bck2brwsr.htmlpage.api.*; +import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; +import org.apidesign.bck2brwsr.htmlpage.api.Page; +import org.apidesign.bck2brwsr.htmlpage.api.Property; +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; + +/** This is the controller class for associated index.html page. The Index + * is autogenerated by parsing the index.html page. It fields represent individual + * elements annotated by "id" in the page. + */ +@Page(xhtml="index.html", className="Index", properties={ + @Property(name="name", type=String.class), + @Property(name="messages", type=String.class, array=true), +}) +public class App { + static { + Index model = new Index(); + model.setName("World"); + model.applyBindings(); + } + + /** + * @param m the model of the index page creates in static initializer + */ + @On(event = CLICK, id="hello") + static void hello(Index m) { + display(m.getHelloMessage(), m); + m.getMessages().add(m.getHelloMessage()); + } + + /** Reacts when mouse moves over the canvas. + * + * @param m the model of the page + * @param x property "x" extracted from the event generated by the browser + * @param y property "y" from the mouse event + */ + @On(event = MOUSE_MOVE, id="canvas") + static void clearPoint(Index m, int x, int y) { + GraphicsContext g = m.canvas.getContext(); + boolean even = (x + y) % 2 == 0; + if (even) { + g.setFillStyle("blue"); + } else { + g.setFillStyle("red"); + } + g.clearRect(0, 0, 1000, 1000); + g.setFont("italic 40px Calibri"); + g.fillText(m.getHelloMessage(), 10, 40); + } + + /** Callback function called by the KnockOut/Java binding on elements + * representing href's with individual messages being their data. + * + * @param data the data associated with the element + * @param m the model of the page + */ + @OnFunction + static void display(String data, Index m) { + GraphicsContext g = m.canvas.getContext(); + g.clearRect(0, 0, 1000, 1000); + g.setFillStyle("black"); + g.setFont("italic 40px Calibri"); + g.fillText(data, 10, 40); + } + + /** Callback function. + * + * @param data data associated with the actual element on the page + * @param m the model of the page + */ + @OnFunction + static void remove(String data, Index m) { + m.getMessages().remove(data); + } + + @ComputedProperty + static String helloMessage(String name) { + return "Hello " + name + "!"; + } + + @ComputedProperty + static boolean noMessages(List messages) { + return messages.isEmpty(); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/src/main/resources/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/src/main/resources/index.html Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,31 @@ + + + + + Bck2Brwsr's Hello World + + +

Loading Bck2Brwsr's Hello World...

+ Your name: + +

+ + +

+ + +
No message displayed yet.
+ + + + + + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/src/test/java/AppTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/src/test/java/AppTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,26 @@ +package ${package}; + +import static org.testng.Assert.*; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** Demonstrating POJO testing of HTML page model. Runs in good old HotSpot + * as it does not reference any HTML elements or browser functionality. Just + * operates on the page model. + * + * @author Jaroslav Tulach + */ +public class AppTest { + private Index model; + + + @BeforeMethod + public void initModel() { + model = new Index().applyBindings(); + } + + @Test public void testHelloMessage() { + model.setName("Joe"); + assertEquals(model.getHelloMessage(), "Hello Joe!", "Cleared after pressing +"); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/src/test/java/InconsistencyTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/src/test/java/InconsistencyTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,40 @@ +package ${package}; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** Bck2brwsr cares about compatibility with real Java. Whatever API is + * supported by bck2brwsr, it needs to behave the same way as when running + * in HotSpot VM. + *

+ * There can be bugs, however. To help us fix them, we kindly ask you to + * write an "inconsistency" test. A test that compares behavior of the API + * between real VM and bck2brwsr VM. This class is skeleton of such test. + * + * @author Jaroslav Tulach + */ +public class InconsistencyTest { + /** A method to demonstrate inconsistency between bck2brwsr and HotSpot. + * Make calls to an API that behaves strangely, return some result at + * the end. No need to use any assert. + * + * @return value to compare between HotSpot and bck2brwsr + */ + @Compare + public int checkStringHashCode() throws Exception { + return "Is string hashCode the same?".hashCode(); + } + + /** Factory method that creates a three tests for each method annotated with + * {@link org.apidesign.bck2brwsr.vmtest.Compare}. One executes the code in + * HotSpot, one in Rhino and the last one compares the results. + * + * @see org.apidesign.bck2brwsr.vmtest.VMTest + */ + @Factory + public static Object[] create() { + return VMTest.create(InconsistencyTest.class); + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,46 @@ +package ${package}; + +import org.apidesign.bck2brwsr.htmlpage.api.OnEvent; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.HtmlFragment; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** Sometimes it is useful to run tests inside of the real browser. + * To do that just annotate your method with {@link org.apidesign.bck2brwsr.vmtest.BrwsrTest} + * and that is it. If your code references elements on the HTML page, + * you can pass in an {@link org.apidesign.bck2brwsr.vmtest.HtmlFragment} which + * will be made available on the page before your test starts. + * + * @author Jaroslav Tulach + */ +public class IntegrationTest { + + /** Write to testing code here. Use assert (but not TestNG's + * Assert, as TestNG is not compiled with target 1.6 yet). + */ + @HtmlFragment( + "

Loading Bck2Brwsr's Hello World...

\n" + + "Your name: \n" + + "\n" + + "

\n" + + " \n" + + "

\n" + ) + @BrwsrTest + public void modifyValueAssertChangeInModel() { + Index m = new Index(); + m.setName("Joe Hacker"); + m.applyBindings(); + assert "Joe Hacker".equals(m.input.getValue()) : "Value is really Joe Hacker: " + m.input.getValue(); + m.input.setValue("Happy Joe"); + m.triggerEvent(m.input, OnEvent.CHANGE); + assert "Happy Joe".equals(m.getName()) : "Name property updated to Happy Joe: " + m.getName(); + } + + @Factory + public static Object[] create() { + return VMTest.create(IntegrationTest.class); + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/archetype/src/test/java/org/apidesign/bck2brwsr/archetype/ArchetypeVersionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/archetype/src/test/java/org/apidesign/bck2brwsr/archetype/ArchetypeVersionTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,101 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.archetype; + +import java.net.URL; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; +import org.testng.annotations.Test; +import static org.testng.Assert.*; +import org.testng.annotations.BeforeClass; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +/** + * + * @author Jaroslav Tulach + */ +public class ArchetypeVersionTest { + private String version; + + public ArchetypeVersionTest() { + } + + @BeforeClass public void readCurrentVersion() throws Exception { + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader(); + URL u = l.getResource("META-INF/maven/org.apidesign.bck2brwsr/bck2brwsr-archetype-html-sample/pom.xml"); + assertNotNull(u, "Own pom found: " + System.getProperty("java.class.path")); + + final XPathFactory fact = XPathFactory.newInstance(); + fact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + + XPathExpression xp = fact.newXPath().compile("project/version/text()"); + + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(u.openStream()); + version = xp.evaluate(dom); + + assertFalse(version.isEmpty(), "There should be some version string"); + } + + + @Test public void testComparePomDepsVersions() throws Exception { + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader(); + URL r = l.getResource("archetype-resources/pom.xml"); + assertNotNull(r, "Archetype pom found"); + + final XPathFactory fact = XPathFactory.newInstance(); + XPathExpression xp2 = fact.newXPath().compile( + "//version[../groupId/text() = 'org.apidesign.bck2brwsr']/text()" + ); + + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream()); + NodeList arch = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET); + + if (arch.getLength() < 3) { + fail("There should be at least three dependencies to bck2brwsr APIs: " + arch.getLength()); + } + + for (int i = 0; i < arch.getLength(); i++) { + assertEquals(arch.item(i).getTextContent(), version, i + "th dependency needs to be on latest version of bck2brwsr"); + } + } + + @Test public void testNbActions() throws Exception { + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader(); + URL r = l.getResource("archetype-resources/nbactions.xml"); + assertNotNull(r, "Archetype nb file found"); + + final XPathFactory fact = XPathFactory.newInstance(); + XPathExpression xp2 = fact.newXPath().compile( + "//goal/text()" + ); + + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream()); + NodeList goals = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET); + + for (int i = 0; i < goals.getLength(); i++) { + String s = goals.item(i).getTextContent(); + if (s.contains("bck2brwsr")) { + assertFalse(s.matches(".*bck2brwsr.*[0-9].*"), "No numbers: " + s); + } + } + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/core/pom.xml --- a/rt/core/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/core/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ org.apidesign.bck2brwsr rt - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr core - 0.5-SNAPSHOT + 0.8-SNAPSHOT Bck2Brwsr Native Annotations http://maven.apache.org diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/brwsrtest/pom.xml --- a/rt/emul/brwsrtest/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/brwsrtest/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -1,15 +1,14 @@ - + 4.0.0 org.apidesign.bck2brwsr emul.pom - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr brwsrtest - 0.5-SNAPSHOT + 0.8-SNAPSHOT Tests Inside Real Browser http://maven.apache.org @@ -43,5 +42,11 @@ ${project.version} test + + ${project.groupId} + launcher.http + ${project.version} + provided + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/pom.xml --- a/rt/emul/compact/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/compact/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ org.apidesign.bck2brwsr emul.pom - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr emul - 0.5-SNAPSHOT + 0.8-SNAPSHOT Bck2Brwsr API Profile http://maven.apache.org @@ -28,6 +28,12 @@ test + ${project.groupId} + launcher.http + ${project.version} + test + + org.netbeans.api org-openide-util-lookup test diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipCompatibilityTest.java --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipCompatibilityTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipCompatibilityTest.java Thu May 02 09:18:22 2013 +0200 @@ -30,7 +30,7 @@ public class ZipCompatibilityTest { @Compare public String testDemoStaticCalculator() throws IOException { - InputStream is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + InputStream is = getClass().getResourceAsStream("demo.static.calculator-TEST.jar"); ZipArchive zip = ZipArchive.createZip(is); final String ts = zip.toString(); return ts.substring(0, 4096) + ts.hashCode(); diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipVsJzLibTest.java --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipVsJzLibTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipVsJzLibTest.java Thu May 02 09:18:22 2013 +0200 @@ -27,10 +27,10 @@ */ public class ZipVsJzLibTest { @Test public void r() throws IOException { - InputStream is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + InputStream is = getClass().getResourceAsStream("demo.static.calculator-TEST.jar"); ZipArchive zip = ZipArchive.createZip(is); - is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + is = getClass().getResourceAsStream("demo.static.calculator-TEST.jar"); ZipArchive real = ZipArchive.createReal(is); real.assertEquals(zip, "Are they the same?"); diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,43 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class AssertionTest { + + @Compare public Object checkAssert() throws ClassNotFoundException { + try { + assert false : "Is assertion status on?"; + return null; + } catch (AssertionError ex) { + return ex.getClass().getName(); + } + } + + @Factory + public static Object[] create() { + return VMTest.create(AssertionTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/BrwsrCheckTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/BrwsrCheckTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,57 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.HtmlFragment; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class BrwsrCheckTest { + + @BrwsrTest public void assertWindowObjectIsDefined() { + assert window() != null : "No window object found!"; + } + + + + + @HtmlFragment("

\n" + + "Hello!\n" + + "

\n") + @BrwsrTest public void accessProvidedFragment() { + assert getElementById("hello") != null : "Element with 'hello' ID found"; + } + + @Factory + public static Object[] create() { + return VMTest.create(BrwsrCheckTest.class); + } + + + @JavaScriptBody(args = {}, body = "return window;") + private static native Object window(); + + @JavaScriptBody(args = { "id" }, body = "return window.document.getElementById(id);") + private static native Object getElementById(String id); +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ByteArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ByteArithmeticTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,147 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ByteArithmeticTest { + + private static byte add(byte x, byte y) { + return (byte)(x + y); + } + + private static byte sub(byte x, byte y) { + return (byte)(x - y); + } + + private static byte mul(byte x, byte y) { + return (byte)(x * y); + } + + private static byte div(byte x, byte y) { + return (byte)(x / y); + } + + private static byte mod(byte x, byte y) { + return (byte)(x % y); + } + + @Compare public byte conversion() { + return (byte)123456; + } + + @Compare public byte addOverflow() { + return add(Byte.MAX_VALUE, (byte)1); + } + + @Compare public byte subUnderflow() { + return sub(Byte.MIN_VALUE, (byte)1); + } + + @Compare public byte addMaxByteAndMaxByte() { + return add(Byte.MAX_VALUE, Byte.MAX_VALUE); + } + + @Compare public byte subMinByteAndMinByte() { + return sub(Byte.MIN_VALUE, Byte.MIN_VALUE); + } + + @Compare public byte multiplyMaxByte() { + return mul(Byte.MAX_VALUE, (byte)2); + } + + @Compare public byte multiplyMaxByteAndMaxByte() { + return mul(Byte.MAX_VALUE, Byte.MAX_VALUE); + } + + @Compare public byte multiplyMinByte() { + return mul(Byte.MIN_VALUE, (byte)2); + } + + @Compare public byte multiplyMinByteAndMinByte() { + return mul(Byte.MIN_VALUE, Byte.MIN_VALUE); + } + + @Compare public byte multiplyPrecision() { + return mul((byte)17638, (byte)1103); + } + + @Compare public byte division() { + return div((byte)1, (byte)2); + } + + @Compare public byte divisionReminder() { + return mod((byte)1, (byte)2); + } + + private static int readShort(byte[] byteCodes, int offset) { + int signed = byteCodes[offset]; + byte b0 = (byte)signed; + return (b0 << 8) | (byteCodes[offset + 1] & 0xff); + } + + private static int readShortArg(byte[] byteCodes, int offsetInstruction) { + return readShort(byteCodes, offsetInstruction + 1); + } + + @Compare public int readIntArgs255and156() { + final byte[] arr = new byte[] { (byte)0, (byte)255, (byte)156 }; + + assert arr[1] == -1 : "First byte: " + arr[1]; + assert arr[2] == -100 : "Second byte: " + arr[2]; + final int ret = readShortArg(arr, 0); + assert ret < 65000: "Value: " + ret; + return ret; + } + + @JavaScriptBody(args = { "arr" }, body = "arr[1] = 255; arr[2] = 156; return arr;") + private static byte[] fill255and156(byte[] arr) { + arr[1] = (byte)255; + arr[2] = (byte)156; + return arr; + } + + @Compare public int readIntArgs255and156JSArray() { + final byte[] arr = fill255and156(new byte[] { 0, 0, 0 }); + + final int ret = readShortArg(arr, 0); + assert ret < 65000: "Value: " + ret; + return ret; + } + + @Compare public int readIntArgsMinus1andMinus100() { + final byte[] arr = new byte[] { (byte)0, (byte)-1, (byte)-100 }; + + assert arr[1] == -1 : "First byte: " + arr[1]; + assert arr[2] == -100 : "Second byte: " + arr[2]; + + return readShortArg(arr, 0); + } + + @Factory + public static Object[] create() { + return VMTest.create(ByteArithmeticTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CloneTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CloneTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,73 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CloneTest { + private int value; + + @Compare + public Object notSupported() throws CloneNotSupportedException { + return this.clone(); + } + + @Compare public String sameClass() throws CloneNotSupportedException { + return new Clnbl().clone().getClass().getName(); + } + + @Compare public boolean differentInstance() throws CloneNotSupportedException { + Clnbl orig = new Clnbl(); + return orig == orig.clone(); + } + + @Compare public int sameReference() throws CloneNotSupportedException { + CloneTest self = this; + Clnbl orig = new Clnbl(); + self.value = 33; + orig.ref = self; + return ((Clnbl)orig.clone()).ref.value; + } + + @Compare public int sameValue() throws CloneNotSupportedException { + Clnbl orig = new Clnbl(); + orig.value = 10; + return ((Clnbl)orig.clone()).value; + } + + @Factory + public static Object[] create() { + return VMTest.create(CloneTest.class); + } + + public static final class Clnbl implements Cloneable { + public CloneTest ref; + private int value; + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareByteArrayTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareByteArrayTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,103 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareByteArrayTest { + @Compare public int byteArraySum() { + byte[] arr = createArray(); + return sumByteArr(arr); + } + + @Compare public int countZeros() { + int zeros = 0; + for (Byte b : createArray()) { + if (b == 0) { + zeros++; + } + } + return zeros; + } + + private static int sumByteArr(byte[] arr) { + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + } + return sum; + } + + @Compare public String noOutOfBounds() { + return atIndex(1); + } + + @Compare public String outOfBounds() { + return atIndex(5); + } + + @Compare public String outOfBoundsMinus() { + return atIndex(-1); + } + + @Compare public String toOfBounds() { + return toIndex(5); + } + + @Compare public String toOfBoundsMinus() { + return toIndex(-1); + } + + @Compare public int multiArrayLength() { + int[][] arr = new int[1][0]; + return arr[0].length; + } + + @Compare public int multiObjectArrayLength() { + Object[][] arr = new Object[1][0]; + return arr[0].length; + } + + private static final int[] arr = { 0, 1, 2 }; + public static String atIndex(int at) { + return "at@" + arr[at]; + } + public static String toIndex(int at) { + arr[at] = 10; + return "ok"; + } + + + @Factory + public static Object[] create() { + return VMTest.create(CompareByteArrayTest.class); + } + + private byte[] createArray() { + byte[] arr = new byte[10]; + arr[5] = 3; + arr[7] = 8; + return arr; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,50 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareHashTest { + @Compare public int hashOfString() { + return "Ahoj".hashCode(); + } + + @Compare public int hashRemainsYieldsZero() { + Object o = new Object(); + return o.hashCode() - o.hashCode(); + } + + @Compare public int initializeInStatic() { + return StaticUse.NON_NULL.hashCode() - StaticUse.NON_NULL.hashCode(); + } + + @Compare public int hashOfInt() { + return Integer.valueOf(Integer.MAX_VALUE).hashCode(); + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareHashTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareIntArrayTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareIntArrayTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,63 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareIntArrayTest { + @Compare public int integerArraySum() { + int[] arr = createArray(); + return sumIntArr(arr); + } + + @Compare public int countZeros() { + int zeros = 0; + for (Integer i : createArray()) { + if (i == 0) { + zeros++; + } + } + return zeros; + } + + private static int sumIntArr(int[] arr) { + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + } + return sum; + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareIntArrayTest.class); + } + + private int[] createArray() { + int[] arr = new int[10]; + arr[5] = 3; + arr[7] = 8; + return arr; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,174 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareStringsTest { + @Compare public String firstChar() { + return "" + ("Hello".toCharArray()[0]); + } + + @Compare public String classCast() { + Object o = firstChar(); + return String.class.cast(o); + } + + @Compare public String classCastThrown() { + Object o = null; + return String.class.cast(o); + } + + @Compare public boolean equalToNull() { + return "Ahoj".equals(null); + } + + @Compare public int highByteLenght() { + byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; + return new String(arr, 0).length(); + } + + @Compare public String highByte() { + byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; + StringBuilder sb = new StringBuilder(); + sb.append("pref:"); + sb.append(new String(arr, 0)); + return sb.toString(); + } + + @Compare public static Object compareURLs() throws MalformedURLException { + return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); + } + + @Compare public String deleteLastTwoCharacters() { + StringBuilder sb = new StringBuilder(); + sb.append("453.0"); + if (sb.toString().endsWith(".0")) { + final int l = sb.length(); + sb.delete(l - 2, l); + } + return sb.toString().toString(); + } + + @Compare public String nameOfStringClass() throws Exception { + return Class.forName("java.lang.String").getName(); + } + @Compare public String nameOfArrayClass() throws Exception { + return Class.forName("org.apidesign.bck2brwsr.tck.CompareHashTest").getName(); + } + + @Compare public String lowerHello() { + return "HeLlO".toLowerCase(); + } + + @Compare public String lowerA() { + return String.valueOf(Character.toLowerCase('A')).toString(); + } + @Compare public String upperHello() { + return "hello".toUpperCase(); + } + + @Compare public String upperA() { + return String.valueOf(Character.toUpperCase('a')).toString(); + } + + @Compare public boolean matchRegExp() throws Exception { + return "58038503".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExp() throws Exception { + return "58038503GH".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExpFully() throws Exception { + return "Hello".matches("Hell"); + } + + @Compare public String emptyCharArray() { + char[] arr = new char[10]; + return new String(arr); + } + + @Compare public String variousCharacterTests() throws Exception { + StringBuilder sb = new StringBuilder(); + + sb.append(Character.isUpperCase('a')); + sb.append(Character.isUpperCase('A')); + sb.append(Character.isLowerCase('a')); + sb.append(Character.isLowerCase('A')); + + sb.append(Character.isLetter('A')); + sb.append(Character.isLetterOrDigit('9')); + sb.append(Character.isLetterOrDigit('A')); + sb.append(Character.isLetter('0')); + + return sb.toString().toString(); + } + + @Compare + public String nullFieldInitialized() { + NullField nf = new NullField(); + return ("" + nf.name).toString(); + } + @Compare + public String toUTFString() throws UnsupportedEncodingException { + byte[] arr = { + (byte) -59, (byte) -67, (byte) 108, (byte) 117, (byte) -59, (byte) -91, + (byte) 111, (byte) 117, (byte) -60, (byte) -115, (byte) 107, (byte) -61, + (byte) -67, (byte) 32, (byte) 107, (byte) -59, (byte) -81, (byte) -59, + (byte) -120 + }; + return new String(arr, "utf-8"); + } + + @Compare + public int stringToBytesLenght() throws UnsupportedEncodingException { + return "\u017dlu\u0165ou\u010dk\u00fd k\u016f\u0148".getBytes("utf8").length; + } + + @Compare public String replaceSeq() { + return "Hello World.".replace(".", "!"); + } + @Compare public String replaceSeqAll() { + return "Hello World! Hello World.".replace("World", "Jarda"); + } + @Compare public String replaceSeqAA() { + String res = "aaa".replace("aa", "b"); + assert res.equals("ba") : "Expecting ba: " + res; + return res; + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareStringsTest.class); + } + + private static final class NullField { + + String name; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/DoubleTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/DoubleTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,65 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class DoubleTest { + @Compare public String integerToString() { + return toStr(1); + } + + @Compare public String integerAndHalfToString() { + return toStr(1.5); + } + + @Compare public double longToAndBack() { + return Double.parseDouble(toStr(Long.MAX_VALUE / 10)); + } + + @Compare public String negativeIntToString() { + return toStr(-10); + } + + @Compare public String negativeIntAndHalfToString() { + return toStr(-10.5); + } + + @Compare public double negativeLongAndBack() { + return Double.parseDouble(toStr(Long.MIN_VALUE / 10)); + } + + @Compare public double canParseExp() { + return Double.parseDouble(toStr(1.7976931348623157e+308)); + } + + private static String toStr(double d) { + return Double.toString(d); + } + + @Factory + public static Object[] create() { + return VMTest.create(DoubleTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,106 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.io.InputStream; +import java.net.URL; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Http; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class HttpResourceTest { + + @Http({ + @Http.Resource(path = "/xhr", content = "Hello Brwsr!", mimeType = "text/plain") + }) + @BrwsrTest + + + public String testReadContentViaXHR() throws Exception { + String msg = read("/xhr"); + assert "Hello Brwsr!".equals(msg) : "The message was " + msg; + return msg; + } + + @Http({ + @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + }) + @BrwsrTest + public String testReadContentViaURL() throws Exception { + URL url = new URL("http:/url"); + String msg = (String) url.getContent(); + assert "Hello via URL!".equals(msg) : "The message was " + msg; + return msg; + } + @Http({ + @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + }) + @BrwsrTest + public String testReadContentViaURLWithStringParam() throws Exception { + URL url = new URL("http:/url"); + String msg = (String) url.getContent(new Class[] { String.class }); + assert "Hello via URL!".equals(msg) : "The message was " + msg; + return msg; + } + + @Http({ + @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + }) + @BrwsrTest + public void testReadByte() throws Exception { + URL url = new URL("http:/bytes"); + final Object res = url.getContent(new Class[] { byte[].class }); + assert res instanceof byte[] : "Expecting byte[]: " + res; + byte[] arr = (byte[]) res; + assert arr.length == 1 : "One byte " + arr.length; + assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); + } + + @Http({ + @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + }) + @BrwsrTest + public void testReadByteViaInputStream() throws Exception { + URL url = new URL("http:/bytes"); + InputStream is = url.openStream(); + byte[] arr = new byte[10]; + int len = is.read(arr); + assert len == 1 : "One byte " + len; + assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); + } + + @JavaScriptBody(args = { "url" }, body = + "var req = new XMLHttpRequest();\n" + + "req.open('GET', url, false);\n" + + "req.send();\n" + + "return req.responseText;" + ) + private static native String read(String url); + + + @Factory + public static Object[] create() { + return VMTest.create(HttpResourceTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceA.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceA.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,34 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +/** + * + * @author Jaroslav Tulach + */ +public class InheritanceA { + private String name; + + public void setA(String n) { + this.name = n; + } + + public String getA() { + return name; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceB.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceB.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,34 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +/** + * + * @author Jaroslav Tulach + */ +public class InheritanceB extends InheritanceA { + private String name; + + public void setB(String n) { + this.name = n; + } + + public String getB() { + return name; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class InheritanceTest { + + @Compare public String checkFieldsIndependent() throws ClassNotFoundException { + InheritanceB ib = new InheritanceB(); + ib.setA("A"); + ib.setB("B"); + return "A: " + ib.getA() + " B: " + ib.getB(); + } + + @Factory + public static Object[] create() { + return VMTest.create(InheritanceTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,166 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class IntegerArithmeticTest { + + private static int add(int x, int y) { + return x + y; + } + + private static int sub(int x, int y) { + return x - y; + } + + private static int mul(int x, int y) { + return x * y; + } + + private static int div(int x, int y) { + return x / y; + } + + private static int mod(int x, int y) { + return x % y; + } + + private static int neg(int x) { + return (-x); + } + + private static float fadd(float x, float y) { + return x + y; + } + + private static double dadd(double x, double y) { + return x + y; + } + + @Compare public int addOverflow() { + return add(Integer.MAX_VALUE, 1); + } + + @Compare public int subUnderflow() { + return sub(Integer.MIN_VALUE, 1); + } + + @Compare public int addMaxIntAndMaxInt() { + return add(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Compare public int subMinIntAndMinInt() { + return sub(Integer.MIN_VALUE, Integer.MIN_VALUE); + } + + @Compare public int multiplyMaxInt() { + return mul(Integer.MAX_VALUE, 2); + } + + @Compare public int multiplyMaxIntAndMaxInt() { + return mul(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Compare public int multiplyMinInt() { + return mul(Integer.MIN_VALUE, 2); + } + + @Compare public int multiplyMinIntAndMinInt() { + return mul(Integer.MIN_VALUE, Integer.MIN_VALUE); + } + + @Compare public int multiplyPrecision() { + return mul(119106029, 1103515245); + } + + @Compare public int division() { + return div(1, 2); + } + + @Compare public int divisionReminder() { + return mod(1, 2); + } + + @Compare public int negativeDivision() { + return div(-7, 3); + } + + @Compare public int negativeDivisionReminder() { + return mod(-7, 3); + } + + @Compare public int conversionFromFloat() { + return (int) fadd(-2, -0.6f); + } + + @Compare public int conversionFromDouble() { + return (int) dadd(-2, -0.6); + } + + @Compare public boolean divByZeroThrowsArithmeticException() { + try { + div(1, 0); + return false; + } catch (final ArithmeticException e) { + return true; + } + } + + @Compare public boolean modByZeroThrowsArithmeticException() { + try { + mod(1, 0); + return false; + } catch (final ArithmeticException e) { + return true; + } + } + + @Compare public int negate() { + return neg(123456); + } + + @Compare public int negateMaxInt() { + return neg(Integer.MAX_VALUE); + } + + @Compare public int negateMinInt() { + return neg(Integer.MIN_VALUE); + } + + @Compare public int sumTwoDimensions() { + int[][] matrix = createMatrix(4, 3); + matrix[0][0] += 10; + return matrix[0][0]; + } + + static int[][] createMatrix(int x, int y) { + return new int[x][y]; + } + + @Factory + public static Object[] create() { + return VMTest.create(IntegerArithmeticTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,376 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class LongArithmeticTest { + + private static long add(long x, long y) { + return (x + y); + } + + private static long sub(long x, long y) { + return (x - y); + } + + private static long mul(long x, long y) { + return (x * y); + } + + private static long div(long x, long y) { + return (x / y); + } + + private static long mod(long x, long y) { + return (x % y); + } + + private static long neg(long x) { + return (-x); + } + + private static long shl(long x, int b) { + return (x << b); + } + + private static long shr(long x, int b) { + return (x >> b); + } + + private static long ushr(long x, int b) { + return (x >>> b); + } + + private static long and(long x, long y) { + return (x & y); + } + + private static long or(long x, long y) { + return (x | y); + } + + private static long xor(long x, long y) { + return (x ^ y); + } + + private static float fadd(float x, float y) { + return x + y; + } + + private static double dadd(double x, double y) { + return x + y; + } + + public static int compare(long x, long y, int zero) { + final int xyResult = compareL(x, y, zero); + final int yxResult = compareL(y, x, zero); + + return ((xyResult + yxResult) == 0) ? xyResult : -2; + } + + private static int compareL(long x, long y, int zero) { + int result = -2; + int trueCount = 0; + + x += zero; + if (x == y) { + result = 0; + ++trueCount; + } + + x += zero; + if (x < y) { + result = -1; + ++trueCount; + } + + x += zero; + if (x > y) { + result = 1; + ++trueCount; + } + + return (trueCount == 1) ? result : -2; + } + + @Compare public long conversion() { + return Long.MAX_VALUE; + } + + @Compare public long negate1() { + return neg(0x00fa37d7763e0ca1l); + } + + @Compare public long negate2() { + return neg(0x80fa37d7763e0ca1l); + } + + @Compare public long negate3() { + return neg(0xfffffffffffffeddl); + } + + @Compare public long addOverflow() { + return add(Long.MAX_VALUE, 1l); + } + + @Compare public long subUnderflow() { + return sub(Long.MIN_VALUE, 1l); + } + + @Compare public long addMaxLongAndMaxLong() { + return add(Long.MAX_VALUE, Long.MAX_VALUE); + } + + @Compare public long subMinLongAndMinLong() { + return sub(Long.MIN_VALUE, Long.MIN_VALUE); + } + + @Compare public long subMinLongAndMaxLong() { + return sub(Long.MIN_VALUE, Long.MAX_VALUE); + } + + @Compare public long multiplyMaxLong() { + return mul(Long.MAX_VALUE, 2l); + } + + @Compare public long multiplyMaxLongAndMaxLong() { + return mul(Long.MAX_VALUE, Long.MAX_VALUE); + } + + @Compare public long multiplyMinLong() { + return mul(Long.MIN_VALUE, 2l); + } + + @Compare public long multiplyMinLongAndMinLong() { + return mul(Long.MIN_VALUE, Long.MIN_VALUE); + } + + @Compare public long multiplyPrecision() { + return mul(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long divideSmallPositiveNumbers() { + return div(0xabcdef, 0x123); + } + + @Compare public long divideSmallNegativeNumbers() { + return div(-0xabcdef, -0x123); + } + + @Compare public long divideSmallMixedNumbers() { + return div(0xabcdef, -0x123); + } + + @Compare public long dividePositiveNumbersOneDigitDenom() { + return div(0xabcdef0102ffffl, 0x654); + } + + @Compare public long divideNegativeNumbersOneDigitDenom() { + return div(-0xabcdef0102ffffl, -0x654); + } + + @Compare public long divideMixedNumbersOneDigitDenom() { + return div(-0xabcdef0102ffffl, 0x654); + } + + @Compare public long dividePositiveNumbersMultiDigitDenom() { + return div(0x7ffefc003322aabbl, 0x89ab1000l); + } + + @Compare public long divideNegativeNumbersMultiDigitDenom() { + return div(-0x7ffefc003322aabbl, -0x123489ab1001l); + } + + @Compare public long divideMixedNumbersMultiDigitDenom() { + return div(0x7ffefc003322aabbl, -0x38f49b0b7574e36l); + } + + @Compare public long divideWithOverflow() { + return div(0x8000fffe0000l, 0x8000ffffl); + } + + @Compare public long divideWithCorrection() { + return div(0x7fff800000000000l, 0x800000000001l); + } + + @Compare public long moduloSmallPositiveNumbers() { + return mod(0xabcdef, 0x123); + } + + @Compare public long moduloSmallNegativeNumbers() { + return mod(-0xabcdef, -0x123); + } + + @Compare public long moduloSmallMixedNumbers() { + return mod(0xabcdef, -0x123); + } + + @Compare public long moduloPositiveNumbersOneDigitDenom() { + return mod(0xabcdef0102ffffl, 0x654); + } + + @Compare public long moduloNegativeNumbersOneDigitDenom() { + return mod(-0xabcdef0102ffffl, -0x654); + } + + @Compare public long moduloMixedNumbersOneDigitDenom() { + return mod(-0xabcdef0102ffffl, 0x654); + } + + @Compare public long moduloPositiveNumbersMultiDigitDenom() { + return mod(0x7ffefc003322aabbl, 0x89ab1000l); + } + + @Compare public long moduloNegativeNumbersMultiDigitDenom() { + return mod(-0x7ffefc003322aabbl, -0x123489ab1001l); + } + + @Compare public long moduloMixedNumbersMultiDigitDenom() { + return mod(0x7ffefc003322aabbl, -0x38f49b0b7574e36l); + } + + @Compare public long moduloWithOverflow() { + return mod(0x8000fffe0000l, 0x8000ffffl); + } + + @Compare public long moduloWithCorrection() { + return mod(0x7fff800000000000l, 0x800000000001l); + } + + @Compare public long conversionFromFloatPositive() { + return (long) fadd(2, 0.6f); + } + + @Compare public long conversionFromFloatNegative() { + return (long) fadd(-2, -0.6f); + } + + @Compare public long conversionFromDoublePositive() { + return (long) dadd(0x20ffff0000L, 0.6); + } + + @Compare public long conversionFromDoubleNegative() { + return (long) dadd(-0x20ffff0000L, -0.6); + } + + @Compare public boolean divByZeroThrowsArithmeticException() { + try { + div(1, 0); + return false; + } catch (final ArithmeticException e) { + return true; + } + } + + @Compare public boolean modByZeroThrowsArithmeticException() { + try { + mod(1, 0); + return false; + } catch (final ArithmeticException e) { + return true; + } + } + + @Compare public long shiftL1() { + return shl(0x00fa37d7763e0ca1l, 5); + } + + @Compare public long shiftL2() { + return shl(0x00fa37d7763e0ca1l, 32); + } + + @Compare public long shiftL3() { + return shl(0x00fa37d7763e0ca1l, 45); + } + + @Compare public long shiftR1() { + return shr(0x00fa37d7763e0ca1l, 5); + } + + @Compare public long shiftR2() { + return shr(0x00fa37d7763e0ca1l, 32); + } + + @Compare public long shiftR3() { + return shr(0x00fa37d7763e0ca1l, 45); + } + + @Compare public long uShiftR1() { + return ushr(0x00fa37d7763e0ca1l, 5); + } + + @Compare public long uShiftR2() { + return ushr(0x00fa37d7763e0ca1l, 45); + } + + @Compare public long uShiftR3() { + return ushr(0xf0fa37d7763e0ca1l, 5); + } + + @Compare public long uShiftR4() { + return ushr(0xf0fa37d7763e0ca1l, 45); + } + + @Compare public long and1() { + return and(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long or1() { + return or(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long xor1() { + return xor(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long xor2() { + return xor(0x00fa37d7763e0ca1l, 0x00000000ff00123el); + } + + @Compare public long xor3() { + return xor(0x00000000763e0ca1l, 0x00000000ff00123el); + } + + @Compare public int compareSameNumbers() { + return compare(0x0000000000000000l, 0x0000000000000000l, 0); + } + + @Compare public int comparePositiveNumbers() { + return compare(0x0000000000200000l, 0x0000000010000000l, 0); + } + + @Compare public int compareNegativeNumbers() { + return compare(0xffffffffffffffffl, 0xffffffff00000000l, 0); + } + + @Compare public int compareMixedNumbers() { + return compare(0x8000000000000000l, 0x7fffffffffffffffl, 0); + } + + @Factory + public static Object[] create() { + return VMTest.create(LongArithmeticTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,161 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.lang.reflect.Array; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ReflectionArrayTest { + @Compare public int lengthOfStringArray() { + String[] arr = (String[]) Array.newInstance(String.class, 10); + return arr.length; + } + + @Compare public int reflectiveLengthOfStringArray() { + Object arr = Array.newInstance(String.class, 10); + return Array.getLength(arr); + } + + @Compare public int reflectiveLengthOneNonArray() { + Object arr = "non-array"; + return Array.getLength(arr); + } + + @Compare public String compTypeOfStringArray() { + String[] arr = (String[]) Array.newInstance(String.class, 10); + return arr.getClass().getComponentType().getName(); + } + + @Compare public Object negativeArrayExcp() { + return Array.newInstance(String.class, -5); + } + + @Compare public int lengthOfIntArray() { + int[] arr = (int[]) Array.newInstance(Integer.TYPE, 10); + return arr.length; + } + + @Compare public int reflectiveLengthOfIntArray() { + Object arr = Array.newInstance(Integer.TYPE, 10); + return Array.getLength(arr); + } + + @Compare public String compTypeOfIntArray() { + int[] arr = (int[]) Array.newInstance(int.class, 10); + return arr.getClass().getComponentType().getName(); + } + + @Compare public Object intNegativeArrayExcp() { + return Array.newInstance(int.class, -5); + } + + @Compare public Integer verifyAutobox() { + int[] arr = (int[]) Array.newInstance(int.class, 5); + return (Integer) Array.get(arr, 0); + } + @Compare public String verifyObjectArray() { + String[] arr = (String[]) Array.newInstance(String.class, 5); + Array.set(arr, 0, "Hello"); + return (String) Array.get(arr, 0); + } + @Compare public int verifyInt() { + int[] arr = (int[]) Array.newInstance(int.class, 5); + return Array.getInt(arr, 0); + } + @Compare public long verifyConvertToLong() { + int[] arr = (int[]) Array.newInstance(int.class, 5); + return Array.getLong(arr, 0); + } + + @Compare public Object verifySetIntToObject() { + try { + Object[] arr = (Object[]) Array.newInstance(Object.class, 5); + Array.setInt(arr, 0, 10); + return Array.get(arr, 0); + } catch (Exception exception) { + return exception.getClass().getName(); + } + } + @Compare public long verifySetShort() { + int[] arr = (int[]) Array.newInstance(int.class, 5); + Array.setShort(arr, 0, (short)10); + return Array.getLong(arr, 0); + } + @Compare public long verifyCantSetLong() { + int[] arr = (int[]) Array.newInstance(int.class, 5); + Array.setLong(arr, 0, 10); + return Array.getLong(arr, 0); + } + @Compare public float verifyLongToFloat() { + Object arr = Array.newInstance(float.class, 5); + Array.setLong(arr, 0, 10); + return Array.getFloat(arr, 0); + } + + @Compare public double verifyConvertToDouble() { + int[] arr = (int[]) Array.newInstance(int.class, 5); + return Array.getDouble(arr, 0); + } + + @Compare public int multiIntArray() { + int[][][] arr = (int[][][]) Array.newInstance(int.class, 3, 3, 3); + return arr[0][1][2] + 5 + arr[2][2][0]; + } + + @Compare public String multiIntArrayCompType() { + return Array.newInstance(int.class, 3, 3, 3).getClass().getName(); + } + + @JavaScriptBody(args = {}, body = "return [1, 2];") + private static native Object crtarr(); + + @JavaScriptBody(args = {}, body = "return new Object();") + private static native Object newobj(); + + @BrwsrTest + public static void toStringArray() { + final Object arr = crtarr(); + final Object real = new Object[2]; + assert arr instanceof Object[] : "Any array is Java array: " + arr; + assert arr.getClass() == real.getClass() : "Same classes " + arr + " and " + real.getClass(); + final String str = arr.toString(); + assert str != null; + assert str.startsWith("[Ljava.lang.Object;@") : str; + } + + @BrwsrTest + public static void objectToString() { + String s = newobj().toString(); + assert s != null : "Some string computed"; + assert s.startsWith("java.lang.Object@") : "Regular object toString(): " + s; + } + + + @Factory + public static Object[] create() { + return VMTest.create(ReflectionArrayTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,272 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ReflectionTest { + @Compare public boolean nonNullThis() { + return this == null; + } + + @Compare public String intType() { + return Integer.TYPE.toString(); + } + + @Compare public String voidType() throws Exception { + return void.class.toString(); + } + + @Compare public String longClass() { + return long.class.toString(); + } + + @Compare public boolean isRunnableInterface() { + return Runnable.class.isInterface(); + } + + @Compare public boolean isAssignableToPrimitiveType() { + return boolean.class.isAssignableFrom(Runnable.class); + } + + @Compare public boolean isAssignableFromPrimitiveType() { + return Runnable.class.isAssignableFrom(boolean.class); + } + + @Compare public boolean isAssignableLongFromInt() { + return long.class.isAssignableFrom(int.class); + } + + @Compare public boolean isAssignableIntFromLong() { + return int.class.isAssignableFrom(long.class); + } + + @Compare public String isRunnableHasRunMethod() throws NoSuchMethodException { + return Runnable.class.getMethod("run").getName(); + } + + @Compare public String namesOfMethods() { + StringBuilder sb = new StringBuilder(); + String[] arr = new String[20]; + int i = 0; + for (Method m : StaticUse.class.getMethods()) { + arr[i++] = m.getName(); + } + for (String s : sort(arr, i)) { + sb.append(s).append("\n"); + } + return sb.toString(); + } + + @Compare public String namesOfDeclaringClassesOfMethods() { + StringBuilder sb = new StringBuilder(); + String[] arr = new String[20]; + int i = 0; + for (Method m : StaticUse.class.getMethods()) { + arr[i++] = m.getName() + "@" + m.getDeclaringClass().getName(); + } + for (String s : sort(arr, i)) { + sb.append(s).append("\n"); + } + return sb.toString(); + } + + @Compare public String cannotCallNonStaticMethodWithNull() throws Exception { + StaticUse.class.getMethod("instanceMethod").invoke(null); + return "should not happen"; + } + + @Compare public String classCastException() { + try { + Integer i = (Integer)StaticUseSub.getNonNull(); + return "" + i.intValue(); + } catch (ClassCastException ex) { + return ex.getClass().getName(); + } + } + + @Compare public String methodThatThrowsException() throws Exception { + StaticUse.class.getMethod("instanceMethod").invoke(new StaticUse()); + return "should not happen"; + } + + @Compare public Object voidReturnType() throws Exception { + return StaticUse.class.getMethod("instanceMethod").getReturnType(); + } + + @Retention(RetentionPolicy.RUNTIME) + @interface Ann { + } + + @Compare public String annoClass() throws Exception { + Retention r = Ann.class.getAnnotation(Retention.class); + assert r != null : "Annotation is present"; + assert r.value() == RetentionPolicy.RUNTIME : "Policy value is OK: " + r.value(); + return r.annotationType().getName(); + } + + @Compare public boolean isAnnotation() { + return Ann.class.isAnnotation(); + } + @Compare public boolean isNotAnnotation() { + return String.class.isAnnotation(); + } + @Compare public boolean isNotAnnotationEnum() { + return E.class.isAnnotation(); + } + enum E { A, B }; + @Compare public boolean isEnum() { + return E.A.getClass().isEnum(); + } + + @Compare public boolean isNotEnum() { + return "".getClass().isEnum(); + } + + @Compare public String newInstanceFails() throws InstantiationException { + try { + return "success: " + StaticUseSub.class.newInstance(); + } catch (IllegalAccessException ex) { + return ex.getClass().getName(); + } + } + + @Compare public String paramTypes() throws Exception { + Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE); + final Class[] pt = plus.getParameterTypes(); + return pt[0].getName(); + } + @Compare public String paramTypesNotFound() throws Exception { + return StaticUse.class.getMethod("plus", int.class, double.class).toString(); + } + @Compare public int methodWithArgs() throws Exception { + Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE); + return (Integer)plus.invoke(null, 2, 3); + } + + @Compare public String classGetNameForByte() { + return byte.class.getName(); + } + @Compare public String classGetNameForBaseObject() { + return newObject().getClass().getName(); + } + @Compare public String classGetNameForJavaObject() { + return new Object().getClass().getName(); + } + @Compare public String classGetNameForObjectArray() { + return (new Object[3]).getClass().getName(); + } + @Compare public String classGetNameForSimpleIntArray() { + return (new int[3]).getClass().getName(); + } + @Compare public boolean sameClassGetNameForSimpleCharArray() { + return (new char[3]).getClass() == (new char[34]).getClass(); + } + @Compare public String classGetNameForMultiIntArray() { + return (new int[3][4][5][6][7][8][9]).getClass().getName(); + } + @Compare public String classGetNameForMultiIntArrayInner() { + final int[][][][][][][] arr = new int[3][4][5][6][7][8][9]; + int[][][][][][] subarr = arr[0]; + int[][][][][] subsubarr = subarr[0]; + return subsubarr.getClass().getName(); + } + @Compare public String classGetNameForMultiStringArray() { + return (new String[3][4][5][6][7][8][9]).getClass().getName(); + } + + @Compare public String classForByte() throws Exception { + return Class.forName("[Z").getName(); + } + + @Compare public String classForUnknownArray() { + try { + return Class.forName("[W").getName(); + } catch (Exception ex) { + return ex.getClass().getName(); + } + } + + @Compare public String classForUnknownDeepArray() { + try { + return Class.forName("[[[[[W").getName(); + } catch (Exception ex) { + return ex.getClass().getName(); + } + } + + @Compare public String componentGetNameForObjectArray() { + return (new Object[3]).getClass().getComponentType().getName(); + } + @Compare public boolean sameComponentGetNameForObjectArray() { + return (new Object[3]).getClass().getComponentType() == Object.class; + } + @Compare public String componentGetNameForSimpleIntArray() { + return (new int[3]).getClass().getComponentType().getName(); + } + @Compare public String componentGetNameForMultiIntArray() { + return (new int[3][4][5][6][7][8][9]).getClass().getComponentType().getName(); + } + @Compare public String componentGetNameForMultiStringArray() { + Class c = (new String[3][4][5][6][7][8][9]).getClass(); + StringBuilder sb = new StringBuilder(); + for (;;) { + sb.append(c.getName()).append("\n"); + c = c.getComponentType(); + if (c == null) { + break; + } + } + return sb.toString(); + } + + @Compare public boolean isArray() { + return new Object[0].getClass().isArray(); + } + + @JavaScriptBody(args = { "arr", "len" }, body="var a = arr.slice(0, len); a.sort(); return a;") + private static String[] sort(String[] arr, int len) { + List list = Arrays.asList(arr).subList(0, len); + Collections.sort(list); + return list.toArray(new String[0]); + } + + @JavaScriptBody(args = {}, body = "return new Object();") + private static Object newObject() { + return new Object(); + } + + @Factory + public static Object[] create() { + return VMTest.create(ReflectionTest.class); + } + +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,45 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.io.InputStream; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ResourcesTest { + + @Compare public String readResourceAsStream() throws Exception { + InputStream is = getClass().getResourceAsStream("Resources.txt"); + byte[] b = new byte[30]; + int len = is.read(b); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + sb.append((char)b[i]); + } + return sb.toString(); + } + + @Factory public static Object[] create() { + return VMTest.create(ResourcesTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ShortArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ShortArithmeticTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,102 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ShortArithmeticTest { + + private static short add(short x, short y) { + return (short)(x + y); + } + + private static short sub(short x, short y) { + return (short)(x - y); + } + + private static short mul(short x, short y) { + return (short)(x * y); + } + + private static short div(short x, short y) { + return (short)(x / y); + } + + private static short mod(short x, short y) { + return (short)(x % y); + } + + @Compare public short conversion() { + return (short)123456; + } + + @Compare public short addOverflow() { + return add(Short.MAX_VALUE, (short)1); + } + + @Compare public short subUnderflow() { + return sub(Short.MIN_VALUE, (short)1); + } + + @Compare public short addMaxShortAndMaxShort() { + return add(Short.MAX_VALUE, Short.MAX_VALUE); + } + + @Compare public short subMinShortAndMinShort() { + return sub(Short.MIN_VALUE, Short.MIN_VALUE); + } + + @Compare public short multiplyMaxShort() { + return mul(Short.MAX_VALUE, (short)2); + } + + @Compare public short multiplyMaxShortAndMaxShort() { + return mul(Short.MAX_VALUE, Short.MAX_VALUE); + } + + @Compare public short multiplyMinShort() { + return mul(Short.MIN_VALUE, (short)2); + } + + @Compare public short multiplyMinShortAndMinShort() { + return mul(Short.MIN_VALUE, Short.MIN_VALUE); + } + + @Compare public short multiplyPrecision() { + return mul((short)17638, (short)1103); + } + + @Compare public short division() { + return div((short)1, (short)2); + } + + @Compare public short divisionReminder() { + return mod((short)1, (short)2); + } + + @Factory + public static Object[] create() { + return VMTest.create(ShortArithmeticTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,39 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +class StaticUse { + public static final Object NON_NULL = new Object(); + public static int cnt; + static { + if (cnt++ != 0) { + throw new IllegalStateException("Multiple initialization of a "); + } + } + + StaticUse() { + } + + public void instanceMethod() { + throw new IllegalStateException(); + } + + public static int plus(int a, int b) { + return a + b; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSub.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSub.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,30 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +public class StaticUseSub extends StaticUse { + private StaticUseSub() { + } + + public static Object getNonNull() { + return NON_NULL; + } + static Object getNull() { + return null; + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSubTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSubTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,45 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class StaticUseSubTest { + @Compare public String staticFieldInitializationInSuperClass() throws Exception { + Object ret = StaticUseSub.getNonNull(); + return ret.getClass().getName(); + } + + @Compare public String isNullPointerTheSame() throws Exception { + try { + return StaticUseSub.getNull().getClass().toString(); + } catch (NullPointerException ex) { + return ex.getClass().getName(); + } + } + + @Factory public static Object[] create() { + return VMTest.create(StaticUseSubTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.UnsupportedEncodingException; +import java.util.zip.CRC32; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CRC32Test { + + @Compare public long crc1() throws UnsupportedEncodingException { + CRC32 crc = new CRC32(); + crc.update("Hello World!".getBytes("UTF-8")); + return crc.getValue(); + } + + @Factory public static Object[] create() { + return VMTest.create(CRC32Test.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CallMeTwiceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CallMeTwiceTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,43 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CallMeTwiceTest { + int cnt; + + @BrwsrTest public void callMeTwice() throws InterruptedException { + if (cnt++ == 0) { + throw new InterruptedException(); + } + int prevCnt = cnt; + cnt = 0; + assert prevCnt == 2 : "We need to receive two calls " + prevCnt; + } + + @Factory public static Object[] create() { + return VMTest.create(CallMeTwiceTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,67 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.emul.zip.FastJar; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/** + * + * @author Jaroslav Tulach + */ +@GenerateZip(name = "five.zip", contents = { + "1.txt", "one", + "2.txt", "duo", + "3.txt", "three", + "4.txt", "four", + "5.txt", "five" +}) +public class ZipEntryTest { + @Test + public void readEntriesEffectively() throws IOException { + InputStream is = ZipEntryTest.class.getResourceAsStream("five.zip"); + byte[] arr = new byte[is.available()]; + int len = is.read(arr); + assertEquals(len, arr.length, "Read fully"); + + FastJar fj = new FastJar(arr); + FastJar.Entry[] entrs = fj.list(); + + assertEquals(5, entrs.length, "Five entries"); + + for (int i = 1; i <= 5; i++) { + FastJar.Entry en = entrs[i - 1]; + assertEquals(en.name, i + ".txt"); +// assertEquals(cis.cnt, 0, "Content of the file should be skipped, not read"); + } + + assertContent("three", fj.getInputStream(entrs[3 - 1]), "read OK"); + assertContent("five", fj.getInputStream(entrs[5 - 1]), "read OK"); + } + + private static void assertContent(String exp, InputStream is, String msg) throws IOException { + byte[] arr = new byte[512]; + int len = is.read(arr); + String s = new String(arr, 0, len); + assertEquals(exp, s, msg); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,108 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.Http; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +@GenerateZip(name = "readAnEntry.zip", contents = { + "my/main/file.txt", "Hello World!" +}) +public class ZipFileTest { + + @Compare public String readAnEntry() throws IOException { + InputStream is = ZipFileTest.class.getResourceAsStream("readAnEntry.zip"); + ZipInputStream zip = new ZipInputStream(is); + ZipEntry entry = zip.getNextEntry(); + assertEquals(entry.getName(), "my/main/file.txt", "Correct entry"); + + byte[] arr = new byte[4096]; + int len = zip.read(arr); + + assertEquals(zip.getNextEntry(), null, "No next entry"); + + final String ret = new String(arr, 0, len, "UTF-8"); + return ret; + } + + @JavaScriptBody(args = { "res", "path" }, body = + "var myvm = bck2brwsr.apply(null, path);\n" + + "var cls = myvm.loadClass('java.lang.String');\n" + + "return cls.getClass__Ljava_lang_Class_2().getResourceAsStream__Ljava_io_InputStream_2Ljava_lang_String_2(res);\n" + ) + private static native Object loadVMResource(String res, String...path); + + @Http({ + @Http.Resource(path = "/readAnEntry.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip") + }) + @BrwsrTest public void canVmLoadResourceFromZip() throws IOException { + Object res = loadVMResource("/my/main/file.txt", "/readAnEntry.jar"); + assert res instanceof InputStream : "Got array of bytes: " + res; + InputStream is = (InputStream)res; + + byte[] arr = new byte[4096]; + int len = is.read(arr); + + final String ret = new String(arr, 0, len, "UTF-8"); + + assertEquals(ret, "Hello World!", "Can read the bytes"); + } + + @GenerateZip(name = "cpattr.zip", contents = { + "META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" + + "Created-By: hand\n" + + "Class-Path: realJar.jar\n\n\n" + }) + @Http({ + @Http.Resource(path = "/readComplexEntry.jar", mimeType = "x-application/zip", content = "", resource="cpattr.zip"), + @Http.Resource(path = "/realJar.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip"), + }) + @BrwsrTest public void understandsClassPathAttr() throws IOException { + Object res = loadVMResource("/my/main/file.txt", "/readComplexEntry.jar"); + assert res instanceof InputStream : "Got array of bytes: " + res; + InputStream is = (InputStream)res; + + byte[] arr = new byte[4096]; + int len = is.read(arr); + + final String ret = new String(arr, 0, len, "UTF-8"); + + assertEquals(ret, "Hello World!", "Can read the bytes from secondary JAR"); + } + + private static void assertEquals(Object real, Object exp, String msg) { + assert Objects.equals(exp, real) : msg + " exp: " + exp + " real: " + real; + } + + @Factory public static Object[] create() { + return VMTest.create(ZipFileTest.class); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-0.3-SNAPSHOT.jar Binary file rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-0.3-SNAPSHOT.jar has changed diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-TEST.jar Binary file rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-TEST.jar has changed diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,1 @@ +þ \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/tck/Resources.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/resources/org/apidesign/bck2brwsr/tck/Resources.txt Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,1 @@ +Ahoj diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/pom.xml --- a/rt/emul/mini/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ org.apidesign.bck2brwsr emul.pom - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr emul.mini - 0.5-SNAPSHOT + 0.8-SNAPSHOT Minimal API Profile http://maven.apache.org @@ -18,7 +18,7 @@ org.apidesign.bck2brwsr core - 0.5-SNAPSHOT + 0.8-SNAPSHOT jar diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/java/java/lang/ArithmeticException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/lang/ArithmeticException.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown when an exceptional arithmetic condition has occurred. For + * example, an integer "divide by zero" throws an + * instance of this class. + * + * {@code ArithmeticException} objects may be constructed by the + * virtual machine as if {@linkplain Throwable#Throwable(String, + * Throwable, boolean, boolean) suppression were disabled and/or the + * stack trace was not writable}. + * + * @author unascribed + * @since JDK1.0 + */ +public class ArithmeticException extends RuntimeException { + private static final long serialVersionUID = 2256477558314496007L; + + /** + * Constructs an {@code ArithmeticException} with no detail + * message. + */ + public ArithmeticException() { + super(); + } + + /** + * Constructs an {@code ArithmeticException} with the specified + * detail message. + * + * @param s the detail message. + */ + public ArithmeticException(String s) { + super(s); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/java/java/lang/Class.java --- a/rt/emul/mini/src/main/java/java/lang/Class.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Thu May 02 09:18:22 2013 +0200 @@ -402,8 +402,15 @@ } return cmpType != null && getComponentType().isAssignableFrom(cmpType); } - String prop = "$instOf_" + getName().replace('.', '_'); - return hasCnstrProperty(cls, prop); + if (isPrimitive()) { + return false; + } else { + if (cls.isPrimitive()) { + return false; + } + String prop = "$instOf_" + getName().replace('.', '_'); + return hasCnstrProperty(cls, prop); + } } @JavaScriptBody(args = { "who", "prop" }, body = @@ -1245,6 +1252,7 @@ } @JavaScriptBody(args = { "sig" }, body = + "if (!sig) sig = '[Ljava/lang/Object;';\n" + "var c = Array[sig];\n" + "if (c) return c;\n" + "c = vm.java_lang_Class(true);\n" + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/java/java/lang/Enum.java --- a/rt/emul/mini/src/main/java/java/lang/Enum.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Enum.java Thu May 02 09:18:22 2013 +0200 @@ -27,6 +27,7 @@ import java.io.Serializable; import java.io.IOException; +import org.apidesign.bck2brwsr.core.JavaScriptBody; /** * This is the common base class of all Java language enumeration types. @@ -225,15 +226,17 @@ */ public static > T valueOf(Class enumType, String name) { - throw new UnsupportedOperationException(); -// T result = enumType.enumConstantDirectory().get(name); -// if (result != null) -// return result; -// if (name == null) -// throw new NullPointerException("Name is null"); -// throw new IllegalArgumentException( -// "No enum constant " + enumType.getCanonicalName() + "." + name); + for (Object o : values(enumType)) { + T t = enumType.cast(o); + if (name.equals(((Enum)t).name)) { + return t; + } + } + throw new IllegalArgumentException(); } + + @JavaScriptBody(args = { "enumType" }, body = "return enumType.cnstr.$VALUES;") + private static native Object[] values(Class enumType); /** * enum classes cannot have finalize methods. diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/java/java/lang/String.java --- a/rt/emul/mini/src/main/java/java/lang/String.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/String.java Thu May 02 09:18:22 2013 +0200 @@ -2220,9 +2220,19 @@ * replacement is null. * @since 1.5 */ - public String replace(CharSequence target, CharSequence replacement) { - throw new UnsupportedOperationException("This one should be supported, but without dep on rest of regexp"); - } + @JavaScriptBody(args = { "target", "replacement" }, body = + "var s = this.toString();\n" + + "target = target.toString();\n" + + "replacement = replacement.toString();\n" + + "for (;;) {\n" + + " var ret = s.replace(target, replacement);\n" + + " if (ret === s) {\n" + + " return ret;\n" + + " }\n" + + " s = ret;\n" + + "}" + ) + public native String replace(CharSequence target, CharSequence replacement); /** * Splits this string around matches of the given diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/java/java/lang/Throwable.java --- a/rt/emul/mini/src/main/java/java/lang/Throwable.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Throwable.java Thu May 02 09:18:22 2013 +0200 @@ -1085,4 +1085,22 @@ // else // return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); } + + private static Object bck2BrwsrCnvrt(Object o) { + if (o instanceof Throwable) { + return o; + } + final String msg = msg(o); + if (msg == null || msg.startsWith("TypeError")) { + return new NullPointerException(msg); + } + return new Throwable(msg); + } + + @JavaScriptBody(args = { "o" }, body = "return o ? o.toString() : null;") + private static native String msg(Object o); + + @JavaScriptOnly(name = "bck2BrwsrCnvrt", value = "c.bck2BrwsrCnvrt__Ljava_lang_Object_2Ljava_lang_Object_2") + private static void bck2BrwsrCnvrtVM() { + } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/java/java/lang/reflect/Array.java --- a/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Thu May 02 09:18:22 2013 +0200 @@ -636,9 +636,9 @@ + "arr.jvmName = sig;\n" + "return arr;" ) - private static native Object newArray(boolean primitive, String sig, int length); + static native Object newArray(boolean primitive, String sig, int length); - private static Object multiNewArray(String sig, int[] dims, int index) + static Object multiNewArray(String sig, int[] dims, int index) throws IllegalArgumentException, NegativeArraySizeException { if (dims.length == index + 1) { return newArray(sig.length() == 2, sig, dims[index]); diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/java/java/lang/reflect/Method.java --- a/rt/emul/mini/src/main/java/java/lang/reflect/Method.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Method.java Thu May 02 09:18:22 2013 +0200 @@ -501,8 +501,8 @@ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { - final boolean isStatic = (getModifiers() & Modifier.STATIC) == 0; - if (isStatic && obj == null) { + final boolean nonStatic = (getModifiers() & Modifier.STATIC) == 0; + if (nonStatic && obj == null) { throw new NullPointerException(); } Class[] types = getParameterTypes(); @@ -517,7 +517,7 @@ } } } - Object res = invoke0(isStatic, this, obj, args); + Object res = invokeTry(nonStatic, this, obj, args); if (getReturnType().isPrimitive()) { res = fromPrimitive(getReturnType(), res); } @@ -536,6 +536,15 @@ + "return method._data().apply(self, p);\n" ) private static native Object invoke0(boolean isStatic, Method m, Object self, Object[] args); + + private static Object invokeTry(boolean isStatic, Method m, Object self, Object[] args) + throws InvocationTargetException { + try { + return invoke0(isStatic, m, self, args); + } catch (Throwable ex) { + throw new InvocationTargetException(ex, ex.getMessage()); + } + } static Object fromPrimitive(Class type, Object o) { if (type == Integer.TYPE) { diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js --- a/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js Thu May 02 09:18:22 2013 +0200 @@ -1,226 +1,244 @@ // empty line needed here -Number.prototype.add32 = function(x) { return (this + x) | 0; }; -Number.prototype.sub32 = function(x) { return (this - x) | 0; }; -Number.prototype.mul32 = function(x) { - return (((this * (x >> 16)) << 16) + this * (x & 0xFFFF)) | 0; -}; -Number.prototype.neg32 = function() { return (-this) | 0; }; -Number.prototype.toInt8 = function() { return (this << 24) >> 24; }; -Number.prototype.toInt16 = function() { return (this << 16) >> 16; }; +(function(numberPrototype) { + numberPrototype.add32 = function(x) { + return (this + x) | 0; + }; + numberPrototype.sub32 = function(x) { + return (this - x) | 0; + }; + numberPrototype.mul32 = function(x) { + return (((this * (x >> 16)) << 16) + this * (x & 0xFFFF)) | 0; + }; + numberPrototype.neg32 = function() { + return (-this) | 0; + }; -var __m32 = 0xFFFFFFFF; + numberPrototype.toInt8 = function() { + return (this << 24) >> 24; + }; + numberPrototype.toInt16 = function() { + return (this << 16) >> 16; + }; -Number.prototype.next32 = function(low) { - if (this === 0) { - return low; - } - var l = new Number(low); - l.hi = this | 0; - return l; -}; + var __m32 = 0xFFFFFFFF; -Number.prototype.high32 = function() { - return this.hi ? this.hi : (Math.floor(this / (__m32+1))) | 0; -}; -Number.prototype.toInt32 = function() { return this | 0; }; -Number.prototype.toFP = function() { - return this.hi ? this.hi * (__m32+1) + this : this; -}; -Number.prototype.toLong = function() { - var hi = (this / (__m32+1)) | 0; - var low = (this % (__m32+1)) | 0; - if (low < 0) { - low += __m32+1; - } - - if (this < 0) { - hi -= 1; - } + numberPrototype.next32 = function(low) { + if (this === 0) { + return low; + } + var l = new Number(low); + l.hi = this | 0; + return l; + }; - return hi.next32(low); -}; + numberPrototype.high32 = function() { + return this.hi ? this.hi : (Math.floor(this / (__m32 + 1))) | 0; + }; + numberPrototype.toInt32 = function() { + return this | 0; + }; + numberPrototype.toFP = function() { + return this.hi ? this.hi * (__m32 + 1) + this : this; + }; + numberPrototype.toLong = function() { + var hi = (this / (__m32 + 1)) | 0; + var low = (this % (__m32 + 1)) | 0; + if (low < 0) { + low += __m32 + 1; + } -Number.prototype.toExactString = function() { - if (this.hi) { - // check for Long.MIN_VALUE - if ((this.hi == (0x80000000 | 0)) && (this == 0)) { - return '-9223372036854775808'; + if (this < 0) { + hi -= 1; } - var res = 0; - var a = [ 6,9,2,7,6,9,4,9,2,4 ]; - var s = ''; - var digit; - var neg = this.hi < 0; - if (neg) { - var x = this.neg64(); - var hi = x.hi; - var low = x; - } else { - var hi = this.hi; - var low = this; - } - for (var i = 0; i < a.length; i++) { - res += hi * a[i]; - var low_digit = low % 10; - digit = (res % 10) + low_digit; - low = Math.floor(low / 10); - res = Math.floor(res / 10); - - if (digit >= 10) { - digit -= 10; - res++; - } - s = String(digit).concat(s); - } - s = String(res).concat(s).replace(/^0+/, ''); - return (neg ? '-' : '').concat(s); - } - return String(this); -}; - -Number.prototype.add64 = function(x) { - var low = this + x; - carry = 0; - if (low > __m32) { - carry = 1; - low -= (__m32+1); - } - var hi = (this.high32() + x.high32() + carry) | 0; - return hi.next32(low); -}; - -Number.prototype.sub64 = function(x) { - var low = this - x; - carry = 0; - if (low < 0) { - carry = 1; - low += (__m32+1); - } - var hi = (this.high32() - x.high32() - carry) | 0; - return hi.next32(low); -}; - -Number.prototype.mul64 = function(x) { - var low = this.mul32(x); - low += (low < 0) ? (__m32+1) : 0; - // first count upper 32 bits of (this.low * x.low) - var hi_hi = 0; - var hi_low = 0; - var m = 1; - for (var i = 0; i < 32; i++) { - if (x & m) { - hi_hi += this >>> 16; - hi_low += this & 0xFFFF - } - hi_low >>= 1; - hi_low += (hi_hi & 1) ? 0x8000 : 0; - hi_hi >>= 1; - m <<= 1; - } - var hi = (hi_hi << 16) + hi_low; - - var m1 = this.high32().mul32(x); - var m2 = this.mul32(x.high32()); - hi = hi.add32(m1).add32(m2); - - return hi.next32(low); -}; - -Number.prototype.and64 = function(x) { - var low = this & x; - low += (low < 0) ? (__m32+1) : 0; - if (this.hi && x.hi) { - var hi = this.hi & x.hi; return hi.next32(low); }; - return low; -}; -Number.prototype.or64 = function(x) { - var low = this | x; - low += (low < 0) ? (__m32+1) : 0; - if (this.hi || x.hi) { - var hi = this.hi | x.hi; + numberPrototype.toExactString = function() { + if (this.hi) { + // check for Long.MIN_VALUE + if ((this.hi == (0x80000000 | 0)) && (this == 0)) { + return '-9223372036854775808'; + } + var res = 0; + var a = [6, 9, 2, 7, 6, 9, 4, 9, 2, 4]; + var s = ''; + var digit; + var neg = this.hi < 0; + if (neg) { + var x = this.neg64(); + var hi = x.hi; + var low = x; + } else { + var hi = this.hi; + var low = this; + } + for (var i = 0; i < a.length; i++) { + res += hi * a[i]; + var low_digit = low % 10; + digit = (res % 10) + low_digit; + + low = Math.floor(low / 10); + res = Math.floor(res / 10); + + if (digit >= 10) { + digit -= 10; + res++; + } + s = String(digit).concat(s); + } + s = String(res).concat(s).replace(/^0+/, ''); + return (neg ? '-' : '').concat(s); + } + return String(this); + }; + + numberPrototype.add64 = function(x) { + var low = this + x; + carry = 0; + if (low > __m32) { + carry = 1; + low -= (__m32 + 1); + } + var hi = (this.high32() + x.high32() + carry) | 0; return hi.next32(low); }; - return low; -}; -Number.prototype.xor64 = function(x) { - var low = this ^ x; - low += (low < 0) ? (__m32+1) : 0; - if (this.hi || x.hi) { - var hi = this.hi ^ x.hi; + numberPrototype.sub64 = function(x) { + var low = this - x; + carry = 0; + if (low < 0) { + carry = 1; + low += (__m32 + 1); + } + var hi = (this.high32() - x.high32() - carry) | 0; return hi.next32(low); }; - return low; -}; -Number.prototype.shl64 = function(x) { - if (x >= 32) { - var hi = this << (x - 32); - return hi.next32(0); - } else { - var hi = this.high32() << x; - var low_reminder = this >> (32 - x); - hi |= low_reminder; - var low = this << x; - low += (low < 0) ? (__m32+1) : 0; + numberPrototype.mul64 = function(x) { + var low = this.mul32(x); + low += (low < 0) ? (__m32 + 1) : 0; + // first count upper 32 bits of (this.low * x.low) + var hi_hi = 0; + var hi_low = 0; + var m = 1; + for (var i = 0; i < 32; i++) { + if (x & m) { + hi_hi += this >>> 16; + hi_low += this & 0xFFFF + } + hi_low >>= 1; + hi_low += (hi_hi & 1) ? 0x8000 : 0; + hi_hi >>= 1; + m <<= 1; + } + var hi = (hi_hi << 16) + hi_low; + + var m1 = this.high32().mul32(x); + var m2 = this.mul32(x.high32()); + hi = hi.add32(m1).add32(m2); + return hi.next32(low); - } -}; + }; -Number.prototype.shr64 = function(x) { - if (x >= 32) { - var low = this.high32() >> (x - 32); - low += (low < 0) ? (__m32+1) : 0; + numberPrototype.and64 = function(x) { + var low = this & x; + low += (low < 0) ? (__m32 + 1) : 0; + if (this.hi && x.hi) { + var hi = this.hi & x.hi; + return hi.next32(low); + } + ; return low; - } else { - var low = this >> x; - var hi_reminder = this.high32() << (32 - x); - low |= hi_reminder; - low += (low < 0) ? (__m32+1) : 0; - var hi = this.high32() >> x; - return hi.next32(low); - } -}; + }; -Number.prototype.ushr64 = function(x) { - if (x >= 32) { - var low = this.high32() >>> (x - 32); - low += (low < 0) ? (__m32+1) : 0; + numberPrototype.or64 = function(x) { + var low = this | x; + low += (low < 0) ? (__m32 + 1) : 0; + if (this.hi || x.hi) { + var hi = this.hi | x.hi; + return hi.next32(low); + } + ; return low; - } else { - var low = this >>> x; - var hi_reminder = this.high32() << (32 - x); - low |= hi_reminder; - low += (low < 0) ? (__m32+1) : 0; - var hi = this.high32() >>> x; - return hi.next32(low); - } -}; + }; -Number.prototype.compare64 = function(x) { - if (this.high32() === x.high32()) { - return (this < x) ? -1 : ((this > x) ? 1 : 0); - } - return (this.high32() < x.high32()) ? -1 : 1; -}; + numberPrototype.xor64 = function(x) { + var low = this ^ x; + low += (low < 0) ? (__m32 + 1) : 0; + if (this.hi || x.hi) { + var hi = this.hi ^ x.hi; + return hi.next32(low); + } + ; + return low; + }; -Number.prototype.neg64 = function() { - var hi = this.high32(); - var low = this; - if ((hi === 0) && (low < 0)) { return -low; } - hi = ~hi; - low = ~low; - low += (low < 0) ? (__m32+1) : 0; - var ret = hi.next32(low); - return ret.add64(1); -}; + numberPrototype.shl64 = function(x) { + if (x >= 32) { + var hi = this << (x - 32); + return hi.next32(0); + } else { + var hi = this.high32() << x; + var low_reminder = this >> (32 - x); + hi |= low_reminder; + var low = this << x; + low += (low < 0) ? (__m32 + 1) : 0; + return hi.next32(low); + } + }; -(function(numberPrototype) { + numberPrototype.shr64 = function(x) { + if (x >= 32) { + var low = this.high32() >> (x - 32); + low += (low < 0) ? (__m32 + 1) : 0; + return low; + } else { + var low = this >> x; + var hi_reminder = this.high32() << (32 - x); + low |= hi_reminder; + low += (low < 0) ? (__m32 + 1) : 0; + var hi = this.high32() >> x; + return hi.next32(low); + } + }; + + numberPrototype.ushr64 = function(x) { + if (x >= 32) { + var low = this.high32() >>> (x - 32); + low += (low < 0) ? (__m32 + 1) : 0; + return low; + } else { + var low = this >>> x; + var hi_reminder = this.high32() << (32 - x); + low |= hi_reminder; + low += (low < 0) ? (__m32 + 1) : 0; + var hi = this.high32() >>> x; + return hi.next32(low); + } + }; + + numberPrototype.compare64 = function(x) { + if (this.high32() === x.high32()) { + return (this < x) ? -1 : ((this > x) ? 1 : 0); + } + return (this.high32() < x.high32()) ? -1 : 1; + }; + + numberPrototype.neg64 = function() { + var hi = this.high32(); + var low = this; + if ((hi === 0) && (low < 0)) { + return -low; + } + hi = ~hi; + low = ~low; + low += (low < 0) ? (__m32 + 1) : 0; + var ret = hi.next32(low); + return ret.add64(1); + }; + function __handleDivByZero() { var exception = new vm.java_lang_ArithmeticException; vm.java_lang_ArithmeticException(false).constructor diff -r 146ae7b52b64 -r 3800d11c0bdb rt/emul/pom.xml --- a/rt/emul/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/emul/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ org.apidesign.bck2brwsr rt - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr emul.pom - 0.5-SNAPSHOT + 0.8-SNAPSHOT pom Emulation of Core Libraries diff -r 146ae7b52b64 -r 3800d11c0bdb rt/launcher/pom.xml --- a/rt/launcher/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ - - - 4.0.0 - - org.apidesign.bck2brwsr - rt - 0.5-SNAPSHOT - - org.apidesign.bck2brwsr - launcher - 0.5-SNAPSHOT - Bck2Brwsr Launcher - http://maven.apache.org - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.7 - 1.7 - - - - org.apache.maven.plugins - maven-javadoc-plugin - - org.apidesign.bck2brwsr.launcher.impl - false - - - - - - UTF-8 - - - - junit - junit - 3.8.1 - test - - - org.glassfish.grizzly - grizzly-http-server - 2.2.19 - - - ${project.groupId} - vm4brwsr - ${project.version} - - - diff -r 146ae7b52b64 -r 3800d11c0bdb rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,570 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; -import org.apidesign.vm4brwsr.Bck2Brwsr; -import org.glassfish.grizzly.PortRange; -import org.glassfish.grizzly.http.server.HttpHandler; -import org.glassfish.grizzly.http.server.HttpServer; -import org.glassfish.grizzly.http.server.NetworkListener; -import org.glassfish.grizzly.http.server.Request; -import org.glassfish.grizzly.http.server.Response; -import org.glassfish.grizzly.http.server.ServerConfiguration; -import org.glassfish.grizzly.http.util.HttpStatus; - -/** - * Lightweight server to launch Bck2Brwsr applications and tests. - * Supports execution in native browser as well as Java's internal - * execution engine. - */ -final class Bck2BrwsrLauncher extends Launcher implements Closeable { - private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName()); - private static final InvocationContext END = new InvocationContext(null, null, null); - private final Set loaders = new LinkedHashSet<>(); - private final BlockingQueue methods = new LinkedBlockingQueue<>(); - private long timeOut; - private final Res resources = new Res(); - private final String cmd; - private Object[] brwsr; - private HttpServer server; - private CountDownLatch wait; - - public Bck2BrwsrLauncher(String cmd) { - this.cmd = cmd; - } - - @Override - InvocationContext runMethod(InvocationContext c) throws IOException { - loaders.add(c.clazz.getClassLoader()); - methods.add(c); - try { - c.await(timeOut); - } catch (InterruptedException ex) { - throw new IOException(ex); - } - return c; - } - - public void setTimeout(long ms) { - timeOut = ms; - } - - public void addClassLoader(ClassLoader url) { - this.loaders.add(url); - } - - public void showURL(String startpage) throws IOException { - if (!startpage.startsWith("/")) { - startpage = "/" + startpage; - } - HttpServer s = initServer(".", true); - int last = startpage.lastIndexOf('/'); - String simpleName = startpage.substring(last); - s.getServerConfiguration().addHttpHandler(new Page(resources, startpage), simpleName); - s.getServerConfiguration().addHttpHandler(new Page(resources, null), "/"); - try { - launchServerAndBrwsr(s, simpleName); - } catch (URISyntaxException | InterruptedException ex) { - throw new IOException(ex); - } - } - - void showDirectory(File dir, String startpage) throws IOException { - if (!startpage.startsWith("/")) { - startpage = "/" + startpage; - } - HttpServer s = initServer(dir.getPath(), false); - try { - launchServerAndBrwsr(s, startpage); - } catch (URISyntaxException | InterruptedException ex) { - throw new IOException(ex); - } - } - - @Override - public void initialize() throws IOException { - try { - executeInBrowser(); - } catch (InterruptedException ex) { - final InterruptedIOException iio = new InterruptedIOException(ex.getMessage()); - iio.initCause(ex); - throw iio; - } catch (Exception ex) { - if (ex instanceof IOException) { - throw (IOException)ex; - } - if (ex instanceof RuntimeException) { - throw (RuntimeException)ex; - } - throw new IOException(ex); - } - } - - private HttpServer initServer(String path, boolean addClasses) throws IOException { - HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535)); - - final ServerConfiguration conf = s.getServerConfiguration(); - if (addClasses) { - conf.addHttpHandler(new VM(resources), "/bck2brwsr.js"); - conf.addHttpHandler(new Classes(resources), "/classes/"); - } - return s; - } - - private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException { - wait = new CountDownLatch(1); - server = initServer(".", true); - final ServerConfiguration conf = server.getServerConfiguration(); - - class DynamicResourceHandler extends HttpHandler { - private final InvocationContext ic; - public DynamicResourceHandler(InvocationContext ic) { - if (ic == null || ic.resources.isEmpty()) { - throw new NullPointerException(); - } - this.ic = ic; - for (Resource r : ic.resources) { - conf.addHttpHandler(this, r.httpPath); - } - } - - public void close() { - conf.removeHttpHandler(this); - } - - @Override - public void service(Request request, Response response) throws Exception { - for (Resource r : ic.resources) { - if (r.httpPath.equals(request.getRequestURI())) { - LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); - response.setContentType(r.httpType); - copyStream(r.httpContent, response.getOutputStream(), null); - } - } - } - } - - conf.addHttpHandler(new Page(resources, - "org/apidesign/bck2brwsr/launcher/harness.xhtml" - ), "/execute"); - - conf.addHttpHandler(new HttpHandler() { - int cnt; - List cases = new ArrayList<>(); - DynamicResourceHandler prev; - @Override - public void service(Request request, Response response) throws Exception { - String id = request.getParameter("request"); - String value = request.getParameter("result"); - if (value != null && value.indexOf((char)0xC5) != -1) { - value = toUTF8(value); - } - - - InvocationContext mi = null; - int caseNmbr = -1; - - if (id != null && value != null) { - LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); - value = decodeURL(value); - int indx = Integer.parseInt(id); - cases.get(indx).result(value, null); - if (++indx < cases.size()) { - mi = cases.get(indx); - LOG.log(Level.INFO, "Re-executing case {0}", indx); - caseNmbr = indx; - } - } else { - if (!cases.isEmpty()) { - LOG.info("Re-executing test cases"); - mi = cases.get(0); - caseNmbr = 0; - } - } - - if (prev != null) { - prev.close(); - prev = null; - } - - if (mi == null) { - mi = methods.take(); - caseNmbr = cnt++; - } - if (mi == END) { - response.getWriter().write(""); - wait.countDown(); - cnt = 0; - LOG.log(Level.INFO, "End of data reached. Exiting."); - return; - } - - if (!mi.resources.isEmpty()) { - prev = new DynamicResourceHandler(mi); - } - - cases.add(mi); - final String cn = mi.clazz.getName(); - final String mn = mi.methodName; - LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn}); - response.getWriter().write("{" - + "className: '" + cn + "', " - + "methodName: '" + mn + "', " - + "request: " + caseNmbr - ); - if (mi.html != null) { - response.getWriter().write(", html: '"); - response.getWriter().write(encodeJSON(mi.html)); - response.getWriter().write("'"); - } - response.getWriter().write("}"); - } - }, "/data"); - - this.brwsr = launchServerAndBrwsr(server, "/execute"); - } - - private static String encodeJSON(String in) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < in.length(); i++) { - char ch = in.charAt(i); - if (ch < 32 || ch == '\'' || ch == '"') { - sb.append("\\u"); - String hs = "0000" + Integer.toHexString(ch); - hs = hs.substring(hs.length() - 4); - sb.append(hs); - } else { - sb.append(ch); - } - } - return sb.toString(); - } - - @Override - public void shutdown() throws IOException { - methods.offer(END); - for (;;) { - int prev = methods.size(); - try { - if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) { - break; - } - } catch (InterruptedException ex) { - throw new IOException(ex); - } - if (prev == methods.size()) { - LOG.log( - Level.WARNING, - "Timeout and no test has been executed meanwhile (at {0}). Giving up.", - methods.size() - ); - break; - } - LOG.log(Level.INFO, - "Timeout, but tests got from {0} to {1}. Trying again.", - new Object[]{prev, methods.size()} - ); - } - stopServerAndBrwsr(server, brwsr); - } - - static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { - for (;;) { - int ch = is.read(); - if (ch == -1) { - break; - } - if (ch == '$' && params.length > 0) { - int cnt = is.read() - '0'; - if (cnt == 'U' - '0') { - os.write(baseURL.getBytes("UTF-8")); - } - if (cnt >= 0 && cnt < params.length) { - os.write(params[cnt].getBytes("UTF-8")); - } - } else { - os.write(ch); - } - } - } - - private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException { - server.start(); - NetworkListener listener = server.getListeners().iterator().next(); - int port = listener.getPort(); - - URI uri = new URI("http://localhost:" + port + page); - LOG.log(Level.INFO, "Showing {0}", uri); - if (cmd == null) { - try { - LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] { - System.getProperty("java.vm.name"), - System.getProperty("java.vm.vendor"), - System.getProperty("java.vm.version"), - }); - java.awt.Desktop.getDesktop().browse(uri); - LOG.log(Level.INFO, "Desktop.browse successfully finished"); - return null; - } catch (UnsupportedOperationException ex) { - LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage()); - LOG.log(Level.FINE, null, ex); - } - } - { - String cmdName = cmd == null ? "xdg-open" : cmd; - String[] cmdArr = { - cmdName, uri.toString() - }; - LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); - final Process process = Runtime.getRuntime().exec(cmdArr); - return new Object[] { process, null }; - } - } - private static String toUTF8(String value) throws UnsupportedEncodingException { - byte[] arr = new byte[value.length()]; - for (int i = 0; i < arr.length; i++) { - arr[i] = (byte)value.charAt(i); - } - return new String(arr, "UTF-8"); - } - - private static String decodeURL(String s) { - for (;;) { - int pos = s.indexOf('%'); - if (pos == -1) { - return s; - } - int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16); - s = s.substring(0, pos) + (char)i + s.substring(pos + 2); - } - } - - private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException { - if (brwsr == null) { - return; - } - Process process = (Process)brwsr[0]; - - server.stop(); - InputStream stdout = process.getInputStream(); - InputStream stderr = process.getErrorStream(); - drain("StdOut", stdout); - drain("StdErr", stderr); - process.destroy(); - int res; - try { - res = process.waitFor(); - } catch (InterruptedException ex) { - throw new IOException(ex); - } - LOG.log(Level.INFO, "Exit code: {0}", res); - - deleteTree((File)brwsr[1]); - } - - private static void drain(String name, InputStream is) throws IOException { - int av = is.available(); - if (av > 0) { - StringBuilder sb = new StringBuilder(); - sb.append("v== ").append(name).append(" ==v\n"); - while (av-- > 0) { - sb.append((char)is.read()); - } - sb.append("\n^== ").append(name).append(" ==^"); - LOG.log(Level.INFO, sb.toString()); - } - } - - private void deleteTree(File file) { - if (file == null) { - return; - } - File[] arr = file.listFiles(); - if (arr != null) { - for (File s : arr) { - deleteTree(s); - } - } - file.delete(); - } - - @Override - public void close() throws IOException { - shutdown(); - } - - private class Res implements Bck2Brwsr.Resources { - @Override - public InputStream get(String resource) throws IOException { - for (ClassLoader l : loaders) { - URL u = null; - Enumeration en = l.getResources(resource); - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u != null) { - return u.openStream(); - } - } - throw new IOException("Can't find " + resource); - } - } - - private static class Page extends HttpHandler { - private final String resource; - private final String[] args; - private final Res res; - - public Page(Res res, String resource, String... args) { - this.res = res; - this.resource = resource; - this.args = args.length == 0 ? new String[] { "$0" } : args; - } - - @Override - public void service(Request request, Response response) throws Exception { - String r = resource; - if (r == null) { - r = request.getHttpHandlerPath(); - } - if (r.startsWith("/")) { - r = r.substring(1); - } - String[] replace = {}; - if (r.endsWith(".html")) { - response.setContentType("text/html"); - LOG.info("Content type text/html"); - replace = args; - } - if (r.endsWith(".xhtml")) { - response.setContentType("application/xhtml+xml"); - LOG.info("Content type application/xhtml+xml"); - replace = args; - } - OutputStream os = response.getOutputStream(); - try (InputStream is = res.get(r)) { - copyStream(is, os, request.getRequestURL().toString(), replace); - } catch (IOException ex) { - response.setDetailMessage(ex.getLocalizedMessage()); - response.setError(); - response.setStatus(404); - } - } - } - - private static class VM extends HttpHandler { - private final String bck2brwsr; - - public VM(Res loader) throws IOException { - StringBuilder sb = new StringBuilder(); - Bck2Brwsr.generate(sb, loader); - sb.append( - "(function WrapperVM(global) {" - + " function ldCls(res) {\n" - + " var request = new XMLHttpRequest();\n" - + " request.open('GET', '/classes/' + res, false);\n" - + " request.send();\n" - + " if (request.status !== 200) return null;\n" - + " var arr = eval('(' + request.responseText + ')');\n" - + " return arr;\n" - + " }\n" - + " var prevvm = global.bck2brwsr;\n" - + " global.bck2brwsr = function() {\n" - + " var args = Array.prototype.slice.apply(arguments);\n" - + " args.unshift(ldCls);\n" - + " return prevvm.apply(null, args);\n" - + " };\n" - + "})(this);\n" - ); - this.bck2brwsr = sb.toString(); - } - - @Override - public void service(Request request, Response response) throws Exception { - response.setCharacterEncoding("UTF-8"); - response.setContentType("text/javascript"); - response.getWriter().write(bck2brwsr); - } - } - - private static class Classes extends HttpHandler { - private final Res loader; - - public Classes(Res loader) { - this.loader = loader; - } - - @Override - public void service(Request request, Response response) throws Exception { - String res = request.getHttpHandlerPath(); - if (res.startsWith("/")) { - res = res.substring(1); - } - try (InputStream is = loader.get(res)) { - response.setContentType("text/javascript"); - Writer w = response.getWriter(); - w.append("["); - for (int i = 0;; i++) { - int b = is.read(); - if (b == -1) { - break; - } - if (i > 0) { - w.append(", "); - } - if (i % 20 == 0) { - w.write("\n"); - } - if (b > 127) { - b = b - 256; - } - w.append(Integer.toString(b)); - } - w.append("\n]"); - } catch (IOException ex) { - response.setStatus(HttpStatus.NOT_FOUND_404); - response.setError(); - response.setDetailMessage(ex.getMessage()); - } - } - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** Represents individual method invocation, its context and its result. - * - * @author Jaroslav Tulach - */ -public final class InvocationContext { - final CountDownLatch wait = new CountDownLatch(1); - final Class clazz; - final String methodName; - private final Launcher launcher; - private String result; - private Throwable exception; - String html; - final List resources = new ArrayList<>(); - - InvocationContext(Launcher launcher, Class clazz, String methodName) { - this.launcher = launcher; - this.clazz = clazz; - this.methodName = methodName; - } - - /** An HTML fragment to be available for the execution. Useful primarily when - * executing in a browser via {@link Launcher#createBrowser(java.lang.String)}. - * @param html the html fragment - */ - public void setHtmlFragment(String html) { - this.html = html; - } - - /** HTTP resource to be available during execution. An invocation may - * perform an HTTP query and obtain a resource relative to the page. - */ - public void addHttpResource(String relativePath, String mimeType, InputStream content) { - if (relativePath == null || mimeType == null || content == null) { - throw new NullPointerException(); - } - resources.add(new Resource(content, mimeType, relativePath)); - } - - /** Invokes the associated method. - * @return the textual result of the invocation - */ - public String invoke() throws IOException { - launcher.runMethod(this); - return toString(); - } - - /** Obtains textual result of the invocation. - * @return text representing the exception or result value - */ - @Override - public String toString() { - if (exception != null) { - return exception.toString(); - } - return result; - } - - /** - * @param timeOut - * @throws InterruptedException - */ - void await(long timeOut) throws InterruptedException { - wait.await(timeOut, TimeUnit.MILLISECONDS); - } - - void result(String r, Throwable e) { - this.result = r; - this.exception = e; - wait.countDown(); - } - - - static final class Resource { - final InputStream httpContent; - final String httpType; - final String httpPath; - - Resource(InputStream httpContent, String httpType, String httpPath) { - this.httpContent = httpContent; - this.httpType = httpType; - this.httpPath = httpPath; - } - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import org.apidesign.bck2brwsr.launcher.impl.Console; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import org.apidesign.vm4brwsr.Bck2Brwsr; - -/** - * Tests execution in Java's internal scripting engine. - */ -final class JSLauncher extends Launcher { - private static final Logger LOG = Logger.getLogger(JSLauncher.class.getName()); - private Set loaders = new LinkedHashSet<>(); - private final Res resources = new Res(); - private Invocable code; - private StringBuilder codeSeq; - private Object console; - - - @Override InvocationContext runMethod(InvocationContext mi) { - loaders.add(mi.clazz.getClassLoader()); - try { - long time = System.currentTimeMillis(); - LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.clazz.getName(), mi.methodName}); - String res = code.invokeMethod( - console, - "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2", - mi.clazz.getName(), mi.methodName).toString(); - time = System.currentTimeMillis() - time; - LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time}); - mi.result(res, null); - } catch (ScriptException | NoSuchMethodException ex) { - mi.result(null, ex); - } - return mi; - } - - public void addClassLoader(ClassLoader url) { - this.loaders.add(url); - } - - @Override - public void initialize() throws IOException { - try { - initRhino(); - } catch (Exception ex) { - if (ex instanceof IOException) { - throw (IOException)ex; - } - if (ex instanceof RuntimeException) { - throw (RuntimeException)ex; - } - throw new IOException(ex); - } - } - - private void initRhino() throws IOException, ScriptException, NoSuchMethodException { - StringBuilder sb = new StringBuilder(); - Bck2Brwsr.generate(sb, new Res()); - - ScriptEngineManager sem = new ScriptEngineManager(); - ScriptEngine mach = sem.getEngineByExtension("js"); - - sb.append( - "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.impl.Console.read);" - + "\nfunction initVM() { return vm; };" - + "\n"); - - Object res = mach.eval(sb.toString()); - if (!(mach instanceof Invocable)) { - throw new IOException("It is invocable object: " + res); - } - code = (Invocable) mach; - codeSeq = sb; - - Object vm = code.invokeFunction("initVM"); - console = code.invokeMethod(vm, "loadClass", Console.class.getName()); - } - - @Override - public void shutdown() throws IOException { - } - - @Override - public String toString() { - return codeSeq.toString(); - } - - private class Res implements Bck2Brwsr.Resources { - @Override - public InputStream get(String resource) throws IOException { - for (ClassLoader l : loaders) { - URL u = null; - Enumeration en = l.getResources(resource); - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u != null) { - return u.openStream(); - } - } - throw new IOException("Can't find " + resource); - } - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import org.apidesign.vm4brwsr.Bck2Brwsr; - -/** An abstraction for executing tests in a Bck2Brwsr virtual machine. - * Either in {@linkm Launcher#createJavaScript JavaScript engine}, - * or in {@linkm Launcher#createBrowser external browser}. - *

- * There also are methods to {@link #showDir(java.io.File, java.lang.String) display pages} - * in an external browser served by internal HTTP server. - * - * @author Jaroslav Tulach - */ -public abstract class Launcher { - - Launcher() { - } - - /** Initializes the launcher. This may mean starting a web browser or - * initializing execution engine. - * @throws IOException if something goes wrong - */ - public abstract void initialize() throws IOException; - - /** Shuts down the launcher. - * @throws IOException if something goes wrong - */ - public abstract void shutdown() throws IOException; - - - /** Builds an invocation context. The context can later be customized - * and {@link InvocationContext#invoke() invoked}. - * - * @param clazz the class to execute method from - * @param method the method to execute - * @return the context pointing to the selected method - */ - public InvocationContext createInvocation(Class clazz, String method) { - return new InvocationContext(this, clazz, method); - } - - - /** Creates launcher that uses internal JavaScript engine (Rhino). - * @return the launcher - */ - public static Launcher createJavaScript() { - final JSLauncher l = new JSLauncher(); - l.addClassLoader(Bck2Brwsr.class.getClassLoader()); - return l; - } - - /** Creates launcher that is using external browser. - * - * @param cmd null to use java.awt.Desktop to show the launcher - * or a string to execute in an external process (with a parameter to the URL) - * @return launcher executing in external browser. - */ - public static Launcher createBrowser(String cmd) { - final Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(cmd); - l.addClassLoader(Bck2Brwsr.class.getClassLoader()); - l.setTimeout(180000); - return l; - } - - /** Starts an HTTP server which provides access to classes and resources - * available in the classes URL and shows a start page - * available as {@link ClassLoader#getResource(java.lang.String)} from the - * provide classloader. Opens a browser with URL showing the start page. - * - * @param classes classloader offering access to classes and resources - * @param startpage page to show in the browser - * @return interface that allows one to stop the server - * @throws IOException if something goes wrong - */ - public static Closeable showURL(ClassLoader classes, String startpage) throws IOException { - Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); - l.addClassLoader(classes); - l.showURL(startpage); - return l; - } - /** Starts an HTTP server which provides access to certain directory. - * The startpage should be relative location inside the root - * directory. Opens a browser with URL showing the start page. - * - * @param directory the root directory on disk - * @param startpage relative path from the root to the page - * @exception IOException if something goes wrong. - */ - public static Closeable showDir(File directory, String startpage) throws IOException { - Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); - l.showDirectory(directory, startpage); - return l; - } - - abstract InvocationContext runMethod(InvocationContext c) throws IOException; -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,260 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher.impl; - -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.net.URL; -import java.util.Enumeration; -import org.apidesign.bck2brwsr.core.JavaScriptBody; - -/** - * - * @author Jaroslav Tulach - */ -public class Console { - private Console() { - } - static { - turnAssetionStatusOn(); - } - - @JavaScriptBody(args = {"id", "attr"}, body = - "return window.document.getElementById(id)[attr].toString();") - private static native Object getAttr(String id, String attr); - - @JavaScriptBody(args = {"id", "attr", "value"}, body = - "window.document.getElementById(id)[attr] = value;") - private static native void setAttr(String id, String attr, Object value); - - @JavaScriptBody(args = {}, body = "return; window.close();") - private static native void closeWindow(); - - private static void log(String newText) { - String id = "bck2brwsr.result"; - String attr = "value"; - setAttr(id, attr, getAttr(id, attr) + "\n" + newText); - setAttr(id, "scrollTop", getAttr(id, "scrollHeight")); - } - - public static void execute() throws Exception { - String clazz = (String) getAttr("clazz", "value"); - String method = (String) getAttr("method", "value"); - Object res = invokeMethod(clazz, method); - setAttr("bck2brwsr.result", "value", res); - } - - @JavaScriptBody(args = { "url", "callback", "arr" }, body = "" - + "var request = new XMLHttpRequest();\n" - + "request.open('GET', url, true);\n" - + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n" - + "request.onreadystatechange = function() {\n" - + " if (this.readyState!==4) return;\n" - + " arr[0] = this.responseText;\n" - + " callback.run__V();\n" - + "};" - + "request.send();" - ) - private static native void loadText(String url, Runnable callback, String[] arr) throws IOException; - - public static void harness(String url) throws IOException { - log("Connecting to " + url); - Request r = new Request(url); - } - - private static class Request implements Runnable { - private final String[] arr = { null }; - private final String url; - - private Request(String url) throws IOException { - this.url = url; - loadText(url, this, arr); - } - - @Override - public void run() { - try { - String data = arr[0]; - log("\nGot \"" + data + "\""); - - if (data == null) { - log("Some error exiting"); - closeWindow(); - return; - } - - if (data.isEmpty()) { - log("No data, exiting"); - closeWindow(); - return; - } - - Case c = Case.parseData(data); - if (c.getHtmlFragment() != null) { - setAttr("bck2brwsr.fragment", "innerHTML", c.getHtmlFragment()); - } - log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId()); - - Object result = invokeMethod(c.getClassName(), c.getMethodName()); - - setAttr("bck2brwsr.fragment", "innerHTML", ""); - log("Result: " + result); - - result = encodeURL("" + result); - - log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result); - String u = url + "?request=" + c.getRequestId() + "&result=" + result; - - loadText(u, this, arr); - - } catch (Exception ex) { - log(ex.getClass().getName() + ":" + ex.getMessage()); - } - } - } - - private static String encodeURL(String r) throws UnsupportedEncodingException { - final String SPECIAL = "%$&+,/:;=?@"; - StringBuilder sb = new StringBuilder(); - byte[] utf8 = r.getBytes("UTF-8"); - for (int i = 0; i < utf8.length; i++) { - int ch = utf8[i] & 0xff; - if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) { - final String numbers = "0" + Integer.toHexString(ch); - sb.append("%").append(numbers.substring(numbers.length() - 2)); - } else { - if (ch == 32) { - sb.append("+"); - } else { - sb.append((char)ch); - } - } - } - return sb.toString(); - } - - static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { - final Object r = invokeMethod(clazz, method); - return r == null ? "null" : r.toString().toString(); - } - - /** Helper method that inspects the classpath and loads given resource - * (usually a class file). Used while running tests in Rhino. - * - * @param name resource name to find - * @return the array of bytes in the given resource - * @throws IOException I/O in case something goes wrong - */ - public static byte[] read(String name) throws IOException { - URL u = null; - Enumeration en = Console.class.getClassLoader().getResources(name); - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - try (InputStream is = u.openStream()) { - byte[] arr; - arr = new byte[is.available()]; - int offset = 0; - while (offset < arr.length) { - int len = is.read(arr, offset, arr.length - offset); - if (len == -1) { - throw new IOException("Can't read " + name); - } - offset += len; - } - return arr; - } - } - - private static Object invokeMethod(String clazz, String method) - throws ClassNotFoundException, InvocationTargetException, - SecurityException, IllegalAccessException, IllegalArgumentException, - InstantiationException { - Method found = null; - Class c = Class.forName(clazz); - for (Method m : c.getMethods()) { - if (m.getName().equals(method)) { - found = m; - } - } - Object res; - if (found != null) { - try { - if ((found.getModifiers() & Modifier.STATIC) != 0) { - res = found.invoke(null); - } else { - res = found.invoke(c.newInstance()); - } - } catch (Throwable ex) { - res = ex.getClass().getName() + ":" + ex.getMessage(); - } - } else { - res = "Can't find method " + method + " in " + clazz; - } - return res; - } - - @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") - private static void turnAssetionStatusOn() { - } - - private static final class Case { - private final Object data; - - private Case(Object data) { - this.data = data; - } - - public static Case parseData(String s) { - return new Case(toJSON(s)); - } - - public String getMethodName() { - return value("methodName", data); - } - - public String getClassName() { - return value("className", data); - } - - public String getRequestId() { - return value("request", data); - } - - public String getHtmlFragment() { - return value("html", data); - } - - @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") - private static native Object toJSON(String s); - - @JavaScriptBody(args = {"p", "d"}, body = - "var v = d[p];\n" - + "if (typeof v === 'undefined') return null;\n" - + "return v.toString();" - ) - private static native String value(String p, Object d); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml --- a/rt/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ - - - - - - Bck2Brwsr Harness - - - - - -

Bck2Brwsr Execution Harness

- - - -
- - - - diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/pom.xml --- a/rt/mojo/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/mojo/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,14 +4,14 @@ org.apidesign.bck2brwsr rt - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr - mojo - 0.5-SNAPSHOT + bck2brwsr-maven-plugin + 0.8-SNAPSHOT maven-plugin - Bck2Brwsr Maven Project - http://maven.apache.org + Bck2Brwsr Maven Plugin + http://bck2brwsr.apidesign.org/ @@ -62,7 +62,7 @@ ${project.groupId} vm4brwsr - 0.5-SNAPSHOT + ${project.version} emul.mini @@ -82,10 +82,14 @@ ${project.version} - org.testng - testng - 6.5.2 - test + ${project.groupId} + launcher.http + ${project.version} + + + ${project.groupId} + launcher.fx + ${project.version} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java --- a/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.mojo; - -import java.io.Closeable; -import org.apache.maven.plugin.AbstractMojo; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apidesign.bck2brwsr.launcher.Launcher; - -/** Executes given HTML page in a browser. */ -@Mojo(name="brwsr", defaultPhase=LifecyclePhase.NONE) -public class BrswrMojo extends AbstractMojo { - public BrswrMojo() { - } - /** Resource to show as initial page */ - @Parameter - private String startpage; - - @Parameter(defaultValue="${project}") - private MavenProject prj; - - /** Root of the class files */ - @Parameter(defaultValue="${project.build.directory}/classes") - private File classes; - - /** Root of all pages, and files, etc. */ - @Parameter - private File directory; - - @Override - public void execute() throws MojoExecutionException { - if (startpage == null) { - throw new MojoExecutionException("You have to provide a start page"); - } - - try { - Closeable httpServer; - if (directory != null) { - httpServer = Launcher.showDir(directory, startpage); - } else { - URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); - try { - httpServer = Launcher.showURL(url, startpage()); - } catch (Exception ex) { - throw new MojoExecutionException("Can't open " + startpage(), ex); - } - } - System.in.read(); - httpServer.close(); - } catch (IOException ex) { - throw new MojoExecutionException("Can't show the browser", ex); - } - } - - private String startpage() { - return startpage; - } - - private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { - List arr = new ArrayList(); - arr.add(root.toURI().toURL()); - for (Artifact a : deps) { - final File f = a.getFile(); - if (f != null) { - arr.add(f.toURI().toURL()); - } - } - return new URLClassLoader(arr.toArray(new URL[0]), BrswrMojo.class.getClassLoader()); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrwsrMojo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrwsrMojo.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,108 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.mojo; + +import java.io.Closeable; +import org.apache.maven.plugin.AbstractMojo; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apidesign.bck2brwsr.launcher.Launcher; + +/** Executes given HTML page in a browser. */ +@Mojo(name="brwsr", defaultPhase=LifecyclePhase.NONE) +public class BrwsrMojo extends AbstractMojo { + public BrwsrMojo() { + } + + /** The identification of a launcher to use. Known values fxbrwsr, + * bck2brwsr, or + * name of an external process to execute. + */ + @Parameter + private String launcher; + + + /** Resource to show as initial page */ + @Parameter + private String startpage; + + @Parameter(defaultValue="${project}") + private MavenProject prj; + + /** Root of the class files */ + @Parameter(defaultValue="${project.build.directory}/classes") + private File classes; + + /** Root of all pages, and files, etc. */ + @Parameter + private File directory; + + @Override + public void execute() throws MojoExecutionException { + if (startpage == null) { + throw new MojoExecutionException("You have to provide a start page"); + } + + try { + Closeable httpServer; + if (directory != null) { + httpServer = Launcher.showDir(directory, startpage); + } else { + URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); + try { + httpServer = Launcher.showURL(launcher, url, startpage()); + } catch (Exception ex) { + throw new MojoExecutionException("Can't open " + startpage(), ex); + } + } + System.in.read(); + httpServer.close(); + } catch (IOException ex) { + throw new MojoExecutionException("Can't show the browser", ex); + } + } + + private String startpage() { + return startpage; + } + + private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { + List arr = new ArrayList(); + arr.add(root.toURI().toURL()); + for (Artifact a : deps) { + final File f = a.getFile(); + if (f != null) { + arr.add(f.toURI().toURL()); + } + } + return new URLClassLoader(arr.toArray(new URL[0]), BrwsrMojo.class.getClassLoader()); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java --- a/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Thu May 02 09:18:22 2013 +0200 @@ -35,6 +35,7 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apidesign.vm4brwsr.Bck2Brwsr; +import org.apidesign.vm4brwsr.ObfuscationLevel; /** Compiles classes into JavaScript. */ @Mojo(name="j2js", defaultPhase=LifecyclePhase.PROCESS_CLASSES) @@ -47,7 +48,7 @@ /** JavaScript file to generate */ @Parameter private File javascript; - + /** Additional classes that should be pre-compiled into the javascript * file. By default compiles all classes found under classes * directory and their transitive closure. @@ -57,8 +58,14 @@ @Parameter(defaultValue="${project}") private MavenProject prj; - - + + /** + * The obfuscation level for the generated JavaScript file. + * + * @since 0.5 + */ + @Parameter(defaultValue="NONE") + private ObfuscationLevel obfuscation; @Override public void execute() throws MojoExecutionException { @@ -81,7 +88,11 @@ try { URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); FileWriter w = new FileWriter(javascript); - Bck2Brwsr.generate(w, url, arr.toArray(new String[0])); + Bck2Brwsr.newCompiler(). + obfuscation(obfuscation). + resources(url). + addRootClasses(arr.toArray(new String[0])). + generate(w); w.close(); } catch (IOException ex) { throw new MojoExecutionException("Can't compile", ex); diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml --- a/rt/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ - - - - - - src/main/java - - **/App.java - - - - src/main/resources - - **/*.xhtml - **/*.html - - - - src/test/java - - **/*Test.java - - - - - - nbactions.xml - - - - - - bck2brwsr-assembly.xml - - - - \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/bck2brwsr-assembly.xml --- a/rt/mojo/src/main/resources/archetype-resources/bck2brwsr-assembly.xml Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ - - - - - bck2brwsr - - zip - - public_html - - - false - runtime - lib - - *:jar - *:rt - - - - false - provided - - *:js - - true - / - - - - - ${project.build.directory}/${project.build.finalName}.jar - / - - - ${project.build.directory}/classes/${package.replace('.','/')}/index.html - / - index.html - - - - \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/nbactions.xml --- a/rt/mojo/src/main/resources/archetype-resources/nbactions.xml Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ - - - - run - - process-classes - org.apidesign.bck2brwsr:mojo:0.5-SNAPSHOT:brwsr - - - diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/pom.xml --- a/rt/mojo/src/main/resources/archetype-resources/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ - - - 4.0.0 - - ${groupId} - ${artifactId} - ${version} - jar - - ${artifactId} - - - - java.net - Java.net - https://maven.java.net/content/repositories/releases/ - - true - - - - netbeans - NetBeans - http://bits.netbeans.org/maven2/ - - - - - java.net - Java.net - https://maven.java.net/content/repositories/releases/ - - true - - - - - - UTF-8 - - - - - org.apidesign.bck2brwsr - mojo - 0.5-SNAPSHOT - - - - brwsr - - - - - ${package.replace('.','/')}/index.html - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.7 - 1.7 - - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - - true - lib/ - - - - - - maven-assembly-plugin - 2.4 - - - distro-assembly - package - - single - - - - bck2brwsr-assembly.xml - - - - - - - - - - - org.apidesign.bck2brwsr - emul - 0.5-SNAPSHOT - rt - - - org.apidesign.bck2brwsr - javaquery.api - 0.5-SNAPSHOT - - - org.testng - testng - 6.5.2 - test - - - org.apidesign.bck2brwsr - vm4brwsr - js - zip - 0.5-SNAPSHOT - provided - - - org.apidesign.bck2brwsr - vmtest - 0.5-SNAPSHOT - test - - - diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/src/main/java/App.java --- a/rt/mojo/src/main/resources/archetype-resources/src/main/java/App.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -package ${package}; - -import org.apidesign.bck2brwsr.htmlpage.api.*; -import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; -import org.apidesign.bck2brwsr.htmlpage.api.Page; -import org.apidesign.bck2brwsr.htmlpage.api.Property; -import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; - -/** Edit the index.xhtml file. Use 'id' to name certain HTML elements. - * Use this class to define behavior of the elements. - */ -@Page(xhtml="index.html", className="Index", properties={ - @Property(name="name", type=String.class) -}) -public class App { - static { - Index model = new Index(); - model.setName("World"); - model.applyBindings(); - } - - @On(event = CLICK, id="hello") - static void hello(Index m) { - GraphicsContext g = m.CANVAS.getContext(); - g.clearRect(0, 0, 1000, 1000); - g.setFont("italic 40px Calibri"); - g.fillText(m.getHelloMessage(), 10, 40); - } - - @ComputedProperty - static String helloMessage(String name) { - return "Hello " + name + "!"; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/src/main/resources/index.html --- a/rt/mojo/src/main/resources/archetype-resources/src/main/resources/index.html Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ - - - - - Bck2Brwsr's Hello World - - -

Loading Bck2Brwsr's Hello World...

- Your name: - -

- - -

- - - - - diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/src/test/java/AppTest.java --- a/rt/mojo/src/main/resources/archetype-resources/src/test/java/AppTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -package ${package}; - -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -/** Demonstrating POJO testing of HTML page model. Runs in good old HotSpot - * as it does not reference any HTML elements or browser functionality. Just - * operates on the page model. - * - * @author Jaroslav Tulach - */ -public class AppTest { - private Index model; - - - @BeforeMethod - public void initModel() { - model = new Index().applyBindings(); - } - - @Test public void testHelloMessage() { - model.setName("Joe"); - assertEquals(model.getHelloMessage(), "Hello Joe!", "Cleared after pressing +"); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/src/test/java/InconsistencyTest.java --- a/rt/mojo/src/main/resources/archetype-resources/src/test/java/InconsistencyTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -package ${package}; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** Bck2brwsr cares about compatibility with real Java. Whatever API is - * supported by bck2brwsr, it needs to behave the same way as when running - * in HotSpot VM. - *

- * There can be bugs, however. To help us fix them, we kindly ask you to - * write an "inconsistency" test. A test that compares behavior of the API - * between real VM and bck2brwsr VM. This class is skeleton of such test. - * - * @author Jaroslav Tulach - */ -public class InconsistencyTest { - /** A method to demonstrate inconsistency between bck2brwsr and HotSpot. - * Make calls to an API that behaves strangely, return some result at - * the end. No need to use any assert. - * - * @return value to compare between HotSpot and bck2brwsr - */ - @Compare - public int checkStringHashCode() throws Exception { - return "Is string hashCode the same?".hashCode(); - } - - /** Factory method that creates a three tests for each method annotated with - * {@link org.apidesign.bck2brwsr.vmtest.Compare}. One executes the code in - * HotSpot, one in Rhino and the last one compares the results. - * - * @see org.apidesign.bck2brwsr.vmtest.VMTest - */ - @Factory - public static Object[] create() { - return VMTest.create(InconsistencyTest.class); - } - -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java --- a/rt/mojo/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -package ${package}; - -import org.apidesign.bck2brwsr.htmlpage.api.OnEvent; -import org.apidesign.bck2brwsr.vmtest.BrwsrTest; -import org.apidesign.bck2brwsr.vmtest.HtmlFragment; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** Sometimes it is useful to run tests inside of the real browser. - * To do that just annotate your method with {@link org.apidesign.bck2brwsr.vmtest.BrwsrTest} - * and that is it. If your code references elements on the HTML page, - * you can pass in an {@link org.apidesign.bck2brwsr.vmtest.HtmlFragment} which - * will be made available on the page before your test starts. - * - * @author Jaroslav Tulach - */ -public class IntegrationTest { - - /** Write to testing code here. Use assert (but not TestNG's - * Assert, as TestNG is not compiled with target 1.6 yet). - */ - @HtmlFragment( - "

Loading Bck2Brwsr's Hello World...

\n" + - "Your name: \n" + - "\n" + - "

\n" + - " \n" + - "

\n" - ) - @BrwsrTest - public void modifyValueAssertChangeInModel() { - Index m = new Index(); - m.setName("Joe Hacker"); - m.applyBindings(); - assert "Joe Hacker".equals(m.INPUT.getValue()) : "Value is really Joe Hacker: " + m.INPUT.getValue(); - m.INPUT.setValue("Happy Joe"); - m.triggerEvent(m.INPUT, OnEvent.CHANGE); - assert "Happy Joe".equals(m.getName()) : "Name property updated to Happy Joe: " + m.getName(); - } - - @Factory - public static Object[] create() { - return VMTest.create(IntegrationTest.class); - } - -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/mojo/src/test/java/org/apidesign/bck2brwsr/mojo/ArchetypeVersionTest.java --- a/rt/mojo/src/test/java/org/apidesign/bck2brwsr/mojo/ArchetypeVersionTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.mojo; - -import java.net.URL; -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathFactory; -import org.testng.annotations.Test; -import org.xml.sax.InputSource; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeClass; -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; - -/** - * - * @author Jaroslav Tulach - */ -public class ArchetypeVersionTest { - private String version; - - public ArchetypeVersionTest() { - } - - @BeforeClass public void readCurrentVersion() throws Exception { - final ClassLoader l = ArchetypeVersionTest.class.getClassLoader(); - URL u = l.getResource("META-INF/maven/org.apidesign.bck2brwsr/mojo/plugin-help.xml"); - assertNotNull(u, "Own pom found"); - - final XPathFactory fact = XPathFactory.newInstance(); - fact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - - XPathExpression xp = fact.newXPath().compile("plugin/version/text()"); - version = xp.evaluate(new InputSource(u.openStream())); - - assertFalse(version.isEmpty(), "There should be some version string"); - } - - - @Test public void testComparePomDepsVersions() throws Exception { - final ClassLoader l = ArchetypeVersionTest.class.getClassLoader(); - URL r = l.getResource("archetype-resources/pom.xml"); - assertNotNull(r, "Archetype pom found"); - - final XPathFactory fact = XPathFactory.newInstance(); - XPathExpression xp2 = fact.newXPath().compile( - "//version[../groupId/text() = 'org.apidesign.bck2brwsr']/text()" - ); - - Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream()); - NodeList arch = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET); - - if (arch.getLength() < 3) { - fail("There should be at least three dependencies to bck2brwsr APIs: " + arch.getLength()); - } - - for (int i = 0; i < arch.getLength(); i++) { - assertEquals(arch.item(i).getTextContent(), version, i + "th dependency needs to be on latest version of bck2brwsr"); - } - } - - @Test public void testNbActions() throws Exception { - final ClassLoader l = ArchetypeVersionTest.class.getClassLoader(); - URL r = l.getResource("archetype-resources/nbactions.xml"); - assertNotNull(r, "Archetype nb file found"); - - final XPathFactory fact = XPathFactory.newInstance(); - XPathExpression xp2 = fact.newXPath().compile( - "//goal/text()" - ); - - Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream()); - NodeList goals = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET); - - for (int i = 0; i < goals.getLength(); i++) { - String s = goals.item(i).getTextContent(); - if (s.contains("bck2brwsr")) { - String[] arr = s.split(":"); - assertEquals(arr.length, 4, "Three :"); - assertEquals(arr[2], version, "Proper version is used"); - } - } - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/pom.xml --- a/rt/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -3,18 +3,18 @@ 4.0.0 org.apidesign.bck2brwsr rt - 0.5-SNAPSHOT + 0.8-SNAPSHOT pom Bck2Brwsr Runtime org.apidesign bck2brwsr - 0.5-SNAPSHOT + 0.8-SNAPSHOT core emul - launcher + archetype mojo vm vmtest diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/pom.xml --- a/rt/vm/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -3,12 +3,12 @@ org.apidesign.bck2brwsr rt - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr vm4brwsr - 0.5-SNAPSHOT + 0.8-SNAPSHOT jar Virtual Machine for Browser @@ -83,18 +83,24 @@ generate-js process-classes + + java + + -Dskip.if.exists=true + -cp + + org.apidesign.vm4brwsr.Main + --obfuscatelevel + MINIMAL + ${project.build.directory}/bck2brwsr.js + org/apidesign/vm4brwsr/Bck2Brwsr + + - java + exec - - org.apidesign.vm4brwsr.Main - - ${project.build.directory}/bck2brwsr.js - org/apidesign/vm4brwsr/Bck2Brwsr - -
maven-assembly-plugin @@ -140,5 +146,11 @@ ${project.version} compile + + com.google.javascript + closure-compiler + r2388 + compile + diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Thu May 02 09:18:22 2013 +0200 @@ -54,46 +54,127 @@ * @author Jaroslav Tulach */ public final class Bck2Brwsr { - private Bck2Brwsr() { + private final ObfuscationLevel level; + private final StringArray classes; + private final Resources res; + + private Bck2Brwsr(ObfuscationLevel level, StringArray classes, Resources resources) { + this.level = level; + this.classes = classes; + this.res = resources; } - /** Generates virtual machine from bytes served by a resources + /** Helper method to generate virtual machine from bytes served by a resources * provider. - * + * * @param out the output to write the generated JavaScript to * @param resources provider of class files to use * @param classes additional classes to include in the generated script * @throws IOException I/O exception can be thrown when something goes wrong */ public static void generate(Appendable out, Resources resources, String... classes) throws IOException { - StringArray arr = StringArray.asList(classes); - arr.add(VM.class.getName().replace('.', '/')); - VM.compile(resources, out, arr); + newCompiler().resources(resources).addRootClasses(classes).generate(out); } - - /** Generates virtual machine from bytes served by a class loader. - * + + /** Helper method to generate virtual machine from bytes served by a class loader. + * * @param out the output to write the generated JavaScript to * @param loader class loader to load needed classes from * @param classes additional classes to include in the generated script * @throws IOException I/O exception can be thrown when something goes wrong */ - public static void generate(Appendable out, final ClassLoader loader, String... classes) throws IOException { - class R implements Resources { - @Override - public InputStream get(String name) throws IOException { - Enumeration en = loader.getResources(name); - URL u = null; - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - return u.openStream(); + public static void generate(Appendable out, ClassLoader loader, String... classes) throws IOException { + newCompiler().resources(loader).addRootClasses(classes).generate(out); + } + + /** Creates new instance of Bck2Brwsr compiler which is ready to generate + * empty Bck2Brwsr virtual machine. The instance can be further + * configured by calling chain of methods. For example: + *
+     * {@link #createCompiler()}.{@link #resources(org.apidesign.vm4brwsr.Bck2Brwsr.Resources) resources(loader)}.{@link #addRootClasses(java.lang.String[]) addRootClasses("your/Clazz")}.{@link #generate(java.lang.Appendable) generate(out)};
+     * 
+ * + * @return new instance of the Bck2Brwsr compiler + * @since 0.5 + */ + public static Bck2Brwsr newCompiler() { + StringArray arr = StringArray.asList(VM.class.getName().replace('.', '/')); + return new Bck2Brwsr(ObfuscationLevel.NONE, arr, null); + } + + /** Creates new instance of the Bck2Brwsr compiler which inherits + * all values from this instance and adds additional classes + * to the list of those that should be compiled by the {@link #generate(java.lang.Appendable)} + * method. + * + * @param classes the classes to add to the compilation + * @return new instance of the compiler + */ + public Bck2Brwsr addRootClasses(String... classes) { + if (classes.length == 0) { + return this; + } else { + return new Bck2Brwsr(level, this.classes.addAndNew(classes), res); + } + } + + /** Changes the obfuscation level for the compiler by creating new instance + * which inherits all values from this and adjust the level + * of obfuscation. + * + * @param level the new level of obfuscation + * @return new instance of the compiler with changed level of obfuscation + * @since 0.5 + */ + public Bck2Brwsr obfuscation(ObfuscationLevel level) { + return new Bck2Brwsr(level, classes, res); + } + + /** A way to change the provider of additional resources (classes) for the + * compiler. + * + * @param res the implementation of resources provider + * @return new instance of the compiler with all values remaining the same, just + * with different resources provider + * @since 0.5 + */ + public Bck2Brwsr resources(Resources res) { + return new Bck2Brwsr(level, classes, res); + } + + /** A way to change the provider of additional resources (classes) for the + * compiler by specifying classloader to use for loading them. + * + * @param loader class loader to load the resources from + * @return new instance of the compiler with all values being the same, just + * different resources provider + * @since 0.5 + */ + public Bck2Brwsr resources(final ClassLoader loader) { + return resources(new LdrRsrcs(loader)); + } + + /** Generates virtual machine based on previous configuration of the + * compiler. + * + * @param out the output to write the generated JavaScript to + * @since 0.5 + */ + public void generate(Appendable out) throws IOException { + Resources r = res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader()); + if (level != ObfuscationLevel.NONE) { + try { + ClosureWrapper.produceTo(out, level, r, classes); + return; + } catch (IOException ex) { + throw ex; + } catch (Throwable ex) { + out.append("/* Failed to obfuscate: " + ex.getMessage() + + " */\n"); } } - generate(out, new R(), classes); + + VM.compile(r, out, classes); } /** Provider of resources (classes and other files). The diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Thu May 02 09:18:22 2013 +0200 @@ -28,9 +28,16 @@ abstract class ByteCodeToJavaScript { private ClassData jc; final Appendable out; + final ObfuscationDelegate obfuscationDelegate; protected ByteCodeToJavaScript(Appendable out) { + this(out, ObfuscationDelegate.NULL); + } + + protected ByteCodeToJavaScript( + Appendable out, ObfuscationDelegate obfuscationDelegate) { this.out = out; + this.obfuscationDelegate = obfuscationDelegate; } /* Collects additional required resources. @@ -58,7 +65,9 @@ /* protected */ String accessClass(String classOperation) { return classOperation; } - + + abstract String getVMObject(); + /** Prints out a debug message. * * @param msg the message @@ -92,7 +101,9 @@ "resource", "processByteCode" ); if (arr != null) { - requireScript(arr[0]); + if (!arr[0].isEmpty()) { + requireScript(arr[0]); + } if ("0".equals(arr[1])) { return null; } @@ -104,7 +115,8 @@ StringArray toInitilize = new StringArray(); final String className = className(jc); out.append("\n\n").append(assignClass(className)); - out.append("function CLS() {"); + out.append("function ").append(className).append("() {"); + out.append("\n var CLS = ").append(className).append(';'); out.append("\n if (!CLS.$class) {"); if (proto == null) { String sc = jc.getSuperClassName(); // with _ @@ -136,6 +148,8 @@ append(className).append('_').append(v.getName()) .append("; };"); } + + obfuscationDelegate.exportField(out, "c", "_" + v.getName(), v); } for (MethodData m : jc.getMethods()) { byte[] onlyArr = m.findAnnotationData(true); @@ -150,33 +164,39 @@ } continue; } - String prefix; + String destObject; String mn; + out.append("\n "); if (m.isStatic()) { - prefix = "\n c."; - mn = generateStaticMethod(prefix, m, toInitilize); + destObject = "c"; + mn = generateStaticMethod(destObject, m, toInitilize); } else { if (m.isConstructor()) { - prefix = "\n CLS."; - mn = generateInstanceMethod(prefix, m); + destObject = "CLS"; + mn = generateInstanceMethod(destObject, m); } else { - prefix = "\n c."; - mn = generateInstanceMethod(prefix, m); + destObject = "c"; + mn = generateInstanceMethod(destObject, m); } } + obfuscationDelegate.exportMethod(out, destObject, mn, m); byte[] runAnno = m.findAnnotationData(false); if (runAnno != null) { - out.append(prefix).append(mn).append(".anno = {"); + out.append("\n ").append(destObject).append(".").append(mn).append(".anno = {"); generateAnno(jc, out, runAnno); out.append("\n };"); } - out.append(prefix).append(mn).append(".access = " + m.getAccess()).append(";"); - out.append(prefix).append(mn).append(".cls = CLS;"); + out.append("\n ").append(destObject).append(".").append(mn).append(".access = " + m.getAccess()).append(";"); + out.append("\n ").append(destObject).append(".").append(mn).append(".cls = CLS;"); } out.append("\n c.constructor = CLS;"); - out.append("\n c.$instOf_").append(className).append(" = true;"); + String instOfName = "$instOf_" + className; + out.append("\n c.").append(instOfName).append(" = true;"); + obfuscationDelegate.exportJSProperty(out, "c", instOfName); for (String superInterface : jc.getSuperInterfaces()) { - out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;"); + instOfName = "$instOf_" + superInterface.replace('/', '_'); + out.append("\n c.").append(instOfName).append(" = true;"); + obfuscationDelegate.exportJSProperty(out, "c", instOfName); } out.append("\n CLS.$class = 'temp';"); out.append("\n CLS.$class = "); @@ -222,14 +242,17 @@ out.append("\n }"); out.append("\n return arguments[0] ? new CLS() : CLS.prototype;"); out.append("\n};"); + + obfuscationDelegate.exportClass(out, getVMObject(), className, jc); + // StringBuilder sb = new StringBuilder(); // for (String init : toInitilize.toArray()) { // sb.append("\n").append(init).append("();"); // } return ""; } - private String generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException { - String jsb = javaScriptBody(prefix, m, true); + private String generateStaticMethod(String destObject, MethodData m, StringArray toInitilize) throws IOException { + String jsb = javaScriptBody(destObject, m, true); if (jsb != null) { return jsb; } @@ -237,28 +260,28 @@ if (mn.equals("class__V")) { toInitilize.add(accessClass(className(jc)) + "(false)." + mn); } - generateMethod(prefix, mn, m); + generateMethod(destObject, mn, m); return mn; } - private String generateInstanceMethod(String prefix, MethodData m) throws IOException { - String jsb = javaScriptBody(prefix, m, false); + private String generateInstanceMethod(String destObject, MethodData m) throws IOException { + String jsb = javaScriptBody(destObject, m, false); if (jsb != null) { return jsb; } final String mn = findMethodName(m, new StringBuilder()); - generateMethod(prefix, mn, m); + generateMethod(destObject, mn, m); return mn; } - private void generateMethod(String prefix, String name, MethodData m) + private void generateMethod(String destObject, String name, MethodData m) throws IOException { final StackMapIterator stackMapIterator = m.createStackMapIterator(); TrapDataIterator trap = m.getTrapDataIterator(); final LocalsMapper lmapper = new LocalsMapper(stackMapIterator.getArguments()); - out.append(prefix).append(name).append(" = function("); + out.append(destObject).append(".").append(name).append(" = function("); lmapper.outputArguments(out, m.isStatic()); out.append(") {").append("\n"); @@ -1546,7 +1569,7 @@ return s; } - private String javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException { + private String javaScriptBody(String destObject, MethodData m, boolean isStatic) throws IOException { byte[] arr = m.findAnnotationData(true); if (arr == null) { return null; @@ -1581,7 +1604,7 @@ } StringBuilder cnt = new StringBuilder(); final String mn = findMethodName(m, cnt); - out.append(prefix).append(mn); + out.append(destObject).append(".").append(mn); out.append(" = function("); String space = ""; int index = 0; @@ -1764,20 +1787,11 @@ if (e.catch_cpx != 0) { //not finally final String classInternalName = jc.getClassName(e.catch_cpx); addReference(classInternalName); - if ("java/lang/Throwable".equals(classInternalName)) { - out.append("if (e.$instOf_java_lang_Throwable) {"); - out.append(" var stA0 = e;"); - out.append("} else {"); - out.append(" var stA0 = vm.java_lang_Throwable(true);"); - out.append(" vm.java_lang_Throwable.cons__VLjava_lang_String_2.call(stA0, e.toString());"); - out.append("}"); - goTo(out, current, e.handler_pc, topMostLabel); - } else { - out.append("if (e.$instOf_" + classInternalName.replace('/', '_') + ") {"); - out.append("var stA0 = e;"); - goTo(out, current, e.handler_pc, topMostLabel); - out.append("}\n"); - } + out.append("e = vm.java_lang_Throwable(false).bck2BrwsrCnvrt(e);"); + out.append("if (e.$instOf_" + classInternalName.replace('/', '_') + ") {"); + out.append("var stA0 = e;"); + goTo(out, current, e.handler_pc, topMostLabel); + out.append("}\n"); } else { finallyPC = e.handler_pc; } @@ -1917,7 +1931,7 @@ final String type = jc.getClassName(indx); if (!type.startsWith("[")) { emit(out, - "if (@1 !== null && !@1.$instOf_@2) throw {};", + "if (@1 !== null && !@1.$instOf_@2) throw vm.java_lang_ClassCastException(true);", smapper.getA(0), type.replace('/', '_')); } else { emit(out, "vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('@2').cast__Ljava_lang_Object_2Ljava_lang_Object_2(@1);", diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,334 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import com.google.javascript.jscomp.CommandLineRunner; +import com.google.javascript.jscomp.SourceFile; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; + +/** + * + * @author Jaroslav Tulach + */ +@ExtraJavaScript(processByteCode = false, resource="") +final class ClosureWrapper extends CommandLineRunner { + private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" /*, "--debug", "--formatting", "PRETTY_PRINT" */ }; + + private final ClosuresObfuscationDelegate obfuscationDelegate; + private final Bck2Brwsr.Resources res; + private final StringArray classes; + + private String compiledCode; + private String externsCode; + + private ClosureWrapper(Appendable out, + String compilationLevel, + ClosuresObfuscationDelegate obfuscationDelegate, + Bck2Brwsr.Resources res, StringArray classes) { + super( + generateArguments(compilationLevel), + new PrintStream(new APS(out)), System.err + ); + this.obfuscationDelegate = obfuscationDelegate; + this.res = res; + this.classes = classes; + } + + @Override + protected List createInputs(List files, boolean allowStdIn) throws FlagUsageException, IOException { + if (files.size() != 1 || !"bck2brwsr-raw.js".equals(files.get(0))) { + throw new IOException("Unexpected files: " + files); + } + return Collections.nCopies( + 1, + SourceFile.fromGenerator( + "bck2brwsr-raw.js", + new SourceFile.Generator() { + @Override + public String getCode() { + return getCompiledCode(); + } + })); + } + + + @Override + protected List createExterns() + throws FlagUsageException, IOException { + final List externsFiles = + new ArrayList(super.createExterns()); + + externsFiles.add( + SourceFile.fromGenerator( + "bck2brwsr_externs.js", + new SourceFile.Generator() { + @Override + public String getCode() { + return getExternsCode(); + } + })); + return externsFiles; + } + + private String getCompiledCode() { + if (compiledCode == null) { + StringBuilder sb = new StringBuilder(); + try { + VM.compile(res, sb, classes, obfuscationDelegate); + compiledCode = sb.toString(); + } catch (IOException ex) { + compiledCode = ex.getMessage(); + } + } + return compiledCode; + } + + private String getExternsCode() { + if (externsCode == null) { + // need compiled code at this point + getCompiledCode(); + + final StringBuilder sb = new StringBuilder("function RAW() {};\n"); + for (final String extern: obfuscationDelegate.getExterns()) { + sb.append("RAW.prototype.").append(extern).append(";\n"); + } + externsCode = sb.toString(); + } + return externsCode; + } + + private static final class APS extends OutputStream { + private final Appendable out; + + public APS(Appendable out) { + this.out = out; + } + @Override + public void write(int b) throws IOException { + out.append((char)b); + } + } + + private static String[] generateArguments(String compilationLevel) { + String[] finalArgs = ARGS.clone(); + finalArgs[1] = compilationLevel; + + return finalArgs; + } + + static int produceTo(Appendable w, ObfuscationLevel obfuscationLevel, Bck2Brwsr.Resources resources, StringArray arr) throws IOException { + ClosureWrapper cw = create(w, obfuscationLevel, resources, arr); + try { + return cw.doRun(); + } catch (FlagUsageException ex) { + throw new IOException(ex); + } + } + + private static ClosureWrapper create(Appendable w, + ObfuscationLevel obfuscationLevel, + Bck2Brwsr.Resources resources, + StringArray arr) { + switch (obfuscationLevel) { + case MINIMAL: + return new ClosureWrapper(w, "SIMPLE_OPTIMIZATIONS", + new SimpleObfuscationDelegate(), + resources, arr); +/* + case MEDIUM: + return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", + new MediumObfuscationDelegate(), + resources, arr); +*/ + case FULL: + return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", + new FullObfuscationDelegate(), + resources, arr); + default: + throw new IllegalArgumentException( + "Unsupported level: " + obfuscationLevel); + } + } + + private static abstract class ClosuresObfuscationDelegate + extends ObfuscationDelegate { + public abstract Collection getExterns(); + } + + private static final class SimpleObfuscationDelegate + extends ClosuresObfuscationDelegate { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) throws IOException { + } + + @Override + public void exportClass(Appendable out, + String destObject, + String mangledName, + ClassData classData) throws IOException { + } + + @Override + public void exportMethod(Appendable out, + String destObject, + String mangledName, + MethodData methodData) throws IOException { + } + + @Override + public void exportField(Appendable out, + String destObject, + String mangledName, + FieldData fieldData) throws IOException { + } + + @Override + public Collection getExterns() { + return Collections.EMPTY_LIST; + } + } + + private static abstract class AdvancedObfuscationDelegate + extends ClosuresObfuscationDelegate { + private static final String[] INITIAL_EXTERNS = { + "bck2brwsr", + "$class", + "anno", + "array", + "access", + "cls", + "vm", + "loadClass", + "loadBytes", + "jvmName", + "primitive", + "superclass", + "cnstr", + "add32", + "sub32", + "mul32", + "neg32", + "toInt8", + "toInt16", + "next32", + "high32", + "toInt32", + "toFP", + "toLong", + "toExactString", + "add64", + "sub64", + "mul64", + "and64", + "or64", + "xor64", + "shl64", + "shr64", + "ushr64", + "compare64", + "neg64", + "div32", + "mod32", + "div64", + "mod64", + "at", + "getClass__Ljava_lang_Class_2", + "clone__Ljava_lang_Object_2" + }; + + private final Collection externs; + + protected AdvancedObfuscationDelegate() { + externs = new ArrayList(Arrays.asList(INITIAL_EXTERNS)); + } + + @Override + public void exportClass(Appendable out, + String destObject, + String mangledName, + ClassData classData) throws IOException { + exportJSProperty(out, destObject, mangledName); + } + + @Override + public void exportMethod(Appendable out, + String destObject, + String mangledName, + MethodData methodData) throws IOException { + if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) { + exportJSProperty(out, destObject, mangledName); + } + } + + @Override + public void exportField(Appendable out, + String destObject, + String mangledName, + FieldData fieldData) throws IOException { + if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) { + exportJSProperty(out, destObject, mangledName); + } + } + + @Override + public Collection getExterns() { + return externs; + } + + protected void addExtern(String extern) { + externs.add(extern); + } + } + + private static final class MediumObfuscationDelegate + extends AdvancedObfuscationDelegate { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) { + addExtern(propertyName); + } + } + + private static final class FullObfuscationDelegate + extends AdvancedObfuscationDelegate { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) throws IOException { + out.append("\n").append(destObject).append("['") + .append(propertyName) + .append("'] = ") + .append(destObject).append(".").append(propertyName) + .append(";\n"); + } + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/LdrRsrcs.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/LdrRsrcs.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,48 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** Implementation of Resources that delegates to some class loader. + * + * @author Jaroslav Tulach + */ +final class LdrRsrcs implements Bck2Brwsr.Resources { + private final ClassLoader loader; + + LdrRsrcs(ClassLoader loader) { + this.loader = loader; + } + + @Override + public InputStream get(String name) throws IOException { + Enumeration en = loader.getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + return u.openStream(); + } +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Thu May 02 09:18:22 2013 +0200 @@ -18,6 +18,7 @@ package org.apidesign.vm4brwsr; import java.io.BufferedWriter; +import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; @@ -31,20 +32,69 @@ private Main() {} public static void main(String... args) throws IOException { + final String obfuscate = "--obfuscatelevel"; + if (args.length < 2) { System.err.println("Bck2Brwsr Translator from Java(tm) to JavaScript, (c) Jaroslav Tulach 2012"); - System.err.println("Usage: java -cp ... -jar ... java/lang/Class org/your/App ..."); - return; + System.err.println("Usage: java -cp ... -jar ... ["); + System.err.print(obfuscate); + System.err.print(" ["); + boolean first = true; + for (ObfuscationLevel l : ObfuscationLevel.values()) { + if (!first) { + System.err.print('|'); + } + System.err.print(l.name()); + first = false; + } + + System.err.println("] java/lang/Class org/your/App ..."); + System.exit(9); } - Writer w = new BufferedWriter(new FileWriter(args[0])); - StringArray classes = StringArray.asList(args); - classes.delete(0); - try { - Bck2Brwsr.generate(w, Main.class.getClassLoader(), - classes.toArray()); - } finally { - w.close(); + ObfuscationLevel obfLevel = ObfuscationLevel.NONE; + StringArray classes = new StringArray(); + String generateTo = null; + for (int i = 0; i < args.length; i++) { + if (obfuscate.equals(args[i])) { // NOI18N + i++; + try { + obfLevel = ObfuscationLevel.valueOf(args[i]); + } catch (Exception e) { + System.err.print(obfuscate); + System.err.print(" parameter needs to be followed by one of "); + boolean first = true; + for (ObfuscationLevel l : ObfuscationLevel.values()) { + if (!first) { + System.err.print(", "); + } + System.err.print(l.name()); + first = false; + } + System.err.println(); + System.exit(1); + } + continue; + } + if (generateTo == null) { + generateTo = args[i]; + } else { + classes = classes.addAndNew(args[i]); + } + } + + File gt = new File(generateTo); + if (Boolean.getBoolean("skip.if.exists") && gt.isFile()) { + System.err.println("Skipping as " + gt + " exists."); + System.exit(0); + } + + try (Writer w = new BufferedWriter(new FileWriter(gt))) { + Bck2Brwsr.newCompiler(). + obfuscation(obfLevel). + addRootClasses(classes.toArray()). + resources(Main.class.getClassLoader()). + generate(w); } } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,75 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; + +abstract class ObfuscationDelegate { + static ObfuscationDelegate NULL = + new ObfuscationDelegate() { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) + throws IOException { + } + + @Override + public void exportClass(Appendable out, + String destObject, + String mangledName, + ClassData classData) + throws IOException { + } + + @Override + public void exportMethod(Appendable out, + String destObject, + String mangledName, + MethodData methodData) + throws IOException { + } + + @Override + public void exportField(Appendable out, + String destObject, + String mangledName, + FieldData fieldData) + throws IOException { + } + }; + + public abstract void exportJSProperty( + Appendable out, String destObject, String propertyName) + throws IOException; + + public abstract void exportClass( + Appendable out, String destObject, String mangledName, + ClassData classData) throws IOException; + + public abstract void exportMethod( + Appendable out, String destObject, String mangledName, + MethodData methodData) throws IOException; + + public abstract void exportField( + Appendable out, String destObject, String mangledName, + FieldData fieldData) throws IOException; +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java Thu May 02 09:18:22 2013 +0200 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +/** + * Defines obfuscation level of produced JavaScript files. + * + * @since 0.5 + */ +public enum ObfuscationLevel { + /** Generated JavaScript is (sort of) human readable. Useful for debugging. + * Dynamic capabilities of the virtual machine work on all classes. + */ + NONE, + /** White spaces are removed. Names of external symbols remain unchanged. + * Dynamic capabilities of the virtual machine work on all classes. + */ + MINIMAL, +// temporarily commented out before merge. not well defined yet: +// MEDIUM, + /** Aggressive obfuscation of everything. Compact, unreadable "one-liner". + * One cannot load classes dynamically. Useful mostly for static compilation + * of self contained application. + */ + FULL +} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java Thu May 02 09:18:22 2013 +0200 @@ -43,6 +43,25 @@ } arr[arr.length - 1] = s; } + + StringArray addAndNew(String... values) { + int j; + String[] tmp; + if (arr == null) { + tmp = new String[values.length]; + j = 0; + } else { + tmp = new String[arr.length + values.length]; + for (int i = 0; i < arr.length; i++) { + tmp[i] = arr[i]; + } + j = arr.length; + } + for (int i = 0; i < values.length;) { + tmp[j++] = values[i++]; + } + return new StringArray(tmp); + } public String[] toArray() { return arr == null ? new String[0] : arr; @@ -93,5 +112,4 @@ } return -1; } - } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Thu May 02 09:18:22 2013 +0200 @@ -28,7 +28,11 @@ public VM(Appendable out) { super(out); } - + + private VM(Appendable out, ObfuscationDelegate obfuscationDelegate) { + super(out, obfuscationDelegate); + } + static { // uses VMLazy to load dynamic classes boolean assertsOn = false; @@ -47,6 +51,12 @@ static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException { new VM(out).doCompile(l, names); } + + static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names, + ObfuscationDelegate obfuscationDelegate) throws IOException { + new VM(out, obfuscationDelegate).doCompile(l, names); + } + protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {"); StringArray processed = new StringArray(); @@ -230,4 +240,9 @@ String accessClass(String className) { return "vm." + className; } + + @Override + String getVMObject() { + return "vm"; + } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Thu May 02 09:18:22 2013 +0200 @@ -151,5 +151,10 @@ String accessClass(String classOperation) { return "vm." + classOperation; } + + @Override + String getVMObject() { + return "vm"; + } } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Thu May 02 09:18:22 2013 +0200 @@ -207,5 +207,12 @@ true ); } + + @Test public void valueOfEnum() throws Exception { + assertExec("can get value of enum", Classes.class, + "valueEnum__Ljava_lang_String_2Ljava_lang_String_2", + "TWO", "TWO" + ); + } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Thu May 02 09:18:22 2013 +0200 @@ -230,4 +230,7 @@ return Application.class.isAssignableFrom(MyApplication.class); } + public static String valueEnum(String v) { + return ClassesMarker.E.valueOf(v).toString(); + } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Thu May 02 09:18:22 2013 +0200 @@ -18,6 +18,7 @@ package org.apidesign.vm4brwsr; import java.io.UnsupportedEncodingException; +import org.apidesign.bck2brwsr.core.JavaScriptBody; /** * @@ -129,4 +130,20 @@ public String toString() { return HELLO + cnt; } + + @JavaScriptBody(args = {}, body = "return [1, 2];") + private static native Object crtarr(); + @JavaScriptBody(args = { "o" }, body = "return o.toString();") + private static native String toStrng(Object o); + + public static String toStringArray(boolean fakeArr, boolean toString) { + final Object arr = fakeArr ? crtarr() : new Object[2]; + final String whole = toString ? arr.toString() : toStrng(arr); + int zav = whole.indexOf('@'); + if (zav <= 0) { + zav = whole.length(); + } + return whole.substring(0, zav).toString().toString(); + } + } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Thu May 02 09:18:22 2013 +0200 @@ -194,6 +194,34 @@ } + @Test public void toStringOnJSArray() throws Exception { + String exp = StringSample.toStringArray(false, true); + + assertExec( + "Treated as Java Object array", + StringSample.class, "toStringArray__Ljava_lang_String_2ZZ", + exp, true, true + ); + } + + @Test public void toStringOnRealArray() throws Exception { + String exp = StringSample.toStringArray(false, true); + + assertExec( + "Is Java Object array", + StringSample.class, "toStringArray__Ljava_lang_String_2ZZ", + exp, false, true + ); + } + + @Test public void valueOfOnJSArray() throws Exception { + assertExec( + "Treated as classical JavaScript array", + StringSample.class, "toStringArray__Ljava_lang_String_2ZZ", + "1,2", true, false + ); + } + private static TestVM code; @BeforeClass diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Thu May 02 09:18:22 2013 +0200 @@ -43,4 +43,9 @@ @Override protected void requireScript(String resourcePath) { } + + @Override + String getVMObject() { + return "global"; + } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/pom.xml --- a/rt/vmtest/pom.xml Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vmtest/pom.xml Thu May 02 09:18:22 2013 +0200 @@ -4,11 +4,11 @@ org.apidesign.bck2brwsr rt - 0.5-SNAPSHOT + 0.8-SNAPSHOT org.apidesign.bck2brwsr vmtest - 0.5-SNAPSHOT + 0.8-SNAPSHOT VM Testing APIs http://bck2brwsr.apidesign.org @@ -50,18 +50,6 @@ ${project.groupId} - vm4brwsr - ${project.version} - jar - - - ${project.groupId} - emul.mini - ${project.version} - test - - - ${project.groupId} launcher ${project.version} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/BrwsrTest.java --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/BrwsrTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/BrwsrTest.java Thu May 02 09:18:22 2013 +0200 @@ -29,6 +29,10 @@ * The browser to is by default executed via {@link java.awt.Desktop#browse(java.net.URI)}, * but one can change that by specifying -Dvmtest.brwsrs=firefox,google-chrome * property. + *

+ * If the annotated method throws {@link InterruptedException}, it will return + * the processing to the browser and after 100ms, called again. This is useful + * for testing asynchronous communication, etc. * * @author Jaroslav Tulach */ diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java Thu May 02 09:18:22 2013 +0200 @@ -53,5 +53,10 @@ String resource() default ""; /** mime type of the resource */ String mimeType(); + /** query parameters. Can be referenced from the {@link #content} as + * $0, $1, etc. The values will be extracted + * from URL parameters of the request. + */ + String[] parameters() default {}; } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java Thu May 02 09:18:22 2013 +0200 @@ -17,6 +17,9 @@ */ package org.apidesign.bck2brwsr.vmtest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.apidesign.bck2brwsr.launcher.Launcher; import org.apidesign.bck2brwsr.vmtest.impl.CompareCase; import org.testng.annotations.Factory; @@ -34,6 +37,9 @@ * @author Jaroslav Tulach */ public final class VMTest { + private final List classes = new ArrayList<>(); + private final List launcher = new ArrayList<>(); + private VMTest() { } @@ -47,10 +53,66 @@ * Each {@link BrwsrTest} annotated method is executed once in {@link Launcher started * browser}. * - * @param clazz the class to inspect + * @param clazz the class (or classes) to inspect * @return the set of created tests */ - public static Object[] create(Class clazz) { - return CompareCase.create(clazz); + public static Object[] create(Class clazz) { + return newTests().withClasses(clazz).build(); + } + + /** Creates new builder for test execution. Continue with methods + * like {@link #withClasses(java.lang.Class[])} or {@link #withLaunchers(java.lang.String[])}. + * Finish the process by calling {@link #build()}. + * + * @return new instance of a builder + * @since 0.7 + */ + public static VMTest newTests() { + return new VMTest(); + } + + /** Adds class (or classes) to the test execution. The classes are inspected + * to contain methods annotated by + * {@link Compare} or {@link BrwsrTest}. Appropriate set of TestNG test + * cases is then created. + *

+ * Each {@link Compare} instance runs the test in different virtual + * machine and at the end they compare the results. + *

+ * Each {@link BrwsrTest} annotated method is executed once in {@link Launcher started + * browser}. + * + * @param classes one or more classes to inspect + * @since 0.7 + */ + public final VMTest withClasses(Class... classes) { + this.classes.addAll(Arrays.asList(classes)); + return this; + } + + /** Adds list of launchers that should be used to execute tests defined + * by {@link Compare} and {@link BrwsrTest} annotations. This value + * can be overrided by using vmtest.brwsrs property. + * List of supported launchers is available in the documentation of + * {@link Launcher}. + * + * @param launcher names of one or more launchers to use for the execution + * of tests + * @since 0.7 + */ + public final VMTest withLaunchers(String... launcher) { + this.launcher.addAll(Arrays.asList(launcher)); + return this; + } + + /** Assembles the provided information into the final array of tests. + * @return array of TestNG tests + * @since 0.7 + */ + public final Object[] build() { + return CompareCase.create( + launcher.toArray(new String[0]), + classes.toArray(new Class[0]) + ); } } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Thu May 02 09:18:22 2013 +0200 @@ -65,17 +65,17 @@ for (Http.Resource r : http) { if (!r.content().isEmpty()) { InputStream is = new ByteArrayInputStream(r.content().getBytes("UTF-8")); - c.addHttpResource(r.path(), r.mimeType(), is); + c.addHttpResource(r.path(), r.mimeType(), r.parameters(), is); } else { InputStream is = m.getDeclaringClass().getResourceAsStream(r.resource()); - c.addHttpResource(r.path(), r.mimeType(), is); + c.addHttpResource(r.path(), r.mimeType(), r.parameters(), is); } } } String res = c.invoke(); value = res; if (fail) { - int idx = res.indexOf(':'); + int idx = res == null ? -1 : res.indexOf(':'); if (idx >= 0) { Class thrwbl = null; try { diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Tue Apr 02 15:40:51 2013 +0200 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Thu May 02 09:18:22 2013 +0200 @@ -53,26 +53,25 @@ * @param clazz the class to inspect * @return the set of created tests */ - public static Object[] create(Class clazz) { - Method[] arr = clazz.getMethods(); + public static Object[] create(String[] brwsr, Class[] classes) { List ret = new ArrayList<>(); final LaunchSetup l = LaunchSetup.INSTANCE; ret.add(l); - String[] brwsr; { String p = System.getProperty("vmtest.brwsrs"); if (p != null) { brwsr = p.split(","); - } else { - brwsr = new String[0]; } } - for (Method m : arr) { - registerCompareCases(m, l, ret, brwsr); - registerBrwsrCases(m, l, ret, brwsr); + for (Class clazz : classes) { + Method[] arr = clazz.getMethods(); + for (Method m : arr) { + registerCompareCases(m, l, ret, brwsr); + registerBrwsrCases(m, l, ret, brwsr); + } } return ret.toArray(); } diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class AssertionTest { - - @Compare public Object checkAssert() throws ClassNotFoundException { - try { - assert false : "Is assertion status on?"; - return null; - } catch (AssertionError ex) { - return ex.getClass().getName(); - } - } - - @Factory - public static Object[] create() { - return VMTest.create(AssertionTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/BrwsrCheckTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/BrwsrCheckTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.core.JavaScriptBody; -import org.apidesign.bck2brwsr.vmtest.BrwsrTest; -import org.apidesign.bck2brwsr.vmtest.HtmlFragment; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class BrwsrCheckTest { - - @BrwsrTest public void assertWindowObjectIsDefined() { - assert window() != null : "No window object found!"; - } - - - - - @HtmlFragment("

\n" - + "Hello!\n" - + "

\n") - @BrwsrTest public void accessProvidedFragment() { - assert getElementById("hello") != null : "Element with 'hello' ID found"; - } - - @Factory - public static Object[] create() { - return VMTest.create(BrwsrCheckTest.class); - } - - - @JavaScriptBody(args = {}, body = "return window;") - private static native Object window(); - - @JavaScriptBody(args = { "id" }, body = "return window.document.getElementById(id);") - private static native Object getElementById(String id); -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ByteArithmeticTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ByteArithmeticTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.core.JavaScriptBody; -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class ByteArithmeticTest { - - private static byte add(byte x, byte y) { - return (byte)(x + y); - } - - private static byte sub(byte x, byte y) { - return (byte)(x - y); - } - - private static byte mul(byte x, byte y) { - return (byte)(x * y); - } - - private static byte div(byte x, byte y) { - return (byte)(x / y); - } - - private static byte mod(byte x, byte y) { - return (byte)(x % y); - } - - @Compare public byte conversion() { - return (byte)123456; - } - - @Compare public byte addOverflow() { - return add(Byte.MAX_VALUE, (byte)1); - } - - @Compare public byte subUnderflow() { - return sub(Byte.MIN_VALUE, (byte)1); - } - - @Compare public byte addMaxByteAndMaxByte() { - return add(Byte.MAX_VALUE, Byte.MAX_VALUE); - } - - @Compare public byte subMinByteAndMinByte() { - return sub(Byte.MIN_VALUE, Byte.MIN_VALUE); - } - - @Compare public byte multiplyMaxByte() { - return mul(Byte.MAX_VALUE, (byte)2); - } - - @Compare public byte multiplyMaxByteAndMaxByte() { - return mul(Byte.MAX_VALUE, Byte.MAX_VALUE); - } - - @Compare public byte multiplyMinByte() { - return mul(Byte.MIN_VALUE, (byte)2); - } - - @Compare public byte multiplyMinByteAndMinByte() { - return mul(Byte.MIN_VALUE, Byte.MIN_VALUE); - } - - @Compare public byte multiplyPrecision() { - return mul((byte)17638, (byte)1103); - } - - @Compare public byte division() { - return div((byte)1, (byte)2); - } - - @Compare public byte divisionReminder() { - return mod((byte)1, (byte)2); - } - - private static int readShort(byte[] byteCodes, int offset) { - int signed = byteCodes[offset]; - byte b0 = (byte)signed; - return (b0 << 8) | (byteCodes[offset + 1] & 0xff); - } - - private static int readShortArg(byte[] byteCodes, int offsetInstruction) { - return readShort(byteCodes, offsetInstruction + 1); - } - - @Compare public int readIntArgs255and156() { - final byte[] arr = new byte[] { (byte)0, (byte)255, (byte)156 }; - - assert arr[1] == -1 : "First byte: " + arr[1]; - assert arr[2] == -100 : "Second byte: " + arr[2]; - final int ret = readShortArg(arr, 0); - assert ret < 65000: "Value: " + ret; - return ret; - } - - @JavaScriptBody(args = { "arr" }, body = "arr[1] = 255; arr[2] = 156; return arr;") - private static byte[] fill255and156(byte[] arr) { - arr[1] = (byte)255; - arr[2] = (byte)156; - return arr; - } - - @Compare public int readIntArgs255and156JSArray() { - final byte[] arr = fill255and156(new byte[] { 0, 0, 0 }); - - final int ret = readShortArg(arr, 0); - assert ret < 65000: "Value: " + ret; - return ret; - } - - @Compare public int readIntArgsMinus1andMinus100() { - final byte[] arr = new byte[] { (byte)0, (byte)-1, (byte)-100 }; - - assert arr[1] == -1 : "First byte: " + arr[1]; - assert arr[2] == -100 : "Second byte: " + arr[2]; - - return readShortArg(arr, 0); - } - - @Factory - public static Object[] create() { - return VMTest.create(ByteArithmeticTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CloneTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CloneTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CloneTest { - private int value; - - @Compare - public Object notSupported() throws CloneNotSupportedException { - return this.clone(); - } - - @Compare public String sameClass() throws CloneNotSupportedException { - return new Clnbl().clone().getClass().getName(); - } - - @Compare public boolean differentInstance() throws CloneNotSupportedException { - Clnbl orig = new Clnbl(); - return orig == orig.clone(); - } - - @Compare public int sameReference() throws CloneNotSupportedException { - CloneTest self = this; - Clnbl orig = new Clnbl(); - self.value = 33; - orig.ref = self; - return ((Clnbl)orig.clone()).ref.value; - } - - @Compare public int sameValue() throws CloneNotSupportedException { - Clnbl orig = new Clnbl(); - orig.value = 10; - return ((Clnbl)orig.clone()).value; - } - - @Factory - public static Object[] create() { - return VMTest.create(CloneTest.class); - } - - public static final class Clnbl implements Cloneable { - public CloneTest ref; - private int value; - - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareByteArrayTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareByteArrayTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareByteArrayTest { - @Compare public int byteArraySum() { - byte[] arr = createArray(); - return sumByteArr(arr); - } - - @Compare public int countZeros() { - int zeros = 0; - for (Byte b : createArray()) { - if (b == 0) { - zeros++; - } - } - return zeros; - } - - private static int sumByteArr(byte[] arr) { - int sum = 0; - for (int i = 0; i < arr.length; i++) { - sum += arr[i]; - } - return sum; - } - - @Compare public String noOutOfBounds() { - return atIndex(1); - } - - @Compare public String outOfBounds() { - return atIndex(5); - } - - @Compare public String outOfBoundsMinus() { - return atIndex(-1); - } - - @Compare public String toOfBounds() { - return toIndex(5); - } - - @Compare public String toOfBoundsMinus() { - return toIndex(-1); - } - - @Compare public int multiArrayLength() { - int[][] arr = new int[1][0]; - return arr[0].length; - } - - @Compare public int multiObjectArrayLength() { - Object[][] arr = new Object[1][0]; - return arr[0].length; - } - - private static final int[] arr = { 0, 1, 2 }; - public static String atIndex(int at) { - return "at@" + arr[at]; - } - public static String toIndex(int at) { - arr[at] = 10; - return "ok"; - } - - - @Factory - public static Object[] create() { - return VMTest.create(CompareByteArrayTest.class); - } - - private byte[] createArray() { - byte[] arr = new byte[10]; - arr[5] = 3; - arr[7] = 8; - return arr; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareHashTest { - @Compare public int hashOfString() { - return "Ahoj".hashCode(); - } - - @Compare public int hashRemainsYieldsZero() { - Object o = new Object(); - return o.hashCode() - o.hashCode(); - } - - @Compare public int initializeInStatic() { - return StaticUse.NON_NULL.hashCode() - StaticUse.NON_NULL.hashCode(); - } - - @Compare public int hashOfInt() { - return Integer.valueOf(Integer.MAX_VALUE).hashCode(); - } - - @Factory - public static Object[] create() { - return VMTest.create(CompareHashTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareIntArrayTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareIntArrayTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareIntArrayTest { - @Compare public int integerArraySum() { - int[] arr = createArray(); - return sumIntArr(arr); - } - - @Compare public int countZeros() { - int zeros = 0; - for (Integer i : createArray()) { - if (i == 0) { - zeros++; - } - } - return zeros; - } - - private static int sumIntArr(int[] arr) { - int sum = 0; - for (int i = 0; i < arr.length; i++) { - sum += arr[i]; - } - return sum; - } - - @Factory - public static Object[] create() { - return VMTest.create(CompareIntArrayTest.class); - } - - private int[] createArray() { - int[] arr = new int[10]; - arr[5] = 3; - arr[7] = 8; - return arr; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,162 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareStringsTest { - @Compare public String firstChar() { - return "" + ("Hello".toCharArray()[0]); - } - - @Compare public String classCast() { - Object o = firstChar(); - return String.class.cast(o); - } - - @Compare public String classCastThrown() { - Object o = null; - return String.class.cast(o); - } - - @Compare public boolean equalToNull() { - return "Ahoj".equals(null); - } - - @Compare public int highByteLenght() { - byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; - return new String(arr, 0).length(); - } - - @Compare public String highByte() { - byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; - StringBuilder sb = new StringBuilder(); - sb.append("pref:"); - sb.append(new String(arr, 0)); - return sb.toString(); - } - - @Compare public static Object compareURLs() throws MalformedURLException { - return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); - } - - @Compare public String deleteLastTwoCharacters() { - StringBuilder sb = new StringBuilder(); - sb.append("453.0"); - if (sb.toString().endsWith(".0")) { - final int l = sb.length(); - sb.delete(l - 2, l); - } - return sb.toString().toString(); - } - - @Compare public String nameOfStringClass() throws Exception { - return Class.forName("java.lang.String").getName(); - } - @Compare public String nameOfArrayClass() throws Exception { - return Class.forName("org.apidesign.bck2brwsr.tck.CompareHashTest").getName(); - } - - @Compare public String lowerHello() { - return "HeLlO".toLowerCase(); - } - - @Compare public String lowerA() { - return String.valueOf(Character.toLowerCase('A')).toString(); - } - @Compare public String upperHello() { - return "hello".toUpperCase(); - } - - @Compare public String upperA() { - return String.valueOf(Character.toUpperCase('a')).toString(); - } - - @Compare public boolean matchRegExp() throws Exception { - return "58038503".matches("\\d*"); - } - - @Compare public boolean doesNotMatchRegExp() throws Exception { - return "58038503GH".matches("\\d*"); - } - - @Compare public boolean doesNotMatchRegExpFully() throws Exception { - return "Hello".matches("Hell"); - } - - @Compare public String emptyCharArray() { - char[] arr = new char[10]; - return new String(arr); - } - - @Compare public String variousCharacterTests() throws Exception { - StringBuilder sb = new StringBuilder(); - - sb.append(Character.isUpperCase('a')); - sb.append(Character.isUpperCase('A')); - sb.append(Character.isLowerCase('a')); - sb.append(Character.isLowerCase('A')); - - sb.append(Character.isLetter('A')); - sb.append(Character.isLetterOrDigit('9')); - sb.append(Character.isLetterOrDigit('A')); - sb.append(Character.isLetter('0')); - - return sb.toString().toString(); - } - - @Compare - public String nullFieldInitialized() { - NullField nf = new NullField(); - return ("" + nf.name).toString(); - } - @Compare - public String toUTFString() throws UnsupportedEncodingException { - byte[] arr = { - (byte) -59, (byte) -67, (byte) 108, (byte) 117, (byte) -59, (byte) -91, - (byte) 111, (byte) 117, (byte) -60, (byte) -115, (byte) 107, (byte) -61, - (byte) -67, (byte) 32, (byte) 107, (byte) -59, (byte) -81, (byte) -59, - (byte) -120 - }; - return new String(arr, "utf-8"); - } - - @Compare - public int stringToBytesLenght() throws UnsupportedEncodingException { - return "\u017dlu\u0165ou\u010dk\u00fd k\u016f\u0148".getBytes("utf8").length; - } - - @Factory - public static Object[] create() { - return VMTest.create(CompareStringsTest.class); - } - - private static final class NullField { - - String name; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/DoubleTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/DoubleTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class DoubleTest { - @Compare public String integerToString() { - return toStr(1); - } - - @Compare public String integerAndHalfToString() { - return toStr(1.5); - } - - @Compare public double longToAndBack() { - return Double.parseDouble(toStr(Long.MAX_VALUE / 10)); - } - - @Compare public String negativeIntToString() { - return toStr(-10); - } - - @Compare public String negativeIntAndHalfToString() { - return toStr(-10.5); - } - - @Compare public double negativeLongAndBack() { - return Double.parseDouble(toStr(Long.MIN_VALUE / 10)); - } - - @Compare public double canParseExp() { - return Double.parseDouble(toStr(1.7976931348623157e+308)); - } - - private static String toStr(double d) { - return Double.toString(d); - } - - @Factory - public static Object[] create() { - return VMTest.create(DoubleTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import java.io.InputStream; -import java.net.URL; -import org.apidesign.bck2brwsr.core.JavaScriptBody; -import org.apidesign.bck2brwsr.vmtest.BrwsrTest; -import org.apidesign.bck2brwsr.vmtest.Http; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class HttpResourceTest { - - @Http({ - @Http.Resource(path = "/xhr", content = "Hello Brwsr!", mimeType = "text/plain") - }) - @BrwsrTest - - - public String testReadContentViaXHR() throws Exception { - String msg = read("/xhr"); - assert "Hello Brwsr!".equals(msg) : "The message was " + msg; - return msg; - } - - @Http({ - @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") - }) - @BrwsrTest - public String testReadContentViaURL() throws Exception { - URL url = new URL("http:/url"); - String msg = (String) url.getContent(); - assert "Hello via URL!".equals(msg) : "The message was " + msg; - return msg; - } - @Http({ - @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") - }) - @BrwsrTest - public String testReadContentViaURLWithStringParam() throws Exception { - URL url = new URL("http:/url"); - String msg = (String) url.getContent(new Class[] { String.class }); - assert "Hello via URL!".equals(msg) : "The message was " + msg; - return msg; - } - - @Http({ - @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") - }) - @BrwsrTest - public void testReadByte() throws Exception { - URL url = new URL("http:/bytes"); - final Object res = url.getContent(new Class[] { byte[].class }); - assert res instanceof byte[] : "Expecting byte[]: " + res; - byte[] arr = (byte[]) res; - assert arr.length == 1 : "One byte " + arr.length; - assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); - } - - @Http({ - @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") - }) - @BrwsrTest - public void testReadByteViaInputStream() throws Exception { - URL url = new URL("http:/bytes"); - InputStream is = url.openStream(); - byte[] arr = new byte[10]; - int len = is.read(arr); - assert len == 1 : "One byte " + len; - assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); - } - - @JavaScriptBody(args = { "url" }, body = - "var req = new XMLHttpRequest();\n" - + "req.open('GET', url, false);\n" - + "req.send();\n" - + "return req.responseText;" - ) - private static native String read(String url); - - - @Factory - public static Object[] create() { - return VMTest.create(HttpResourceTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceA.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceA.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -/** - * - * @author Jaroslav Tulach - */ -public class InheritanceA { - private String name; - - public void setA(String n) { - this.name = n; - } - - public String getA() { - return name; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceB.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceB.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -/** - * - * @author Jaroslav Tulach - */ -public class InheritanceB extends InheritanceA { - private String name; - - public void setB(String n) { - this.name = n; - } - - public String getB() { - return name; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class InheritanceTest { - - @Compare public String checkFieldsIndependent() throws ClassNotFoundException { - InheritanceB ib = new InheritanceB(); - ib.setA("A"); - ib.setB("B"); - return "A: " + ib.getA() + " B: " + ib.getB(); - } - - @Factory - public static Object[] create() { - return VMTest.create(InheritanceTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class IntegerArithmeticTest { - - private static int add(int x, int y) { - return x + y; - } - - private static int sub(int x, int y) { - return x - y; - } - - private static int mul(int x, int y) { - return x * y; - } - - private static int div(int x, int y) { - return x / y; - } - - private static int mod(int x, int y) { - return x % y; - } - - private static int neg(int x) { - return (-x); - } - - private static float fadd(float x, float y) { - return x + y; - } - - private static double dadd(double x, double y) { - return x + y; - } - - @Compare public int addOverflow() { - return add(Integer.MAX_VALUE, 1); - } - - @Compare public int subUnderflow() { - return sub(Integer.MIN_VALUE, 1); - } - - @Compare public int addMaxIntAndMaxInt() { - return add(Integer.MAX_VALUE, Integer.MAX_VALUE); - } - - @Compare public int subMinIntAndMinInt() { - return sub(Integer.MIN_VALUE, Integer.MIN_VALUE); - } - - @Compare public int multiplyMaxInt() { - return mul(Integer.MAX_VALUE, 2); - } - - @Compare public int multiplyMaxIntAndMaxInt() { - return mul(Integer.MAX_VALUE, Integer.MAX_VALUE); - } - - @Compare public int multiplyMinInt() { - return mul(Integer.MIN_VALUE, 2); - } - - @Compare public int multiplyMinIntAndMinInt() { - return mul(Integer.MIN_VALUE, Integer.MIN_VALUE); - } - - @Compare public int multiplyPrecision() { - return mul(119106029, 1103515245); - } - - @Compare public int division() { - return div(1, 2); - } - - @Compare public int divisionReminder() { - return mod(1, 2); - } - - @Compare public int negativeDivision() { - return div(-7, 3); - } - - @Compare public int negativeDivisionReminder() { - return mod(-7, 3); - } - - @Compare public int conversionFromFloat() { - return (int) fadd(-2, -0.6f); - } - - @Compare public int conversionFromDouble() { - return (int) dadd(-2, -0.6); - } - - @Compare public boolean divByZeroThrowsArithmeticException() { - try { - div(1, 0); - return false; - } catch (final ArithmeticException e) { - return true; - } - } - - @Compare public boolean modByZeroThrowsArithmeticException() { - try { - mod(1, 0); - return false; - } catch (final ArithmeticException e) { - return true; - } - } - - @Compare public int negate() { - return neg(123456); - } - - @Compare public int negateMaxInt() { - return neg(Integer.MAX_VALUE); - } - - @Compare public int negateMinInt() { - return neg(Integer.MIN_VALUE); - } - - @Compare public int sumTwoDimensions() { - int[][] matrix = createMatrix(4, 3); - matrix[0][0] += 10; - return matrix[0][0]; - } - - static int[][] createMatrix(int x, int y) { - return new int[x][y]; - } - - @Factory - public static Object[] create() { - return VMTest.create(IntegerArithmeticTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,376 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class LongArithmeticTest { - - private static long add(long x, long y) { - return (x + y); - } - - private static long sub(long x, long y) { - return (x - y); - } - - private static long mul(long x, long y) { - return (x * y); - } - - private static long div(long x, long y) { - return (x / y); - } - - private static long mod(long x, long y) { - return (x % y); - } - - private static long neg(long x) { - return (-x); - } - - private static long shl(long x, int b) { - return (x << b); - } - - private static long shr(long x, int b) { - return (x >> b); - } - - private static long ushr(long x, int b) { - return (x >>> b); - } - - private static long and(long x, long y) { - return (x & y); - } - - private static long or(long x, long y) { - return (x | y); - } - - private static long xor(long x, long y) { - return (x ^ y); - } - - private static float fadd(float x, float y) { - return x + y; - } - - private static double dadd(double x, double y) { - return x + y; - } - - public static int compare(long x, long y, int zero) { - final int xyResult = compareL(x, y, zero); - final int yxResult = compareL(y, x, zero); - - return ((xyResult + yxResult) == 0) ? xyResult : -2; - } - - private static int compareL(long x, long y, int zero) { - int result = -2; - int trueCount = 0; - - x += zero; - if (x == y) { - result = 0; - ++trueCount; - } - - x += zero; - if (x < y) { - result = -1; - ++trueCount; - } - - x += zero; - if (x > y) { - result = 1; - ++trueCount; - } - - return (trueCount == 1) ? result : -2; - } - - @Compare public long conversion() { - return Long.MAX_VALUE; - } - - @Compare public long negate1() { - return neg(0x00fa37d7763e0ca1l); - } - - @Compare public long negate2() { - return neg(0x80fa37d7763e0ca1l); - } - - @Compare public long negate3() { - return neg(0xfffffffffffffeddl); - } - - @Compare public long addOverflow() { - return add(Long.MAX_VALUE, 1l); - } - - @Compare public long subUnderflow() { - return sub(Long.MIN_VALUE, 1l); - } - - @Compare public long addMaxLongAndMaxLong() { - return add(Long.MAX_VALUE, Long.MAX_VALUE); - } - - @Compare public long subMinLongAndMinLong() { - return sub(Long.MIN_VALUE, Long.MIN_VALUE); - } - - @Compare public long subMinLongAndMaxLong() { - return sub(Long.MIN_VALUE, Long.MAX_VALUE); - } - - @Compare public long multiplyMaxLong() { - return mul(Long.MAX_VALUE, 2l); - } - - @Compare public long multiplyMaxLongAndMaxLong() { - return mul(Long.MAX_VALUE, Long.MAX_VALUE); - } - - @Compare public long multiplyMinLong() { - return mul(Long.MIN_VALUE, 2l); - } - - @Compare public long multiplyMinLongAndMinLong() { - return mul(Long.MIN_VALUE, Long.MIN_VALUE); - } - - @Compare public long multiplyPrecision() { - return mul(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); - } - - @Compare public long divideSmallPositiveNumbers() { - return div(0xabcdef, 0x123); - } - - @Compare public long divideSmallNegativeNumbers() { - return div(-0xabcdef, -0x123); - } - - @Compare public long divideSmallMixedNumbers() { - return div(0xabcdef, -0x123); - } - - @Compare public long dividePositiveNumbersOneDigitDenom() { - return div(0xabcdef0102ffffl, 0x654); - } - - @Compare public long divideNegativeNumbersOneDigitDenom() { - return div(-0xabcdef0102ffffl, -0x654); - } - - @Compare public long divideMixedNumbersOneDigitDenom() { - return div(-0xabcdef0102ffffl, 0x654); - } - - @Compare public long dividePositiveNumbersMultiDigitDenom() { - return div(0x7ffefc003322aabbl, 0x89ab1000l); - } - - @Compare public long divideNegativeNumbersMultiDigitDenom() { - return div(-0x7ffefc003322aabbl, -0x123489ab1001l); - } - - @Compare public long divideMixedNumbersMultiDigitDenom() { - return div(0x7ffefc003322aabbl, -0x38f49b0b7574e36l); - } - - @Compare public long divideWithOverflow() { - return div(0x8000fffe0000l, 0x8000ffffl); - } - - @Compare public long divideWithCorrection() { - return div(0x7fff800000000000l, 0x800000000001l); - } - - @Compare public long moduloSmallPositiveNumbers() { - return mod(0xabcdef, 0x123); - } - - @Compare public long moduloSmallNegativeNumbers() { - return mod(-0xabcdef, -0x123); - } - - @Compare public long moduloSmallMixedNumbers() { - return mod(0xabcdef, -0x123); - } - - @Compare public long moduloPositiveNumbersOneDigitDenom() { - return mod(0xabcdef0102ffffl, 0x654); - } - - @Compare public long moduloNegativeNumbersOneDigitDenom() { - return mod(-0xabcdef0102ffffl, -0x654); - } - - @Compare public long moduloMixedNumbersOneDigitDenom() { - return mod(-0xabcdef0102ffffl, 0x654); - } - - @Compare public long moduloPositiveNumbersMultiDigitDenom() { - return mod(0x7ffefc003322aabbl, 0x89ab1000l); - } - - @Compare public long moduloNegativeNumbersMultiDigitDenom() { - return mod(-0x7ffefc003322aabbl, -0x123489ab1001l); - } - - @Compare public long moduloMixedNumbersMultiDigitDenom() { - return mod(0x7ffefc003322aabbl, -0x38f49b0b7574e36l); - } - - @Compare public long moduloWithOverflow() { - return mod(0x8000fffe0000l, 0x8000ffffl); - } - - @Compare public long moduloWithCorrection() { - return mod(0x7fff800000000000l, 0x800000000001l); - } - - @Compare public long conversionFromFloatPositive() { - return (long) fadd(2, 0.6f); - } - - @Compare public long conversionFromFloatNegative() { - return (long) fadd(-2, -0.6f); - } - - @Compare public long conversionFromDoublePositive() { - return (long) dadd(0x20ffff0000L, 0.6); - } - - @Compare public long conversionFromDoubleNegative() { - return (long) dadd(-0x20ffff0000L, -0.6); - } - - @Compare public boolean divByZeroThrowsArithmeticException() { - try { - div(1, 0); - return false; - } catch (final ArithmeticException e) { - return true; - } - } - - @Compare public boolean modByZeroThrowsArithmeticException() { - try { - mod(1, 0); - return false; - } catch (final ArithmeticException e) { - return true; - } - } - - @Compare public long shiftL1() { - return shl(0x00fa37d7763e0ca1l, 5); - } - - @Compare public long shiftL2() { - return shl(0x00fa37d7763e0ca1l, 32); - } - - @Compare public long shiftL3() { - return shl(0x00fa37d7763e0ca1l, 45); - } - - @Compare public long shiftR1() { - return shr(0x00fa37d7763e0ca1l, 5); - } - - @Compare public long shiftR2() { - return shr(0x00fa37d7763e0ca1l, 32); - } - - @Compare public long shiftR3() { - return shr(0x00fa37d7763e0ca1l, 45); - } - - @Compare public long uShiftR1() { - return ushr(0x00fa37d7763e0ca1l, 5); - } - - @Compare public long uShiftR2() { - return ushr(0x00fa37d7763e0ca1l, 45); - } - - @Compare public long uShiftR3() { - return ushr(0xf0fa37d7763e0ca1l, 5); - } - - @Compare public long uShiftR4() { - return ushr(0xf0fa37d7763e0ca1l, 45); - } - - @Compare public long and1() { - return and(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); - } - - @Compare public long or1() { - return or(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); - } - - @Compare public long xor1() { - return xor(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); - } - - @Compare public long xor2() { - return xor(0x00fa37d7763e0ca1l, 0x00000000ff00123el); - } - - @Compare public long xor3() { - return xor(0x00000000763e0ca1l, 0x00000000ff00123el); - } - - @Compare public int compareSameNumbers() { - return compare(0x0000000000000000l, 0x0000000000000000l, 0); - } - - @Compare public int comparePositiveNumbers() { - return compare(0x0000000000200000l, 0x0000000010000000l, 0); - } - - @Compare public int compareNegativeNumbers() { - return compare(0xffffffffffffffffl, 0xffffffff00000000l, 0); - } - - @Compare public int compareMixedNumbers() { - return compare(0x8000000000000000l, 0x7fffffffffffffffl, 0); - } - - @Factory - public static Object[] create() { - return VMTest.create(LongArithmeticTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionArrayTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import java.lang.reflect.Array; -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class ReflectionArrayTest { - @Compare public int lengthOfStringArray() { - String[] arr = (String[]) Array.newInstance(String.class, 10); - return arr.length; - } - - @Compare public int reflectiveLengthOfStringArray() { - Object arr = Array.newInstance(String.class, 10); - return Array.getLength(arr); - } - - @Compare public int reflectiveLengthOneNonArray() { - Object arr = "non-array"; - return Array.getLength(arr); - } - - @Compare public String compTypeOfStringArray() { - String[] arr = (String[]) Array.newInstance(String.class, 10); - return arr.getClass().getComponentType().getName(); - } - - @Compare public Object negativeArrayExcp() { - return Array.newInstance(String.class, -5); - } - - @Compare public int lengthOfIntArray() { - int[] arr = (int[]) Array.newInstance(Integer.TYPE, 10); - return arr.length; - } - - @Compare public int reflectiveLengthOfIntArray() { - Object arr = Array.newInstance(Integer.TYPE, 10); - return Array.getLength(arr); - } - - @Compare public String compTypeOfIntArray() { - int[] arr = (int[]) Array.newInstance(int.class, 10); - return arr.getClass().getComponentType().getName(); - } - - @Compare public Object intNegativeArrayExcp() { - return Array.newInstance(int.class, -5); - } - - @Compare public Integer verifyAutobox() { - int[] arr = (int[]) Array.newInstance(int.class, 5); - return (Integer) Array.get(arr, 0); - } - @Compare public String verifyObjectArray() { - String[] arr = (String[]) Array.newInstance(String.class, 5); - Array.set(arr, 0, "Hello"); - return (String) Array.get(arr, 0); - } - @Compare public int verifyInt() { - int[] arr = (int[]) Array.newInstance(int.class, 5); - return Array.getInt(arr, 0); - } - @Compare public long verifyConvertToLong() { - int[] arr = (int[]) Array.newInstance(int.class, 5); - return Array.getLong(arr, 0); - } - - @Compare public Object verifySetIntToObject() { - try { - Object[] arr = (Object[]) Array.newInstance(Object.class, 5); - Array.setInt(arr, 0, 10); - return Array.get(arr, 0); - } catch (Exception exception) { - return exception.getClass().getName(); - } - } - @Compare public long verifySetShort() { - int[] arr = (int[]) Array.newInstance(int.class, 5); - Array.setShort(arr, 0, (short)10); - return Array.getLong(arr, 0); - } - @Compare public long verifyCantSetLong() { - int[] arr = (int[]) Array.newInstance(int.class, 5); - Array.setLong(arr, 0, 10); - return Array.getLong(arr, 0); - } - @Compare public float verifyLongToFloat() { - Object arr = Array.newInstance(float.class, 5); - Array.setLong(arr, 0, 10); - return Array.getFloat(arr, 0); - } - - @Compare public double verifyConvertToDouble() { - int[] arr = (int[]) Array.newInstance(int.class, 5); - return Array.getDouble(arr, 0); - } - - @Compare public int multiIntArray() { - int[][][] arr = (int[][][]) Array.newInstance(int.class, 3, 3, 3); - return arr[0][1][2] + 5 + arr[2][2][0]; - } - - @Compare public String multiIntArrayCompType() { - return Array.newInstance(int.class, 3, 3, 3).getClass().getName(); - } - - - @Factory - public static Object[] create() { - return VMTest.create(ReflectionArrayTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.apidesign.bck2brwsr.core.JavaScriptBody; -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class ReflectionTest { - @Compare public boolean nonNullThis() { - return this == null; - } - - @Compare public String intType() { - return Integer.TYPE.toString(); - } - - @Compare public String voidType() throws Exception { - return void.class.toString(); - } - - @Compare public String longClass() { - return long.class.toString(); - } - - @Compare public boolean isRunnableInterface() { - return Runnable.class.isInterface(); - } - - @Compare public String isRunnableHasRunMethod() throws NoSuchMethodException { - return Runnable.class.getMethod("run").getName(); - } - - @Compare public String namesOfMethods() { - StringBuilder sb = new StringBuilder(); - String[] arr = new String[20]; - int i = 0; - for (Method m : StaticUse.class.getMethods()) { - arr[i++] = m.getName(); - } - for (String s : sort(arr, i)) { - sb.append(s).append("\n"); - } - return sb.toString(); - } - - @Compare public String namesOfDeclaringClassesOfMethods() { - StringBuilder sb = new StringBuilder(); - String[] arr = new String[20]; - int i = 0; - for (Method m : StaticUse.class.getMethods()) { - arr[i++] = m.getName() + "@" + m.getDeclaringClass().getName(); - } - for (String s : sort(arr, i)) { - sb.append(s).append("\n"); - } - return sb.toString(); - } - - @Compare public String cannotCallNonStaticMethodWithNull() throws Exception { - StaticUse.class.getMethod("instanceMethod").invoke(null); - return "should not happen"; - } - - @Compare public Object voidReturnType() throws Exception { - return StaticUse.class.getMethod("instanceMethod").getReturnType(); - } - - @Retention(RetentionPolicy.RUNTIME) - @interface Ann { - } - - @Compare public String annoClass() throws Exception { - Retention r = Ann.class.getAnnotation(Retention.class); - assert r != null : "Annotation is present"; - assert r.value() == RetentionPolicy.RUNTIME : "Policy value is OK: " + r.value(); - return r.annotationType().getName(); - } - - @Compare public boolean isAnnotation() { - return Ann.class.isAnnotation(); - } - @Compare public boolean isNotAnnotation() { - return String.class.isAnnotation(); - } - @Compare public boolean isNotAnnotationEnum() { - return E.class.isAnnotation(); - } - enum E { A, B }; - @Compare public boolean isEnum() { - return E.A.getClass().isEnum(); - } - - @Compare public boolean isNotEnum() { - return "".getClass().isEnum(); - } - - @Compare public String newInstanceFails() throws InstantiationException { - try { - return "success: " + StaticUseSub.class.newInstance(); - } catch (IllegalAccessException ex) { - return ex.getClass().getName(); - } - } - - @Compare public String paramTypes() throws Exception { - Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE); - final Class[] pt = plus.getParameterTypes(); - return pt[0].getName(); - } - @Compare public String paramTypesNotFound() throws Exception { - return StaticUse.class.getMethod("plus", int.class, double.class).toString(); - } - @Compare public int methodWithArgs() throws Exception { - Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE); - return (Integer)plus.invoke(null, 2, 3); - } - - @Compare public String classGetNameForByte() { - return byte.class.getName(); - } - @Compare public String classGetNameForBaseObject() { - return newObject().getClass().getName(); - } - @Compare public String classGetNameForJavaObject() { - return new Object().getClass().getName(); - } - @Compare public String classGetNameForObjectArray() { - return (new Object[3]).getClass().getName(); - } - @Compare public String classGetNameForSimpleIntArray() { - return (new int[3]).getClass().getName(); - } - @Compare public boolean sameClassGetNameForSimpleCharArray() { - return (new char[3]).getClass() == (new char[34]).getClass(); - } - @Compare public String classGetNameForMultiIntArray() { - return (new int[3][4][5][6][7][8][9]).getClass().getName(); - } - @Compare public String classGetNameForMultiIntArrayInner() { - final int[][][][][][][] arr = new int[3][4][5][6][7][8][9]; - int[][][][][][] subarr = arr[0]; - int[][][][][] subsubarr = subarr[0]; - return subsubarr.getClass().getName(); - } - @Compare public String classGetNameForMultiStringArray() { - return (new String[3][4][5][6][7][8][9]).getClass().getName(); - } - - @Compare public String classForByte() throws Exception { - return Class.forName("[Z").getName(); - } - - @Compare public String classForUnknownArray() { - try { - return Class.forName("[W").getName(); - } catch (Exception ex) { - return ex.getClass().getName(); - } - } - - @Compare public String classForUnknownDeepArray() { - try { - return Class.forName("[[[[[W").getName(); - } catch (Exception ex) { - return ex.getClass().getName(); - } - } - - @Compare public String componentGetNameForObjectArray() { - return (new Object[3]).getClass().getComponentType().getName(); - } - @Compare public boolean sameComponentGetNameForObjectArray() { - return (new Object[3]).getClass().getComponentType() == Object.class; - } - @Compare public String componentGetNameForSimpleIntArray() { - return (new int[3]).getClass().getComponentType().getName(); - } - @Compare public String componentGetNameForMultiIntArray() { - return (new int[3][4][5][6][7][8][9]).getClass().getComponentType().getName(); - } - @Compare public String componentGetNameForMultiStringArray() { - Class c = (new String[3][4][5][6][7][8][9]).getClass(); - StringBuilder sb = new StringBuilder(); - for (;;) { - sb.append(c.getName()).append("\n"); - c = c.getComponentType(); - if (c == null) { - break; - } - } - return sb.toString(); - } - - @Compare public boolean isArray() { - return new Object[0].getClass().isArray(); - } - - @JavaScriptBody(args = { "arr", "len" }, body="var a = arr.slice(0, len); a.sort(); return a;") - private static String[] sort(String[] arr, int len) { - List list = Arrays.asList(arr).subList(0, len); - Collections.sort(list); - return list.toArray(new String[0]); - } - - @JavaScriptBody(args = {}, body = "return new Object();") - private static Object newObject() { - return new Object(); - } - - @Factory - public static Object[] create() { - return VMTest.create(ReflectionTest.class); - } - -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import java.io.InputStream; -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class ResourcesTest { - - @Compare public String readResourceAsStream() throws Exception { - InputStream is = getClass().getResourceAsStream("Resources.txt"); - byte[] b = new byte[30]; - int len = is.read(b); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < len; i++) { - sb.append((char)b[i]); - } - return sb.toString(); - } - - @Factory public static Object[] create() { - return VMTest.create(ResourcesTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ShortArithmeticTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ShortArithmeticTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class ShortArithmeticTest { - - private static short add(short x, short y) { - return (short)(x + y); - } - - private static short sub(short x, short y) { - return (short)(x - y); - } - - private static short mul(short x, short y) { - return (short)(x * y); - } - - private static short div(short x, short y) { - return (short)(x / y); - } - - private static short mod(short x, short y) { - return (short)(x % y); - } - - @Compare public short conversion() { - return (short)123456; - } - - @Compare public short addOverflow() { - return add(Short.MAX_VALUE, (short)1); - } - - @Compare public short subUnderflow() { - return sub(Short.MIN_VALUE, (short)1); - } - - @Compare public short addMaxShortAndMaxShort() { - return add(Short.MAX_VALUE, Short.MAX_VALUE); - } - - @Compare public short subMinShortAndMinShort() { - return sub(Short.MIN_VALUE, Short.MIN_VALUE); - } - - @Compare public short multiplyMaxShort() { - return mul(Short.MAX_VALUE, (short)2); - } - - @Compare public short multiplyMaxShortAndMaxShort() { - return mul(Short.MAX_VALUE, Short.MAX_VALUE); - } - - @Compare public short multiplyMinShort() { - return mul(Short.MIN_VALUE, (short)2); - } - - @Compare public short multiplyMinShortAndMinShort() { - return mul(Short.MIN_VALUE, Short.MIN_VALUE); - } - - @Compare public short multiplyPrecision() { - return mul((short)17638, (short)1103); - } - - @Compare public short division() { - return div((short)1, (short)2); - } - - @Compare public short divisionReminder() { - return mod((short)1, (short)2); - } - - @Factory - public static Object[] create() { - return VMTest.create(ShortArithmeticTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -class StaticUse { - public static final Object NON_NULL = new Object(); - public static int cnt; - static { - if (cnt++ != 0) { - throw new IllegalStateException("Multiple initialization of a "); - } - } - - StaticUse() { - } - - public void instanceMethod() { - } - - public static int plus(int a, int b) { - return a + b; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSub.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSub.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -public class StaticUseSub extends StaticUse { - private StaticUseSub() { - } - - public static Object getNonNull() { - return NON_NULL; - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSubTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUseSubTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.tck; - -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class StaticUseSubTest { - @Compare public String staticFieldInitializationInSuperClass() throws Exception { - Object ret = StaticUseSub.getNonNull(); - return ret.getClass().getName(); - } - - @Factory public static Object[] create() { - return VMTest.create(StaticUseSubTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.vmtest.impl; - -import java.io.UnsupportedEncodingException; -import java.util.zip.CRC32; -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CRC32Test { - - @Compare public long crc1() throws UnsupportedEncodingException { - CRC32 crc = new CRC32(); - crc.update("Hello World!".getBytes("UTF-8")); - return crc.getValue(); - } - - @Factory public static Object[] create() { - return VMTest.create(CRC32Test.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.vmtest.impl; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import org.apidesign.bck2brwsr.emul.zip.FastJar; -import org.testng.annotations.Test; -import static org.testng.Assert.*; - -/** - * - * @author Jaroslav Tulach - */ -@GenerateZip(name = "five.zip", contents = { - "1.txt", "one", - "2.txt", "duo", - "3.txt", "three", - "4.txt", "four", - "5.txt", "five" -}) -public class ZipEntryTest { - @Test - public void readEntriesEffectively() throws IOException { - InputStream is = ZipEntryTest.class.getResourceAsStream("five.zip"); - byte[] arr = new byte[is.available()]; - int len = is.read(arr); - assertEquals(len, arr.length, "Read fully"); - - FastJar fj = new FastJar(arr); - FastJar.Entry[] entrs = fj.list(); - - assertEquals(5, entrs.length, "Five entries"); - - for (int i = 1; i <= 5; i++) { - FastJar.Entry en = entrs[i - 1]; - assertEquals(en.name, i + ".txt"); -// assertEquals(cis.cnt, 0, "Content of the file should be skipped, not read"); - } - - assertContent("three", fj.getInputStream(entrs[3 - 1]), "read OK"); - assertContent("five", fj.getInputStream(entrs[5 - 1]), "read OK"); - } - - private static void assertContent(String exp, InputStream is, String msg) throws IOException { - byte[] arr = new byte[512]; - int len = is.read(arr); - String s = new String(arr, 0, len); - assertEquals(exp, s, msg); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.vmtest.impl; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Objects; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import org.apidesign.bck2brwsr.core.JavaScriptBody; -import org.apidesign.bck2brwsr.vmtest.BrwsrTest; -import org.apidesign.bck2brwsr.vmtest.Compare; -import org.apidesign.bck2brwsr.vmtest.Http; -import org.apidesign.bck2brwsr.vmtest.VMTest; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -@GenerateZip(name = "readAnEntry.zip", contents = { - "my/main/file.txt", "Hello World!" -}) -public class ZipFileTest { - - @Compare public String readAnEntry() throws IOException { - InputStream is = ZipFileTest.class.getResourceAsStream("readAnEntry.zip"); - ZipInputStream zip = new ZipInputStream(is); - ZipEntry entry = zip.getNextEntry(); - assertEquals(entry.getName(), "my/main/file.txt", "Correct entry"); - - byte[] arr = new byte[4096]; - int len = zip.read(arr); - - assertEquals(zip.getNextEntry(), null, "No next entry"); - - final String ret = new String(arr, 0, len, "UTF-8"); - return ret; - } - - @JavaScriptBody(args = { "res", "path" }, body = - "var myvm = bck2brwsr.apply(null, path);\n" - + "var cls = myvm.loadClass('java.lang.String');\n" - + "return cls.getClass__Ljava_lang_Class_2().getResourceAsStream__Ljava_io_InputStream_2Ljava_lang_String_2(res);\n" - ) - private static native Object loadVMResource(String res, String...path); - - @Http({ - @Http.Resource(path = "/readAnEntry.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip") - }) - @BrwsrTest public void canVmLoadResourceFromZip() throws IOException { - Object res = loadVMResource("/my/main/file.txt", "/readAnEntry.jar"); - assert res instanceof InputStream : "Got array of bytes: " + res; - InputStream is = (InputStream)res; - - byte[] arr = new byte[4096]; - int len = is.read(arr); - - final String ret = new String(arr, 0, len, "UTF-8"); - - assertEquals(ret, "Hello World!", "Can read the bytes"); - } - - @GenerateZip(name = "cpattr.zip", contents = { - "META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" - + "Created-By: hand\n" - + "Class-Path: realJar.jar\n\n\n" - }) - @Http({ - @Http.Resource(path = "/readComplexEntry.jar", mimeType = "x-application/zip", content = "", resource="cpattr.zip"), - @Http.Resource(path = "/realJar.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip"), - }) - @BrwsrTest public void understandsClassPathAttr() throws IOException { - Object res = loadVMResource("/my/main/file.txt", "/readComplexEntry.jar"); - assert res instanceof InputStream : "Got array of bytes: " + res; - InputStream is = (InputStream)res; - - byte[] arr = new byte[4096]; - int len = is.read(arr); - - final String ret = new String(arr, 0, len, "UTF-8"); - - assertEquals(ret, "Hello World!", "Can read the bytes from secondary JAR"); - } - - private static void assertEquals(Object real, Object exp, String msg) { - assert Objects.equals(exp, real) : msg + " exp: " + exp + " real: " + real; - } - - @Factory public static Object[] create() { - return VMTest.create(ZipFileTest.class); - } -} diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe --- a/rt/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -þ \ No newline at end of file diff -r 146ae7b52b64 -r 3800d11c0bdb rt/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/Resources.txt --- a/rt/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/Resources.txt Tue Apr 02 15:40:51 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -Ahoj