This is an automated email from the ASF dual-hosted git repository. jtulach pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans-html4j.git
commit 998a5086c49ab1a16b1e926c53d0a49a13bb4482 Author: Jaroslav Tulach <jaroslav.tul...@apidesign.org> AuthorDate: Sat May 4 10:10:31 2019 +0200 Allow Presenters to define their own way to reference itself --- boot-fx/pom.xml | 7 ++ .../netbeans/html/boot/fx/AbstractFXPresenter.java | 32 ++++++- .../html/boot/fx/AbstractFXPresenterTest.java | 77 +++++++++++++++++ boot/pom.xml | 7 ++ .../java/org/netbeans/html/boot/impl/FnUtils.java | 3 + .../html/boot/impl/JavaScriptProcesor.java | 19 ++++- .../netbeans/html/boot/spi/FallbackIdentity.java | 56 +++++++++++++ .../main/java/org/netbeans/html/boot/spi/Fn.java | 57 +++++++------ .../html/boot/spi/FallbackIdentityTest.java | 97 ++++++++++++++++++++++ .../java/org/netbeans/html/ko4j/CacheObjs.java | 10 +-- .../main/java/org/netbeans/html/ko4j/Knockout.java | 3 +- .../main/java/org/netbeans/html/ko4j/MapObjs.java | 26 +++--- 12 files changed, 345 insertions(+), 49 deletions(-) diff --git a/boot-fx/pom.xml b/boot-fx/pom.xml index f486bc4..d095f04 100644 --- a/boot-fx/pom.xml +++ b/boot-fx/pom.xml @@ -83,6 +83,13 @@ <artifactId>javafx-web</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.netbeans.api</groupId> + <artifactId>org-netbeans-modules-nbjunit</artifactId> + <version>${netbeans.version}</version> + <scope>test</scope> + <type>jar</type> + </dependency> </dependencies> <description>A presentation provider to show JavaFX WebView when a Java/HTML based application is about to boot.</description> diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java index 965d337..26e1329 100644 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java @@ -50,7 +50,7 @@ import org.netbeans.html.boot.spi.Fn; * @author Jaroslav Tulach */ public abstract class AbstractFXPresenter implements Fn.Presenter, -Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable { +Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable, Fn.Identity { static final Logger LOG = Logger.getLogger(FXPresenter.class.getName()); protected static int cnt; protected Runnable onLoad; @@ -62,6 +62,7 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable { private JSObject newPOJOImpl; private Object undefined; private JavaValues values; + private Id id; @Override protected AbstractFXPresenter clone() { @@ -72,6 +73,7 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable { p.undefined = null; p.newPOJOImpl = null; p.values = null; + p.id = null; return p; } catch (CloneNotSupportedException ex) { throw new IllegalStateException(ex); @@ -602,4 +604,32 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable { return pojo[0]; } + @Override + public synchronized Fn.Identity id() { + if (id == null) { + id = new Id(this); + } + return id; + } + + @Override + public Fn.Presenter presenter() { + return this; + } + + private static final class Id extends WeakReference<AbstractFXPresenter> implements Fn.Identity { + Id(AbstractFXPresenter referent) { + super(referent); + } + + @Override + public Fn.Identity id() { + return this; + } + + @Override + public Fn.Presenter presenter() { + return get(); + } + } } diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java new file mode 100644 index 0000000..7176dbf --- /dev/null +++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java @@ -0,0 +1,77 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.html.boot.fx; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.net.URL; +import javafx.scene.web.WebView; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.junit.NbTestCase; +import org.testng.Assert; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import org.testng.annotations.Test; + +public class AbstractFXPresenterTest { + @Test + public void id() { + AbstractFXPresenter p1 = new AbstractFXPresenter() { + @Override + void waitFinished() { + } + + @Override + WebView findView(URL resource) { + return null; + } + }; + AbstractFXPresenter p2 = new AbstractFXPresenter() { + @Override + void waitFinished() { + } + + @Override + WebView findView(URL resource) { + return null; + } + }; + + Fn.Identity id1 = Fn.id(p1); + Fn.Identity id12 = Fn.id(p1); + + assertSame(id1, id12); + assertEquals(id1, id12); + + Fn.Identity id2 = Fn.id(p2); + Fn.Identity id22 = Fn.id(p2); + + assertSame(id2, id22); + assertEquals(id2, id22); + + Assert.assertNotEquals(id1, id2); + + Reference<AbstractFXPresenter> ref1 = new WeakReference<>(p1); + p1 = null; + NbTestCase.assertGC("Presenter can disappear", ref1); + + AbstractFXPresenter p2Clone = p2.clone(); + Assert.assertNotEquals(p2.id(), p2Clone.id()); + } +} diff --git a/boot/pom.xml b/boot/pom.xml index c86d162..59fc227 100644 --- a/boot/pom.xml +++ b/boot/pom.xml @@ -77,6 +77,13 @@ <version>${project.version}</version> <type>jar</type> </dependency> + <dependency> + <groupId>org.netbeans.api</groupId> + <artifactId>org-netbeans-modules-nbjunit</artifactId> + <version>${netbeans.version}</version> + <scope>test</scope> + <type>jar</type> + </dependency> </dependencies> <description>Builder to launch your Java/HTML based application.</description> </project> diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java index 9d47b06..02e753f 100644 --- a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java +++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java @@ -651,6 +651,9 @@ public final class FnUtils { if (name.equals(Fn.Presenter.class.getName())) { return Fn.Presenter.class; } + if (name.equals(Fn.Identity.class.getName())) { + return Fn.Identity.class; + } if (name.equals(Fn.ToJavaScript.class.getName())) { return Fn.ToJavaScript.class; } diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java index 3f259fa..5e421ae 100644 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java @@ -400,19 +400,30 @@ public final class JavaScriptProcesor extends AbstractProcessor { source.append("package ").append(pkgName).append(";\n"); source.append("public final class $JsCallbacks$ {\n"); source.append(" static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n"); - source.append(" private final java.lang.ref.Reference<org.netbeans.html.boot.spi.Fn.Presenter> ref;\n"); + source.append(" private final org.netbeans.html.boot.spi.Fn.Identity id;\n"); source.append(" private $JsCallbacks$ next;\n"); source.append(" private $JsCallbacks$(org.netbeans.html.boot.spi.Fn.Presenter p) {\n"); - source.append(" this.ref = new java.lang.ref.WeakReference<org.netbeans.html.boot.spi.Fn.Presenter>(p);\n"); + source.append(" this.id = org.netbeans.html.boot.spi.Fn.id(p);\n"); source.append(" }\n"); source.append(" synchronized final $JsCallbacks$ current() {\n"); source.append(" org.netbeans.html.boot.spi.Fn.Presenter now = org.netbeans.html.boot.spi.Fn.activePresenter();\n"); source.append(" $JsCallbacks$ thiz = this;\n"); + source.append(" $JsCallbacks$ prev = null;\n"); source.append(" for (;;) {\n"); - source.append(" if (now == thiz.ref.get()) return thiz;\n"); + source.append(" if (thiz.id != null) {\n"); + source.append(" org.netbeans.html.boot.spi.Fn.Presenter thizPresenter = thiz.id.presenter();\n"); + source.append(" if (thizPresenter == null) {\n"); + source.append(" if (prev != null) {\n"); + source.append(" prev.next = thiz.next;\n"); + source.append(" }\n"); + source.append(" } else {\n"); + source.append(" if (thizPresenter == now) return thiz;\n"); + source.append(" }\n"); + source.append(" }\n"); source.append(" if (thiz.next == null) {\n"); source.append(" return thiz.next = new $JsCallbacks$(now);\n"); source.append(" }\n"); + source.append(" prev = thiz;\n"); source.append(" thiz = thiz.next;\n"); source.append(" }\n"); source.append(" }\n"); @@ -486,7 +497,7 @@ public final class JavaScriptProcesor extends AbstractProcessor { sep = ", "; } source.append(") throws Throwable {\n"); - source.append(" org.netbeans.html.boot.spi.Fn.Presenter p = ref.get(); \n"); + source.append(" org.netbeans.html.boot.spi.Fn.Presenter p = id.presenter(); \n"); source.append(convert); if (useTryResources()) { source.append(" try (java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p)) { \n"); diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java b/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java new file mode 100644 index 0000000..a1d0cfc --- /dev/null +++ b/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.html.boot.spi; + +import java.lang.ref.WeakReference; + +final class FallbackIdentity extends WeakReference<Fn.Presenter> implements Fn.Identity { + private final int hashCode; + + FallbackIdentity(Fn.Presenter p) { + super(p); + this.hashCode = p.hashCode(); + } + + @Override + public Fn.Identity id() { + return this; + } + + @Override + public Fn.Presenter presenter() { + return get(); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof FallbackIdentity) { + return ((FallbackIdentity) obj).presenter() == presenter(); + } + return false; + } +} diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java index 0b8eb35..f98abde 100644 --- a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java +++ b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java @@ -23,8 +23,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; import java.net.URL; import java.util.HashMap; import java.util.HashSet; @@ -40,7 +38,7 @@ import org.netbeans.html.boot.impl.FnContext; * @author Jaroslav Tulach */ public abstract class Fn { - private final Reference<Presenter> presenter; + private final Identity presenter; /** * @deprecated Ineffective as of 0.6. @@ -58,7 +56,7 @@ public abstract class Fn { * @since 0.6 */ protected Fn(Presenter presenter) { - this.presenter = new WeakReference<Presenter>(presenter); + this.presenter = id(presenter); } /** True, if currently active presenter is the same as presenter this @@ -67,7 +65,7 @@ public abstract class Fn { * @return true, if proper presenter is used */ public final boolean isValid() { - return FnContext.currentPresenter(false) == presenter.get(); + return FnContext.currentPresenter(false) == presenter.presenter(); } /** Helper method to check if the provided instance is valid function. @@ -178,6 +176,16 @@ public abstract class Fn { } return FnContext.activate(p); } + + public static Identity id(final Presenter p) { + if (p == null) { + return null; + } + if (p instanceof Identity) { + return ((Identity) p).id(); + } + return new FallbackIdentity(p); + } /** Invokes the defined function with specified <code>this</code> and * appropriate arguments. @@ -211,7 +219,7 @@ public abstract class Fn { * @since 0.7 */ protected final Presenter presenter() { - return presenter.get(); + return presenter.presenter(); } /** The representation of a <em>presenter</em> - usually a browser window. @@ -324,8 +332,17 @@ public abstract class Fn { public Fn defineFn(String code, String[] names, boolean[] keepAlive); } + public interface Identity { + public Identity id(); + public Presenter presenter(); + @Override + public int hashCode(); + @Override + public boolean equals(Object obj); + } + private static class Preload extends Fn { - private static Map<String, Set<Reference<Presenter>>> LOADED; + private static Map<String, Set<Identity>> LOADED; private final Fn fn; private final String resource; private final Class<?> caller; @@ -350,21 +367,21 @@ public abstract class Fn { } private void loadResource() throws Exception { - Reference<Presenter> ref = super.presenter; - if (ref == null) { - ref = new WeakReference<Fn.Presenter>(FnContext.currentPresenter(false)); + Identity id = super.presenter; + if (id == null) { + id = id(FnContext.currentPresenter(false)); } - Fn.Presenter realPresenter = ref == null ? null : ref.get(); + Fn.Presenter realPresenter = id == null ? null : id.presenter(); if (realPresenter != null) { if (LOADED == null) { - LOADED = new HashMap<String, Set<Reference<Presenter>>>(); + LOADED = new HashMap<String, Set<Identity>>(); } - Set<Reference<Presenter>> there = LOADED.get(resource); + Set<Identity> there = LOADED.get(resource); if (there == null) { - there = new HashSet<Reference<Fn.Presenter>>(); + there = new HashSet<Identity>(); LOADED.put(resource, there); } - if (addNewRef(there, ref)) { + if (there.add(id)) { final ClassLoader l = caller.getClassLoader(); InputStream is = l.getResourceAsStream(resource); if (is == null && resource.startsWith("/")) { @@ -382,14 +399,6 @@ public abstract class Fn { } } } - - private static synchronized boolean addNewRef(Set<Reference<Presenter>> set, Reference<Presenter> ref) { - for (Reference<Presenter> r : set) { - if (r.get() == ref.get()) { - return false; - } - } - return set.add(ref); - } } + } diff --git a/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java b/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java new file mode 100644 index 0000000..760531f --- /dev/null +++ b/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.html.boot.spi; + +import java.io.Reader; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.net.URL; +import org.netbeans.junit.NbTestCase; +import static org.testng.Assert.*; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class FallbackIdentityTest { + + public FallbackIdentityTest() { + } + + @Test + public void testIdAndWeak() { + Fn.Presenter p = new Fn.Presenter() { + @Override + public Fn defineFn(String arg0, String... arg1) { + return null; + } + + @Override + public void displayPage(URL arg0, Runnable arg1) { + } + + @Override + public void loadScript(Reader arg0) throws Exception { + } + }; + + Fn.Identity id1 = Fn.id(p); + Fn.Identity id2 = Fn.id(p); + + assertNotSame(id1, id2); + assertEquals(id1, id2); + assertEquals(id1.hashCode(), id2.hashCode()); + + Reference<Fn.Presenter> ref = new WeakReference<>(p); + p = null; + NbTestCase.assertGC("Presenter is held weakly", ref); + } + + @Test + public void testPresenterCanProvideItsOwnIdentity() { + class IdPresenter implements Fn.Presenter, Fn.Identity { + @Override + public Fn defineFn(String code, String... names) { + return null; + } + + @Override + public void displayPage(URL page, Runnable onPageLoad) { + } + + @Override + public void loadScript(Reader code) throws Exception { + } + + @Override + public Fn.Identity id() { + return this; + } + + @Override + public Fn.Presenter presenter() { + return this; + } + } + IdPresenter p = new IdPresenter(); + + assertSame(p, Fn.id(p)); + } +} diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java index 74fe85d..8b969ff 100644 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java @@ -19,26 +19,24 @@ package org.netbeans.html.ko4j; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; import org.netbeans.html.boot.spi.Fn; final class CacheObjs { /* both @GuardedBy CacheObjs.class */ - private static CacheObjs[] list = new CacheObjs[16]; + private static final CacheObjs[] list = new CacheObjs[16]; private static int listAt = 0; - private final Reference<Fn.Presenter> ref; + private final Fn.Identity ref; /* both @GuardedBy presenter single threaded access */ private Object[] jsObjects; private int jsIndex; private CacheObjs(Fn.Presenter p) { - this.ref = new WeakReference<Fn.Presenter>(p); + this.ref = Fn.id(p); } Fn.Presenter get() { - return ref.get(); + return ref.presenter(); } static synchronized CacheObjs find(Fn.Presenter key) { diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java index f2f7635..883d27b 100644 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java @@ -20,7 +20,6 @@ package org.netbeans.html.ko4j; import java.io.Closeable; import java.io.IOException; -import java.lang.ref.Reference; import java.util.concurrent.Executor; import net.java.html.js.JavaScriptBody; import net.java.html.js.JavaScriptResource; @@ -156,7 +155,7 @@ final class Knockout { if (obj == null) { return null; } else { - return (Fn.Presenter) ((Reference<?>)obj).get(); + return ((Fn.Identity) obj).presenter(); } } } diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java index 96114ec..c2b996e 100644 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java @@ -19,14 +19,12 @@ package org.netbeans.html.ko4j; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; import java.util.List; import net.java.html.json.Models; import org.netbeans.html.boot.spi.Fn; final class MapObjs { - private static Reference<Fn.Presenter> onlyPresenter; + private static Fn.Identity onlyPresenter; private static boolean usePresenter; static { @@ -40,8 +38,12 @@ final class MapObjs { private final List<Object> all; - private MapObjs(Object... arr) { - this.all = Models.asList(arr); + private MapObjs(Fn.Identity id1, Object js) { + this.all = Models.asList(id1, js); + } + + private MapObjs(Fn.Identity id1, Object js1, Fn.Identity id2, Object js2) { + this.all = Models.asList(id1, js1, id2, js2); } @@ -60,9 +62,9 @@ final class MapObjs { } } if (now == null) { - return new MapObjs(new WeakReference<Fn.Presenter>(key), js); + return new MapObjs(Fn.id(key), js); } else { - return new MapObjs(onlyPresenter, now, new WeakReference<Fn.Presenter>(key), js); + return new MapObjs(onlyPresenter, now, Fn.id(key), js); } } } @@ -99,15 +101,15 @@ final class MapObjs { return this; } } - all.add(new WeakReference<Fn.Presenter>(key)); + all.add(Fn.id(key)); all.add(js); return this; } boolean isSameKey(int index, Fn.Presenter key) { Object at = all.get(index); - if (at instanceof Reference<?>) { - at = ((Reference<?>)at).get(); + if (at instanceof Fn.Identity) { + at = ((Fn.Identity)at).presenter(); } return at == key; } @@ -131,11 +133,11 @@ final class MapObjs { } private static Fn.Presenter getOnlyPresenter() { - final Fn.Presenter p = onlyPresenter == null ? null : onlyPresenter.get(); + final Fn.Presenter p = onlyPresenter == null ? null : onlyPresenter.presenter(); return p; } private static void setOnlyPresenter(Fn.Presenter p) { - onlyPresenter = new WeakReference<Fn.Presenter>(p); + onlyPresenter = Fn.id(p); } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists