diff --git a/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java --- a/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java +++ b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java @@ -52,6 +52,8 @@ import net.java.html.boot.BrowserBuilder; import net.java.html.js.JavaScriptBody; import org.netbeans.html.boot.fx.AbstractFXPresenter; +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.context.spi.Contexts.Id; /** Utility methods to use {@link WebView} and {@link JavaScriptBody} code * in existing JavaFX applications. @@ -82,6 +84,16 @@ *

* This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)} * relies on the value. Please don't alter it. + *

+ * Since introduction of {@link Id technology identifiers} the + * provided args strings are also passed to the + * {@link BrwsrCtx context} when it is being + * {@link Contexts#newBuilder(java.lang.Object...) created} + * and can influence the selection + * of available technologies + * (like {@link org.netbeans.html.json.spi.Technology}, + * {@link org.netbeans.html.json.spi.Transfer} or + * {@link org.netbeans.html.json.spi.WSTransfer}). * * @param webView the instance of Web View to tweak * @param url the URL of the HTML page to load into the view @@ -94,7 +106,10 @@ Class onPageLoad, String methodName, String... args ) { - BrowserBuilder.newBrowser(new Load(webView)). + Object[] context = new Object[args.length + 1]; + System.arraycopy(args, 0, context, 1, args.length); + context[0] = new Load(webView); + BrowserBuilder.newBrowser(context). loadPage(url.toExternalForm()). loadClass(onPageLoad). invoke(methodName, args). diff --git a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java @@ -65,6 +65,7 @@ import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.boot.spi.Fn.Presenter; import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.context.spi.Contexts.Id; import org.netbeans.html.boot.impl.FindResources; import org.netbeans.html.boot.impl.FnContext; import org.netbeans.html.boot.impl.FnUtils; @@ -121,6 +122,15 @@ /** Entry method to obtain a new browser builder. Follow by calling * its instance methods like {@link #loadClass(java.lang.Class)} and * {@link #loadPage(java.lang.String)}. + * Since introduction of {@link Id technology identifiers} the + * provided context objects are also passed to the + * {@link BrwsrCtx context} when it is being + * {@link Contexts#newBuilder(java.lang.Object...) created} + * and can influence the selection + * of available technologies + * (like {@link org.netbeans.html.json.spi.Technology}, + * {@link org.netbeans.html.json.spi.Transfer} or + * {@link org.netbeans.html.json.spi.WSTransfer}) by name. * * @param context any instances that should be available to the builder - * implementation dependant @@ -301,7 +311,7 @@ if (browserClass != null) { browserClass[0] = newClazz; } - Contexts.Builder cb = Contexts.newBuilder(); + Contexts.Builder cb = Contexts.newBuilder(context); if (!Contexts.fillInByProviders(newClazz, cb)) { LOG.log(Level.WARNING, "Using empty technology for {0}", newClazz); } diff --git a/context/src/main/java/net/java/html/BrwsrCtx.java b/context/src/main/java/net/java/html/BrwsrCtx.java --- a/context/src/main/java/net/java/html/BrwsrCtx.java +++ b/context/src/main/java/net/java/html/BrwsrCtx.java @@ -47,6 +47,7 @@ import org.netbeans.html.context.impl.CtxAccssr; import org.netbeans.html.context.impl.CtxImpl; import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.context.spi.Contexts.Id; /** Represents context where the net.java.html.json.Model * and other objects @@ -54,7 +55,12 @@ * The context is also associated with the actual HTML technology * in the HTML page - there is likely to be different context for * knockout.js and different one - * for angular. + * for angular. Since version 1.1 + * the content of contexts can be selected by registering + * implementations under specific + * {@link Id technology identifiers} and requesting them during + * {@link Contexts#newBuilder(java.lang.Object...) construction} of the + * context. * * @author Jaroslav Tulach */ diff --git a/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java b/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java --- a/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java +++ b/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java @@ -44,8 +44,10 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import net.java.html.BrwsrCtx; +import org.netbeans.html.context.spi.Contexts; /** Implementation detail. Holds list of technologies for particular * {@link BrwsrCtx}. @@ -54,13 +56,15 @@ */ public final class CtxImpl { private final List> techs; + private final Object[] context; - public CtxImpl() { - techs = new ArrayList>(); + public CtxImpl(Object[] context) { + this(context, new ArrayList>()); } - private CtxImpl(List> techs) { + private CtxImpl(Object[] context, List> techs) { this.techs = techs; + this.context = context; } public static Tech find(BrwsrCtx context, Class technology) { @@ -74,9 +78,9 @@ } public BrwsrCtx build() { - Collections.sort(techs); + Collections.sort(techs, new BindCompare()); final List> arr = Collections.unmodifiableList(techs); - CtxImpl impl = new CtxImpl(arr); + CtxImpl impl = new CtxImpl(context, arr); BrwsrCtx ctx = CtxAccssr.getDefault().newContext(impl); return ctx; } @@ -85,7 +89,7 @@ techs.add(new Bind(type, impl, priority)); } - private static final class Bind implements Comparable> { + private static final class Bind { private final Class clazz; private final Tech impl; private final int priority; @@ -97,16 +101,39 @@ } @Override - public int compareTo(Bind o) { - if (priority != o.priority) { - return priority - o.priority; - } - return clazz.getName().compareTo(o.clazz.getName()); - } - - @Override public String toString() { return "Bind{" + "clazz=" + clazz + "@" + clazz.getClassLoader() + ", impl=" + impl + ", priority=" + priority + '}'; } } + + private final class BindCompare implements Comparator> { + boolean isPrefered(Bind b) { + final Class implClazz = b.impl.getClass(); + Contexts.Id id = implClazz.getAnnotation(Contexts.Id.class); + if (id == null) { + return false; + } + for (String v : id.value()) { + for (Object c : context) { + if (v.equals(c)) { + return true; + } + } + } + return false; + } + + @Override + public int compare(Bind o1, Bind o2) { + boolean p1 = isPrefered(o1); + boolean p2 = isPrefered(o2); + if (p1 != p2) { + return p1 ? -1 : 1; + } + if (o1.priority != o2.priority) { + return o1.priority - o2.priority; + } + return o1.clazz.getName().compareTo(o2.clazz.getName()); + } + } // end of BindCompare } diff --git a/context/src/main/java/org/netbeans/html/context/spi/Contexts.java b/context/src/main/java/org/netbeans/html/context/spi/Contexts.java --- a/context/src/main/java/org/netbeans/html/context/spi/Contexts.java +++ b/context/src/main/java/org/netbeans/html/context/spi/Contexts.java @@ -42,6 +42,10 @@ */ package org.netbeans.html.context.spi; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ServiceLoader; import net.java.html.BrwsrCtx; import org.netbeans.html.context.impl.CtxImpl; @@ -59,11 +63,23 @@ /** Creates new, empty builder for creation of {@link BrwsrCtx}. At the * end call the {@link Builder#build()} method to generate the context. - * + * + * @param context instances of various classes or names of {@link Id technologies} + * to be preferred and used in the built {@link BrwsrCtx context}. + * @return new instance of the builder + * @since 1.1 + */ + public static Builder newBuilder(Object... context) { + return new Builder(context); + } + /** Creates new, empty builder for creation of {@link BrwsrCtx}. At the + * end call the {@link Builder#build()} method to generate the context. + * Simply calls {@link #newBuilder(java.lang.Object...) newBuilder(new Object[0])}. + * * @return new instance of the builder */ public static Builder newBuilder() { - return new Builder(); + return newBuilder(new Object[0]); } /** Seeks for the specified technology in the provided context. @@ -118,6 +134,23 @@ } return found; } + + /** Identifies the technologies passed to {@link Builder context builder} + * by a name. Each implementation of a technology + * {@link Builder#register(java.lang.Class, java.lang.Object, int) registered into a context} + * can be annotated with a name (or multiple names). Such implementation + * will later be + * {@link Contexts#fillInByProviders(java.lang.Class, org.netbeans.html.context.spi.Contexts.Builder) preferred during lookup} + * if their name(s) has been requested in when + * {@link Contexts#newBuilder(java.lang.Object...) creating a context}. + * @since 1.1 + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface Id { + /** Identifier(s) for the implementation. */ + public String[] value(); + } /** Implementors of various HTML technologies should * register their implementation via org.openide.util.lookup.ServiceProvider, so @@ -152,9 +185,10 @@ * @author Jaroslav Tulach */ public static final class Builder { - private final CtxImpl impl = new CtxImpl(); + private final CtxImpl impl; - Builder() { + public Builder(Object[] context) { + this.impl = new CtxImpl(context); } /** Registers new technology into the context. Each technology is diff --git a/context/src/test/java/net/java/html/BrwsrCtxTest.java b/context/src/test/java/net/java/html/BrwsrCtxTest.java --- a/context/src/test/java/net/java/html/BrwsrCtxTest.java +++ b/context/src/test/java/net/java/html/BrwsrCtxTest.java @@ -44,6 +44,7 @@ import org.netbeans.html.context.spi.Contexts; import static org.testng.Assert.*; +import org.testng.annotations.Test; /** * @@ -72,4 +73,48 @@ assertTrue(arr[0], "Runnable was executed"); } + + @Test public void defaultOrderOfRegistrations() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder()); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R1.class, "R1 is registered at value 10"); + } + + @Test public void preferOne() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder("one")); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R1.class, "R1 is registered at value 10"); + } + + @Test public void preferTwo() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder("two")); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R2.class, "R2 is preferred"); + } + + @Test public void preferBoth() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder("one", "two")); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R1.class, "R1 is registered at value 10"); + } + + private static BrwsrCtx registerRs(Contexts.Builder b) { + b.register(Runnable.class, new R1(), 10); + b.register(Runnable.class, new R2(), 20); + return b.build(); + } + + @Contexts.Id("one") + static final class R1 implements Runnable { + @Override + public void run() { + } + } + @Contexts.Id("two") + static final class R2 implements Runnable { + @Override + public void run() { + } + } + } \ No newline at end of file diff --git a/json/src/main/java/org/netbeans/html/json/spi/Technology.java b/json/src/main/java/org/netbeans/html/json/spi/Technology.java --- a/json/src/main/java/org/netbeans/html/json/spi/Technology.java +++ b/json/src/main/java/org/netbeans/html/json/spi/Technology.java @@ -45,10 +45,17 @@ import net.java.html.BrwsrCtx; import net.java.html.json.Model; import net.java.html.json.Models; +import org.netbeans.html.context.spi.Contexts.Id; /** An implementation of a binding between model classes (see {@link Model}) * and particular technology like knockout.js * in a browser window, etc. + * Since introduction of {@link Id technology identifiers} one can choose between + * different background implementations to handle the conversion and + * communication requests. The currently known provider is + * org.netbeans.html:ko4j module which registers + * a knockout.js + * implementation called ko4j. * * @author Jaroslav Tulach */ diff --git a/json/src/main/java/org/netbeans/html/json/spi/Transfer.java b/json/src/main/java/org/netbeans/html/json/spi/Transfer.java --- a/json/src/main/java/org/netbeans/html/json/spi/Transfer.java +++ b/json/src/main/java/org/netbeans/html/json/spi/Transfer.java @@ -45,9 +45,17 @@ import java.io.IOException; import java.io.InputStream; import org.netbeans.html.context.spi.Contexts.Builder; +import org.netbeans.html.context.spi.Contexts.Id; /** A {@link Builder service provider interface} responsible for * conversion of JSON objects to Java ones and vice-versa. + * Since introduction of {@link Id technology identifiers} one can choose between + * different background implementations to handle the conversion and + * communication requests. The known providers include + * org.netbeans.html:ko4j module which registers + * a native browser implementation called xhr, and a + * org.netbeans.html:ko-ws-tyrus module which registers + * Java based implementation named tyrus. * * @author Jaroslav Tulach */ diff --git a/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java b/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java --- a/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java +++ b/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java @@ -44,9 +44,17 @@ import net.java.html.BrwsrCtx; import org.netbeans.html.context.spi.Contexts.Provider; +import org.netbeans.html.context.spi.Contexts.Id; /** Interface for providers of WebSocket protocol. Register into a - * {@link BrwsrCtx context} in your own {@link Provider} + * {@link BrwsrCtx context} in your own {@link Provider}. + * Since introduction of {@link Id technology identifiers} one can choose between + * different background implementations to handle the conversion and + * communication requests. The known providers include + * org.netbeans.html:ko4j module which registers + * a native browser implementation called websocket, and a + * org.netbeans.html:ko-ws-tyrus module which registers + * Java based implementation named tyrus. * * @author Jaroslav Tulach * @param internal implementation type representing the socket diff --git a/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java b/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java --- a/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java +++ b/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java @@ -46,6 +46,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; @@ -140,16 +141,9 @@ @Override public BrwsrCtx createContext() { try { - Class fxCls = loadOSGiClass( - "org.netbeans.html.ko4j.FXContext", - FrameworkUtil.getBundle(KnockoutFelixTCKImpl.class).getBundleContext() - ); - final Constructor cnstr = fxCls.getConstructor(Fn.Presenter.class); - cnstr.setAccessible(true); - Object fx = cnstr.newInstance(browserContext); Contexts.Builder cb = Contexts.newBuilder(). - register(Technology.class, (Technology)fx, 10). - register(Transfer.class, (Transfer)fx, 10). + register(Technology.class, (Technology)osgiInstance("KOTech"), 10). + register(Transfer.class, (Transfer)osgiInstance("KOTransfer"), 10). register(Executor.class, (Executor)browserContext, 10); // if (fx.areWebSocketsSupported()) { // cb.register(WSTransfer.class, fx, 10); @@ -160,6 +154,17 @@ } } + private Object osgiInstance(String simpleName) throws IllegalAccessException, SecurityException, IllegalArgumentException, Exception, NoSuchMethodException, InstantiationException, InvocationTargetException { + Class fxCls = loadOSGiClass( + "org.netbeans.html.ko4j." + simpleName, + FrameworkUtil.getBundle(KnockoutFelixTCKImpl.class).getBundleContext() + ); + final Constructor cnstr = fxCls.getDeclaredConstructor(); + cnstr.setAccessible(true); + Object fx = cnstr.newInstance(); + return fx; + } + @Override public Object createJSON(Map values) { JSONObject json = new JSONObject(); diff --git a/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java --- a/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java +++ b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java @@ -46,6 +46,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; @@ -135,16 +136,9 @@ @Override public BrwsrCtx createContext() { try { - Class fxCls = loadOSGiClass( - "org.netbeans.html.ko4j.FXContext", - FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class).getBundleContext() - ); - final Constructor cnstr = fxCls.getConstructor(Fn.Presenter.class); - cnstr.setAccessible(true); - Object fx = cnstr.newInstance(browserContext); Contexts.Builder cb = Contexts.newBuilder(). - register(Technology.class, (Technology)fx, 10). - register(Transfer.class, (Transfer)fx, 10). + register(Technology.class, (Technology)osgiInstance("KOTech"), 10). + register(Transfer.class, (Transfer)osgiInstance("KOTransfer"), 10). register(Executor.class, (Executor)browserContext, 10); // if (fx.areWebSocketsSupported()) { // cb.register(WSTransfer.class, fx, 10); @@ -154,6 +148,16 @@ throw new IllegalStateException(ex); } } + private Object osgiInstance(String simpleName) throws IllegalAccessException, SecurityException, IllegalArgumentException, Exception, NoSuchMethodException, InstantiationException, InvocationTargetException { + Class fxCls = loadOSGiClass( + "org.netbeans.html.ko4j." + simpleName, + FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class).getBundleContext() + ); + final Constructor cnstr = fxCls.getDeclaredConstructor(); + cnstr.setAccessible(true); + Object fx = cnstr.newInstance(); + return fx; + } @Override public Object createJSON(Map values) { diff --git a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java --- a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java @@ -82,6 +82,7 @@ * * @author Jaroslav Tulach */ +@Contexts.Id("tyrus") @ServiceProvider(service = Contexts.Provider.class) public final class TyrusContext implements Contexts.Provider, WSTransfer, Transfer { diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java @@ -42,7 +42,9 @@ */ package org.netbeans.html.ko4j; +import java.util.logging.Logger; import net.java.html.json.Model; +import net.java.html.json.OnReceive; import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.context.spi.Contexts.Provider; @@ -54,35 +56,49 @@ /** Support for knockout.js * and its Java binding via {@link Model model classes}. * Registers {@link Provider}, so {@link java.util.ServiceLoader} can find it. + * The provider registers following technologies: + *

* * @author Jaroslav Tulach * @since 0.7 */ @ServiceProvider(service = Provider.class) public final class KO4J implements Provider { - private final Fn.Presenter presenter; - private FXContext c; + static final Logger LOG = Logger.getLogger(KOSockets.class.getName()); + private KOTech ko4j; + private KOTransfer trans; + private KOSockets socks; public KO4J() { this(null); } - + + @Deprecated public KO4J(Fn.Presenter presenter) { - this.presenter = presenter; - } - - private FXContext getKO() { - if (c == null) { - c = new FXContext(presenter == null ? Fn.activePresenter() : presenter); - } - return c; } /** Return instance of the knockout.js for Java technology. * @return non-null instance */ public Technology knockout() { - return getKO(); + if (ko4j == null) { + ko4j = new KOTech(); + } + return ko4j; } /** Browser based implementation of transfer interface. Uses @@ -91,7 +107,10 @@ * @return non-null instance */ public Transfer transfer() { - return getKO(); + if (trans == null) { + trans = new KOTransfer(); + } + return trans; } /** Returns browser based implementation of websocket transfer. @@ -102,7 +121,13 @@ * WebSocket object in the browser */ public WSTransfer websockets() { - return getKO().areWebSocketsSupported() ? getKO() : null; + if (!KOSockets.areWebSocketsSupported()) { + return null; + } + if (socks == null) { + socks = new KOSockets(); + } + return socks; } /** Registers technologies at position 100: diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java rename from ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java rename to ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java @@ -42,20 +42,9 @@ */ package org.netbeans.html.ko4j; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -import java.util.logging.Logger; -import org.netbeans.html.boot.spi.Fn; -import org.netbeans.html.json.spi.FunctionBinding; +import net.java.html.js.JavaScriptBody; +import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.json.spi.JSONCall; -import org.netbeans.html.json.spi.PropertyBinding; -import org.netbeans.html.json.spi.Technology; -import org.netbeans.html.json.spi.Transfer; import org.netbeans.html.json.spi.WSTransfer; /** This is an implementation package - just @@ -65,134 +54,13 @@ * * @author Jaroslav Tulach */ -final class FXContext -implements Technology.BatchInit, Technology.ValueMutated, -Transfer, WSTransfer { - static final Logger LOG = Logger.getLogger(FXContext.class.getName()); - private Object[] jsObjects; - private int jsIndex; - - public FXContext(Fn.Presenter browserContext) { +@Contexts.Id("websocket") +final class KOSockets +implements WSTransfer { + KOSockets() { } @Override - public Object wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) { - String[] propNames = new String[propArr.length]; - boolean[] propReadOnly = new boolean[propArr.length]; - Object[] propValues = new Object[propArr.length]; - for (int i = 0; i < propNames.length; i++) { - propNames[i] = propArr[i].getPropertyName(); - propReadOnly[i] = propArr[i].isReadOnly(); - propValues[i] = propArr[i].getValue(); - } - String[] funcNames = new String[funcArr.length]; - for (int i = 0; i < funcNames.length; i++) { - funcNames[i] = funcArr[i].getFunctionName(); - } - Object ret = getJSObject(); - Knockout.wrapModel(ret, model, - propNames, propReadOnly, propValues, propArr, - funcNames, funcArr - ); - return ret; - } - - private Object getJSObject() { - int len = 64; - if (jsObjects != null && jsIndex < (len = jsObjects.length)) { - return jsObjects[jsIndex++]; - } - jsObjects = Knockout.allocJS(len * 2); - jsIndex = 1; - return jsObjects[0]; - } - - @Override - public Object wrapModel(Object model) { - throw new UnsupportedOperationException(); - } - - @Override - public void bind(PropertyBinding b, Object model, Object data) { - throw new UnsupportedOperationException(); - } - - @Override - public void valueHasMutated(Object data, String propertyName) { - Knockout.valueHasMutated(data, propertyName, null, null); - } - - @Override - public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) { - Knockout.valueHasMutated(data, propertyName, oldValue, newValue); - } - - @Override - public void expose(FunctionBinding fb, Object model, Object d) { - throw new UnsupportedOperationException(); - } - - @Override - public void applyBindings(Object data) { - Knockout.applyBindings(data); - } - - @Override - public Object wrapArray(Object[] arr) { - return arr; - } - - @Override - public void extract(Object obj, String[] props, Object[] values) { - LoadJSON.extractJSON(obj, props, values); - } - - @Override - public void loadJSON(final JSONCall call) { - if (call.isJSONP()) { - String me = LoadJSON.createJSONP(call); - LoadJSON.loadJSONP(call.composeURL(me), me); - } else { - String data = null; - if (call.isDoOutput()) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - call.writeData(bos); - data = new String(bos.toByteArray(), "UTF-8"); - } catch (IOException ex) { - call.notifyError(ex); - } - } - LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data); - } - } - - @Override - public M toModel(Class modelClass, Object data) { - return modelClass.cast(Knockout.toModel(data)); - } - - @Override - public Object toJSON(InputStream is) throws IOException { - StringBuilder sb = new StringBuilder(); - InputStreamReader r = new InputStreamReader(is); - for (;;) { - int ch = r.read(); - if (ch == -1) { - break; - } - sb.append((char)ch); - } - return LoadJSON.parse(sb.toString()); - } - - @Override - public void runSafe(final Runnable r) { - LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!"); - r.run(); - } - - @Override public LoadWS open(String url, JSONCall onReply) { return new LoadWS(onReply, url); } @@ -207,27 +75,8 @@ socket.close(); } - boolean areWebSocketsSupported() { - return Knockout.areWebSocketsSupported(); + @JavaScriptBody(args = {}, body = "if (window['WebSocket']) return true; else return false;") + static final boolean areWebSocketsSupported() { + return false; } - - private static final class TrueFn extends Fn implements Fn.Presenter { - @Override - public Object invoke(Object thiz, Object... args) throws Exception { - return true; - } - - @Override - public Fn defineFn(String code, String... names) { - return this; - } - - @Override - public void displayPage(URL page, Runnable onPageLoad) { - } - - @Override - public void loadScript(Reader code) throws Exception { - } - } // end of TrueFn } diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java copy from ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java copy to ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java @@ -42,21 +42,11 @@ */ package org.netbeans.html.ko4j; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -import java.util.logging.Logger; -import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.json.spi.FunctionBinding; -import org.netbeans.html.json.spi.JSONCall; import org.netbeans.html.json.spi.PropertyBinding; import org.netbeans.html.json.spi.Technology; -import org.netbeans.html.json.spi.Transfer; -import org.netbeans.html.json.spi.WSTransfer; +import static org.netbeans.html.ko4j.KO4J.LOG; /** This is an implementation package - just * include its JAR on classpath and use official {@link Context} API @@ -65,14 +55,13 @@ * * @author Jaroslav Tulach */ -final class FXContext -implements Technology.BatchInit, Technology.ValueMutated, -Transfer, WSTransfer { - static final Logger LOG = Logger.getLogger(FXContext.class.getName()); +@Contexts.Id("ko4j") +final class KOTech +implements Technology.BatchInit, Technology.ValueMutated { private Object[] jsObjects; private int jsIndex; - public FXContext(Fn.Presenter browserContext) { + public KOTech() { } @Override @@ -141,93 +130,15 @@ public Object wrapArray(Object[] arr) { return arr; } - + @Override - public void extract(Object obj, String[] props, Object[] values) { - LoadJSON.extractJSON(obj, props, values); - } - - @Override - public void loadJSON(final JSONCall call) { - if (call.isJSONP()) { - String me = LoadJSON.createJSONP(call); - LoadJSON.loadJSONP(call.composeURL(me), me); - } else { - String data = null; - if (call.isDoOutput()) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - call.writeData(bos); - data = new String(bos.toByteArray(), "UTF-8"); - } catch (IOException ex) { - call.notifyError(ex); - } - } - LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data); - } - } + public void runSafe(final Runnable r) { + LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!"); + r.run(); + } @Override public M toModel(Class modelClass, Object data) { return modelClass.cast(Knockout.toModel(data)); } - - @Override - public Object toJSON(InputStream is) throws IOException { - StringBuilder sb = new StringBuilder(); - InputStreamReader r = new InputStreamReader(is); - for (;;) { - int ch = r.read(); - if (ch == -1) { - break; - } - sb.append((char)ch); - } - return LoadJSON.parse(sb.toString()); - } - - @Override - public void runSafe(final Runnable r) { - LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!"); - r.run(); - } - - @Override - public LoadWS open(String url, JSONCall onReply) { - return new LoadWS(onReply, url); - } - - @Override - public void send(LoadWS socket, JSONCall data) { - socket.send(data); - } - - @Override - public void close(LoadWS socket) { - socket.close(); - } - - boolean areWebSocketsSupported() { - return Knockout.areWebSocketsSupported(); - } - - private static final class TrueFn extends Fn implements Fn.Presenter { - @Override - public Object invoke(Object thiz, Object... args) throws Exception { - return true; - } - - @Override - public Fn defineFn(String code, String... names) { - return this; - } - - @Override - public void displayPage(URL page, Runnable onPageLoad) { - } - - @Override - public void loadScript(Reader code) throws Exception { - } - } // end of TrueFn } diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java copy from ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java copy to ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java @@ -43,20 +43,12 @@ package org.netbeans.html.ko4j; import java.io.ByteArrayOutputStream; -import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -import java.util.logging.Logger; -import org.netbeans.html.boot.spi.Fn; -import org.netbeans.html.json.spi.FunctionBinding; +import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.json.spi.JSONCall; -import org.netbeans.html.json.spi.PropertyBinding; -import org.netbeans.html.json.spi.Technology; import org.netbeans.html.json.spi.Transfer; -import org.netbeans.html.json.spi.WSTransfer; /** This is an implementation package - just * include its JAR on classpath and use official {@link Context} API @@ -65,84 +57,13 @@ * * @author Jaroslav Tulach */ -final class FXContext -implements Technology.BatchInit, Technology.ValueMutated, -Transfer, WSTransfer { - static final Logger LOG = Logger.getLogger(FXContext.class.getName()); - private Object[] jsObjects; - private int jsIndex; - - public FXContext(Fn.Presenter browserContext) { +@Contexts.Id("xhr") +final class KOTransfer +implements Transfer { + KOTransfer() { } @Override - public Object wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) { - String[] propNames = new String[propArr.length]; - boolean[] propReadOnly = new boolean[propArr.length]; - Object[] propValues = new Object[propArr.length]; - for (int i = 0; i < propNames.length; i++) { - propNames[i] = propArr[i].getPropertyName(); - propReadOnly[i] = propArr[i].isReadOnly(); - propValues[i] = propArr[i].getValue(); - } - String[] funcNames = new String[funcArr.length]; - for (int i = 0; i < funcNames.length; i++) { - funcNames[i] = funcArr[i].getFunctionName(); - } - Object ret = getJSObject(); - Knockout.wrapModel(ret, model, - propNames, propReadOnly, propValues, propArr, - funcNames, funcArr - ); - return ret; - } - - private Object getJSObject() { - int len = 64; - if (jsObjects != null && jsIndex < (len = jsObjects.length)) { - return jsObjects[jsIndex++]; - } - jsObjects = Knockout.allocJS(len * 2); - jsIndex = 1; - return jsObjects[0]; - } - - @Override - public Object wrapModel(Object model) { - throw new UnsupportedOperationException(); - } - - @Override - public void bind(PropertyBinding b, Object model, Object data) { - throw new UnsupportedOperationException(); - } - - @Override - public void valueHasMutated(Object data, String propertyName) { - Knockout.valueHasMutated(data, propertyName, null, null); - } - - @Override - public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) { - Knockout.valueHasMutated(data, propertyName, oldValue, newValue); - } - - @Override - public void expose(FunctionBinding fb, Object model, Object d) { - throw new UnsupportedOperationException(); - } - - @Override - public void applyBindings(Object data) { - Knockout.applyBindings(data); - } - - @Override - public Object wrapArray(Object[] arr) { - return arr; - } - - @Override public void extract(Object obj, String[] props, Object[] values) { LoadJSON.extractJSON(obj, props, values); } @@ -168,11 +89,6 @@ } @Override - public M toModel(Class modelClass, Object data) { - return modelClass.cast(Knockout.toModel(data)); - } - - @Override public Object toJSON(InputStream is) throws IOException { StringBuilder sb = new StringBuilder(); InputStreamReader r = new InputStreamReader(is); @@ -185,49 +101,4 @@ } return LoadJSON.parse(sb.toString()); } - - @Override - public void runSafe(final Runnable r) { - LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!"); - r.run(); - } - - @Override - public LoadWS open(String url, JSONCall onReply) { - return new LoadWS(onReply, url); - } - - @Override - public void send(LoadWS socket, JSONCall data) { - socket.send(data); - } - - @Override - public void close(LoadWS socket) { - socket.close(); - } - - boolean areWebSocketsSupported() { - return Knockout.areWebSocketsSupported(); - } - - private static final class TrueFn extends Fn implements Fn.Presenter { - @Override - public Object invoke(Object thiz, Object... args) throws Exception { - return true; - } - - @Override - public Fn defineFn(String code, String... names) { - return this; - } - - @Override - public void displayPage(URL page, Runnable onPageLoad) { - } - - @Override - public void loadScript(Reader code) throws Exception { - } - } // end of TrueFn } diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java @@ -153,9 +153,4 @@ static Object toModel(Object wrapper) { return toModelImpl(wrapper); } - - @JavaScriptBody(args = {}, body = "if (window['WebSocket']) return true; else return false;") - static final boolean areWebSocketsSupported() { - return false; - } } diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java --- a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java @@ -66,7 +66,6 @@ import org.netbeans.html.json.spi.WSTransfer; import org.netbeans.html.json.tck.KOTest; import org.netbeans.html.json.tck.KnockoutTCK; -import org.netbeans.html.boot.impl.FnContext; import org.openide.util.lookup.ServiceProvider; import org.testng.Assert; import static org.testng.Assert.*; @@ -154,12 +153,12 @@ @Override public BrwsrCtx createContext() { - FXContext fx = new FXContext(browserContext); + KO4J ko4j = new KO4J(); Contexts.Builder cb = Contexts.newBuilder(). - register(Technology.class, fx, 10). - register(Transfer.class, fx, 10); - if (fx.areWebSocketsSupported()) { - cb.register(WSTransfer.class, fx, 10); + register(Technology.class, ko4j.knockout(), 10). + register(Transfer.class, ko4j.transfer(), 10); + if (ko4j.websockets() != null) { + cb.register(WSTransfer.class, ko4j.websockets(), 10); } cb.register(Executor.class, (Executor)browserContext, 10); cb.register(Fn.Presenter.class, browserContext, 10); diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html --- a/src/main/javadoc/overview.html +++ b/src/main/javadoc/overview.html @@ -75,6 +75,27 @@ yet the application code can be written in Java.

+

What's New in Version 1.1?

+ +

+ The content of a {@link net.java.html.BrwsrCtx context} + can be selected by registering implementations under specific + {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers} + and requesting them during + {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction} + of the context. org.netbeans.html:ko4j module's implementation + offers ko4j, xhr and websocket identifiers + for its registered services + (e.g. {@link org.netbeans.html.json.spi.Technology}, + {@link org.netbeans.html.json.spi.Transfer} and + {@link org.netbeans.html.json.spi.WSTransfer}). + org.netbeans.html:ko-ws-tyrus + module registers its + {@link org.netbeans.html.json.spi.Transfer Java based JSON} and + {@link org.netbeans.html.json.spi.WSTransfer WebSocket} implementations + under the name tyrus. +

+

What's New in Version 1.0?