# HG changeset patch # User Jaroslav Tulach # Date 1450217975 -3600 # Node ID 3534e15dc44681266c68f9d7a20b5579da360431 # Parent f5d6e09568b63f14fe642626fec8e8f27b80e748 Introducing @Component annotation, adding basic annotation and providing its (reflection based) implementation in ko4j module. diff -r f5d6e09568b6 -r 3534e15dc446 json/src/main/java/net/java/html/json/Component.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/json/src/main/java/net/java/html/json/Component.java Tue Dec 15 23:19:35 2015 +0100 @@ -0,0 +1,77 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.json; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Defines a reusable piece of code and UI. Put the annotation on a + * method that accepts a class generated by the {@link Model} annotation + * and returns another class generated with the help of the + * {@link Model} annotation. The first annotation describes the + * input parameters. The latter one the internal structure of the + * {@link #template() declarative UI snippet}. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Component { + /** + * Gives the component a name. + * @return name that one uses from the UI to reference this component + */ + public String name(); + /** + * Location of associated file with UI. Usually (but not always) a fragment + * of HTML that + * @return + */ + public String template(); + /** + * Technologies that should be preferred when processing the + * {@link #template} of this component. + * @return ids of technologies to prefer inside of the component + */ + public String[] techIds() default {}; +} diff -r f5d6e09568b6 -r 3534e15dc446 json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Mon Dec 14 06:10:54 2015 +0100 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Tue Dec 15 23:19:35 2015 +0100 @@ -58,6 +58,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.WeakHashMap; @@ -78,6 +79,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; @@ -90,6 +92,8 @@ import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.FileObject; +import javax.tools.StandardLocation; +import net.java.html.json.Component; import net.java.html.json.ComputedProperty; import net.java.html.json.Function; import net.java.html.json.Model; @@ -113,7 +117,8 @@ "net.java.html.json.OnReceive", "net.java.html.json.OnPropertyChange", "net.java.html.json.ComputedProperty", - "net.java.html.json.Property" + "net.java.html.json.Property", + "net.java.html.json.Component" }) public final class ModelProcessor extends AbstractProcessor { private static final Logger LOG = Logger.getLogger(ModelProcessor.class.getName()); @@ -127,6 +132,11 @@ ok = false; } } + for (Element e : roundEnv.getElementsAnnotatedWith(Component.class)) { + if (!processComponent(e)) { + ok = false; + } + } if (roundEnv.processingOver()) { models.clear(); for (Map.Entry entry : verify.entrySet()) { @@ -2191,4 +2201,44 @@ return null; } + private boolean processComponent(Element e) { + try { + Component c = e.getAnnotation(Component.class); + if (c == null) { + return false; + } + if (e.getKind() != ElementKind.METHOD) { + error("@Component can only annotate a method", e); + return false; + } + ExecutableElement ee = (ExecutableElement) e; + if (ee.getParameters().size() != 1) { + error("Method annotated by @Component needs to take one @Model generated parameter", e); + return false; + } + FileObject def = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/html+java/components/" + c.name(), e); + OutputStreamWriter w = new OutputStreamWriter(def.openOutputStream()); + Properties p = new Properties(); + final TypeMirror typeParam = ee.getParameters().get(0).asType(); + String fqnParam; + if (typeParam.getKind() == TypeKind.ERROR) { + fqnParam = findPkgName(ee) + "." + typeParam; + } else { + final TypeElement elem = (TypeElement) processingEnv.getTypeUtils().asElement(typeParam); + fqnParam = processingEnv.getElementUtils().getBinaryName(elem).toString(); + } + p.setProperty("paramName", fqnParam); + p.setProperty("methodName", e.getSimpleName().toString()); + TypeElement clazz = (TypeElement)e.getEnclosingElement(); + final String fqn = clazz.getQualifiedName().toString(); + p.setProperty("className", fqn); + p.setProperty("template", findPkgName(e).replace('.', '/') + '/' + c.template()); + p.store(w, "Generated by " + e); + w.close(); + return true; + } catch (IOException ex) { + error(ex.getMessage(), e); + return false; + } + } } diff -r f5d6e09568b6 -r 3534e15dc446 json/src/test/java/net/java/html/json/ComponentTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/json/src/test/java/net/java/html/json/ComponentTest.java Tue Dec 15 23:19:35 2015 +0100 @@ -0,0 +1,59 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.json; + +public class ComponentTest { + @Model(className = "CompParams", properties = { + }) + static final class ParamsCntrl { + } + @Model(className = "CompModel", properties = { + }) + static final class ModelCntrl { + } + + @Component(name = "test-cmp", template = "next.html") + static CompModel makeComponent(CompParams p) { + return null; + } +} diff -r f5d6e09568b6 -r 3534e15dc446 ko4j/src/main/java/org/netbeans/html/ko4j/ComponentLoader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/ComponentLoader.java Tue Dec 15 23:19:35 2015 +0100 @@ -0,0 +1,155 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.ko4j; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.Enumeration; +import java.util.Properties; +import net.java.html.BrwsrCtx; +import net.java.html.js.JavaScriptBody; +import net.java.html.json.Models; + +final class ComponentLoader { + @JavaScriptBody(args = {}, wait4js = false, javacall = true, body = + "var myLoader = {\n" + + "};\n" + + "myLoader.getConfig = function(name, callback) { \n" + + " function myClbk(config) {\n" + + " console.log('myClbk: ' + config.ko4j + ' ' + config.properties);\n" + + " callback(config);\n" + + " }\n" + + " @org.netbeans.html.ko4j.ComponentLoader::loadConfig(Ljava/lang/String;Ljava/lang/Object;)(name, myClbk);\n" + + "}\n" + + "myLoader.loadComponent = function(name, componentConfig, callback) {\n" + + " console.log('loadComponent: ' + componentConfig.ko4j + ' ' + componentConfig.properties);\n" + + " callback(null);\n" + + "}\n" + + "myLoader.loadTemplate = function(name, templateConfig, callback) {\n" + + " console.log('loadTemplate: ' + templateConfig.ko4j + ' ' + templateConfig.properties);\n" + + " if (templateConfig !== null) {\n" + + " @org.netbeans.html.ko4j.ComponentLoader::loadTemplate(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)(name, templateConfig, callback);\n" + + " } else {\n" + + " callback(null);\n" + + " }\n" + + "}\n" + + "myLoader.loadViewModel = function(name, config, callback) { \n" + + " console.log('loadViewModel ' + name + ' temp: ' + config);\n" + + " var f = function(params, info) {\n" + + " if (!params) params = null;\n" + + " return @org.netbeans.html.ko4j.ComponentLoader::loadViewModel(Ljava/lang/String;Ljava/util/Properties;Ljava/lang/Object;Ljava/lang/Object;)(name, config, params, info);\n" + + " };\n" + + " callback(f);\n" + + "}\n" + + "ko.components.loaders.unshift(myLoader);\n" + ) + public static native void initialize(); + + static void loadConfig(String name, Object callback) throws IOException { + System.err.println("config for " + name); + Enumeration en = ComponentLoader.class.getClassLoader().getResources("META-INF/html+java/components/" + name); + while (en.hasMoreElements()) { + URL u = en.nextElement(); + InputStream is = u.openStream(); + Properties p = new Properties(); + p.load(is); + is.close(); + callbackConfig(callback, p, p.getProperty("template")); + return; + } + callbackConfig(callback, null, null); + } + static void loadTemplate(String name, String template, Object callback) throws IOException { + InputStream is = ComponentLoader.class.getClassLoader().getResourceAsStream(template); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + os.write(ch); + } + is.close(); + String text = os.toString("UTF-8"); + callbackTemplate(callback, text); + } + + @JavaScriptBody(args = { "callback", "param", "template" }, body = + "callback(param ? {\n" + + " 'ko4j': true,\n" + + " 'viewModel': param,\n" + + " 'template': template\n" + + " } : null);", wait4js = false) + private static native void callbackConfig(Object callback, Properties param, String template); + + @JavaScriptBody(args = { "callback", "param" }, body = "callback(ko.utils.parseHtmlFragment(param));", wait4js = false) + private static native void callbackTemplate(Object callback, String param); + + static Object loadViewModel(String name, Properties props, Object params, Object config) throws IOException { + Object res = null; + try { + String paramName = props.getProperty("paramName"); + Class paramType = Class.forName(paramName); + String className = props.getProperty("className"); + Class classType = Class.forName(className); + Method m = classType.getMethod(props.getProperty("methodName"), paramType); + + BrwsrCtx ctx = BrwsrCtx.findDefault(ComponentLoader.class); + Object data; + if (params == null) { + data = paramType.newInstance(); + } else { + data = Models.fromRaw(ctx, paramType, params); + } + m.setAccessible(true); + res = m.invoke(null, data); + props.put(res, res); + } catch (Exception ex) { + throw new IOException(ex); + } + return Models.toRaw(res); + } +} diff -r f5d6e09568b6 -r 3534e15dc446 ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Mon Dec 14 06:10:54 2015 +0100 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Tue Dec 15 23:19:35 2015 +0100 @@ -148,6 +148,10 @@ Object model, String prop, Object oldValue, Object newValue ); + static Object applyBindings(String id, Object bindings) { + ComponentLoader.initialize(); + return applyBindings0(id, bindings); + } @JavaScriptBody(args = { "id", "bindings" }, body = "var d = window['document'];\n" + "var e = id ? d['getElementById'](id) : d['body'];\n" + @@ -155,7 +159,7 @@ "ko['applyBindings'](bindings, e);\n" + "return bindings['ko4j'];\n" ) - native static Object applyBindings(String id, Object bindings); + private native static Object applyBindings0(String id, Object bindings); @JavaScriptBody(args = { "cnt" }, body = "var arr = new Array(cnt);\n" +