From a0868c0595cad285d8752adea043148b3301b302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Link?= Date: Sun, 5 Nov 2023 12:08:51 +0100 Subject: [PATCH 1/5] Migrated swing-view stuff from https://github.com/link-intersystems/java-swing-mvc-plugin-architecture. --- lis-commons-swing-view/pom.xml | 41 ++++ .../swing/view/AbstractView.java | 52 +++++ .../swing/view/AbstractViewSite.java | 11 + .../swing/view/ContainerViewContent.java | 35 +++ .../swing/view/DefaultViewSite.java | 35 +++ .../swing/view/RootViewSite.java | 17 ++ .../link_intersystems/swing/view/View.java | 8 + .../swing/view/ViewContent.java | 24 ++ .../swing/view/ViewSite.java | 10 + .../swing/view/action/ViewInstallAction.java | 30 +++ .../AbstractViewLayoutContribution.java | 65 ++++++ .../swing/view/layout/DefaultViewLayout.java | 56 +++++ .../swing/view/layout/ViewLayout.java | 10 + .../view/layout/ViewLayoutContribution.java | 9 + .../swing/view/menu/MenuContribution.java | 11 + .../view/window/SubWindowViewContent.java | 19 ++ .../swing/view/window/WindowView.java | 67 ++++++ .../swing/view/window/WindowViewContent.java | 28 +++ .../swing/view/ViewSiteMock.java | 44 ++++ .../swing/action/spi/ServiceLoaderAction.java | 25 +++ lis-commons-util-context/pom.xml | 14 ++ .../util/context/AbstractContext.java | 151 +++++++++++++ .../util/context/Context.java | 101 +++++++++ .../util/context/ContextListener.java | 11 + .../util/context/ContextObjectException.java | 15 ++ .../util/context/DefaultContext.java | 69 ++++++ .../context/DuplicateObjectException.java | 17 ++ .../util/context/NoSuchObjectException.java | 17 ++ .../util/context/ObjectQualifier.java | 57 +++++ .../util/context/QualifiedObject.java | 37 +++ .../util/context/dsl/Action.java | 11 + .../util/context/dsl/ContextDsl.java | 202 +++++++++++++++++ .../util/context/dsl/When.java | 11 + .../util/context/ContextListenerMock.java | 35 +++ .../util/context/DefaultContextTest.java | 212 ++++++++++++++++++ .../util/context/QualifiedObjectTest.java | 33 +++ .../util/context/dsl/ContextDslTest.java | 130 +++++++++++ .../util/service/ServiceLocator.java | 19 ++ pom.xml | 16 +- 39 files changed, 1753 insertions(+), 2 deletions(-) create mode 100644 lis-commons-swing-view/pom.xml create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractView.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractViewSite.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ContainerViewContent.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/DefaultViewSite.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/RootViewSite.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/View.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewSite.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/action/ViewInstallAction.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/menu/MenuContribution.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/SubWindowViewContent.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowViewContent.java create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java create mode 100644 lis-commons-swing/src/main/java/com/link_intersystems/swing/action/spi/ServiceLoaderAction.java create mode 100644 lis-commons-util-context/pom.xml create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/AbstractContext.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextListener.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextObjectException.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DefaultContext.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DuplicateObjectException.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/NoSuchObjectException.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ObjectQualifier.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/QualifiedObject.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/Action.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/ContextDsl.java create mode 100644 lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/When.java create mode 100644 lis-commons-util-context/src/test/java/com/link_intersystems/util/context/ContextListenerMock.java create mode 100644 lis-commons-util-context/src/test/java/com/link_intersystems/util/context/DefaultContextTest.java create mode 100644 lis-commons-util-context/src/test/java/com/link_intersystems/util/context/QualifiedObjectTest.java create mode 100644 lis-commons-util-context/src/test/java/com/link_intersystems/util/context/dsl/ContextDslTest.java create mode 100644 lis-commons-util/src/main/java/com/link_intersystems/util/service/ServiceLocator.java diff --git a/lis-commons-swing-view/pom.xml b/lis-commons-swing-view/pom.xml new file mode 100644 index 00000000..dcd31fee --- /dev/null +++ b/lis-commons-swing-view/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + com.link-intersystems.commons + lis-commons + 1.9.7-SNAPSHOT + + + lis-commons-swing-view + + + + + com.link-intersystems.commons + lis-commons-swing + + + com.link-intersystems.commons + lis-commons-util-context + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + \ No newline at end of file diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractView.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractView.java new file mode 100644 index 00000000..6f1fb618 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractView.java @@ -0,0 +1,52 @@ +package com.link_intersystems.swing.view; + +import com.link_intersystems.util.context.Context; +import com.link_intersystems.util.context.dsl.ContextDsl; + +import static java.util.Objects.requireNonNull; + +public abstract class AbstractView implements View { + + private ViewSite viewSite; + private ContextDsl contextDsl; + + @Override + public void install(ViewSite viewSite) { + this.viewSite = requireNonNull(viewSite); + Context viewContext = viewSite.getViewContext(); + this.contextDsl = new ContextDsl(viewContext); + doInstall(viewSite); + } + + protected ViewSite getViewSite() { + return viewSite; + } + + protected ContextDsl getContextDsl() { + return contextDsl; + } + + protected abstract void doInstall(ViewSite viewSite); + + @Override + public void uninstall() { + if (viewSite == null) { + return; + } + + doUninstall(viewSite); + + viewSite = null; + contextDsl.dispose(); + } + + protected void doUninstall(ViewSite viewSite) { + ViewContent viewContent = viewSite.getViewContent(); + viewContent.setComponent(null); + } + + protected ViewSite createSubViewSite(ViewContent viewContent) { + Context viewContext = viewSite.getViewContext(); + return new DefaultViewSite(viewContent, viewContext); + } +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractViewSite.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractViewSite.java new file mode 100644 index 00000000..cc7e01a6 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/AbstractViewSite.java @@ -0,0 +1,11 @@ +package com.link_intersystems.swing.view; + +import com.link_intersystems.util.context.Context; + +public abstract class AbstractViewSite implements ViewSite { + + public abstract ViewContent getViewContent(); + + public abstract Context getViewContext(); + +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ContainerViewContent.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ContainerViewContent.java new file mode 100644 index 00000000..bf9e6768 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ContainerViewContent.java @@ -0,0 +1,35 @@ +package com.link_intersystems.swing.view; + +import java.awt.*; +import java.util.Objects; + +public class ContainerViewContent implements ViewContent { + + private Container container; + private Object contentLayoutConstraints; + + private Component viewContent; + + public ContainerViewContent(Container container, Object contentLayoutConstraints) { + this.container = Objects.requireNonNull(container); + this.contentLayoutConstraints = contentLayoutConstraints; + } + + @Override + public void setComponent(Component viewContent) { + if (this.viewContent != null) { + container.remove(this.viewContent); + } + + this.viewContent = viewContent; + + if (this.viewContent != null) { + this.container.add(this.viewContent, contentLayoutConstraints); + } + } + + @Override + public Component getParent() { + return container; + } +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/DefaultViewSite.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/DefaultViewSite.java new file mode 100644 index 00000000..060afc39 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/DefaultViewSite.java @@ -0,0 +1,35 @@ +package com.link_intersystems.swing.view; + +import com.link_intersystems.util.context.Context; +import com.link_intersystems.util.context.DefaultContext; + +import static java.util.Objects.requireNonNull; + +public class DefaultViewSite extends AbstractViewSite { + private ViewContent viewContent; + private Context viewContext; + + public DefaultViewSite(ViewContent viewContent) { + this(viewContent, new DefaultContext()); + } + + public DefaultViewSite(Context viewContext) { + this(ViewContent.nullInstance(), viewContext); + } + + public DefaultViewSite(ViewContent viewContent, Context viewContext) { + this.viewContent = requireNonNull(viewContent); + this.viewContext = requireNonNull(viewContext); + } + + @Override + public ViewContent getViewContent() { + return viewContent; + } + + @Override + public Context getViewContext() { + return viewContext; + } + +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/RootViewSite.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/RootViewSite.java new file mode 100644 index 00000000..8fc36b91 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/RootViewSite.java @@ -0,0 +1,17 @@ +package com.link_intersystems.swing.view; + +import com.link_intersystems.swing.view.window.WindowViewContent; +import com.link_intersystems.util.context.Context; +import com.link_intersystems.util.context.DefaultContext; + +public class RootViewSite extends DefaultViewSite { + + public RootViewSite() { + this(new DefaultContext()); + } + + public RootViewSite(Context viewContext) { + super(new WindowViewContent(), viewContext); + } + +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/View.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/View.java new file mode 100644 index 00000000..b6727bdc --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/View.java @@ -0,0 +1,8 @@ +package com.link_intersystems.swing.view; + +public interface View { + void install(ViewSite viewSite); + + void uninstall(); + +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java new file mode 100644 index 00000000..e2a2a159 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java @@ -0,0 +1,24 @@ +package com.link_intersystems.swing.view; + +import java.awt.*; + +public interface ViewContent { + + public static ViewContent nullInstance() { + return new ViewContent(){ + + @Override + public void setComponent(Component component) { + } + + @Override + public Component getParent() { + return null; + } + }; + } + + void setComponent(Component component); + + Component getParent(); +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewSite.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewSite.java new file mode 100644 index 00000000..c14147ac --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewSite.java @@ -0,0 +1,10 @@ +package com.link_intersystems.swing.view; + +import com.link_intersystems.util.context.Context; + +public interface ViewSite { + + ViewContent getViewContent(); + Context getViewContext(); + +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/action/ViewInstallAction.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/action/ViewInstallAction.java new file mode 100644 index 00000000..6dd3bf34 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/action/ViewInstallAction.java @@ -0,0 +1,30 @@ +package com.link_intersystems.swing.view.action; + +import com.link_intersystems.swing.view.View; +import com.link_intersystems.swing.view.ViewSite; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +public class ViewInstallAction extends AbstractAction { + + private final Supplier viewSiteSupplier; + private final Supplier viewSupplier; + + private View view; + + public ViewInstallAction(Supplier viewSiteSupplier, Supplier viewSupplier) { + this.viewSiteSupplier = requireNonNull(viewSiteSupplier); + this.viewSupplier = requireNonNull(viewSupplier); + } + + @Override + public void actionPerformed(ActionEvent e) { + view = viewSupplier.get(); + view.install(viewSiteSupplier.get()); + } + +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java new file mode 100644 index 00000000..bb9f1173 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java @@ -0,0 +1,65 @@ +package com.link_intersystems.swing.view.layout; + +import com.link_intersystems.swing.view.View; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public abstract class AbstractViewLayoutContribution implements ViewLayoutContribution { + + private static class ViewInstallation { + private ViewLayout viewLayout; + private String viewSiteName; + + public ViewInstallation(ViewLayout viewLayout, String viewSiteName) { + this.viewLayout = viewLayout; + this.viewSiteName = viewSiteName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ViewInstallation that = (ViewInstallation) o; + return Objects.equals(viewLayout, that.viewLayout) && Objects.equals(viewSiteName, that.viewSiteName); + } + + @Override + public int hashCode() { + return Objects.hash(viewLayout, viewSiteName); + } + } + + private Map installedViews = new IdentityHashMap<>(); + + @Override + public final void install(ViewLayout viewLayout) { + doInstall(new ViewLayout() { + @Override + public void install(String viewSiteName, View view) { + viewLayout.install(viewSiteName, view); + installedViews.put(new ViewInstallation(viewLayout, viewSiteName), view); + } + + @Override + public void remove(String viewSiteName) { + viewLayout.remove(viewSiteName); + installedViews.remove(new ViewInstallation(viewLayout, viewSiteName)); + } + }); + } + + protected abstract void doInstall(ViewLayout viewLayout); + + @Override + public final void uninstall(ViewLayout viewLayout) { + Set> installedViewEntries = installedViews.entrySet(); + + for (Map.Entry installedViewEntry : installedViewEntries) { + ViewInstallation viewInstallation = installedViewEntry.getKey(); + viewInstallation.viewLayout.remove(viewInstallation.viewSiteName); + } + } +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java new file mode 100644 index 00000000..4d4d24d8 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java @@ -0,0 +1,56 @@ +package com.link_intersystems.swing.view.layout; + +import com.link_intersystems.swing.view.ContainerViewContent; +import com.link_intersystems.swing.view.DefaultViewSite; +import com.link_intersystems.swing.view.View; +import com.link_intersystems.swing.view.ViewSite; +import com.link_intersystems.util.context.Context; + +import java.awt.*; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +public class DefaultViewLayout implements ViewLayout { + + private Map layout = new HashMap<>(); + private Map installedViews = new HashMap<>(); + private Context viewContext; + private Container viewContainer; + + public DefaultViewLayout(Context viewContext, Container viewContainer) { + this.viewContext = requireNonNull(viewContext); + this.viewContainer = requireNonNull(viewContainer); + } + + public void addViewSite(String name, Object layoutConstraints) { + ViewSite layoutViewSite = new DefaultViewSite(new ContainerViewContent(viewContainer, layoutConstraints), viewContext); + layout.put(requireNonNull(name), layoutViewSite); + + } + + @Override + public void install(String viewSiteName, View view) { + ViewSite viewSite = layout.get(viewSiteName); + if (viewSite == null) { + throw new IllegalArgumentException("No viewSite named " + viewSiteName + " existent in " + this); + } + view.install(viewSite); + installedViews.put(viewSiteName, view); + viewContainer.revalidate(); + } + + @Override + public void remove(String viewSiteName) { + View view = installedViews.get(viewSiteName); + if (view != null) { + view.uninstall(); + viewContainer.revalidate(); + } + } + + public void dispose() { + installedViews.keySet().forEach(this::remove); + } +} \ No newline at end of file diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java new file mode 100644 index 00000000..fe2c85ea --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java @@ -0,0 +1,10 @@ +package com.link_intersystems.swing.view.layout; + +import com.link_intersystems.swing.view.View; + +public interface ViewLayout { + + void install(String viewSiteName, View view); + + void remove(String viewSiteName); +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java new file mode 100644 index 00000000..969bbfa8 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java @@ -0,0 +1,9 @@ +package com.link_intersystems.swing.view.layout; + + + public interface ViewLayoutContribution { + + void install(ViewLayout viewLayout); + + void uninstall(ViewLayout viewLayout); + } diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/menu/MenuContribution.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/menu/MenuContribution.java new file mode 100644 index 00000000..49c43d16 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/menu/MenuContribution.java @@ -0,0 +1,11 @@ +package com.link_intersystems.swing.view.menu; + +import com.link_intersystems.util.context.Context; + +import javax.swing.*; + +public interface MenuContribution { + String getMenuPath(); + + Action getAction(Context context); +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/SubWindowViewContent.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/SubWindowViewContent.java new file mode 100644 index 00000000..d3730a23 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/SubWindowViewContent.java @@ -0,0 +1,19 @@ +package com.link_intersystems.swing.view.window; + +import java.awt.*; + +import static java.util.Objects.requireNonNull; + +public class SubWindowViewContent extends WindowViewContent { + + private Component parent; + + public SubWindowViewContent(Component parent) { + this.parent = requireNonNull(parent); + } + + @Override + public Component getParent() { + return parent; + } +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java new file mode 100644 index 00000000..d529aba1 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java @@ -0,0 +1,67 @@ +package com.link_intersystems.swing.view.window; + +import com.link_intersystems.swing.action.ActionTrigger; +import com.link_intersystems.swing.view.AbstractView; +import com.link_intersystems.swing.view.ViewContent; +import com.link_intersystems.swing.view.ViewSite; +import com.link_intersystems.util.context.Context; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE; + +public abstract class WindowView extends AbstractView { + + public static final String DEFAULT_CLOSE_ACTION = WindowView.class.getName() + ".closeAction"; + private ActionTrigger actionTrigger = new ActionTrigger(this); + + private Window window; + + private WindowAdapter closeHandler = new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + onCloseWindow(getViewSite()); + } + }; + + @Override + protected void doInstall(ViewSite viewSite) { + window = createWindow(viewSite); + setDefaultCloseOperation(window, DO_NOTHING_ON_CLOSE); + window.addWindowListener(closeHandler); + ViewContent viewContent = viewSite.getViewContent(); + viewContent.setComponent(window); + } + + protected void setDefaultCloseOperation(Window window, int closeOperation) { + if (window instanceof JFrame) { + JFrame frame = (JFrame) window; + frame.setDefaultCloseOperation(closeOperation); + } else if (window instanceof JDialog) { + JDialog dialog = (JDialog) window; + dialog.setDefaultCloseOperation(closeOperation); + } + } + + protected abstract Window createWindow(ViewSite viewSite); + + @Override + protected void doUninstall(ViewSite viewSite) { + super.doUninstall(viewSite); + + window.removeWindowListener(closeHandler); + closeHandler = null; + + window.dispose(); + window = null; + } + + protected void onCloseWindow(ViewSite viewSite) { + uninstall(); + Context viewContext = viewSite.getViewContext(); + viewContext.ifContains(Action.class, DEFAULT_CLOSE_ACTION, actionTrigger::performAction); + } +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowViewContent.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowViewContent.java new file mode 100644 index 00000000..656343a0 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowViewContent.java @@ -0,0 +1,28 @@ +package com.link_intersystems.swing.view.window; + +import com.link_intersystems.swing.view.ViewContent; + +import java.awt.*; + +public class WindowViewContent implements ViewContent { + + private Component component; + + @Override + public void setComponent(Component component) { + if(this.component != null){ + this.component.setVisible(false); + } + + this.component = component; + + if(this.component != null){ + this.component.setVisible(true); + } + } + + @Override + public Component getParent() { + return null; + } +} diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java new file mode 100644 index 00000000..194b0861 --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java @@ -0,0 +1,44 @@ +package com.link_intersystems.swing.view; + +import com.link_intersystems.util.context.Context; +import com.link_intersystems.util.context.DefaultContext; + +import java.awt.*; + +public class ViewSiteMock implements ViewSite{ + + private ViewContent viewContent = new ViewContent() { + @Override + public void setComponent(Component component) { + content = component; + } + + @Override + public Component getParent() { + return parent; + } + }; + + private Context context = new DefaultContext(); + + private Component content; + private Component parent; + + public Component getContent() { + return content; + } + + public void setParent(Component parent) { + this.parent = parent; + } + + @Override + public ViewContent getViewContent() { + return viewContent; + } + + @Override + public Context getViewContext() { + return context; + } +} diff --git a/lis-commons-swing/src/main/java/com/link_intersystems/swing/action/spi/ServiceLoaderAction.java b/lis-commons-swing/src/main/java/com/link_intersystems/swing/action/spi/ServiceLoaderAction.java new file mode 100644 index 00000000..1975ddf3 --- /dev/null +++ b/lis-commons-swing/src/main/java/com/link_intersystems/swing/action/spi/ServiceLoaderAction.java @@ -0,0 +1,25 @@ +package com.link_intersystems.swing.action.spi; + +import com.link_intersystems.swing.action.concurrent.DefaultTaskAction; +import com.link_intersystems.util.concurrent.task.TaskProgress; + +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; + +public class ServiceLoaderAction extends DefaultTaskAction, Void> { + + private Class serviceType; + + public ServiceLoaderAction(Class serviceType) { + this.serviceType = requireNonNull(serviceType); + } + + @Override + protected List doInBackground(TaskProgress taskProgress) { + return ServiceLoader.load(serviceType).stream().map(ServiceLoader.Provider::get).collect(Collectors.toList()); + } + +} diff --git a/lis-commons-util-context/pom.xml b/lis-commons-util-context/pom.xml new file mode 100644 index 00000000..738b8da9 --- /dev/null +++ b/lis-commons-util-context/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + + com.link-intersystems.commons + lis-commons + 1.9.7-SNAPSHOT + + + lis-commons-util-context + + \ No newline at end of file diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/AbstractContext.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/AbstractContext.java new file mode 100644 index 00000000..ef00955d --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/AbstractContext.java @@ -0,0 +1,151 @@ +package com.link_intersystems.util.context; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public abstract class AbstractContext implements Context { + + protected abstract QualifiedObject removeInstance(ObjectQualifier objectQualifier); + + protected abstract QualifiedObject getInstance(ObjectQualifier objectQualifier); + + @SuppressWarnings("CanBeFinal") + private static class ViewContextListenerRegistration { + + private ObjectQualifier objectQualifier; + private ContextListener contextListener; + + public ViewContextListenerRegistration(ObjectQualifier objectQualifier, ContextListener contextListener) { + this.objectQualifier = requireNonNull(objectQualifier); + this.contextListener = requireNonNull(contextListener); + } + + @SuppressWarnings("unchecked") + public ContextListener cast(ObjectQualifier objectQualifier) { + if (accept(objectQualifier)) { + return (ContextListener) contextListener; + } + return null; + } + + public boolean accept(ObjectQualifier objectQualifier) { + return this.objectQualifier.equals(objectQualifier); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ViewContextListenerRegistration that = (ViewContextListenerRegistration) o; + return Objects.equals(objectQualifier, that.objectQualifier) && Objects.equals(contextListener, that.contextListener); + } + + @Override + public int hashCode() { + return Objects.hash(objectQualifier, contextListener); + } + } + + private List> viewContextListenerRegistrations = new ArrayList<>(); + + @Override + public void addViewContextListener(ObjectQualifier objectQualifier, ContextListener contextListener) { + ViewContextListenerRegistration listenerRegistration = new ViewContextListenerRegistration<>(objectQualifier, contextListener); + viewContextListenerRegistrations.add(listenerRegistration); + + onViewContextListenerAdded(objectQualifier, contextListener); + } + + protected void onViewContextListenerAdded(ObjectQualifier objectQualifier, ContextListener contextListener) { + QualifiedObject registration = getInstance(objectQualifier); + if (registration != null) { + T object = registration.getObject(); + contextListener.added(this, object); + } + } + + + @Override + public void removeViewContextListener(ObjectQualifier objectQualifier, ContextListener contextListener) { + ViewContextListenerRegistration listenerRegistration = new ViewContextListenerRegistration<>(objectQualifier, contextListener); + if (viewContextListenerRegistrations.remove(listenerRegistration)) { + onViewContextListenerRemoved(listenerRegistration.contextListener); + } + } + + protected void onViewContextListenerRemoved(ContextListener contextListener) { + } + + @Override + public void put(ObjectQualifier objectQualifier, O object) { + QualifiedObject qualifiedObject = new QualifiedObject<>(objectQualifier, object); + + if (putInstance(qualifiedObject)) { + onPutInstance(qualifiedObject); + return; + } + + throw new DuplicateObjectException(objectQualifier); + } + + protected abstract boolean putInstance(QualifiedObject qualifiedObject); + + + protected void onPutInstance(QualifiedObject qualifiedObject) { + for (ViewContextListenerRegistration listenerRegistration : viewContextListenerRegistrations) { + ObjectQualifier objectQualifier = qualifiedObject.getQualifier(); + ContextListener contextListener = listenerRegistration.cast(objectQualifier); + if (contextListener != null) { + T object = qualifiedObject.getObject(); + contextListener.added(this, object); + } + } + } + + @SuppressWarnings("unchecked") + @Override + public T get(ObjectQualifier objectQualifier) { + if (objectQualifier == null) { + return null; + } + + QualifiedObject qualifiedObject = getInstance(objectQualifier); + + if (qualifiedObject == null) { + throw new NoSuchObjectException(objectQualifier); + } + + return (T) qualifiedObject.getObject(); + } + + @Override + public boolean remove(ObjectQualifier objectQualifier) { + QualifiedObject removedInstance = removeInstance(objectQualifier); + + if (removedInstance != null) { + onRemoveInstance(removedInstance); + } + + return removedInstance != null; + } + + protected void onRemoveInstance(QualifiedObject removedRegistration) { + for (ViewContextListenerRegistration listenerRegistration : viewContextListenerRegistrations) { + ObjectQualifier objectQualifier = removedRegistration.getQualifier(); + ContextListener contextListener = listenerRegistration.cast(objectQualifier); + if (contextListener != null) { + T removedObject = removedRegistration.getObject(); + contextListener.removed(this, removedObject); + } + } + } + + @Override + public String toString() { + return getClass().getName() + "[" + Integer.toHexString(System.identityHashCode(this)) + "]"; + } +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java new file mode 100644 index 00000000..f62580ad --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java @@ -0,0 +1,101 @@ +package com.link_intersystems.util.context; + +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public interface Context { + + default void addViewContextListener(Class type, ContextListener contextListener) { + addViewContextListener(type, null, contextListener); + } + + default void addViewContextListener(Class type, String name, ContextListener contextListener) { + ObjectQualifier objectQualifier = new ObjectQualifier<>(type, name); + + addViewContextListener(objectQualifier, contextListener); + } + + void addViewContextListener(ObjectQualifier objectQualifier, ContextListener contextListener); + + default void removeViewContextListener(Class type, ContextListener contextListener) { + removeViewContextListener(type, null, contextListener); + } + + default void removeViewContextListener(Class type, String name, ContextListener contextListener) { + ObjectQualifier objectQualifier = new ObjectQualifier<>(type, name); + removeViewContextListener(objectQualifier, contextListener); + } + + void removeViewContextListener(ObjectQualifier objectQualifier, ContextListener contextListener); + + default boolean contains(Class type) { + return contains(type, null); + } + + default boolean contains(Class type, String name) { + return contains(new ObjectQualifier<>(type, name)); + } + + boolean contains(ObjectQualifier objectQualifier); + + default void ifContains(Class type, Consumer objectConsumer) { + ifContains(type, null, objectConsumer); + } + + default void ifContains(Class type, String name, Consumer objectConsumer) { + ifContains(new ObjectQualifier<>(type, name), objectConsumer); + } + + default void ifContains(ObjectQualifier objectQualifier, Consumer objectConsumer) { + if (contains(objectQualifier)) { + T object = get(objectQualifier); + objectConsumer.accept(object); + } + } + + default T get(Class type) throws ContextObjectException { + return get(type, null); + } + + default T get(Class type, String name) throws ContextObjectException { + return get(new ObjectQualifier<>(type, name)); + } + + T get(ObjectQualifier objectQualifier) throws ContextObjectException; + + default public Supplier getSupplier(Class type) { + return getSupplier(type, null); + } + + default public Supplier getSupplier(Class type, String name) { + return getSupplier(new ObjectQualifier<>(type, name)); + } + + default public Supplier getSupplier(ObjectQualifier objectQualifier) { + return () -> get(objectQualifier); + } + + default boolean remove(Class type) { + return remove(type, null); + } + + default boolean remove(Class type, String name) { + ObjectQualifier objectQualifier = new ObjectQualifier<>(type, name); + return remove(objectQualifier); + } + + boolean remove(ObjectQualifier objectQualifier); + + default void put(Class type, O object) throws ContextObjectException { + put(type, null, object); + } + + default void put(Class type, String name, O object) throws ContextObjectException { + put(new ObjectQualifier<>(type, name), object); + } + + void put(ObjectQualifier objectQualifier, O object) throws ContextObjectException; + + public Stream> stream(); +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextListener.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextListener.java new file mode 100644 index 00000000..52d5b3f0 --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextListener.java @@ -0,0 +1,11 @@ +package com.link_intersystems.util.context; + +import java.util.EventListener; + +public interface ContextListener extends EventListener { + + public void added(Context context, T instance); + + public void removed(Context context, T instance); + +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextObjectException.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextObjectException.java new file mode 100644 index 00000000..dac3470a --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ContextObjectException.java @@ -0,0 +1,15 @@ +package com.link_intersystems.util.context; + +import static java.util.Objects.requireNonNull; + +public class ContextObjectException extends RuntimeException { + private final ObjectQualifier objectQualifier; + + public ContextObjectException(ObjectQualifier objectQualifier) { + this.objectQualifier = requireNonNull(objectQualifier); + } + + public ObjectQualifier getQualifier() { + return objectQualifier; + } +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DefaultContext.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DefaultContext.java new file mode 100644 index 00000000..421769d1 --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DefaultContext.java @@ -0,0 +1,69 @@ +package com.link_intersystems.util.context; + + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public class DefaultContext extends AbstractContext implements AutoCloseable { + + private Map, QualifiedObject> qualifiedInstances; + + public DefaultContext() { + this(ConcurrentHashMap::new); + } + + public DefaultContext(Supplier, QualifiedObject>> mapSupplier) { + this.qualifiedInstances = mapSupplier.get(); + } + + @Override + protected boolean putInstance(QualifiedObject qualifiedObject) { + ObjectQualifier objectQualifier = qualifiedObject.getQualifier(); + QualifiedObject existentQualifiedObject = getInstance(objectQualifier); + + if (existentQualifiedObject != null) { + return existentQualifiedObject.equals(qualifiedObject); + } + + return qualifiedInstances.put(objectQualifier, qualifiedObject) == null; + } + + @Override + protected QualifiedObject removeInstance(ObjectQualifier objectQualifier) { + return qualifiedInstances.remove(objectQualifier); + } + + @SuppressWarnings("unchecked") + @Override + protected QualifiedObject getInstance(ObjectQualifier objectQualifier) { + return (QualifiedObject) qualifiedInstances.get(objectQualifier); + } + + @Override + public boolean contains(ObjectQualifier objectQualifier) { + return qualifiedInstances.containsKey(objectQualifier); + } + + @Override + public Stream> stream() { + return qualifiedInstances.values().stream(); + } + + @Override + public void close() throws Exception { + Collection> values = qualifiedInstances.values(); + for (QualifiedObject qualifiedObject : values) { + Object object = qualifiedObject.getObject(); + + if (object != this && object instanceof AutoCloseable) { + AutoCloseable autoCloseable = (AutoCloseable) object; + autoCloseable.close(); + } + + remove(qualifiedObject.getQualifier()); + } + } +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DuplicateObjectException.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DuplicateObjectException.java new file mode 100644 index 00000000..2dde278d --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/DuplicateObjectException.java @@ -0,0 +1,17 @@ +package com.link_intersystems.util.context; + +public class DuplicateObjectException extends ContextObjectException { + + public DuplicateObjectException(ObjectQualifier objectQualifier) { + super(objectQualifier); + } + + @Override + public String getMessage() { + StringBuilder sb = new StringBuilder(); + sb.append("Object "); + sb.append(getQualifier()); + sb.append(" does already exist in context."); + return sb.toString(); + } +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/NoSuchObjectException.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/NoSuchObjectException.java new file mode 100644 index 00000000..f356df66 --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/NoSuchObjectException.java @@ -0,0 +1,17 @@ +package com.link_intersystems.util.context; + +public class NoSuchObjectException extends ContextObjectException { + + public NoSuchObjectException(ObjectQualifier objectQualifier) { + super(objectQualifier); + } + + @Override + public String getMessage() { + StringBuilder sb = new StringBuilder(); + sb.append("Object "); + sb.append(getQualifier()); + sb.append(" does not exist in context."); + return sb.toString(); + } +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ObjectQualifier.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ObjectQualifier.java new file mode 100644 index 00000000..e2d2b498 --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/ObjectQualifier.java @@ -0,0 +1,57 @@ +package com.link_intersystems.util.context; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class ObjectQualifier { + + private Class type; + private String name; + + public ObjectQualifier(Class type) { + this(type, null); + } + + public ObjectQualifier(Class type, String name) { + this.type = requireNonNull(type); + this.name = name; + } + + public String getName() { + return name; + } + + public Class getType() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ObjectQualifier that = (ObjectQualifier) o; + return Objects.equals(getType(), that.getType()) && Objects.equals(getName(), that.getName()); + } + + @Override + public int hashCode() { + return Objects.hash(getType(), getName()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getType().getName()); + + String name = getName(); + if (name != null) { + sb.append("['"); + sb.append(getName()); + sb.append("']"); + } + + return sb.toString(); + } +} + diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/QualifiedObject.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/QualifiedObject.java new file mode 100644 index 00000000..b3b47f22 --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/QualifiedObject.java @@ -0,0 +1,37 @@ +package com.link_intersystems.util.context; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class QualifiedObject { + + private ObjectQualifier objectQualifier; + private T object; + + public QualifiedObject(ObjectQualifier objectQualifier, T object) { + this.objectQualifier = requireNonNull(objectQualifier); + this.object = requireNonNull(object); + } + + public ObjectQualifier getQualifier() { + return objectQualifier; + } + + public T getObject() { + return object; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + QualifiedObject that = (QualifiedObject) o; + return Objects.equals(getQualifier(), that.getQualifier()) && getObject() == that.getObject(); + } + + @Override + public int hashCode() { + return Objects.hash(getQualifier(), System.identityHashCode(getObject())); + } +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/Action.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/Action.java new file mode 100644 index 00000000..0043033f --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/Action.java @@ -0,0 +1,11 @@ +package com.link_intersystems.util.context.dsl; + +import java.util.function.Consumer; + +public interface Action { + void then(Consumer consumer); + + void then(Runnable runnable); + + void dispose(); +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/ContextDsl.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/ContextDsl.java new file mode 100644 index 00000000..122771fd --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/ContextDsl.java @@ -0,0 +1,202 @@ +package com.link_intersystems.util.context.dsl; + +import com.link_intersystems.util.context.Context; +import com.link_intersystems.util.context.ContextListener; +import com.link_intersystems.util.context.ObjectQualifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; + +public class ContextDsl { + + private static class ContextMediatorListenerAdapter implements ContextListener { + + private Consumer onInstanceAdded; + private Consumer onInstanceRemoved; + private T setModel; + private T defaultModel; + + public ContextMediatorListenerAdapter(Consumer modelSetter) { + this(modelSetter, modelSetter); + } + + public ContextMediatorListenerAdapter(Consumer onInstanceAdded, Consumer onInstanceRemoved) { + this.onInstanceAdded = onInstanceAdded; + this.onInstanceRemoved = onInstanceRemoved; + } + + @Override + public void added(Context context, T object) { + if (onInstanceAdded != null) { + onInstanceAdded.accept(object); + } + + this.setModel = object; + } + + @Override + public void removed(Context context, T object) { + if (onInstanceRemoved != null) { + onInstanceRemoved.accept(object); + } + + this.setModel = null; + } + + } + + private static class RunnableListenerAdapter implements ContextListener { + + private Runnable addedRunnable; + private Runnable removedRunnable; + + public RunnableListenerAdapter(Runnable addedRunnable, Runnable removedRunnable) { + this.addedRunnable = addedRunnable; + this.removedRunnable = removedRunnable; + } + + @Override + public void added(Context context, T object) { + if (addedRunnable != null) { + addedRunnable.run(); + } + } + + @Override + public void removed(Context context, T object) { + if (removedRunnable != null) { + removedRunnable.run(); + } + } + } + + private static abstract class AbstractWhen implements When { + + private final List> actions = new ArrayList<>(); + + protected Action add(Action action) { + actions.add(action); + return action; + } + + @Override + public void dispose() { + actions.forEach(Action::dispose); + actions.clear(); + } + } + + private static abstract class AbstractAction implements Action { + + private Context context; + private ObjectQualifier objectQualifier; + + protected final List> listeners = new ArrayList<>(); + + public AbstractAction(Context context, ObjectQualifier objectQualifier) { + this.context = context; + this.objectQualifier = objectQualifier; + } + + + @Override + public void dispose() { + listeners.forEach(l -> context.removeViewContextListener(objectQualifier, l)); + listeners.clear(); + } + } + + private List unregisterListenersRunnables = new ArrayList<>(); + + private Context context; + + public ContextDsl(Context context) { + this.context = context; + } + + public When when(Class modelType) { + return when(modelType, null); + } + + public When when(Class modelType, String name) { + ObjectQualifier objectQualifier = new ObjectQualifier<>(modelType, name); + return when(objectQualifier); + } + + public When when(ObjectQualifier objectQualifier) { + return new AbstractWhen<>() { + @Override + public Action added() { + return add(new AbstractAction(context, objectQualifier) { + + @Override + public void then(Consumer consumer) { + ContextMediatorListenerAdapter listenerAdapter = new ContextMediatorListenerAdapter<>(consumer, null); + listeners.add(listenerAdapter); + context.addViewContextListener(objectQualifier, listenerAdapter); + unregisterListenersRunnables.add(() -> context.removeViewContextListener(objectQualifier, listenerAdapter)); + } + + @Override + public void then(Runnable runnable) { + RunnableListenerAdapter runnableListenerAdapter = new RunnableListenerAdapter<>(requireNonNull(runnable), null); + listeners.add(runnableListenerAdapter); + context.addViewContextListener(objectQualifier, runnableListenerAdapter); + unregisterListenersRunnables.add(() -> context.removeViewContextListener(objectQualifier, runnableListenerAdapter)); + } + }); + } + + @Override + public Action removed() { + return add(new AbstractAction(context, objectQualifier) { + + @Override + public void then(Consumer consumer) { + ContextMediatorListenerAdapter listenerAdapter = new ContextMediatorListenerAdapter<>(null, or -> consumer.accept(null)); + listeners.add(listenerAdapter); + context.addViewContextListener(objectQualifier, listenerAdapter); + unregisterListenersRunnables.add(() -> context.removeViewContextListener(objectQualifier, listenerAdapter)); + } + + @Override + public void then(Runnable runnable) { + RunnableListenerAdapter runnableListenerAdapter = new RunnableListenerAdapter<>(null, requireNonNull(runnable)); + listeners.add(runnableListenerAdapter); + context.addViewContextListener(objectQualifier, runnableListenerAdapter); + unregisterListenersRunnables.add(() -> context.removeViewContextListener(objectQualifier, runnableListenerAdapter)); + } + }); + } + + @Override + public Action changed() { + return add(new AbstractAction(context, objectQualifier) { + @Override + public void then(Consumer consumer) { + ContextMediatorListenerAdapter listenerAdapter = new ContextMediatorListenerAdapter<>(consumer, or -> consumer.accept(null)); + listeners.add(listenerAdapter); + context.addViewContextListener(objectQualifier, listenerAdapter); + unregisterListenersRunnables.add(() -> context.removeViewContextListener(objectQualifier, listenerAdapter)); + } + + @Override + public void then(Runnable runnable) { + RunnableListenerAdapter runnableListenerAdapter = new RunnableListenerAdapter<>(requireNonNull(runnable), requireNonNull(runnable)); + listeners.add(runnableListenerAdapter); + context.addViewContextListener(objectQualifier, runnableListenerAdapter); + unregisterListenersRunnables.add(() -> context.removeViewContextListener(objectQualifier, runnableListenerAdapter)); + } + }); + } + }; + } + + public void dispose() { + unregisterListenersRunnables.forEach(Runnable::run); + unregisterListenersRunnables.clear(); + } +} diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/When.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/When.java new file mode 100644 index 00000000..606100e1 --- /dev/null +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/dsl/When.java @@ -0,0 +1,11 @@ +package com.link_intersystems.util.context.dsl; + +public interface When { + Action removed(); + + Action added(); + + Action changed(); + + void dispose(); +} diff --git a/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/ContextListenerMock.java b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/ContextListenerMock.java new file mode 100644 index 00000000..a6368cb3 --- /dev/null +++ b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/ContextListenerMock.java @@ -0,0 +1,35 @@ +package com.link_intersystems.util.context; + +import com.link_intersystems.util.context.Context; +import com.link_intersystems.util.context.ContextListener; + +import java.util.LinkedList; +import java.util.List; + +public class ContextListenerMock implements ContextListener { + + + private LinkedList objects = new LinkedList<>(); + + + @Override + public void added(Context context, T object) { + objects.add(object); + } + + @Override + public void removed(Context context, T object) { + objects.remove(object); + } + + public List getObjects() { + return objects; + } + + public T getLastest() { + if (objects.isEmpty()) { + return null; + } + return objects.getLast(); + } +} diff --git a/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/DefaultContextTest.java b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/DefaultContextTest.java new file mode 100644 index 00000000..0180c20d --- /dev/null +++ b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/DefaultContextTest.java @@ -0,0 +1,212 @@ +package com.link_intersystems.util.context; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +class DefaultContextTest { + + private ContextListenerMock contextListener; + private DefaultContext context; + + @BeforeEach + void setUp() { + context = new DefaultContext(); + contextListener = new ContextListenerMock<>(); + } + + @Test + void putObjectTwice() { + context.put(String.class, "A"); + assertThrows(DuplicateObjectException.class, () -> context.put(String.class, new String("A"))); + } + + @Test + void putSameObjectTwice() { + context.put(String.class, "A"); + context.put(String.class, "A"); + } + + @Test + void putNamedObjectTwice() { + context.put(String.class, "model1", "A"); + assertThrows(DuplicateObjectException.class, () -> context.put(String.class, "model1", new String("A"))); + } + + @Test + void putNamedObject() { + context.put(String.class, "A"); + context.put(String.class, "model1", "A"); + } + + @Test + void put() { + context.put(String.class, "A"); + + assertSame("A", context.get(String.class)); + } + + @Test + void getNamedObject() { + context.put(String.class, "A"); + context.put(String.class, "model1", "A"); + + assertSame("A", context.get(String.class, "model1")); + } + + @Test + void remove() { + context.put(String.class, "A"); + + assertTrue(context.remove(String.class)); + + assertThrows(NoSuchObjectException.class, () -> context.get(String.class)); + } + + @Test + void removeUnknownInstance() { + context.put(String.class, "A"); + + assertFalse(context.remove(String.class, "someName")); + } + + @Test + void removeNamedObject() { + context.put(String.class, "model1", "A"); + + context.remove(String.class, "model1"); + + assertThrows(NoSuchObjectException.class, () -> context.get(String.class, "model1")); + } + + + @Test + void addListenerAfterObjectAdded() { + context.put(String.class, "A"); + + context.addViewContextListener(String.class, contextListener); + + assertSame("A", contextListener.getLastest()); + } + + @Test + void addListenerBeforeObjectAdded() { + context.addViewContextListener(String.class, contextListener); + + context.put(String.class, "A"); + + assertSame("A", contextListener.getLastest()); + } + + @Test + void addListenerAfterObjectRemoved() { + context.put(String.class, "A"); + context.remove(String.class); + + context.addViewContextListener(String.class, contextListener); + + assertNull(contextListener.getLastest()); + } + + @Test + void addListenerBeforeObjectRemoved() { + context.put(String.class, "A"); + context.addViewContextListener(String.class, contextListener); + + context.remove(String.class); + + assertNull(contextListener.getLastest()); + } + + @Test + void removeListenerAfterObjectAdded() { + context.addViewContextListener(String.class, contextListener); + context.put(String.class, "A"); + + context.removeViewContextListener(String.class, contextListener); + + context.remove(String.class); + context.put(String.class, "A"); + + assertEquals(Collections.singletonList("A"), contextListener.getObjects()); + } + + @Test + void removeListenerBeforeObjectAdded() { + context.addViewContextListener(String.class, contextListener); + + context.removeViewContextListener(String.class, contextListener); + + context.put(String.class, "A"); + assertEquals(Collections.emptyList(), contextListener.getObjects()); + } + + @Test + void unspecificObjectListenerAddedBeforeObjects() { + context.addViewContextListener(String.class, contextListener); + context.put(String.class, "model1", "A"); + context.put(String.class, "model2", "B"); + + assertEquals(Collections.emptyList(), contextListener.getObjects()); + } + + @Test + void specificObjectListenerAddedBeforeObjects() { + context.addViewContextListener(String.class, "model2", contextListener); + context.put(String.class, "model1", "A"); + context.put(String.class, "model2", "B"); + + assertEquals(List.of("B"), contextListener.getObjects()); + } + + @Test + void unspecificObjectListenerAddedAfterObjects() { + context.put(String.class, "model1", "A"); + context.put(String.class, "model2", "B"); + + context.addViewContextListener(String.class, contextListener); + + assertEquals(Collections.emptyList(), contextListener.getObjects()); + } + + @Test + void specificObjectListenerAddedAfterObjects() { + context.put(String.class, "model1", "A"); + context.put(String.class, "model2", "B"); + + context.addViewContextListener(String.class, "model2", contextListener); + + assertEquals(List.of("B"), contextListener.getObjects()); + } + + @Test + void contains() { + context.put(String.class, "A"); + context.put(String.class, "model1", "B"); + context.put(String.class, "model2", "C"); + + + assertTrue(context.contains(String.class)); + assertTrue(context.contains(String.class, "model1")); + assertTrue(context.contains(String.class, "model2")); + } + + @Test + void stream() { + context.put(String.class, "model1", "A"); + context.put(String.class, "model2", "B"); + + + List instances = context.stream().map(QualifiedObject::getObject).collect(Collectors.toList()); + assertEquals(2, instances.size()); + assertTrue(instances.contains("A")); + assertTrue(instances.contains("B")); + + context.stream().map(QualifiedObject::getQualifier).collect(Collectors.toList()).forEach(context::remove); + } +} \ No newline at end of file diff --git a/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/QualifiedObjectTest.java b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/QualifiedObjectTest.java new file mode 100644 index 00000000..2eeb4690 --- /dev/null +++ b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/QualifiedObjectTest.java @@ -0,0 +1,33 @@ +package com.link_intersystems.util.context; + +import com.link_intersystems.util.context.ObjectQualifier; +import com.link_intersystems.util.context.QualifiedObject; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class QualifiedObjectTest { + + @Test + void testEquals() { + String aString = "a"; + ObjectQualifier aObjectQualifier = new ObjectQualifier<>(String.class, "a"); + QualifiedObject aInstance = new QualifiedObject<>(aObjectQualifier, aString); + + assertEquals(aInstance, new QualifiedObject<>(aObjectQualifier, aString)); + assertNotEquals(aInstance, new QualifiedObject<>(aObjectQualifier, new String(aString))); + + QualifiedObject bInstance = new QualifiedObject<>(new ObjectQualifier<>(String.class, "b"), "b"); + assertNotEquals(aInstance, bInstance); + } + + @Test + void testHashCode() { + String aString = "a"; + ObjectQualifier aObjectQualifier = new ObjectQualifier<>(String.class, "a"); + QualifiedObject aInstance = new QualifiedObject<>(aObjectQualifier, aString); + + assertEquals(aInstance.hashCode(), new QualifiedObject<>(aObjectQualifier, aString).hashCode()); + } +} \ No newline at end of file diff --git a/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/dsl/ContextDslTest.java b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/dsl/ContextDslTest.java new file mode 100644 index 00000000..1db794bf --- /dev/null +++ b/lis-commons-util-context/src/test/java/com/link_intersystems/util/context/dsl/ContextDslTest.java @@ -0,0 +1,130 @@ +package com.link_intersystems.util.context.dsl; + +import com.link_intersystems.util.context.Context; +import com.link_intersystems.util.context.DefaultContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ContextDslTest { + + private ClassWithProperty classWithProperty; + + private static class ClassWithProperty { + private String text; + private boolean executed; + + public void setText(String text) { + this.text = text; + } + + public void execute() { + executed = !executed; + } + } + + + private Context context; + private ContextDsl contextDsl; + + @BeforeEach + void setUp() { + context = new DefaultContext(); + contextDsl = new ContextDsl(context); + + classWithProperty = new ClassWithProperty(); + } + + @Test + void whenModelChangedRun() { + context.put(String.class, "A"); + + contextDsl.when(String.class).changed().then(classWithProperty::execute); + + assertTrue(classWithProperty.executed); + context.remove(String.class); + assertFalse(classWithProperty.executed); + } + + @Test + void whenModelAddedRun() { + context.put(String.class, "A"); + + contextDsl.when(String.class).added().then(classWithProperty::execute); + + assertTrue(classWithProperty.executed); + context.remove(String.class); + assertTrue(classWithProperty.executed); + } + + @Test + void whenModelRemovedRun() { + context.put(String.class, "A"); + + contextDsl.when(String.class).removed().then(classWithProperty::execute); + assertFalse(classWithProperty.executed); + context.remove(String.class); + assertTrue(classWithProperty.executed); + } + + @Test + void whenModelAddedSetModel() { + context.put(String.class, "A"); + + contextDsl.when(String.class).added().then(classWithProperty::setText); + + assertSame("A", classWithProperty.text); + context.remove(String.class); + assertEquals("A", classWithProperty.text); + } + + @Test + void whenModelRemovedSetModel() { + classWithProperty.text = "A"; + context.put(String.class, "A"); + + contextDsl.when(String.class).removed().then(classWithProperty::setText); + + assertSame("A", classWithProperty.text); + context.remove(String.class); + assertNull(classWithProperty.text); + } + + @Test + void whenAddedAfterModelAvailable() { + context.put(String.class, "A"); + + contextDsl.when(String.class).changed().then(classWithProperty::setText); + + assertSame("A", classWithProperty.text); + context.remove(String.class); + assertNull(classWithProperty.text); + } + + @Test + void whenAddedBeforeModelAvailable() { + contextDsl.when(String.class).changed().then(classWithProperty::setText); + + context.put(String.class, "A"); + + assertSame("A", classWithProperty.text); + context.remove(String.class); + assertNull(classWithProperty.text); + } + + @Test + void disposeAll() { + + classWithProperty.text = "A"; + + When when = contextDsl.when(String.class); + Action addedModelAction = when.added(); + addedModelAction.then(classWithProperty::setText); + + when.dispose(); + context.put(String.class, "B"); + + assertEquals("A", classWithProperty.text); + } +} \ No newline at end of file diff --git a/lis-commons-util/src/main/java/com/link_intersystems/util/service/ServiceLocator.java b/lis-commons-util/src/main/java/com/link_intersystems/util/service/ServiceLocator.java new file mode 100644 index 00000000..3f89556b --- /dev/null +++ b/lis-commons-util/src/main/java/com/link_intersystems/util/service/ServiceLocator.java @@ -0,0 +1,19 @@ +package com.link_intersystems.util.service; + +public interface ServiceLocator { + + public static ServiceLocator nullInstance(){ + return new ServiceLocator() { + @Override + public T getService(Class type, String name) { + return null; + } + }; + } + + default public T getService(Class type) { + return getService(type, null); + } + + T getService(Class type, String name); +} diff --git a/pom.xml b/pom.xml index f6d0bf44..406a9545 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,9 @@ lis-commons-lang lis-commons-lang-criteria lis-commons-beans + lis-commons-beans-records lis-commons-util + lis-commons-util-context lis-commons-test lis-commons-graph lis-commons-events @@ -35,10 +37,10 @@ lis-commons-sql-hibernate lis-commons-net lis-commons-net-test - lis-commons-swing lis-commons-io lis-commons-security - lis-commons-beans-records + lis-commons-swing + lis-commons-swing-view @@ -109,6 +111,16 @@ lis-commons-sql 1.9.7-SNAPSHOT + + com.link-intersystems.commons + lis-commons-util-context + 1.9.7-SNAPSHOT + + + com.link-intersystems.commons + lis-commons-swing-view + 1.9.7-SNAPSHOT + com.link-intersystems.commons lis-commons-sql-hibernate From fcfa05132804bcb3423c0d73c0ba107ef6cb172e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Link?= Date: Sun, 5 Nov 2023 12:38:25 +0100 Subject: [PATCH 2/5] Added tests. --- .../swing/view/AbstractViewTest.java | 47 ++++++++++++++++ .../swing/view/ContainerViewContentTest.java | 56 +++++++++++++++++++ .../swing/view/DefaultViewSiteTest.java | 40 +++++++++++++ .../view/window/WindowViewContentTest.java | 45 +++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/AbstractViewTest.java create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ContainerViewContentTest.java create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/DefaultViewSiteTest.java create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewContentTest.java diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/AbstractViewTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/AbstractViewTest.java new file mode 100644 index 00000000..c80472f5 --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/AbstractViewTest.java @@ -0,0 +1,47 @@ +package com.link_intersystems.swing.view; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class AbstractViewTest { + + private AbstractView abstractView; + private ViewSiteMock viewSiteMock; + + @Test + void install() { + abstractView = spy(AbstractView.class); + + assertNull(abstractView.getContextDsl()); + + viewSiteMock = new ViewSiteMock(); + abstractView.install(viewSiteMock); + + verify(abstractView, times(1)).doInstall(viewSiteMock); + + assertEquals(viewSiteMock, abstractView.getViewSite()); + assertNotNull(abstractView.getContextDsl()); + } + + @Test + void uninstall() { + install(); + reset(abstractView); + + abstractView.uninstall(); + + verify(abstractView, times(1)).doUninstall(viewSiteMock); + } + + @Test + void createSubViewSite() { + install(); + + ViewContent content = mock(ViewContent.class); + ViewSite subViewSite = abstractView.createSubViewSite(content); + + assertInstanceOf(DefaultViewSite.class, subViewSite); + } +} \ No newline at end of file diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ContainerViewContentTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ContainerViewContentTest.java new file mode 100644 index 00000000..2681b0fd --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ContainerViewContentTest.java @@ -0,0 +1,56 @@ +package com.link_intersystems.swing.view; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.swing.*; +import java.awt.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +class ContainerViewContentTest { + + private JPanel container; + private LayoutManager2 layoutManager; + private ContainerViewContent containerViewContent; + + @BeforeEach + void setUp() { + container = new JPanel(); + layoutManager = mock(LayoutManager2.class); + container.setLayout(layoutManager); + + containerViewContent = new ContainerViewContent(container, BorderLayout.SOUTH); + } + + + @Test + void getParent() { + + assertEquals(container, containerViewContent.getParent()); + } + + @Test + void setComponent() { + JLabel viewContent = new JLabel(); + containerViewContent.setComponent(viewContent); + + assertEquals(1, container.getComponents().length); + assertEquals(viewContent, container.getComponents()[0]); + verify(layoutManager, times(1)).addLayoutComponent(viewContent, BorderLayout.SOUTH); + } + + @Test + void resetComponent() { + setComponent(); + + JLabel anotherLabel = new JLabel(); + containerViewContent.setComponent(anotherLabel); + + assertEquals(1, container.getComponents().length); + assertEquals(anotherLabel, container.getComponents()[0]); + + verify(layoutManager, times(1)).addLayoutComponent(anotherLabel, BorderLayout.SOUTH); + } +} \ No newline at end of file diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/DefaultViewSiteTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/DefaultViewSiteTest.java new file mode 100644 index 00000000..9f69f822 --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/DefaultViewSiteTest.java @@ -0,0 +1,40 @@ +package com.link_intersystems.swing.view; + +import com.link_intersystems.util.context.Context; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; + +class DefaultViewSiteTest { + + @Test + void contentAndContext() { + ViewContent viewContent = mock(ViewContent.class); + Context context = mock(Context.class); + DefaultViewSite defaultViewSite = new DefaultViewSite(viewContent, context); + + assertEquals(viewContent, defaultViewSite.getViewContent()); + assertEquals(context, defaultViewSite.getViewContext()); + } + + @Test + void contentOnly() { + ViewContent viewContent = mock(ViewContent.class); + + DefaultViewSite defaultViewSite = new DefaultViewSite(viewContent); + + assertEquals(viewContent, defaultViewSite.getViewContent()); + assertNotNull(defaultViewSite.getViewContext()); + } + + @Test + void contextOnly() { + Context context = mock(Context.class); + + DefaultViewSite defaultViewSite = new DefaultViewSite(context); + + assertEquals(context, defaultViewSite.getViewContext()); + assertNotNull(defaultViewSite.getViewContent()); + } +} \ No newline at end of file diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewContentTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewContentTest.java new file mode 100644 index 00000000..2ac72ad6 --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewContentTest.java @@ -0,0 +1,45 @@ +package com.link_intersystems.swing.view.window; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.awt.*; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.*; + +class WindowViewContentTest { + + private WindowViewContent windowViewContent; + + @BeforeEach + void setUp() { + windowViewContent = new WindowViewContent(); + + + } + + @Test + void setComponent() { + Component component = mock(Component.class); + windowViewContent.setComponent(component); + + verify(component, times(1)).setVisible(true); + } + + @Test + void resetComponent() { + setComponent(); + + Component component = mock(Component.class); + windowViewContent.setComponent(component); + + verify(component, times(1)).setVisible(true); + } + + @Test + void getParent() { + + assertNull(windowViewContent.getParent()); + } +} \ No newline at end of file From e5f28d9ddedea74e9439e18a6868fcdcb4e8094a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Link?= Date: Sun, 5 Nov 2023 16:39:45 +0100 Subject: [PATCH 3/5] Added more tests. --- lis-commons-beans-mockito/README.md | 23 ++++++ lis-commons-beans-mockito/pom.xml | 27 +++++++ .../beans/mockito/BeanMatcher.java | 31 ++++++++ .../beans/mockito/BeanMatchers.java | 17 +++++ .../beans/mockito/PropertiesMatcher.java | 44 +++++++++++ .../beans/mockito/BeanMatchersTest.java | 48 ++++++++++++ .../beans/mockito/Person.java | 32 ++++++++ .../beans/mockito/PersonFixture.java | 24 ++++++ .../beans/mockito/PropertiesMatcherTest.java | 36 +++++++++ lis-commons-beans/pom.xml | 15 ---- .../mockito/beans/BeanMatchers.java | 16 ---- .../mockito/beans/PropertiesMatcher.java | 25 ------- lis-commons-swing-view/pom.xml | 5 ++ .../swing/view/ViewContent.java | 2 +- .../swing/view/window/WindowCloseAction.java | 28 +++++++ .../swing/view/window/WindowView.java | 58 ++++++++++---- .../swing/view/ViewSiteMock.java | 9 ++- .../view/window/WindowCloseActionTest.java | 30 ++++++++ .../swing/view/window/WindowViewTest.java | 75 +++++++++++++++++++ lis-commons-swing/pom.xml | 9 ++- .../swing/action/ActionTrigger.java | 3 +- .../swing/table/ListTableModelTest.java | 2 +- .../util/context/Context.java | 16 ++++ pom.xml | 4 +- 24 files changed, 498 insertions(+), 81 deletions(-) create mode 100644 lis-commons-beans-mockito/README.md create mode 100644 lis-commons-beans-mockito/pom.xml create mode 100644 lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatcher.java create mode 100644 lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatchers.java create mode 100644 lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/PropertiesMatcher.java create mode 100644 lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/BeanMatchersTest.java create mode 100644 lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/Person.java create mode 100644 lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PersonFixture.java create mode 100644 lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PropertiesMatcherTest.java delete mode 100644 lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/BeanMatchers.java delete mode 100644 lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/PropertiesMatcher.java create mode 100644 lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowCloseAction.java create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowCloseActionTest.java create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewTest.java diff --git a/lis-commons-beans-mockito/README.md b/lis-commons-beans-mockito/README.md new file mode 100644 index 00000000..401bb7f1 --- /dev/null +++ b/lis-commons-beans-mockito/README.md @@ -0,0 +1,23 @@ +# lis-commons-beans-mockito + +A mockito extension for bean related stuff. + +## BeanMatchers + +The BeanMatchers class provides support for java bean related equality matching. + +> verify(personSetter, times(1)).setPerson(BeanMatchers.propertiesEqual(expectedPerson)); +> +> // or exclude properties +> +> verify(personSetter, times(1)).setPerson(BeanMatchers.propertiesEqual(expectedPerson,"firstname")); + +### Custom BeanMatcher + +You can also create a custom BeanMatcher with another BeansFactory. E.g. one can create BeanMatcher that supports +Java records. + +> BeansFactory recordBeansFactory = BeansFactory.getInstance("record"); +> BeanMatcher recordBeanMatcher = new BeanMatcher(recordBeansFactory); +> +> verify(personSetter, times(1)).setPerson(recordBeanMatcher.propertiesEqual(expectedPersonRecord)); \ No newline at end of file diff --git a/lis-commons-beans-mockito/pom.xml b/lis-commons-beans-mockito/pom.xml new file mode 100644 index 00000000..46be8062 --- /dev/null +++ b/lis-commons-beans-mockito/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + com.link-intersystems.commons + lis-commons + 1.9.7-SNAPSHOT + + + lis-commons-beans-mockito + A mockito extension for bean related stuff. + + + + com.link-intersystems.commons + lis-commons-beans + + + org.mockito + mockito-core + compile + + + + \ No newline at end of file diff --git a/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatcher.java b/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatcher.java new file mode 100644 index 00000000..83cfd135 --- /dev/null +++ b/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatcher.java @@ -0,0 +1,31 @@ +package com.link_intersystems.beans.mockito; + +import com.link_intersystems.beans.BeansFactory; +import org.mockito.ArgumentMatcher; + +import static java.util.Objects.requireNonNull; +import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; + +public class BeanMatcher { + + public static final BeanMatcher DEFAULT = new BeanMatcher(); + + private BeansFactory beansFactory; + + public BeanMatcher() { + this(BeansFactory.getDefault()); + } + + public BeanMatcher(BeansFactory beansFactory) { + this.beansFactory = requireNonNull(beansFactory); + } + + public T propertiesEqual(Object aBean, String... excludeProperties) { + reportMatcher(new PropertiesMatcher<>(aBean, beansFactory, excludeProperties)); + return null; + } + + private void reportMatcher(ArgumentMatcher matcher) { + mockingProgress().getArgumentMatcherStorage().reportMatcher(matcher); + } +} diff --git a/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatchers.java b/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatchers.java new file mode 100644 index 00000000..21e2b317 --- /dev/null +++ b/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/BeanMatchers.java @@ -0,0 +1,17 @@ +package com.link_intersystems.beans.mockito; + +import com.link_intersystems.beans.BeansFactory; + +public class BeanMatchers { + + public static T propertiesEqual(Object aBean, String... excludeProperties) { + return BeanMatcher.DEFAULT.propertiesEqual(aBean, excludeProperties); + } + + public static T propertiesEqual(BeansFactory beansFactory, Object aBean, String... excludeProperties) { + BeanMatcher beanMatcher = new BeanMatcher(beansFactory); + beanMatcher.propertiesEqual(aBean, excludeProperties); + return null; + } + +} diff --git a/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/PropertiesMatcher.java b/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/PropertiesMatcher.java new file mode 100644 index 00000000..f9afdc92 --- /dev/null +++ b/lis-commons-beans-mockito/src/main/java/com/link_intersystems/beans/mockito/PropertiesMatcher.java @@ -0,0 +1,44 @@ +package com.link_intersystems.beans.mockito; + +import com.link_intersystems.beans.Bean; +import com.link_intersystems.beans.BeansFactory; +import com.link_intersystems.beans.Property; +import com.link_intersystems.beans.PropertyList; +import org.mockito.ArgumentMatcher; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; + +public class PropertiesMatcher implements ArgumentMatcher { + + private final Bean expectedBean; + private final List excludeProperties; + private final BeansFactory beansFactory; + + public PropertiesMatcher(T expectedBean, String... excludeProperties) { + this(expectedBean, BeansFactory.getDefault(), excludeProperties); + } + + public PropertiesMatcher(T expectedBean, BeansFactory beansFactory, String... excludeProperties) { + this.beansFactory = requireNonNull(beansFactory); + this.expectedBean = beansFactory.createBean(expectedBean, Object.class); + this.excludeProperties = Arrays.asList(excludeProperties); + } + + @Override + public boolean matches(T argument) { + Bean actualBean = beansFactory.createBean(argument, Object.class); + + Predicate exclucedPropertiesPredicate = pd -> !excludeProperties.contains(pd.getPropertyDesc().getName()); + PropertyList expectedProperties = expectedBean.getProperties(); + PropertyList expectedFilteredProperties = expectedProperties.filter(exclucedPropertiesPredicate); + + PropertyList actualProperties = actualBean.getProperties(); + PropertyList actualFilteredProperties = actualProperties.filter(exclucedPropertiesPredicate); + + return expectedFilteredProperties.equals(actualFilteredProperties); + } +} diff --git a/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/BeanMatchersTest.java b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/BeanMatchersTest.java new file mode 100644 index 00000000..29972795 --- /dev/null +++ b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/BeanMatchersTest.java @@ -0,0 +1,48 @@ +package com.link_intersystems.beans.mockito; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static com.link_intersystems.beans.mockito.BeanMatchers.propertiesEqual; +import static org.mockito.Mockito.*; + +class BeanMatchersTest { + + interface PersonSetter { + public void setPerson(Person person); + } + + private Person johnDoe; + private Person janeDoe; + + @BeforeEach + void setUp() { + PersonFixture personFixture = new PersonFixture(); + johnDoe = personFixture.getJohnDoe(); + janeDoe = personFixture.getJaneDoe(); + } + + @Test + void testPropertiesEqual() { + PersonSetter personSetter = mock(PersonSetter.class); + + personSetter.setPerson(johnDoe); + + Person johnDoeCopy = new Person(); + johnDoeCopy.setFirstname("John"); + johnDoeCopy.setLastname("Doe"); + johnDoeCopy.setAge(37); + + verify(personSetter, times(1)).setPerson(propertiesEqual(johnDoeCopy)); + } + + @Test + void testPropertiesEqualExcludes() { + PersonSetter personSetter = mock(PersonSetter.class); + + personSetter.setPerson(johnDoe); + + + verify(personSetter, times(1)).setPerson(propertiesEqual(janeDoe, "firstname", "age")); + } +} \ No newline at end of file diff --git a/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/Person.java b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/Person.java new file mode 100644 index 00000000..b8e9d526 --- /dev/null +++ b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/Person.java @@ -0,0 +1,32 @@ +package com.link_intersystems.beans.mockito; + +public class Person { + + String firstname; + String lastname; + int age; + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PersonFixture.java b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PersonFixture.java new file mode 100644 index 00000000..ee92cd55 --- /dev/null +++ b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PersonFixture.java @@ -0,0 +1,24 @@ +package com.link_intersystems.beans.mockito; + +public class PersonFixture { + + public Person getJohnDoe() { + Person johnDoe = new Person(); + + johnDoe.setFirstname("John"); + johnDoe.setLastname("Doe"); + johnDoe.setAge(37); + + return johnDoe; + } + + public Person getJaneDoe() { + Person janeDoe = new Person(); + + janeDoe.setFirstname("Jane"); + janeDoe.setLastname("Doe"); + janeDoe.setAge(35); + + return janeDoe; + } +} diff --git a/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PropertiesMatcherTest.java b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PropertiesMatcherTest.java new file mode 100644 index 00000000..1448b387 --- /dev/null +++ b/lis-commons-beans-mockito/src/test/java/com/link_intersystems/beans/mockito/PropertiesMatcherTest.java @@ -0,0 +1,36 @@ +package com.link_intersystems.beans.mockito; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PropertiesMatcherTest { + + private PersonFixture personFixture; + private PropertiesMatcher johnDoeMatcher; + + @BeforeEach + void setUp() { + personFixture = new PersonFixture(); + johnDoeMatcher = new PropertiesMatcher<>(personFixture.getJohnDoe()); + } + + @Test + void matches() { + assertTrue(johnDoeMatcher.matches(personFixture.getJohnDoe())); + } + + @Test + void wontMatch() { + assertFalse(johnDoeMatcher.matches(personFixture.getJaneDoe())); + } + + @Test + void matchExcludes() { + johnDoeMatcher = new PropertiesMatcher<>(personFixture.getJohnDoe(), "firstname", "age"); + + assertTrue(johnDoeMatcher.matches(personFixture.getJaneDoe())); + } +} \ No newline at end of file diff --git a/lis-commons-beans/pom.xml b/lis-commons-beans/pom.xml index 91ddb5e0..c2e8e5fd 100644 --- a/lis-commons-beans/pom.xml +++ b/lis-commons-beans/pom.xml @@ -33,19 +33,4 @@ - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - \ No newline at end of file diff --git a/lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/BeanMatchers.java b/lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/BeanMatchers.java deleted file mode 100644 index 86e7392e..00000000 --- a/lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/BeanMatchers.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.link_intersystems.mockito.beans; - -import org.mockito.ArgumentMatcher; - -import static org.mockito.internal.progress.ThreadSafeMockingProgress.*; - -public class BeanMatchers { - public static T propertiesEqual(Object aBean) { - reportMatcher(new PropertiesMatcher<>(aBean)); - return null; - } - - private static void reportMatcher(ArgumentMatcher matcher) { - mockingProgress().getArgumentMatcherStorage().reportMatcher(matcher); - } -} diff --git a/lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/PropertiesMatcher.java b/lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/PropertiesMatcher.java deleted file mode 100644 index 80e484ad..00000000 --- a/lis-commons-beans/src/test/java/com/link_intersystems/mockito/beans/PropertiesMatcher.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.link_intersystems.mockito.beans; - -import com.link_intersystems.beans.Bean; -import com.link_intersystems.beans.BeansFactory; -import org.mockito.ArgumentMatcher; - -public class PropertiesMatcher implements ArgumentMatcher { - - private final Bean expectedBean; - - public PropertiesMatcher(T expectedBean) { - this(expectedBean, BeansFactory.getDefault()); - } - - public PropertiesMatcher(T expectedBean, BeansFactory beansFactory) { - this.expectedBean = beansFactory.createBean(expectedBean, Object.class); - } - - @Override - public boolean matches(T argument) { - BeansFactory factory = BeansFactory.getDefault(); - Bean argumentBean = factory.createBean(argument, Object.class); - return expectedBean.propertiesEqual(argumentBean); - } -} diff --git a/lis-commons-swing-view/pom.xml b/lis-commons-swing-view/pom.xml index dcd31fee..faa00ecd 100644 --- a/lis-commons-swing-view/pom.xml +++ b/lis-commons-swing-view/pom.xml @@ -21,6 +21,11 @@ com.link-intersystems.commons lis-commons-util-context + + com.link-intersystems.commons + lis-commons-beans-mockito + test + diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java index e2a2a159..14e97eb3 100644 --- a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/ViewContent.java @@ -5,7 +5,7 @@ public interface ViewContent { public static ViewContent nullInstance() { - return new ViewContent(){ + return new ViewContent() { @Override public void setComponent(Component component) { diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowCloseAction.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowCloseAction.java new file mode 100644 index 00000000..d69e9d93 --- /dev/null +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowCloseAction.java @@ -0,0 +1,28 @@ +package com.link_intersystems.swing.view.window; + +import com.link_intersystems.swing.action.ActionTrigger; +import com.link_intersystems.swing.view.View; + +import javax.swing.*; +import java.awt.event.WindowEvent; + +public interface WindowCloseAction { + + public static final WindowCloseAction DEFAULT = new WindowCloseAction() { + }; + + public static WindowCloseAction actionAdapter(Action action) { + return new WindowCloseAction() { + @Override + public void actionPerformed(WindowEvent windowEvent, View view) { + ActionTrigger.performAction(windowEvent.getSource(), action); + WindowCloseAction.super.actionPerformed(windowEvent, view); + } + }; + } + + default + public void actionPerformed(WindowEvent windowEvent, View view) { + view.uninstall(); + } +} diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java index d529aba1..a912a508 100644 --- a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/window/WindowView.java @@ -1,6 +1,6 @@ package com.link_intersystems.swing.view.window; -import com.link_intersystems.swing.action.ActionTrigger; +import com.link_intersystems.events.awt.WindowEventMethod; import com.link_intersystems.swing.view.AbstractView; import com.link_intersystems.swing.view.ViewContent; import com.link_intersystems.swing.view.ViewSite; @@ -8,34 +8,62 @@ import javax.swing.*; import java.awt.*; -import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.Optional; +import static java.util.Objects.requireNonNull; import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE; public abstract class WindowView extends AbstractView { public static final String DEFAULT_CLOSE_ACTION = WindowView.class.getName() + ".closeAction"; - private ActionTrigger actionTrigger = new ActionTrigger(this); - private Window window; - private WindowAdapter closeHandler = new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - onCloseWindow(getViewSite()); - } - }; + private int closeOperation; + private WindowListener closeHandler; + + public WindowView() { + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + setWindowCloseMethod(WindowEventMethod.CLOSING); + } @Override protected void doInstall(ViewSite viewSite) { window = createWindow(viewSite); - setDefaultCloseOperation(window, DO_NOTHING_ON_CLOSE); + setDefaultCloseOperation(window, closeOperation); window.addWindowListener(closeHandler); ViewContent viewContent = viewSite.getViewContent(); viewContent.setComponent(window); } + /** + * @param windowConstantsValue a value of {@link WindowConstants}. + */ + public void setDefaultCloseOperation(int windowConstantsValue) { + setDefaultCloseOperation(window, windowConstantsValue); + } + + /** + * Sets the event method that will cause a {@link WindowCloseAction}, registered to the {@link Context} + * with the {@link #DEFAULT_CLOSE_ACTION} name, to be performed. + * + * @param windowCloseMethod a constant of {@link WindowEventMethod}. Default is #{@link WindowEventMethod#CLOSING}. + */ + public void setWindowCloseMethod(WindowEventMethod windowCloseMethod) { + requireNonNull(windowCloseMethod); + + if (window != null) { + window.removeWindowListener(closeHandler); + } + + closeHandler = windowCloseMethod.listener((e) -> onCloseWindow(e, getViewSite())); + + if (window != null) { + window.addWindowListener(closeHandler); + } + } + protected void setDefaultCloseOperation(Window window, int closeOperation) { if (window instanceof JFrame) { JFrame frame = (JFrame) window; @@ -59,9 +87,11 @@ protected void doUninstall(ViewSite viewSite) { window = null; } - protected void onCloseWindow(ViewSite viewSite) { - uninstall(); + protected void onCloseWindow(WindowEvent windowEvent, ViewSite viewSite) { Context viewContext = viewSite.getViewContext(); - viewContext.ifContains(Action.class, DEFAULT_CLOSE_ACTION, actionTrigger::performAction); + + Optional windowCloseAction = viewContext.find(WindowCloseAction.class, DEFAULT_CLOSE_ACTION); + + windowCloseAction.orElse(WindowCloseAction.DEFAULT).actionPerformed(windowEvent, this); } } diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java index 194b0861..3d905b7b 100644 --- a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/ViewSiteMock.java @@ -3,9 +3,10 @@ import com.link_intersystems.util.context.Context; import com.link_intersystems.util.context.DefaultContext; +import javax.swing.*; import java.awt.*; -public class ViewSiteMock implements ViewSite{ +public class ViewSiteMock implements ViewSite { private ViewContent viewContent = new ViewContent() { @Override @@ -19,7 +20,7 @@ public Component getParent() { } }; - private Context context = new DefaultContext(); + private DefaultContext context = new DefaultContext(); private Component content; private Component parent; @@ -41,4 +42,8 @@ public ViewContent getViewContent() { public Context getViewContext() { return context; } + + public void addContextObject(Class type, String name, T contextObject) { + context.put(type, name, contextObject); + } } diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowCloseActionTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowCloseActionTest.java new file mode 100644 index 00000000..55668631 --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowCloseActionTest.java @@ -0,0 +1,30 @@ +package com.link_intersystems.swing.view.window; + +import com.link_intersystems.beans.mockito.BeanMatchers; +import com.link_intersystems.swing.view.View; +import org.junit.jupiter.api.Test; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.WindowEvent; + +import static org.mockito.Mockito.*; + +class WindowCloseActionTest { + + @Test + void actionAdapter() { + Action action = mock(Action.class); + Window window = mock(Window.class); + View view = mock(View.class); + + WindowCloseAction windowCloseAction = WindowCloseAction.actionAdapter(action); + + windowCloseAction.actionPerformed(new WindowEvent(window, WindowEvent.WINDOW_CLOSING), view); + + ActionEvent actionEvent = new ActionEvent(window, ActionEvent.ACTION_PERFORMED, ""); + + verify(action, times(1)).actionPerformed(BeanMatchers.propertiesEqual(actionEvent)); + } +} \ No newline at end of file diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewTest.java new file mode 100644 index 00000000..5989cb33 --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/window/WindowViewTest.java @@ -0,0 +1,75 @@ +package com.link_intersystems.swing.view.window; + +import com.link_intersystems.events.awt.WindowEventMethod; +import com.link_intersystems.swing.view.View; +import com.link_intersystems.swing.view.ViewSite; +import com.link_intersystems.swing.view.ViewSiteMock; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowEvent; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class WindowViewTest { + + private JFrame frame; + private WindowView windowView; + private ViewSiteMock viewSiteMock; + + @BeforeEach + void setUp() { + frame = new JFrame(); + windowView = new WindowView() { + @Override + protected Window createWindow(ViewSite viewSite) { + + return frame; + } + }; + + viewSiteMock = new ViewSiteMock(); + } + + @Test + void install() { + windowView.install(viewSiteMock); + + assertEquals(frame, viewSiteMock.getContent()); + } + + @Test + void uninstall() { + install(); + + WindowCloseAction closeAction = spy(WindowCloseAction.class); + viewSiteMock.addContextObject(WindowCloseAction.class, WindowView.DEFAULT_CLOSE_ACTION, closeAction); + + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + + verify(closeAction, times(1)).actionPerformed(any(WindowEvent.class), any(View.class)); + + assertNull(viewSiteMock.getContent(), "frame uninstalled"); + } + + @Test + void setWindowCloseMethod() { + install(); + + windowView.setWindowCloseMethod(WindowEventMethod.CLOSED); + + WindowCloseAction closeAction = spy(WindowCloseAction.class); + viewSiteMock.addContextObject(WindowCloseAction.class, WindowView.DEFAULT_CLOSE_ACTION, closeAction); + + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSED)); + + verify(closeAction, times(1)).actionPerformed(any(WindowEvent.class), any(View.class)); + + assertNull(viewSiteMock.getContent(), "frame uninstalled"); + } +} \ No newline at end of file diff --git a/lis-commons-swing/pom.xml b/lis-commons-swing/pom.xml index 8c0157f0..e934efb7 100644 --- a/lis-commons-swing/pom.xml +++ b/lis-commons-swing/pom.xml @@ -1,5 +1,6 @@ - + lis-commons com.link-intersystems.commons @@ -10,7 +11,8 @@ lis-commons-swing LIS Commons Swing - Link Intersystems Commons Swing (lis-commons-swing) provides support for Java swing related tasks. + Link Intersystems Commons Swing (lis-commons-swing) provides support for Java swing related tasks. + @@ -33,8 +35,7 @@ com.link-intersystems.commons - lis-commons-beans - test-jar + lis-commons-beans-mockito test diff --git a/lis-commons-swing/src/main/java/com/link_intersystems/swing/action/ActionTrigger.java b/lis-commons-swing/src/main/java/com/link_intersystems/swing/action/ActionTrigger.java index b3d5d4ea..023c8107 100644 --- a/lis-commons-swing/src/main/java/com/link_intersystems/swing/action/ActionTrigger.java +++ b/lis-commons-swing/src/main/java/com/link_intersystems/swing/action/ActionTrigger.java @@ -19,7 +19,8 @@ public static void performAction(Object actionEventSource, ActionListener action } public static void performAction(Object actionEventSource, ActionListener actionListener, String command, int actionId) { - actionListener.actionPerformed(new ActionEvent(actionEventSource, actionId, command)); + ActionEvent actionEvent = new ActionEvent(actionEventSource, actionId, command); + actionListener.actionPerformed(actionEvent); } private Object actionEventSource; diff --git a/lis-commons-swing/src/test/java/com/link_intersystems/swing/table/ListTableModelTest.java b/lis-commons-swing/src/test/java/com/link_intersystems/swing/table/ListTableModelTest.java index 618a7bcd..63ce6b91 100644 --- a/lis-commons-swing/src/test/java/com/link_intersystems/swing/table/ListTableModelTest.java +++ b/lis-commons-swing/src/test/java/com/link_intersystems/swing/table/ListTableModelTest.java @@ -7,7 +7,7 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; -import static com.link_intersystems.mockito.beans.BeanMatchers.*; +import static com.link_intersystems.beans.mockito.BeanMatchers.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java index f62580ad..d32e99b2 100644 --- a/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java +++ b/lis-commons-util-context/src/main/java/com/link_intersystems/util/context/Context.java @@ -1,5 +1,6 @@ package com.link_intersystems.util.context; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Stream; @@ -54,6 +55,21 @@ default void ifContains(ObjectQualifier objectQualifier, Consumer obje } } + default Optional find(Class type) throws ContextObjectException { + return find(type, null); + } + + default Optional find(Class type, String name) throws ContextObjectException { + return find(new ObjectQualifier<>(type, name)); + } + + default Optional find(ObjectQualifier objectQualifier) throws ContextObjectException { + if (contains(objectQualifier)) { + return Optional.of(get(objectQualifier)); + } + return Optional.empty(); + } + default T get(Class type) throws ContextObjectException { return get(type, null); } diff --git a/pom.xml b/pom.xml index 406a9545..34cd89cc 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ lis-commons-lang-criteria lis-commons-beans lis-commons-beans-records + lis-commons-beans-mockito lis-commons-util lis-commons-util-context lis-commons-test @@ -159,9 +160,8 @@ com.link-intersystems.commons - lis-commons-beans + lis-commons-beans-mockito 1.9.7-SNAPSHOT - test-jar com.link-intersystems.commons From 01fbeb14ced6106f8324fe1e106407577ad280bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Link?= Date: Mon, 6 Nov 2023 10:54:46 +0100 Subject: [PATCH 4/5] Run build with xvfb to support swing tests. --- .github/workflows/maven.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 68cae27e..d679076c 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -28,6 +28,8 @@ jobs: - name: Build and Deploy with Maven env: COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - run: ./mvnw -B deploy coveralls:report + uses: GabrielBB/xvfb-action@v1 + with: + run: ./mvnw -B deploy coveralls:report - name: Clear Caches run: curl -X PURGE https://camo.githubusercontent.com/8b5dee301fc3aee86900e1db08654773df3bd0938cd1d1009be98a34deb7478b/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f6c696e6b2d696e74657273797374656d732f6c69732d636f6d6d6f6e732f62616467652e7376673f6272616e63683d6d6173746572 From e39ec12ed8f84c56b0d9f256785759185a423e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Link?= Date: Mon, 6 Nov 2023 11:46:25 +0100 Subject: [PATCH 5/5] Added more tests. --- .../AbstractViewLayoutContribution.java | 33 ++++++- .../swing/view/layout/DefaultViewLayout.java | 21 ++++ .../swing/view/layout/ViewLayout.java | 4 + .../view/layout/ViewLayoutContribution.java | 12 ++- .../AbstractViewLayoutContributionTest.java | 96 +++++++++++++++++++ .../view/layout/DefaultViewLayoutTest.java | 58 +++++++++++ 6 files changed, 215 insertions(+), 9 deletions(-) create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContributionTest.java create mode 100644 lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/DefaultViewLayoutTest.java diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java index bb9f1173..33ccedff 100644 --- a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContribution.java @@ -2,11 +2,12 @@ import com.link_intersystems.swing.view.View; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; +/** + * A {@link ViewLayoutContribution} that keeps track of all installed views and therefore + * doesn't need an implementor to handle {@link #uninstall(ViewLayout)}. + */ public abstract class AbstractViewLayoutContribution implements ViewLayoutContribution { private static class ViewInstallation { @@ -32,11 +33,28 @@ public int hashCode() { } } - private Map installedViews = new IdentityHashMap<>(); + private String viewLayoutId = ViewLayout.MAIN_ID; + + private Map installedViews = new HashMap<>(); + + @Override + public String getViewLayoutId() { + return viewLayoutId; + } @Override public final void install(ViewLayout viewLayout) { + if (!getViewLayoutId().equals(viewLayout.getId())) { + throw new IllegalArgumentException("Can not install " + this + " in " + viewLayout); + } + doInstall(new ViewLayout() { + + @Override + public String getId() { + return viewLayout.getId(); + } + @Override public void install(String viewSiteName, View view) { viewLayout.install(viewSiteName, view); @@ -62,4 +80,9 @@ public final void uninstall(ViewLayout viewLayout) { viewInstallation.viewLayout.remove(viewInstallation.viewSiteName); } } + + @Override + public String toString() { + return "AbstractViewLayoutContribution{" + "viewLayoutId='" + viewLayoutId + '\'' + '}'; + } } diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java index 4d4d24d8..aa0b2f85 100644 --- a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/DefaultViewLayout.java @@ -14,16 +14,30 @@ public class DefaultViewLayout implements ViewLayout { + private String id; private Map layout = new HashMap<>(); private Map installedViews = new HashMap<>(); private Context viewContext; private Container viewContainer; public DefaultViewLayout(Context viewContext, Container viewContainer) { + this(MAIN_ID, viewContext, viewContainer); + } + + public DefaultViewLayout(String id, Context viewContext, Container viewContainer) { + this.id = requireNonNull(id); + if (id.isBlank()) { + throw new IllegalArgumentException("id must not be blank"); + } this.viewContext = requireNonNull(viewContext); this.viewContainer = requireNonNull(viewContainer); } + @Override + public String getId() { + return id; + } + public void addViewSite(String name, Object layoutConstraints) { ViewSite layoutViewSite = new DefaultViewSite(new ContainerViewContent(viewContainer, layoutConstraints), viewContext); layout.put(requireNonNull(name), layoutViewSite); @@ -53,4 +67,11 @@ public void remove(String viewSiteName) { public void dispose() { installedViews.keySet().forEach(this::remove); } + + @Override + public String toString() { + return "DefaultViewLayout{" + + "id='" + id + '\'' + + '}'; + } } \ No newline at end of file diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java index fe2c85ea..c4e19b25 100644 --- a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayout.java @@ -4,6 +4,10 @@ public interface ViewLayout { + public static final String MAIN_ID = "main"; + + public String getId(); + void install(String viewSiteName, View view); void remove(String viewSiteName); diff --git a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java index 969bbfa8..359be4f4 100644 --- a/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java +++ b/lis-commons-swing-view/src/main/java/com/link_intersystems/swing/view/layout/ViewLayoutContribution.java @@ -1,9 +1,13 @@ package com.link_intersystems.swing.view.layout; - public interface ViewLayoutContribution { +public interface ViewLayoutContribution { - void install(ViewLayout viewLayout); + default public String getViewLayoutId() { + return ViewLayout.MAIN_ID; + } - void uninstall(ViewLayout viewLayout); - } + void install(ViewLayout viewLayout); + + void uninstall(ViewLayout viewLayout); +} diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContributionTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContributionTest.java new file mode 100644 index 00000000..77d38e6d --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/AbstractViewLayoutContributionTest.java @@ -0,0 +1,96 @@ +package com.link_intersystems.swing.view.layout; + +import com.link_intersystems.swing.view.View; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class AbstractViewLayoutContributionTest { + + private AbstractViewLayoutContribution viewLayoutContribution; + private ViewLayout viewLayout; + private View view; + + @BeforeEach + void setUp() { + viewLayoutContribution = spy(AbstractViewLayoutContribution.class); + + viewLayout = spy(ViewLayout.class); + doReturn(ViewLayout.MAIN_ID).when(viewLayout).getId(); + + view = mock(View.class); + } + + @Test + void contributeToWrongViewLayout() { + when(viewLayout.getId()).thenReturn("UNKNOWN"); + + assertThrows(IllegalArgumentException.class, () -> viewLayoutContribution.install(viewLayout)); + } + + @Test + void install() { + doAnswer((Answer) invocation -> { + ViewLayout layout = invocation.getArgument(0, ViewLayout.class); + assertEquals(ViewLayout.MAIN_ID, layout.getId()); + layout.install("A", view); + return null; + }).when(viewLayoutContribution).doInstall(Mockito.any(ViewLayout.class)); + + viewLayoutContribution.install(viewLayout); + + verify(viewLayout, times(1)).install("A", view); + } + + @Test + void installMultipleView() { + View view2 = mock(View.class); + + doAnswer((Answer) invocation -> { + ViewLayout layout = invocation.getArgument(0, ViewLayout.class); + layout.install("A", view); + layout.install("B", view2); + return null; + }).when(viewLayoutContribution).doInstall(Mockito.any(ViewLayout.class)); + + viewLayoutContribution.install(viewLayout); + + verify(viewLayout, times(1)).install("A", view); + verify(viewLayout, times(1)).install("B", view2); + } + + @Test + void removePreviousViewOnInstall() { + install(); + + doAnswer((Answer) invocation -> { + ViewLayout layout = invocation.getArgument(0, ViewLayout.class); + layout.remove("A"); + layout.install("B", view); + return null; + }).when(viewLayoutContribution).doInstall(Mockito.any(ViewLayout.class)); + + viewLayoutContribution.install(viewLayout); + + verify(viewLayout, times(1)).install("B", view); + } + + @Test + void uninstall() { + install(); + + viewLayoutContribution.uninstall(viewLayout); + + verify(viewLayout, times(1)).remove("A"); + } + + @Test + void testToString() { + + assertNotNull(viewLayoutContribution.toString()); + } +} \ No newline at end of file diff --git a/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/DefaultViewLayoutTest.java b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/DefaultViewLayoutTest.java new file mode 100644 index 00000000..beab7280 --- /dev/null +++ b/lis-commons-swing-view/src/test/java/com/link_intersystems/swing/view/layout/DefaultViewLayoutTest.java @@ -0,0 +1,58 @@ +package com.link_intersystems.swing.view.layout; + +import com.link_intersystems.swing.view.View; +import com.link_intersystems.swing.view.ViewSite; +import com.link_intersystems.util.context.DefaultContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.awt.*; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +class DefaultViewLayoutTest { + + private DefaultViewLayout defaultViewLayout; + private Container container; + private DefaultContext context; + private View view; + + @BeforeEach + void setUp() { + context = new DefaultContext(); + container = mock(Container.class); + defaultViewLayout = new DefaultViewLayout(context, container); + + defaultViewLayout.addViewSite("N", BorderLayout.NORTH); + defaultViewLayout.addViewSite("S", BorderLayout.SOUTH); + defaultViewLayout.addViewSite("C", BorderLayout.CENTER); + + view = mock(View.class); + } + + @Test + void installUnknownViewSite() { + assertThrows(IllegalArgumentException.class, () -> defaultViewLayout.install("NORTH", view)); + } + + @Test + void install() { + defaultViewLayout.install("N", view); + + verify(view, times(1)).install(Mockito.any(ViewSite.class)); + verify(container, times(1)).revalidate(); + } + + @Test + void dispose() { + install(); + reset(container); + + defaultViewLayout.dispose(); + + verify(view, times(1)).uninstall(); + verify(container, times(1)).revalidate(); + } +} \ No newline at end of file