From 4c393523feaca12f6e2dbbd2cbdf667cbdcd498e Mon Sep 17 00:00:00 2001 From: Mark Herwege Date: Tue, 2 Sep 2025 08:25:06 +0200 Subject: [PATCH 1/6] sitemap registry Signed-off-by: Mark Herwege --- bom/openhab-core/pom.xml | 6 + .../org.openhab.core.io.rest.sitemap/pom.xml | 12 +- .../sitemap/SitemapSubscriptionService.java | 68 +-- .../sitemap/internal/SitemapResource.java | 247 ++++---- .../internal/WidgetsChangeListener.java | 66 +- .../sitemap/internal/SitemapResourceTest.java | 231 +++---- .../org.openhab.core.model.sitemap/bnd.bnd | 3 + .../org.openhab.core.model.sitemap/pom.xml | 5 + .../openhab/core/model/sitemap/Sitemap.xtext | 428 +++++++------ .../core/model/sitemap/SitemapProvider.java | 54 -- .../sitemap/internal/SitemapProviderImpl.java | 385 ++++++++++-- .../sitemap/validation/SitemapValidator.xtend | 205 +++++-- bundles/org.openhab.core.sitemap/.classpath | 42 ++ bundles/org.openhab.core.sitemap/.project | 23 + bundles/org.openhab.core.sitemap/NOTICE | 14 + bundles/org.openhab.core.sitemap/pom.xml | 25 + .../java/org/openhab/core/sitemap/Button.java | 96 +++ .../core/sitemap/ButtonDefinition.java | 97 +++ .../org/openhab/core/sitemap/Buttongrid.java | 41 ++ .../java/org/openhab/core/sitemap/Chart.java | 138 +++++ .../org/openhab/core/sitemap/Colorpicker.java | 25 + .../core/sitemap/Colortemperaturepicker.java | 57 ++ .../org/openhab/core/sitemap/Condition.java | 72 +++ .../org/openhab/core/sitemap/Default.java | 39 ++ .../java/org/openhab/core/sitemap/Frame.java | 25 + .../java/org/openhab/core/sitemap/Group.java | 25 + .../java/org/openhab/core/sitemap/Image.java | 54 ++ .../java/org/openhab/core/sitemap/Input.java | 41 ++ .../openhab/core/sitemap/LinkableWidget.java | 41 ++ .../org/openhab/core/sitemap/Mapping.java | 84 +++ .../org/openhab/core/sitemap/Mapview.java | 39 ++ .../core/sitemap/NonLinkableWidget.java | 25 + .../java/org/openhab/core/sitemap/Parent.java | 26 + .../java/org/openhab/core/sitemap/Rule.java | 57 ++ .../org/openhab/core/sitemap/Selection.java | 40 ++ .../org/openhab/core/sitemap/Setpoint.java | 72 +++ .../org/openhab/core/sitemap/Sitemap.java | 92 +++ .../java/org/openhab/core/sitemap/Slider.java | 100 ++++ .../java/org/openhab/core/sitemap/Switch.java | 40 ++ .../java/org/openhab/core/sitemap/Text.java | 25 + .../java/org/openhab/core/sitemap/Video.java | 54 ++ .../org/openhab/core/sitemap/Webview.java | 54 ++ .../java/org/openhab/core/sitemap/Widget.java | 185 ++++++ .../internal/ButtonDefinitionImpl.java | 80 +++ .../core/sitemap/internal/ButtonImpl.java | 89 +++ .../core/sitemap/internal/ButtongridImpl.java | 48 ++ .../core/sitemap/internal/ChartImpl.java | 111 ++++ .../sitemap/internal/ColorpickerImpl.java | 32 + .../internal/ColortemperaturepickerImpl.java | 58 ++ .../core/sitemap/internal/ConditionImpl.java | 67 +++ .../core/sitemap/internal/DefaultImpl.java | 45 ++ .../core/sitemap/internal/FrameImpl.java | 32 + .../core/sitemap/internal/GroupImpl.java | 32 + .../core/sitemap/internal/ImageImpl.java | 56 ++ .../core/sitemap/internal/InputImpl.java | 45 ++ .../sitemap/internal/LinkableWidgetImpl.java | 48 ++ .../core/sitemap/internal/MappingImpl.java | 69 +++ .../core/sitemap/internal/MapviewImpl.java | 45 ++ .../internal/NonLinkableWidgetImpl.java | 32 + .../core/sitemap/internal/RuleImpl.java | 59 ++ .../core/sitemap/internal/SelectionImpl.java | 48 ++ .../core/sitemap/internal/SetpointImpl.java | 69 +++ .../core/sitemap/internal/SitemapImpl.java | 85 +++ .../core/sitemap/internal/SliderImpl.java | 91 +++ .../core/sitemap/internal/SwitchImpl.java | 48 ++ .../core/sitemap/internal/TextImpl.java | 32 + .../core/sitemap/internal/VideoImpl.java | 56 ++ .../core/sitemap/internal/WebviewImpl.java | 56 ++ .../core/sitemap/internal/WidgetImpl.java | 155 +++++ .../internal/registry/SitemapFactoryImpl.java | 162 +++++ .../registry/SitemapRegistryImpl.java | 78 +++ .../core/sitemap/registry/SitemapFactory.java | 95 +++ .../sitemap/registry/SitemapProvider.java | 46 ++ .../sitemap/registry/SitemapRegistry.java | 42 ++ bundles/org.openhab.core.ui/pom.xml | 9 +- .../UIComponentSitemapProvider.java | 562 +++++++----------- .../ui/internal/items/ItemUIRegistryImpl.java | 189 +++--- .../internal/proxy/ProxyServletService.java | 40 +- .../openhab/core/ui/items/ItemUIProvider.java | 2 +- .../openhab/core/ui/items/ItemUIRegistry.java | 18 +- .../items/ItemUIRegistryImplTest.java | 260 ++++---- .../proxy/ProxyServletServiceTest.java | 18 +- bundles/pom.xml | 1 + .../openhab-core/src/main/feature/feature.xml | 4 +- .../itest.bndrun | 1 + .../itest.bndrun | 1 + .../itest.bndrun | 1 + .../itest.bndrun | 1 + .../itest.bndrun | 4 +- .../org.openhab.core.thing.tests/itest.bndrun | 1 + .../org.openhab.core.voice.tests/itest.bndrun | 4 +- 91 files changed, 5237 insertions(+), 1348 deletions(-) delete mode 100644 bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapProvider.java create mode 100644 bundles/org.openhab.core.sitemap/.classpath create mode 100644 bundles/org.openhab.core.sitemap/.project create mode 100644 bundles/org.openhab.core.sitemap/NOTICE create mode 100644 bundles/org.openhab.core.sitemap/pom.xml create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Button.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/ButtonDefinition.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colorpicker.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colortemperaturepicker.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Default.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Frame.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Group.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Image.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/LinkableWidget.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapping.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapview.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/NonLinkableWidget.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Parent.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Rule.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Selection.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Setpoint.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Slider.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Switch.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Text.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Video.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Webview.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonDefinitionImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtongridImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ChartImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColorpickerImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColortemperaturepickerImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ConditionImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/DefaultImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/FrameImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/GroupImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ImageImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/InputImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/LinkableWidgetImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MappingImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MapviewImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/NonLinkableWidgetImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/RuleImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SelectionImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SetpointImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SitemapImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SliderImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SwitchImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/TextImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/VideoImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WebviewImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapFactory.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapProvider.java create mode 100644 bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapRegistry.java diff --git a/bom/openhab-core/pom.xml b/bom/openhab-core/pom.xml index 2beba0b65d6..c426c70ccdb 100644 --- a/bom/openhab-core/pom.xml +++ b/bom/openhab-core/pom.xml @@ -88,6 +88,12 @@ ${project.version} compile + + org.openhab.core.bundles + org.openhab.core.sitemap + ${project.version} + compile + org.openhab.core.bundles org.openhab.core.transform diff --git a/bundles/org.openhab.core.io.rest.sitemap/pom.xml b/bundles/org.openhab.core.io.rest.sitemap/pom.xml index 4ea6496f1f1..17fb86a1186 100644 --- a/bundles/org.openhab.core.io.rest.sitemap/pom.xml +++ b/bundles/org.openhab.core.io.rest.sitemap/pom.xml @@ -25,11 +25,6 @@ org.openhab.core.io.rest ${project.version} - - org.openhab.core.bundles - org.openhab.core.model.sitemap - ${project.version} - org.openhab.core.bundles org.openhab.core.io.rest.core @@ -37,7 +32,7 @@ org.openhab.core.bundles - org.openhab.core.model.core + org.openhab.core.sitemap ${project.version} @@ -56,11 +51,6 @@ org.openhab.core.thing ${project.version} - - org.openhab.core.bom - org.openhab.core.bom.compile-model - pom - diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/SitemapSubscriptionService.java b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/SitemapSubscriptionService.java index 5e047750485..abbd7067e55 100644 --- a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/SitemapSubscriptionService.java +++ b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/SitemapSubscriptionService.java @@ -24,10 +24,9 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import org.eclipse.emf.common.util.BasicEList; -import org.eclipse.emf.common.util.EList; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.RegistryChangeListener; import org.openhab.core.events.Event; import org.openhab.core.events.EventSubscriber; import org.openhab.core.i18n.TimeZoneProvider; @@ -36,12 +35,10 @@ import org.openhab.core.items.GroupItem; import org.openhab.core.items.Item; import org.openhab.core.items.events.ItemStatePredictedEvent; -import org.openhab.core.model.core.EventType; -import org.openhab.core.model.core.ModelRepositoryChangeListener; -import org.openhab.core.model.sitemap.SitemapProvider; -import org.openhab.core.model.sitemap.sitemap.LinkableWidget; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.LinkableWidget; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.registry.SitemapRegistry; import org.openhab.core.thing.events.ChannelDescriptionChangedEvent; import org.openhab.core.ui.items.ItemUIRegistry; import org.osgi.framework.BundleContext; @@ -51,8 +48,6 @@ import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,11 +61,12 @@ * Subscribing to whole sitemaps is discouraged, since a large number of item updates may result in a high SSE traffic. * * @author Kai Kreuzer - Initial contribution + * @author Mark Herwege - Implement sitemap registry */ @Component(service = { SitemapSubscriptionService.class, EventSubscriber.class }, configurationPid = "org.openhab.sitemapsubscription") @NonNullByDefault -public class SitemapSubscriptionService implements ModelRepositoryChangeListener, EventSubscriber { +public class SitemapSubscriptionService implements RegistryChangeListener, EventSubscriber { private static final String SITEMAP_PAGE_SEPARATOR = "#"; private static final String SITEMAP_SUFFIX = ".sitemap"; @@ -88,10 +84,9 @@ public interface SitemapSubscriptionCallback { } private final ItemUIRegistry itemUIRegistry; + private final SitemapRegistry sitemapRegistry; private final TimeZoneProvider timeZoneProvider; - private final List sitemapProviders = new ArrayList<>(); - /* subscription id -> sitemap+page */ private final Map scopeOfSubscription = new ConcurrentHashMap<>(); @@ -109,15 +104,19 @@ public interface SitemapSubscriptionCallback { @Activate public SitemapSubscriptionService(Map config, final @Reference ItemUIRegistry itemUIRegistry, - final @Reference TimeZoneProvider timeZoneProvider, BundleContext bundleContext) { + final @Reference SitemapRegistry sitemapRegistry, final @Reference TimeZoneProvider timeZoneProvider, + BundleContext bundleContext) { this.itemUIRegistry = itemUIRegistry; + this.sitemapRegistry = sitemapRegistry; this.timeZoneProvider = timeZoneProvider; this.bundleContext = bundleContext; applyConfig(config); + sitemapRegistry.addRegistryChangeListener(this); } @Deactivate protected void deactivate() { + sitemapRegistry.removeRegistryChangeListener(this); scopeOfSubscription.clear(); callbacks.clear(); creationInstants.clear(); @@ -144,17 +143,6 @@ private void applyConfig(Map config) { } } - @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) - public void addSitemapProvider(SitemapProvider provider) { - sitemapProviders.add(provider); - provider.addModelChangeListener(this); - } - - public void removeSitemapProvider(SitemapProvider provider) { - sitemapProviders.remove(provider); - provider.removeModelChangeListener(this); - } - /** * Creates a new subscription with the given id. * @@ -275,8 +263,8 @@ private void addCallbackToListener(String sitemapName, @Nullable String pageId, listener.widgetsChangeListener().addCallback(callback); } - public EList collectWidgets(String sitemapName, @Nullable String pageId) { - EList widgets = new BasicEList<>(); + public List collectWidgets(String sitemapName, @Nullable String pageId) { + List widgets = new ArrayList<>(); Sitemap sitemap = getSitemap(sitemapName); if (sitemap == null) { @@ -329,28 +317,28 @@ private String getScopeIdentifier(String sitemapName, @Nullable String pageId) { } private @Nullable Sitemap getSitemap(String sitemapName) { - for (SitemapProvider provider : sitemapProviders) { - Sitemap sitemap = provider.getSitemap(sitemapName); - if (sitemap != null) { - return sitemap; - } - } - return null; + return sitemapRegistry.get(sitemapName); } @Override - public void modelChanged(String modelName, EventType type) { - if (type != EventType.MODIFIED || !modelName.endsWith(SITEMAP_SUFFIX)) { - return; // we process only sitemap modifications here - } + public void added(Sitemap element) { + // Nothing to do + } + + @Override + public void removed(Sitemap element) { + // Nothing to do + } - String changedSitemapName = modelName.substring(0, modelName.length() - SITEMAP_SUFFIX.length()); + @Override + public void updated(Sitemap oldElement, Sitemap element) { + String changedSitemapName = oldElement.getName(); for (Entry listenerEntry : pageChangeListeners.entrySet()) { String sitemapWithPage = listenerEntry.getKey(); String sitemapName = extractSitemapName(sitemapWithPage); - EList widgets; + List widgets; if (sitemapName.equals(changedSitemapName)) { if (isPageListener(sitemapWithPage)) { String pageId = extractPageId(sitemapWithPage); diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java index d9ad389c8d2..4a3926c5e06 100644 --- a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java +++ b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java @@ -12,8 +12,9 @@ */ package org.openhab.core.io.rest.sitemap.internal; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.net.URI; -import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; @@ -23,6 +24,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -53,9 +55,6 @@ import javax.ws.rs.sse.Sse; import javax.ws.rs.sse.SseEventSink; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.auth.Role; @@ -79,30 +78,29 @@ import org.openhab.core.items.events.ItemStateChangedEvent; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.HSBType; -import org.openhab.core.model.sitemap.SitemapProvider; -import org.openhab.core.model.sitemap.sitemap.Button; -import org.openhab.core.model.sitemap.sitemap.ButtonDefinition; -import org.openhab.core.model.sitemap.sitemap.Buttongrid; -import org.openhab.core.model.sitemap.sitemap.Chart; -import org.openhab.core.model.sitemap.sitemap.ColorArray; -import org.openhab.core.model.sitemap.sitemap.Colortemperaturepicker; -import org.openhab.core.model.sitemap.sitemap.Condition; -import org.openhab.core.model.sitemap.sitemap.Frame; -import org.openhab.core.model.sitemap.sitemap.IconRule; -import org.openhab.core.model.sitemap.sitemap.Image; -import org.openhab.core.model.sitemap.sitemap.Input; -import org.openhab.core.model.sitemap.sitemap.LinkableWidget; -import org.openhab.core.model.sitemap.sitemap.Mapping; -import org.openhab.core.model.sitemap.sitemap.Mapview; -import org.openhab.core.model.sitemap.sitemap.Selection; -import org.openhab.core.model.sitemap.sitemap.Setpoint; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.Slider; -import org.openhab.core.model.sitemap.sitemap.Switch; -import org.openhab.core.model.sitemap.sitemap.Video; -import org.openhab.core.model.sitemap.sitemap.VisibilityRule; -import org.openhab.core.model.sitemap.sitemap.Webview; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.Button; +import org.openhab.core.sitemap.ButtonDefinition; +import org.openhab.core.sitemap.Buttongrid; +import org.openhab.core.sitemap.Chart; +import org.openhab.core.sitemap.Colortemperaturepicker; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Frame; +import org.openhab.core.sitemap.Image; +import org.openhab.core.sitemap.Input; +import org.openhab.core.sitemap.LinkableWidget; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Mapview; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Selection; +import org.openhab.core.sitemap.Setpoint; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Slider; +import org.openhab.core.sitemap.Switch; +import org.openhab.core.sitemap.Video; +import org.openhab.core.sitemap.Webview; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.registry.SitemapRegistry; import org.openhab.core.types.State; import org.openhab.core.ui.items.ItemUIRegistry; import org.openhab.core.ui.items.ItemUIRegistry.WidgetLabelSource; @@ -111,8 +109,6 @@ import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants; import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired; import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect; @@ -121,8 +117,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.MapMaker; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -150,6 +144,7 @@ * @author Laurent Garnier - Added releaseCmd field for mappings used for switch element * @author Laurent Garnier - Added support for Buttongrid as container for Button elements * @author Laurent Garnier - Added support for new sitemap element Colortemperaturepicker + * @author Mark Herwege - Implement sitemap registry, remove Guava dependency */ @Component(service = { RESTResource.class, EventSubscriber.class }) @JaxrsResource @@ -171,7 +166,7 @@ public class SitemapResource private static final long TIMEOUT_IN_MS = 30000; - private SseBroadcaster<@NonNull SseSinkInfo> broadcaster; + private SseBroadcaster broadcaster; @Context @NonNullByDefault({}) @@ -190,13 +185,12 @@ public class SitemapResource Sse sse; private final ItemUIRegistry itemUIRegistry; + private final SitemapRegistry sitemapRegistry; private final SitemapSubscriptionService subscriptions; private final LocaleService localeService; private final TimeZoneProvider timeZoneProvider; - private final java.util.List sitemapProviders = new ArrayList<>(); - - private final Map knownSubscriptions = new MapMaker().weakValues().makeMap(); + private final WeakValueConcurrentHashMap knownSubscriptions = new WeakValueConcurrentHashMap<>(); private final ScheduledExecutorService scheduler = ThreadPoolManager .getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON); @@ -207,10 +201,12 @@ public class SitemapResource @Activate public SitemapResource( // final @Reference ItemUIRegistry itemUIRegistry, // + final @Reference SitemapRegistry sitemapRegistry, // final @Reference LocaleService localeService, // final @Reference TimeZoneProvider timeZoneProvider, // final @Reference SitemapSubscriptionService subscriptions) { this.itemUIRegistry = itemUIRegistry; + this.sitemapRegistry = sitemapRegistry; this.localeService = localeService; this.timeZoneProvider = timeZoneProvider; this.subscriptions = subscriptions; @@ -242,15 +238,6 @@ protected void deactivate() { broadcaster.close(); } - @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) - public void addSitemapProvider(SitemapProvider provider) { - sitemapProviders.add(provider); - } - - public void removeSitemapProvider(SitemapProvider provider) { - sitemapProviders.remove(provider); - } - @GET @Produces(MediaType.APPLICATION_JSON) @Operation(operationId = "getSitemaps", summary = "Get all available sitemaps.", responses = { @@ -429,6 +416,7 @@ public void getSitemapEvents(@Context final SseEventSink sseEventSink, @Context private void getSitemapEvents(SseEventSink sseEventSink, HttpServletResponse response, String subscriptionId, @Nullable String sitemapname, @Nullable String pageId, boolean subscribeToWholeSitemap) { + // Clean up stale subscriptions final SseSinkInfo sinkInfo = knownSubscriptions.get(subscriptionId); if (sinkInfo == null) { logger.debug("Subscription id {} does not exist.", subscriptionId); @@ -460,19 +448,19 @@ private PageDTO getPageBean(String sitemapName, String pageId, URI uri, Locale l Sitemap sitemap = getSitemap(sitemapName); if (sitemap != null) { if (pageId.equals(sitemap.getName())) { - EList children = itemUIRegistry.getChildren(sitemap); + List children = itemUIRegistry.getChildren(sitemap); return createPageBean(sitemapName, sitemap.getLabel(), sitemap.getIcon(), sitemap.getName(), children, false, isLeaf(children), uri, locale, timeout, includeHidden); } else { Widget pageWidget = itemUIRegistry.getWidget(sitemap, pageId); if (pageWidget instanceof LinkableWidget widget) { - EList children = itemUIRegistry.getChildren(widget); + List children = itemUIRegistry.getChildren(widget); PageDTO pageBean = createPageBean(sitemapName, itemUIRegistry.getLabel(pageWidget), itemUIRegistry.getCategory(pageWidget), pageId, children, false, isLeaf(children), uri, locale, timeout, includeHidden); - EObject parentPage = pageWidget.eContainer(); - while (parentPage instanceof Frame) { - parentPage = parentPage.eContainer(); + Parent parentPage = widget.getParent(); + while (parentPage instanceof Frame frameParent) { + parentPage = frameParent.getParent(); } if (parentPage instanceof Widget parentPageWidget) { String parentId = itemUIRegistry.getWidgetId(parentPageWidget); @@ -507,28 +495,16 @@ private PageDTO getPageBean(String sitemapName, String pageId, URI uri, Locale l public Collection getSitemapBeans(URI uri) { Collection beans = new LinkedList<>(); - Set names = new HashSet<>(); logger.debug("Received HTTP GET request at '{}'.", UriBuilder.fromUri(uri).build().toASCIIString()); - for (SitemapProvider provider : sitemapProviders) { - for (String modelName : provider.getSitemapNames()) { - Sitemap sitemap = provider.getSitemap(modelName); - if (sitemap != null) { - if (!names.contains(modelName)) { - names.add(modelName); - SitemapDTO bean = new SitemapDTO(); - bean.name = modelName; - bean.icon = sitemap.getIcon(); - bean.label = sitemap.getLabel(); - bean.link = UriBuilder.fromUri(uri).path(bean.name).build().toASCIIString(); - bean.homepage = new PageDTO(); - bean.homepage.link = bean.link + "/" + sitemap.getName(); - beans.add(bean); - } else { - logger.warn("Found duplicate sitemap name '{}' - ignoring it. Please check your configuration.", - modelName); - } - } - } + for (Sitemap sitemap : sitemapRegistry.getAll()) { + SitemapDTO bean = new SitemapDTO(); + bean.name = sitemap.getName(); + bean.icon = sitemap.getIcon(); + bean.label = sitemap.getLabel(); + bean.link = UriBuilder.fromUri(uri).path(bean.name).build().toASCIIString(); + bean.homepage = new PageDTO(); + bean.homepage.link = bean.link + "/" + sitemap.getName(); + beans.add(bean); } return beans; } @@ -560,8 +536,8 @@ private SitemapDTO createSitemapBean(String sitemapName, Sitemap sitemap, URI ur } private PageDTO createPageBean(String sitemapName, @Nullable String title, @Nullable String icon, String pageId, - @Nullable EList children, boolean drillDown, boolean isLeaf, URI uri, Locale locale, - boolean timeout, boolean includeHiddenWidgets) { + @Nullable List children, boolean drillDown, boolean isLeaf, URI uri, Locale locale, boolean timeout, + boolean includeHiddenWidgets) { PageDTO bean = new PageDTO(); bean.timeout = timeout; bean.id = pageId; @@ -593,17 +569,18 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null WidgetDTO bean = new WidgetDTO(); State itemState = null; - if (widget.getItem() != null) { + String itemName = widget.getItem(); + if (itemName != null) { try { - Item item = itemUIRegistry.getItem(widget.getItem()); + Item item = itemUIRegistry.getItem(itemName); itemState = item.getState(); - String widgetTypeName = widget.eClass().getInstanceTypeName() - .substring(widget.eClass().getInstanceTypeName().lastIndexOf(".") + 1); + String widgetTypeName = widget.getWidgetType(); boolean isMapview = "mapview".equalsIgnoreCase(widgetTypeName); Predicate itemFilter = (i -> CoreItemFactory.LOCATION.equals(i.getType())); bean.item = EnrichedItemDTOMapper.map(item, isMapview, itemFilter, UriBuilder.fromUri(uri).path("items/{itemName}"), locale, timeZoneProvider.getTimeZone()); - bean.state = itemUIRegistry.getState(widget).toFullString(); + State widgetState = itemUIRegistry.getState(widget); + bean.state = widgetState != null ? widgetState.toFullString() : null; // In case the widget state is identical to the item state, its value is set to null. if (bean.state != null && bean.state.equals(bean.item.state)) { bean.state = null; @@ -614,7 +591,7 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null } bean.widgetId = widgetId; bean.icon = itemUIRegistry.getCategory(widget); - bean.staticIcon = widget.getStaticIcon() != null || !widget.getIconRules().isEmpty(); + bean.staticIcon = widget.isStaticIcon() || !widget.getIconRules().isEmpty(); bean.labelcolor = convertItemValueColor(itemUIRegistry.getLabelColor(widget), itemState); bean.valuecolor = convertItemValueColor(itemUIRegistry.getValueColor(widget), itemState); bean.iconcolor = convertItemValueColor(itemUIRegistry.getIconColor(widget), itemState); @@ -622,10 +599,10 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null bean.labelSource = itemUIRegistry.getLabelSource(widget).toString(); bean.pattern = itemUIRegistry.getFormatPattern(widget); bean.unit = itemUIRegistry.getUnitForWidget(widget); - bean.type = widget.eClass().getName(); + bean.type = widget.getWidgetType(); bean.visibility = itemUIRegistry.getVisiblity(widget); if (widget instanceof LinkableWidget linkableWidget) { - EList children = itemUIRegistry.getChildren(linkableWidget); + List children = itemUIRegistry.getChildren(linkableWidget); if (widget instanceof Frame || widget instanceof Buttongrid) { for (Widget child : children) { String wID = itemUIRegistry.getWidgetId(child); @@ -680,7 +657,8 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null if (videoWidget.getEncoding() != null) { bean.encoding = videoWidget.getEncoding(); } - if (videoWidget.getEncoding() != null && videoWidget.getEncoding().toLowerCase().contains("hls")) { + String encoding = videoWidget.getEncoding(); + if (encoding != null && encoding.toLowerCase().contains("hls")) { bean.url = videoWidget.getUrl(); } else { bean.url = buildProxyUrl(sitemapName, videoWidget, uri); @@ -696,8 +674,8 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null if (widget instanceof Chart chartWidget) { bean.service = chartWidget.getService(); bean.period = chartWidget.getPeriod(); - bean.legend = chartWidget.getLegend(); - bean.forceAsItem = chartWidget.getForceAsItem(); + bean.legend = chartWidget.hasLegend(); + bean.forceAsItem = chartWidget.forceAsItem(); bean.yAxisDecimalPattern = chartWidget.getYAxisDecimalPattern(); bean.interpolation = chartWidget.getInterpolation(); if (chartWidget.getRefresh() > 0) { @@ -726,7 +704,7 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null } if (widget instanceof Button buttonWidget) { // Get the icon from the widget only - if (widget.getIcon() == null && widget.getStaticIcon() == null && widget.getIconRules().isEmpty()) { + if (widget.getIcon() == null && widget.getIconRules().isEmpty()) { bean.icon = null; bean.staticIcon = null; } @@ -765,14 +743,14 @@ private String buildProxyUrl(String sitemapName, Widget widget, URI uri) { return sb.toString(); } - private boolean isLeaf(EList children) { + private boolean isLeaf(List children) { for (Widget w : children) { if (w instanceof Frame frame) { - if (isLeaf(frame.getChildren())) { + if (isLeaf(frame.getWidgets())) { return false; } } else if (w instanceof Buttongrid grid) { - if (isLeaf(grid.getChildren())) { + if (isLeaf(grid.getWidgets())) { return false; } } else if (w instanceof LinkableWidget linkableWidget) { @@ -785,18 +763,11 @@ private boolean isLeaf(EList children) { } private @Nullable Sitemap getSitemap(String sitemapname) { - for (SitemapProvider provider : sitemapProviders) { - Sitemap sitemap = provider.getSitemap(sitemapname); - if (sitemap != null) { - return sitemap; - } - } - - return null; + return sitemapRegistry.get(sitemapname); } private boolean blockUntilChangeOccurs(String sitemapname, @Nullable String pageId) { - EList widgets = subscriptions.collectWidgets(sitemapname, pageId); + List widgets = subscriptions.collectWidgets(sitemapname, pageId); if (widgets.isEmpty()) { return false; } @@ -862,46 +833,30 @@ private Set getAllItems(List widgets) { } // Consider all items inside the frame if (widget instanceof Frame frame) { - items.addAll(getAllItems(frame.getChildren())); + items.addAll(getAllItems(frame.getWidgets())); } else if (widget instanceof Buttongrid grid) { - items.addAll(getAllItems(grid.getChildren())); + items.addAll(getAllItems(grid.getWidgets())); } // Consider items involved in any icon condition - items.addAll(getItemsInIconCond(widget.getIconRules())); + items.addAll(getItemsInRuleConditions(widget.getIconRules())); // Consider items involved in any visibility, labelcolor, valuecolor and iconcolor condition - items.addAll(getItemsInVisibilityCond(widget.getVisibility())); - items.addAll(getItemsInColorCond(widget.getLabelColor())); - items.addAll(getItemsInColorCond(widget.getValueColor())); - items.addAll(getItemsInColorCond(widget.getIconColor())); - } - return items; - } - - private Set getItemsInVisibilityCond(EList ruleList) { - Set items = new HashSet<>(); - for (VisibilityRule rule : ruleList) { - getItemsInConditions(rule.getConditions(), items); - } - return items; - } - - private Set getItemsInColorCond(EList colorList) { - Set items = new HashSet<>(); - for (ColorArray rule : colorList) { - getItemsInConditions(rule.getConditions(), items); + items.addAll(getItemsInRuleConditions(widget.getVisibility())); + items.addAll(getItemsInRuleConditions(widget.getLabelColor())); + items.addAll(getItemsInRuleConditions(widget.getValueColor())); + items.addAll(getItemsInRuleConditions(widget.getIconColor())); } return items; } - private Set getItemsInIconCond(EList ruleList) { + private Set getItemsInRuleConditions(List ruleList) { Set items = new HashSet<>(); - for (IconRule rule : ruleList) { + for (Rule rule : ruleList) { getItemsInConditions(rule.getConditions(), items); } return items; } - private void getItemsInConditions(@Nullable EList conditions, Set items) { + private void getItemsInConditions(@Nullable List conditions, Set items) { if (conditions != null) { for (Condition condition : conditions) { String itemName = condition.getItem(); @@ -975,6 +930,52 @@ public void sseEventSinkRemoved(SseEventSink sink, SseSinkInfo info) { knownSubscriptions.remove(info.subscriptionId); } + /** + * This is a replacement implementation for Google Guava new MapMaker().weakValues().makeMap(), to + * avoid pulling in a Guava dependency in this class. + * + * @param key + * @param value + */ + private class WeakValueConcurrentHashMap { + // Map from key → WeakReference to value + private final Map> backingMap = new ConcurrentHashMap<>(); + private final ReferenceQueue refQueue = new ReferenceQueue<>(); + + // Custom WeakReference that remembers its key + private static class WeakValueRef extends WeakReference { + final K key; + + WeakValueRef(K key, V value, ReferenceQueue queue) { + super(value, queue); + this.key = key; + } + } + + public void put(K key, V value) { + processQueue(); + backingMap.put(key, new WeakValueRef<>(key, value, refQueue)); + } + + public @Nullable V get(K key) { + processQueue(); + WeakValueRef ref = backingMap.get(key); + return ref != null ? ref.get() : null; + } + + public void remove(K key) { + processQueue(); + backingMap.remove(key); + } + + private void processQueue() { + WeakValueRef ref; + while ((ref = (WeakValueRef) refQueue.poll()) != null) { + backingMap.remove(ref.key, ref); // remove only if still mapped + } + } + } + private static class BlockingStateChangeListener { private final Set items; private boolean changed = false; diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java index 6bbb3173ffd..82b60b91936 100644 --- a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java +++ b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java @@ -22,7 +22,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import org.eclipse.emf.common.util.EList; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.common.ThreadPoolManager; import org.openhab.core.events.Event; @@ -36,15 +36,13 @@ import org.openhab.core.items.events.ItemEvent; import org.openhab.core.items.events.ItemStateChangedEvent; import org.openhab.core.library.CoreItemFactory; -import org.openhab.core.model.sitemap.sitemap.Button; -import org.openhab.core.model.sitemap.sitemap.Buttongrid; -import org.openhab.core.model.sitemap.sitemap.Chart; -import org.openhab.core.model.sitemap.sitemap.ColorArray; -import org.openhab.core.model.sitemap.sitemap.Condition; -import org.openhab.core.model.sitemap.sitemap.Frame; -import org.openhab.core.model.sitemap.sitemap.IconRule; -import org.openhab.core.model.sitemap.sitemap.VisibilityRule; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.Button; +import org.openhab.core.sitemap.Buttongrid; +import org.openhab.core.sitemap.Chart; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Frame; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Widget; import org.openhab.core.types.State; import org.openhab.core.ui.items.ItemUIRegistry; import org.openhab.core.ui.items.ItemUIRegistry.WidgetLabelSource; @@ -57,6 +55,7 @@ * @author Laurent Garnier - Support added for multiple AND conditions in labelcolor/valuecolor/visibility * @author Laurent Garnier - New widget icon parameter based on conditional rules * @author Laurent Garnier - Buttongrid as container for Button elements + * @author Mark Herwege - Implement sitemap registry */ public class WidgetsChangeListener implements EventSubscriber { @@ -67,7 +66,7 @@ public class WidgetsChangeListener implements EventSubscriber { private final String pageId; private final ItemUIRegistry itemUIRegistry; private final TimeZoneProvider timeZoneProvider; - private EList widgets; + private List widgets; private Set items; private final HashSet filterItems = new HashSet<>(); private final List callbacks = Collections.synchronizedList(new ArrayList<>()); @@ -82,7 +81,7 @@ public class WidgetsChangeListener implements EventSubscriber { * @param widgets the list of widgets that are part of the page. */ public WidgetsChangeListener(String sitemapName, String pageId, final ItemUIRegistry itemUIRegistry, - final TimeZoneProvider timeZoneProvider, EList widgets) { + final TimeZoneProvider timeZoneProvider, List widgets) { this.sitemapName = sitemapName; this.pageId = pageId; this.itemUIRegistry = itemUIRegistry; @@ -91,7 +90,7 @@ public WidgetsChangeListener(String sitemapName, String pageId, final ItemUIRegi updateItemsAndWidgets(widgets); } - private void updateItemsAndWidgets(EList widgets) { + private void updateItemsAndWidgets(List widgets) { this.widgets = widgets; items = getAllItems(widgets); filterItems.clear(); @@ -124,34 +123,34 @@ public void removeCallback(SitemapSubscriptionCallback callback) { * the widget list to get the items for added to all bundles containing REST resources * @return all items that are represented by the list of widgets */ - private Set getAllItems(EList widgets) { + private Set getAllItems(List widgets) { Set items = new HashSet<>(); if (itemUIRegistry != null) { for (Widget widget : widgets) { addItemWithName(items, widget.getItem()); if (widget instanceof Frame frame) { - items.addAll(getAllItems(frame.getChildren())); + items.addAll(getAllItems(frame.getWidgets())); } else if (widget instanceof Buttongrid grid) { - items.addAll(getAllItems(grid.getChildren())); + items.addAll(getAllItems(grid.getWidgets())); } // now scan icon rules - for (IconRule rule : widget.getIconRules()) { + for (Rule rule : widget.getIconRules()) { addItemsFromConditions(items, rule.getConditions()); } // now scan visibility rules - for (VisibilityRule rule : widget.getVisibility()) { + for (Rule rule : widget.getVisibility()) { addItemsFromConditions(items, rule.getConditions()); } // now scan label color rules - for (ColorArray rule : widget.getLabelColor()) { + for (Rule rule : widget.getLabelColor()) { addItemsFromConditions(items, rule.getConditions()); } // now scan value color rules - for (ColorArray rule : widget.getValueColor()) { + for (Rule rule : widget.getValueColor()) { addItemsFromConditions(items, rule.getConditions()); } // now scan icon color rules - for (ColorArray rule : widget.getIconColor()) { + for (Rule rule : widget.getIconColor()) { addItemsFromConditions(items, rule.getConditions()); } } @@ -159,7 +158,7 @@ private Set getAllItems(EList widgets) { return items; } - private void addItemsFromConditions(Set items, @Nullable EList conditions) { + private void addItemsFromConditions(Set items, @Nullable List conditions) { if (conditions != null) { for (Condition condition : conditions) { addItemWithName(items, condition.getItem()); @@ -206,11 +205,12 @@ private Set constructSitemapEvents(Item item, State state, List 0; + Integer refresh = chartWidget.getRefresh(); + skipWidget = refresh != null && refresh > 0; } if (!skipWidget || definesVisibilityOrColorOrIcon(w, item.getName())) { SitemapWidgetEvent event = constructSitemapEventForWidget(item, state, w); @@ -228,10 +228,10 @@ private SitemapWidgetEvent constructSitemapEventForWidget(Item item, State state event.labelSource = itemUIRegistry.getLabelSource(widget).toString(); event.widgetId = itemUIRegistry.getWidgetId(widget); event.icon = itemUIRegistry.getCategory(widget); - event.reloadIcon = widget.getStaticIcon() == null; + event.reloadIcon = !widget.isStaticIcon(); if (widget instanceof Button buttonWidget) { // Get the icon from the widget only - if (widget.getIcon() == null && widget.getStaticIcon() == null && widget.getIconRules().isEmpty()) { + if (widget.getIcon() == null && widget.getIconRules().isEmpty()) { event.icon = null; event.reloadIcon = false; } @@ -243,12 +243,11 @@ private SitemapWidgetEvent constructSitemapEventForWidget(Item item, State state event.descriptionChanged = false; // event.item contains the (potentially changed) data of the item belonging to // the widget including its state (in event.item.state) - boolean itemBelongsToWidget = widget.getItem() != null && widget.getItem().equals(item.getName()); + boolean itemBelongsToWidget = widget.getItem() != null && item.getName().equals(widget.getItem()); final Item itemToBeSent = itemBelongsToWidget ? item : getItemForWidget(widget); State stateToBeSent = null; if (itemToBeSent != null) { - String widgetTypeName = widget.eClass().getInstanceTypeName() - .substring(widget.eClass().getInstanceTypeName().lastIndexOf(".") + 1); + String widgetTypeName = widget.getClass().getSimpleName(); boolean drillDown = "mapview".equalsIgnoreCase(widgetTypeName); Predicate itemFilter = (i -> CoreItemFactory.LOCATION.equals(i.getType())); event.item = EnrichedItemDTOMapper.map(itemToBeSent, drillDown, itemFilter, null, null, @@ -256,7 +255,8 @@ private SitemapWidgetEvent constructSitemapEventForWidget(Item item, State state // event.state is an adjustment of the item state to the widget type. stateToBeSent = itemBelongsToWidget ? state : itemToBeSent.getState(); - event.state = itemUIRegistry.convertState(widget, itemToBeSent, stateToBeSent).toFullString(); + State convertedState = itemUIRegistry.convertState(widget, itemToBeSent, stateToBeSent); + event.state = convertedState == null ? null : convertedState.toFullString(); // In case this state is identical to the item state, its value is set to null. if (event.state != null && event.state.equals(event.item.state)) { event.state = null; @@ -288,12 +288,12 @@ private boolean definesVisibilityOrColorOrIcon(Widget w, String name) { || w.getIconRules().stream().anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name)); } - private boolean conditionsDependsOnItem(@Nullable EList conditions, String name) { + private boolean conditionsDependsOnItem(@Nullable List conditions, String name) { return conditions != null && conditions.stream().anyMatch(c -> name.equals(c.getItem())); } - public void sitemapContentChanged(EList widgets) { - updateItemsAndWidgets(widgets); + public void sitemapContentChanged(List<@NonNull Widget> widgets2) { + updateItemsAndWidgets(widgets2); SitemapChangedEvent changeEvent = new SitemapChangedEvent(); changeEvent.pageId = pageId; diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java b/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java index 9c7413f1287..d6edd82e98c 100644 --- a/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java +++ b/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java @@ -15,10 +15,9 @@ import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; -import static org.hamcrest.collection.IsEmptyCollection.empty; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -31,9 +30,6 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; -import org.eclipse.emf.common.util.BasicEList; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.ecore.EClass; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -51,14 +47,12 @@ import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.PercentType; -import org.openhab.core.model.sitemap.SitemapProvider; -import org.openhab.core.model.sitemap.sitemap.ColorArray; -import org.openhab.core.model.sitemap.sitemap.Condition; -import org.openhab.core.model.sitemap.sitemap.Group; -import org.openhab.core.model.sitemap.sitemap.IconRule; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.VisibilityRule; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Group; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.registry.SitemapRegistry; import org.openhab.core.test.java.JavaTest; import org.openhab.core.types.Command; import org.openhab.core.types.State; @@ -71,6 +65,7 @@ * * @author Henning Treu - Initial contribution * @author Laurent Garnier - Extended tests for static icon and icon based on conditional rules + * @author Mark Herwege - Implement sitemap registry */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -81,10 +76,8 @@ public class SitemapResourceTest extends JavaTest { private static final String HTTP_HEADER_X_ATMOSPHERE_TRANSPORT = "X-Atmosphere-Transport"; private static final String ITEM_NAME = "itemName"; - private static final String SUBPAGE_ITEM_NAME = "subpabeItemName"; private static final String ITEM_LABEL = "item label"; private static final String SITEMAP_PATH = "/sitemaps"; - private static final String SITEMAP_MODEL_NAME = "sitemapModel"; private static final String SITEMAP_NAME = "defaultSitemap"; private static final String SITEMAP_TITLE = "Default Sitemap"; private static final String VISIBILITY_RULE_ITEM_NAME = "visibilityRuleItem"; @@ -123,20 +116,19 @@ public class SitemapResourceTest extends JavaTest { private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock; private @Mock @NonNullByDefault({}) LocaleService localeServiceMock; private @Mock @NonNullByDefault({}) HttpServletRequest requestMock; - private @Mock @NonNullByDefault({}) SitemapProvider sitemapProviderMock; + private @Mock @NonNullByDefault({}) SitemapRegistry sitemapRegistryMock; private @Mock @NonNullByDefault({}) UriInfo uriInfoMock; private @Mock @NonNullByDefault({}) BundleContext bundleContextMock; - private EList widgets = new BasicEList<>(); + private List widgets = new ArrayList<>(); @BeforeEach public void setup() throws Exception { - subscriptions = new SitemapSubscriptionService(Collections.emptyMap(), itemUIRegistryMock, timeZoneProviderMock, - bundleContextMock); - subscriptions.addSitemapProvider(sitemapProviderMock); + subscriptions = new SitemapSubscriptionService(Collections.emptyMap(), itemUIRegistryMock, sitemapRegistryMock, + timeZoneProviderMock, bundleContextMock); - sitemapResource = new SitemapResource(itemUIRegistryMock, localeServiceMock, timeZoneProviderMock, - subscriptions); + sitemapResource = new SitemapResource(itemUIRegistryMock, sitemapRegistryMock, localeServiceMock, + timeZoneProviderMock, subscriptions); when(uriInfoMock.getAbsolutePathBuilder()).thenReturn(UriBuilder.fromPath(SITEMAP_PATH)); when(uriInfoMock.getBaseUriBuilder()).thenReturn(UriBuilder.fromPath(SITEMAP_PATH)); @@ -154,9 +146,8 @@ public void setup() throws Exception { when(localeServiceMock.getLocale(null)).thenReturn(Locale.US); - configureSitemapProviderMock(); + configureSitemapRegistryMock(); configureSitemapMock(); - sitemapResource.addSitemapProvider(sitemapProviderMock); widgets = initSitemapWidgetsWithSubpages(); configureItemUIRegistry(PercentType.HUNDRED, OnOffType.ON); @@ -165,15 +156,6 @@ public void setup() throws Exception { when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(null); } - @Test - public void whenNoSitemapProvidersAreSetShouldReturnEmptyList() { - sitemapResource.removeSitemapProvider(sitemapProviderMock); - Response sitemaps = sitemapResource.getSitemaps(); - - assertThat(sitemaps.getEntity(), instanceOf(Collection.class)); - assertThat((Collection) sitemaps.getEntity(), is(empty())); - } - @Test public void whenSitemapsAreProvidedShouldReturnSitemapBeans() { Response sitemaps = sitemapResource.getSitemaps(); @@ -182,8 +164,8 @@ public void whenSitemapsAreProvidedShouldReturnSitemapBeans() { @SuppressWarnings("unchecked") SitemapDTO dto = ((Collection) sitemaps.getEntity()).iterator().next(); - assertThat(dto.name, is(SITEMAP_MODEL_NAME)); - assertThat(dto.link, is(SITEMAP_PATH + "/" + SITEMAP_MODEL_NAME)); + assertThat(dto.name, is(SITEMAP_NAME)); + assertThat(dto.link, is(SITEMAP_PATH + "/" + SITEMAP_NAME)); } @Test @@ -197,7 +179,7 @@ public void whenLongPollingWholeSitemapShouldObserveAllItems() throws ItemNotFou // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getSitemapData(headersMock, null, SITEMAP_MODEL_NAME, null, false); + Response response = sitemapResource.getSitemapData(headersMock, null, SITEMAP_NAME, null, false); SitemapDTO sitemapDTO = (SitemapDTO) response.getEntity(); // assert that the item state change did trigger the blocking method to return @@ -217,8 +199,7 @@ public void whenLongPollingSpecificPageMustNotObserveAllItems() throws ItemNotFo // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); // assert that the item state change did trigger the blocking method to return @@ -234,8 +215,7 @@ public void whenLongPollingShouldObserveItems() { // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); // assert that the item state change did trigger the blocking method to return @@ -243,7 +223,7 @@ public void whenLongPollingShouldObserveItems() { } @Test - public void whenLongPollingShouldObserveItemsFromVisibilityRules() { + public void whenLongPollingShouldObserveItemsFromRules() { ItemEvent itemEvent = mock(ItemEvent.class); when(itemEvent.getItemName()).thenReturn(visibilityRuleItem.getName()); executeWithDelay(() -> sitemapResource.receive(itemEvent)); @@ -251,8 +231,7 @@ public void whenLongPollingShouldObserveItemsFromVisibilityRules() { // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); // assert that the item state change did trigger the blocking method to return @@ -268,8 +247,7 @@ public void whenLongPollingShouldObserveItemsFromLabelColorConditions() { // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); // assert that the item state change did trigger the blocking method to return @@ -285,8 +263,7 @@ public void whenLongPollingShouldObserveItemsFromValueColorConditions() { // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.timeout, is(false)); // assert that the item state change did trigger the blocking method to @@ -302,8 +279,7 @@ public void whenLongPollingShouldObserveItemsFromIconColorConditions() { // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.timeout, is(false)); // assert that the item state change did trigger the blocking method to @@ -319,8 +295,7 @@ public void whenLongPollingShouldObserveItemsFromIconConditions() { // non-null is sufficient here. when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); // assert that the item state change did trigger the blocking method to return @@ -346,8 +321,7 @@ public void whenGetPageDataShouldReturnPageBean() throws ItemNotFoundException { // Disable long polling when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(null); - Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, - false); + Response response = sitemapResource.getPageData(headersMock, null, SITEMAP_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.id, is(SITEMAP_NAME)); @@ -402,9 +376,9 @@ private void configureItemUIRegistryWithSubpages(State state1, State state2, Sta Group group1 = (Group) widgets.getFirst(); Group group2 = (Group) widgets.get(4); - when(itemUIRegistryMock.getChildren(defaultSitemapMock)).thenReturn(new BasicEList<>(List.of(group1, group2))); + when(itemUIRegistryMock.getChildren(defaultSitemapMock)).thenReturn(List.of(group1, group2)); configureCommonUIRegistryMockMethods(); - EList subpage1Widgets = new BasicEList<>(group1.getChildren()); + List subpage1Widgets = group1.getWidgets(); configureItemUIRegistryForWidget(group1, FRAME_ID, GROUP_ICON, GROUP_LABEL, WidgetLabelSource.SITEMAP_WIDGET, true, null, null, null, null); @@ -412,7 +386,7 @@ private void configureItemUIRegistryWithSubpages(State state1, State state2, Sta configureWidgetStatesPage1(state1, state2); - EList subpage2Widgets = new BasicEList<>(group2.getChildren()); + List subpage2Widgets = group2.getWidgets(); configureItemUIRegistryForWidget(group2, FRAME_ID, GROUP_ICON, GROUP_LABEL, WidgetLabelSource.SITEMAP_WIDGET, true, null, null, null, null); @@ -422,7 +396,7 @@ private void configureItemUIRegistryWithSubpages(State state1, State state2, Sta } private void configureItemUIRegistry(State state1, State state2) throws ItemNotFoundException { - EList mainpageWidgets = new BasicEList<>(widgets.subList(1, 4)); + List mainpageWidgets = widgets.subList(1, 4); when(itemUIRegistryMock.getChildren(defaultSitemapMock)).thenReturn(mainpageWidgets); configureCommonUIRegistryMockMethods(); @@ -440,7 +414,7 @@ private void configureCommonUIRegistryMockMethods() throws ItemNotFoundException } private void configureWidgetStatesPage1(State state1, State state2) { - EList mainpageWidgets = new BasicEList<>(widgets.subList(1, 4)); + List mainpageWidgets = widgets.subList(1, 4); Widget w1 = mainpageWidgets.getFirst(); configureItemUIRegistryForWidget(w1, WIDGET1_ID, WIDGET1_ICON, WIDGET1_LABEL, WidgetLabelSource.SITEMAP_WIDGET, true, "GREEN", "BLUE", "ORANGE", state1); @@ -474,112 +448,112 @@ private void configureItemUIRegistryForWidget(Widget w, String widgetId, String when(itemUIRegistryMock.getState(w)).thenReturn(state); } - private EList initSitemapWidgets() { + private List initSitemapWidgets() { // Initialize a sitemap containing 2 widgets linked to the same number item, // one slider and one switch, // which has one subpage // add icon rules to the mock widget: - Class classToMock = IconRule.class; - IconRule iconRule = mock(classToMock); + Class classToMock = Rule.class; + Rule iconRule = mock(classToMock); Condition conditon0 = mock(Condition.class); when(conditon0.getItem()).thenReturn(ICON_ITEM_NAME); - EList conditions0 = new BasicEList<>(); + List conditions0 = new ArrayList<>(); conditions0.add(conditon0); when(iconRule.getConditions()).thenReturn(conditions0); - EList iconRulesW1 = new BasicEList<>(); + List iconRulesW1 = new ArrayList<>(); iconRulesW1.add(iconRule); // add visibility rules to the mock widget: - VisibilityRule visibilityRule = mock(VisibilityRule.class); + Rule visibilityRule = mock(Rule.class); Condition conditon = mock(Condition.class); when(conditon.getItem()).thenReturn(VISIBILITY_RULE_ITEM_NAME); - EList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(conditon); when(visibilityRule.getConditions()).thenReturn(conditions); - EList visibilityRulesW1 = new BasicEList<>(1); + List visibilityRulesW1 = new ArrayList<>(1); visibilityRulesW1.add(visibilityRule); // add label color conditions to the item: - ColorArray labelColor = mock(ColorArray.class); + Rule labelColor = mock(Rule.class); Condition conditon1 = mock(Condition.class); when(conditon1.getItem()).thenReturn(LABEL_COLOR_ITEM_NAME); - EList conditions1 = new BasicEList<>(); + List conditions1 = new ArrayList<>(); conditions1.add(conditon1); when(labelColor.getConditions()).thenReturn(conditions1); - EList labelColorsW1 = new BasicEList<>(); + List labelColorsW1 = new ArrayList<>(); labelColorsW1.add(labelColor); // add value color conditions to the item: - ColorArray valueColor = mock(ColorArray.class); + Rule valueColor = mock(Rule.class); Condition conditon2 = mock(Condition.class); when(conditon2.getItem()).thenReturn(VALUE_COLOR_ITEM_NAME); - EList conditions2 = new BasicEList<>(); + List conditions2 = new ArrayList<>(); conditions2.add(conditon2); when(valueColor.getConditions()).thenReturn(conditions2); - EList valueColorsW1 = new BasicEList<>(); + List valueColorsW1 = new ArrayList<>(); valueColorsW1.add(valueColor); // add icon color conditions to the item: - ColorArray iconColor = mock(ColorArray.class); + Rule iconColor = mock(Rule.class); Condition conditon3 = mock(Condition.class); when(conditon3.getItem()).thenReturn(ICON_COLOR_ITEM_NAME); - EList conditions3 = new BasicEList<>(); + List conditions3 = new ArrayList<>(); conditions3.add(conditon3); when(iconColor.getConditions()).thenReturn(conditions3); - EList iconColorsW1 = new BasicEList<>(); + List iconColorsW1 = new ArrayList<>(); iconColorsW1.add(iconColor); - EClass sliderEClass = mockEClass("slider", "org.openhab.core.model.sitemap.Slider"); + String sliderType = "Slider"; - Widget w1 = mockWidget(iconRulesW1, visibilityRulesW1, labelColorsW1, valueColorsW1, iconColorsW1, sliderEClass, - WIDGET1_LABEL, null, null); + Widget w1 = mockWidget(iconRulesW1, visibilityRulesW1, labelColorsW1, valueColorsW1, iconColorsW1, sliderType, + WIDGET1_LABEL, null, false); - EList iconRules = new BasicEList<>(); - EList visibilityRules = new BasicEList<>(); - EList labelColors = new BasicEList<>(); - EList valueColors = new BasicEList<>(); - EList iconColors = new BasicEList<>(); + List iconRules = new ArrayList<>(); + List visibilityRules = new ArrayList<>(); + List labelColors = new ArrayList<>(); + List valueColors = new ArrayList<>(); + List iconColors = new ArrayList<>(); - EClass switchEClass = mockEClass("switch", "org.openhab.core.model.sitemap.Switch"); + String switchType = "Switch"; - Widget w2 = mockWidget(iconRules, visibilityRules, labelColors, valueColors, iconColors, switchEClass, null, - WIDGET2_ICON, null); + Widget w2 = mockWidget(iconRules, visibilityRules, labelColors, valueColors, iconColors, switchType, null, + WIDGET2_ICON, false); mock(Widget.class); - Widget w3 = mockWidget(iconRules, visibilityRules, labelColors, valueColors, iconColors, switchEClass, - WIDGET3_LABEL, null, WIDGET3_ICON); + Widget w3 = mockWidget(iconRules, visibilityRules, labelColors, valueColors, iconColors, switchType, + WIDGET3_LABEL, WIDGET3_ICON, true); - EList widgets = new BasicEList<>(3); + List widgets = new ArrayList<>(3); widgets.add(w1); widgets.add(w2); widgets.add(w3); return widgets; } - private EList initSitemapWidgetsWithSubpages() { - EList baseWidgets = initSitemapWidgets(); + private List initSitemapWidgetsWithSubpages() { + List baseWidgets = initSitemapWidgets(); - EClass groupEClass = mockEClass("group", "org.openhab.core.model.sitemap.Group"); + String groupType = "Group"; - EList iconRules = new BasicEList<>(); - EList visibilityRules = new BasicEList<>(); - EList labelColors = new BasicEList<>(); - EList valueColors = new BasicEList<>(); - EList iconColors = new BasicEList<>(); + List iconRules = new ArrayList<>(); + List visibilityRules = new ArrayList<>(); + List labelColors = new ArrayList<>(); + List valueColors = new ArrayList<>(); + List iconColors = new ArrayList<>(); - Widget group1 = mockGroup(iconRules, visibilityRules, labelColors, valueColors, iconColors, groupEClass, - GROUP_LABEL, null, GROUP_ICON, baseWidgets); + Widget group1 = mockGroup(iconRules, visibilityRules, labelColors, valueColors, iconColors, groupType, + GROUP_LABEL, GROUP_ICON, true, baseWidgets); - EClass switchEClass = mockEClass("switch", "org.openhab.core.model.sitemap.Switch"); + String switchType = "Switch"; - Widget w4 = mockWidget(iconRules, visibilityRules, labelColors, valueColors, iconColors, switchEClass, - WIDGET4_LABEL, null, WIDGET4_ICON); + Widget w4 = mockWidget(iconRules, visibilityRules, labelColors, valueColors, iconColors, switchType, + WIDGET4_LABEL, WIDGET4_ICON, true); - Widget group2 = mockGroup(iconRules, visibilityRules, labelColors, valueColors, iconColors, groupEClass, - GROUP_LABEL, null, GROUP_ICON, new BasicEList<>(List.of(w4))); + Widget group2 = mockGroup(iconRules, visibilityRules, labelColors, valueColors, iconColors, groupType, + GROUP_LABEL, GROUP_ICON, true, new ArrayList<>(List.of(w4))); - EList allWidgets = new BasicEList<>(); + List allWidgets = new ArrayList<>(); allWidgets.add(group1); allWidgets.addAll(baseWidgets); allWidgets.add(group2); @@ -587,40 +561,33 @@ private EList initSitemapWidgetsWithSubpages() { return allWidgets; } - private static EClass mockEClass(String eClassName, String eClassInstanceName) { - EClass sliderEClass = mock(EClass.class); - when(sliderEClass.getName()).thenReturn(eClassName); - when(sliderEClass.getInstanceTypeName()).thenReturn(eClassInstanceName); - return sliderEClass; - } - - private static Widget mockWidget(EList iconRules1, EList visibilityRules1, - EList labelColors1, EList valueColors1, EList iconColors1, - EClass eClass, String widgetLabel, String widgetIcon, String widgetStaticIcon) { + private static Widget mockWidget(List iconRules1, List visibilityRules1, List labelColors1, + List valueColors1, List iconColors1, String widgetType, String widgetLabel, String widgetIcon, + boolean widgetStaticIcon) { Widget w = mock(Widget.class); - mockWidgetMethods(iconRules1, visibilityRules1, labelColors1, valueColors1, iconColors1, eClass, widgetLabel, - widgetIcon, widgetStaticIcon, w); + mockWidgetMethods(iconRules1, visibilityRules1, labelColors1, valueColors1, iconColors1, widgetType, + widgetLabel, widgetIcon, widgetStaticIcon, w); when(w.getItem()).thenReturn(ITEM_NAME); return w; } - private static Group mockGroup(EList iconRules1, EList visibilityRules1, - EList labelColors1, EList valueColors1, EList iconColors1, - EClass eClass, String widgetLabel, String widgetIcon, String widgetStaticIcon, EList children) { + private static Group mockGroup(List iconRules1, List visibilityRules1, List labelColors1, + List valueColors1, List iconColors1, String widgetType, String widgetLabel, String widgetIcon, + boolean widgetStaticIcon, List children) { Group w = mock(Group.class); - mockWidgetMethods(iconRules1, visibilityRules1, labelColors1, valueColors1, iconColors1, eClass, widgetLabel, - widgetIcon, widgetStaticIcon, w); - when(w.getChildren()).thenReturn(children); + mockWidgetMethods(iconRules1, visibilityRules1, labelColors1, valueColors1, iconColors1, widgetType, + widgetLabel, widgetIcon, widgetStaticIcon, w); + when(w.getWidgets()).thenReturn(children); return w; } - private static void mockWidgetMethods(EList iconRules1, EList visibilityRules1, - EList labelColors1, EList valueColors1, EList iconColors1, - EClass eClass, String widgetLabel, String widgetIcon, String widgetStaticIcon, Widget w) { - when(w.eClass()).thenReturn(eClass); + private static void mockWidgetMethods(List iconRules1, List visibilityRules1, List labelColors1, + List valueColors1, List iconColors1, String widgetType, String widgetLabel, String widgetIcon, + boolean widgetStaticIcon, Widget w) { + when(w.getWidgetType()).thenReturn(widgetType); when(w.getLabel()).thenReturn(widgetLabel); when(w.getIcon()).thenReturn(widgetIcon); - when(w.getStaticIcon()).thenReturn(widgetStaticIcon); + when(w.isStaticIcon()).thenReturn(widgetStaticIcon); when(w.getIconRules()).thenReturn(iconRules1); when(w.getVisibility()).thenReturn(visibilityRules1); when(w.getLabelColor()).thenReturn(labelColors1); @@ -634,9 +601,9 @@ private void configureSitemapMock() { when(defaultSitemapMock.getIcon()).thenReturn(""); } - private void configureSitemapProviderMock() { - when(sitemapProviderMock.getSitemapNames()).thenReturn(Set.of(SITEMAP_MODEL_NAME)); - when(sitemapProviderMock.getSitemap(SITEMAP_MODEL_NAME)).thenReturn(defaultSitemapMock); + private void configureSitemapRegistryMock() { + when(sitemapRegistryMock.getAll()).thenReturn(Set.of(defaultSitemapMock)); + when(sitemapRegistryMock.get(SITEMAP_NAME)).thenReturn(defaultSitemapMock); } private static class TestItem extends GenericItem { diff --git a/bundles/org.openhab.core.model.sitemap/bnd.bnd b/bundles/org.openhab.core.model.sitemap/bnd.bnd index 08b21881f9e..d285ed9b799 100644 --- a/bundles/org.openhab.core.model.sitemap/bnd.bnd +++ b/bundles/org.openhab.core.model.sitemap/bnd.bnd @@ -13,8 +13,11 @@ Export-Package: org.openhab.core.model.sitemap,\ org.openhab.core.model.sitemap.validation Import-Package: org.apache.log4j,\ org.eclipse.jdt.annotation;resolution:=optional,\ + org.openhab.core.common.registry, \ org.openhab.core.items.dto,\ org.openhab.core.model.core,\ + org.openhab.core.sitemap, \ + org.openhab.core.sitemap.registry, \ org.eclipse.xtext.xbase.lib,\ org.osgi.framework,\ org.osgi.service.cm,\ diff --git a/bundles/org.openhab.core.model.sitemap/pom.xml b/bundles/org.openhab.core.model.sitemap/pom.xml index 89dd2addc88..26fe88b22ea 100644 --- a/bundles/org.openhab.core.model.sitemap/pom.xml +++ b/bundles/org.openhab.core.model.sitemap/pom.xml @@ -20,6 +20,11 @@ org.openhab.core.model.core ${project.version} + + org.openhab.core.bundles + org.openhab.core.sitemap + ${project.version} + diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext index a89e623c9df..a7a03e23b1d 100644 --- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext +++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext @@ -4,232 +4,225 @@ import "http://www.eclipse.org/emf/2002/Ecore" as ecore generate sitemap "https://openhab.org/model/Sitemap" SitemapModel: - 'sitemap' Sitemap; + 'sitemap' ModelSitemap; -Sitemap: +ModelSitemap: name=ID ('label=' label=STRING)? ('icon=' icon=STRING)? '{' - (children+=Widget)+ + (children+=ModelWidget)+ '}'; -Widget: - (LinkableWidget | NonLinkableWidget); +ModelWidget: + (ModelLinkableWidget | ModelNonLinkableWidget); -NonLinkableWidget: - Switch | Selection | Slider | Setpoint | Video | Chart | Webview | Colorpicker | Colortemperaturepicker | Mapview | Input | Button | Default; +ModelNonLinkableWidget: + ModelSwitch | ModelSelection | ModelSlider | ModelSetpoint | ModelVideo | ModelChart | ModelWebview | ModelColorpicker | ModelColortemperaturepicker | ModelMapview | ModelInput | ModelButton | ModelDefault; -LinkableWidget: - (Text | Group | Image | Frame | Buttongrid) +ModelLinkableWidget: + (ModelText | ModelGroup | ModelImage | ModelFrame | ModelButtongrid) ('{' - (children+=Widget)+ + (children+=ModelWidget)+ '}')?; -Frame: - {Frame} 'Frame' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Text: - {Text} 'Text' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Group: - 'Group' (('item=' item=GroupItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Image: - 'Image' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('url=' url=STRING)? & ('refresh=' refresh=INT)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Video: - 'Video' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('url=' url=STRING) & ('encoding=' encoding=STRING)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Chart: - 'Chart' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('service=' service=STRING)? & ('refresh=' refresh=INT)? & ('period=' period=Period) & - ('legend=' legend=BOOLEAN_OBJECT)? & ('forceasitem=' forceAsItem=BOOLEAN_OBJECT)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')? & - ('yAxisDecimalPattern=' yAxisDecimalPattern=(STRING))? & - ('interpolation=' interpolation=(STRING))?); - -Webview: - 'Webview' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('height=' height=INT)? & ('url=' url=STRING) & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Switch: - 'Switch' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('mappings=[' mappings+=Mapping (',' mappings+=Mapping)* ']')? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Mapview: - 'Mapview' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('height=' height=INT)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Slider: - 'Slider' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - (switchEnabled?='switchSupport')? & (releaseOnly?='releaseOnly')? & - ('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? & ('step=' step=Number)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Selection: - 'Selection' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('mappings=[' mappings+=Mapping (',' mappings+=Mapping)* ']')? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Setpoint: - 'Setpoint' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? & ('step=' step=Number)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Colorpicker: - 'Colorpicker' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Colortemperaturepicker: - 'Colortemperaturepicker' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Input: - 'Input' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('inputHint=' inputHint=STRING)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Buttongrid: - {Buttongrid} 'Buttongrid' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('buttons=[' buttons+=ButtonDefinition (',' buttons+=ButtonDefinition)* ']')? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Button: - 'Button' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('row=' row=INT) & ('column=' column=INT) & (stateless?='stateless')? & - ('click=' cmd=Command) & ('release=' releaseCmd=Command)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -Default: - 'Default' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? & - (('icon=' icon=Icon) | - ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') | - ('staticIcon=' staticIcon=Icon))? & - ('height=' height=INT)? & - ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? & - ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? & - ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? & - ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?); - -ButtonDefinition: +ModelFrame: + {ModelFrame} 'Frame' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelText: + {ModelText} 'Text' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelGroup: + 'Group' (('item=' item=GroupItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelImage: + 'Image' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('url=' url=STRING) | ('refresh=' refresh=INT) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelVideo: + 'Video' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('url=' url=STRING) | ('encoding=' encoding=STRING) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelChart: + 'Chart' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('service=' service=STRING) | ('refresh=' refresh=INT) | ('period=' period=Period) | + ('legend=' legend=BOOLEAN_OBJECT) | ('forceasitem=' forceAsItem=BOOLEAN_OBJECT) | + ('yAxisDecimalPattern=' yAxisDecimalPattern=(STRING)) | + ('interpolation=' interpolation=(STRING)) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelWebview: + 'Webview' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('height=' height=INT) | ('url=' url=STRING) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelSwitch: + 'Switch' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('mappings=' mappings=ModelMappingList) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelMapview: + 'Mapview' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('height=' height=INT) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelSlider: + 'Slider' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + (switchEnabled?='switchSupport') | (releaseOnly?='releaseOnly') | + ('minValue=' minValue=Number) | ('maxValue=' maxValue=Number) | ('step=' step=Number) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelSelection: + 'Selection' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('mappings=' mappings=ModelMappingList) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelSetpoint: + 'Setpoint' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('minValue=' minValue=Number) | ('maxValue=' maxValue=Number) | ('step=' step=Number) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelColorpicker: + 'Colorpicker' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelColortemperaturepicker: + 'Colortemperaturepicker' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('minValue=' minValue=Number) | ('maxValue=' maxValue=Number) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelInput: + 'Input' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('inputHint=' inputHint=STRING) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelButtongrid: + {ModelButtongrid} 'Buttongrid' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('buttons=' buttons=ModelButtonDefinitionList) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelButton: + 'Button' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('row=' row=INT) | ('column=' column=INT) | (stateless?='stateless') | + ('click=' cmd=Command) | ('release=' releaseCmd=Command) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelDefault: + 'Default' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) | + ('icon=' icon=Icon) | ('icon=' IconRules=ModelIconRuleList) | ('staticIcon=' staticIcon=Icon) | + ('height=' height=INT) | + ('labelcolor=' labelColor=ModelColorArrayList) | + ('valuecolor=' valueColor=ModelColorArrayList) | + ('iconcolor=' iconColor=ModelColorArrayList) | + ('visibility=' visibility=ModelVisibilityRuleList))*; + +ModelButtonDefinitionList returns ModelButtonDefinitionList: + '[' elements+=ModelButtonDefinition (',' elements+=ModelButtonDefinition)* ']' +; + +ModelButtonDefinition: row=INT ':' column=INT ':' cmd=Command '=' label=(ID | STRING) ('=' icon=Icon)?; -Mapping: +ModelMappingList returns ModelMappingList: + '[' elements+=ModelMapping (',' elements+=ModelMapping)* ']' +; + +ModelMapping: cmd=Command (':' releaseCmd=Command)? '=' label=(ID | STRING) ('=' icon=Icon)?; -VisibilityRule: - conditions+=Condition ('AND' conditions+=Condition)*; +ModelColorArrayList returns ModelColorArrayList: + '[' elements+=ModelColorArray (',' elements+=ModelColorArray)* ']' +; + +ModelColorArray: + ((conditions+=ModelCondition ('AND' conditions+=ModelCondition)*) '=')? (arg=STRING); + +ModelIconRuleList returns ModelIconRuleList: + '[' elements+=ModelIconRule (',' elements+=ModelIconRule)* ']' +; + +ModelIconRule: + ((conditions+=ModelCondition ('AND' conditions+=ModelCondition)*) '=')? (arg=Icon); + +ModelVisibilityRuleList returns ModelVisibilityRuleList: + '[' elements+=ModelVisibilityRule (',' elements+=ModelVisibilityRule)* ']' +; + +ModelVisibilityRule: + conditions+=ModelCondition ('AND' conditions+=ModelCondition)*; + +ModelCondition: + (item=ID)? (condition=('==' | '>' | '<' | '>=' | '<=' | '!='))? (sign=('-' | '+'))? (state=XState); ItemRef: ID; @@ -247,15 +240,6 @@ IconName: Period: (ID '-')? ID; -ColorArray: - ((conditions+=Condition ('AND' conditions+=Condition)*) '=')? (arg=STRING); - -IconRule: - ((conditions+=Condition ('AND' conditions+=Condition)*) '=')? (arg=Icon); - -Condition: - (item=ID)? (condition=('==' | '>' | '<' | '>=' | '<=' | '!='))? (sign=('-' | '+'))? (state=XState); - Command returns ecore::EString: Number | ID | STRING; diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapProvider.java b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapProvider.java deleted file mode 100644 index 8bb8c8fad59..00000000000 --- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2010-2025 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.core.model.sitemap; - -import java.util.Set; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.model.core.ModelRepositoryChangeListener; -import org.openhab.core.model.sitemap.sitemap.Sitemap; - -@NonNullByDefault -public interface SitemapProvider { - - /** - * This method provides access to sitemap model files, loads them and returns the object model tree. - * - * @param sitemapName the name of the sitemap to load - * @return the object model tree, null if it is not found - */ - @Nullable - Sitemap getSitemap(String sitemapName); - - /** - * Returns the names of all available sitemaps - * - * @return names of provided sitemaps - */ - Set getSitemapNames(); - - /** - * Add a listener which will be informed subsequently once a model has changed - * - * @param listener - */ - void addModelChangeListener(ModelRepositoryChangeListener listener); - - /** - * Remove a model change listener again - * - * @param listener - */ - void removeModelChangeListener(ModelRepositoryChangeListener listener); -} diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/internal/SitemapProviderImpl.java b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/internal/SitemapProviderImpl.java index 302a1522ff7..cb211c95757 100644 --- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/internal/SitemapProviderImpl.java +++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/internal/SitemapProviderImpl.java @@ -12,20 +12,74 @@ */ package org.openhab.core.model.sitemap.internal; +import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.stream.Collectors; +import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.AbstractProvider; +import org.openhab.core.common.registry.ProviderChangeListener; import org.openhab.core.model.core.EventType; import org.openhab.core.model.core.ModelRepository; import org.openhab.core.model.core.ModelRepositoryChangeListener; -import org.openhab.core.model.sitemap.SitemapProvider; -import org.openhab.core.model.sitemap.sitemap.Sitemap; +import org.openhab.core.model.sitemap.sitemap.ModelButton; +import org.openhab.core.model.sitemap.sitemap.ModelButtonDefinition; +import org.openhab.core.model.sitemap.sitemap.ModelButtonDefinitionList; +import org.openhab.core.model.sitemap.sitemap.ModelButtongrid; +import org.openhab.core.model.sitemap.sitemap.ModelChart; +import org.openhab.core.model.sitemap.sitemap.ModelColorArray; +import org.openhab.core.model.sitemap.sitemap.ModelColorArrayList; +import org.openhab.core.model.sitemap.sitemap.ModelColortemperaturepicker; +import org.openhab.core.model.sitemap.sitemap.ModelCondition; +import org.openhab.core.model.sitemap.sitemap.ModelDefault; +import org.openhab.core.model.sitemap.sitemap.ModelIconRule; +import org.openhab.core.model.sitemap.sitemap.ModelIconRuleList; +import org.openhab.core.model.sitemap.sitemap.ModelImage; +import org.openhab.core.model.sitemap.sitemap.ModelInput; +import org.openhab.core.model.sitemap.sitemap.ModelLinkableWidget; +import org.openhab.core.model.sitemap.sitemap.ModelMapping; +import org.openhab.core.model.sitemap.sitemap.ModelMappingList; +import org.openhab.core.model.sitemap.sitemap.ModelMapview; +import org.openhab.core.model.sitemap.sitemap.ModelSelection; +import org.openhab.core.model.sitemap.sitemap.ModelSetpoint; +import org.openhab.core.model.sitemap.sitemap.ModelSitemap; +import org.openhab.core.model.sitemap.sitemap.ModelSlider; +import org.openhab.core.model.sitemap.sitemap.ModelSwitch; +import org.openhab.core.model.sitemap.sitemap.ModelVideo; +import org.openhab.core.model.sitemap.sitemap.ModelVisibilityRule; +import org.openhab.core.model.sitemap.sitemap.ModelVisibilityRuleList; +import org.openhab.core.model.sitemap.sitemap.ModelWebview; +import org.openhab.core.model.sitemap.sitemap.ModelWidget; +import org.openhab.core.sitemap.Button; +import org.openhab.core.sitemap.ButtonDefinition; +import org.openhab.core.sitemap.Buttongrid; +import org.openhab.core.sitemap.Chart; +import org.openhab.core.sitemap.Colortemperaturepicker; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Default; +import org.openhab.core.sitemap.Image; +import org.openhab.core.sitemap.Input; +import org.openhab.core.sitemap.LinkableWidget; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Mapview; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Selection; +import org.openhab.core.sitemap.Setpoint; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Slider; +import org.openhab.core.sitemap.Switch; +import org.openhab.core.sitemap.Video; +import org.openhab.core.sitemap.Webview; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.registry.SitemapFactory; +import org.openhab.core.sitemap.registry.SitemapProvider; +import org.openhab.core.sitemap.registry.SitemapRegistry; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -37,94 +91,337 @@ * This class provides access to the sitemap model files. * * @author Kai Kreuzer - Initial contribution + * @author Mark Herwege - Separate registry from model */ @NonNullByDefault -@Component(service = SitemapProvider.class) -public class SitemapProviderImpl implements SitemapProvider, ModelRepositoryChangeListener { +@Component(service = SitemapProvider.class, immediate = true) +public class SitemapProviderImpl extends AbstractProvider + implements SitemapProvider, ModelRepositoryChangeListener { private static final String SITEMAP_MODEL_NAME = "sitemap"; protected static final String SITEMAP_FILEEXT = "." + SITEMAP_MODEL_NAME; + protected static final String MODEL_TYPE_PREFIX = "Model"; private final Logger logger = LoggerFactory.getLogger(SitemapProviderImpl.class); private final ModelRepository modelRepo; + private final SitemapRegistry sitemapRegistry; + private final SitemapFactory sitemapFactory; - private final Map sitemapModelCache = new ConcurrentHashMap<>(); - - private final Set modelChangeListeners = new CopyOnWriteArraySet<>(); + private final Map sitemapCache = new ConcurrentHashMap<>(); @Activate - public SitemapProviderImpl(final @Reference ModelRepository modelRepo) { + public SitemapProviderImpl(final @Reference ModelRepository modelRepo, + final @Reference SitemapRegistry sitemapRegistry, final @Reference SitemapFactory sitemapFactory) { this.modelRepo = modelRepo; + this.sitemapRegistry = sitemapRegistry; + this.sitemapFactory = sitemapFactory; refreshSitemapModels(); + sitemapRegistry.addSitemapProvider(this); modelRepo.addModelRepositoryChangeListener(this); } @Deactivate protected void deactivate() { modelRepo.removeModelRepositoryChangeListener(this); - sitemapModelCache.clear(); + sitemapRegistry.removeSitemapProvider(this); + sitemapCache.clear(); } @Override public @Nullable Sitemap getSitemap(String sitemapName) { - String filename = sitemapName + SITEMAP_FILEEXT; - Sitemap sitemap = sitemapModelCache.get(filename); + Sitemap sitemap = sitemapCache.get(sitemapName); if (sitemap == null) { logger.trace("Sitemap {} cannot be found", sitemapName); - return null; } + return sitemap; + } - if (!sitemap.getName().equals(sitemapName)) { - logger.warn( - "Filename `{}` does not match the name `{}` of the sitemap - please fix this as you might see unexpected behavior otherwise.", - filename, sitemap.getName()); + private void refreshSitemapModels() { + sitemapCache.clear(); + Iterable sitemapFilenames = modelRepo.getAllModelNamesOfType(SITEMAP_MODEL_NAME); + for (String filename : sitemapFilenames) { + ModelSitemap modelSitemap = (ModelSitemap) modelRepo.getModel(filename); + if (modelSitemap != null) { + String sitemapName = filename.substring(0, filename.length() - SITEMAP_FILEEXT.length()); + if (!modelSitemap.getName().equals(sitemapName)) { + logger.warn( + "Filename `{}` does not match the name `{}` of the sitemap - please fix this as you might see unexpected behavior otherwise.", + filename, modelSitemap.getName()); + } + Sitemap sitemap = parseModelSitemap(modelSitemap); + sitemapCache.put(sitemapName, sitemap); + } } + } + + private Sitemap parseModelSitemap(ModelSitemap modelSitemap) { + Sitemap sitemap = sitemapFactory.createSitemap(modelSitemap.getName()); + sitemap.setLabel(modelSitemap.getLabel()); + sitemap.setIcon(modelSitemap.getIcon()); + List widgets = sitemap.getWidgets(); + modelSitemap.getChildren().forEach(child -> addWidget(widgets, child, sitemap)); return sitemap; } + private void addWidget(List widgets, ModelWidget modelWidget, Parent parent) { + String widgetType = getWidgetType(modelWidget); + Widget widget = sitemapFactory.createWidget(widgetType, parent); + if (widget != null) { + switch (widget) { + case Image imageWidget: + ModelImage modelImage = (ModelImage) modelWidget; + imageWidget.setUrl(modelImage.getUrl()); + imageWidget.setRefresh(modelImage.getRefresh()); + break; + case Video videoWidget: + ModelVideo modelVideo = (ModelVideo) modelWidget; + videoWidget.setUrl(modelVideo.getUrl()); + videoWidget.setEncoding(modelVideo.getEncoding()); + break; + case Chart chartWidget: + ModelChart modelChart = (ModelChart) modelWidget; + chartWidget.setService(modelChart.getService()); + chartWidget.setRefresh(modelChart.getRefresh()); + chartWidget.setPeriod(modelChart.getPeriod()); + chartWidget.setLegend(modelChart.getLegend()); + chartWidget.setForceAsItem(modelChart.getForceAsItem()); + chartWidget.setYAxisDecimalPattern(modelChart.getYAxisDecimalPattern()); + chartWidget.setInterpolation(modelChart.getInterpolation()); + break; + case Webview webviewWidget: + ModelWebview modelWebview = (ModelWebview) modelWidget; + webviewWidget.setHeight(modelWebview.getHeight()); + webviewWidget.setUrl(modelWebview.getUrl()); + break; + case Switch switchWidget: + ModelSwitch modelSwitch = (ModelSwitch) modelWidget; + addWidgetMappings(switchWidget.getMappings(), modelSwitch.getMappings()); + break; + case Mapview mapviewWidget: + ModelMapview modelMapview = (ModelMapview) modelWidget; + mapviewWidget.setHeight(modelMapview.getHeight()); + break; + case Slider sliderWidget: + ModelSlider modelSlider = (ModelSlider) modelWidget; + sliderWidget.setMinValue(modelSlider.getMinValue()); + sliderWidget.setMaxValue(modelSlider.getMaxValue()); + sliderWidget.setStep(modelSlider.getStep()); + sliderWidget.setSwitchEnabled(modelSlider.isSwitchEnabled()); + sliderWidget.setReleaseOnly(modelSlider.isReleaseOnly()); + break; + case Selection selectionWidget: + ModelSelection modelSelection = (ModelSelection) modelWidget; + addWidgetMappings(selectionWidget.getMappings(), modelSelection.getMappings()); + break; + case Input inputWidget: + ModelInput modelInput = (ModelInput) modelWidget; + inputWidget.setInputHint(modelInput.getInputHint()); + break; + case Setpoint setpointWidget: + ModelSetpoint modelSetpoint = (ModelSetpoint) modelWidget; + setpointWidget.setMinValue(modelSetpoint.getMinValue()); + setpointWidget.setMaxValue(modelSetpoint.getMaxValue()); + setpointWidget.setStep(modelSetpoint.getStep()); + break; + case Colortemperaturepicker colortemperaturepickerWidget: + ModelColortemperaturepicker modelColortemperaturepicker = (ModelColortemperaturepicker) modelWidget; + colortemperaturepickerWidget.setMinValue(modelColortemperaturepicker.getMinValue()); + colortemperaturepickerWidget.setMaxValue(modelColortemperaturepicker.getMaxValue()); + break; + case Buttongrid buttongridWidget: + ModelButtongrid modelButtongrid = (ModelButtongrid) modelWidget; + addWidgetButtons(buttongridWidget.getButtons(), modelButtongrid.getButtons()); + break; + case Button buttonWidget: + ModelButton modelButton = (ModelButton) modelWidget; + buttonWidget.setRow(modelButton.getRow()); + buttonWidget.setColumn(modelButton.getColumn()); + buttonWidget.setStateless(modelButton.isStateless()); + buttonWidget.setCmd(modelButton.getCmd()); + buttonWidget.setReleaseCmd(modelButton.getReleaseCmd()); + break; + case Default defaultWidget: + ModelDefault modelDefault = (ModelDefault) modelWidget; + defaultWidget.setHeight(modelDefault.getHeight()); + break; + default: + break; + } + + widget.setItem(modelWidget.getItem()); + widget.setLabel(modelWidget.getLabel()); + String staticIcon = modelWidget.getStaticIcon(); + if (staticIcon != null && !staticIcon.isEmpty()) { + widget.setIcon(staticIcon); + widget.setStaticIcon(true); + } else { + widget.setIcon(modelWidget.getIcon()); + } + + if (modelWidget instanceof ModelLinkableWidget modelLinkableWidget) { + LinkableWidget linkableWidget = (LinkableWidget) widget; + List childWidgets = linkableWidget.getWidgets(); + modelLinkableWidget.getChildren() + .forEach(childModelWidget -> addWidget(childWidgets, childModelWidget, linkableWidget)); + } + + addWidgetVisibilityRules(widget.getVisibility(), modelWidget.getVisibility()); + addWidgetColorRules(widget.getLabelColor(), modelWidget.getLabelColor()); + addWidgetColorRules(widget.getValueColor(), modelWidget.getValueColor()); + addWidgetColorRules(widget.getIconColor(), modelWidget.getIconColor()); + addWidgetIconRules(widget.getIconRules(), modelWidget.getIconRules()); + + widgets.add(widget); + } + } + + private String getWidgetType(ModelWidget modelWidget) { + String instanceTypeName = modelWidget.eClass().getInstanceTypeName(); + String widgetType = instanceTypeName + .substring(instanceTypeName.lastIndexOf("." + MODEL_TYPE_PREFIX) + MODEL_TYPE_PREFIX.length() + 1); + return widgetType; + } + + private void addWidgetMappings(List mappings, @Nullable ModelMappingList modelMappingList) { + if (modelMappingList != null) { + EList modelMappings = modelMappingList.getElements(); + modelMappings.forEach(modelMapping -> { + Mapping mapping = sitemapFactory.createMapping(); + mapping.setCmd(modelMapping.getCmd()); + mapping.setReleaseCmd(modelMapping.getReleaseCmd()); + mapping.setLabel(modelMapping.getLabel()); + mapping.setIcon(modelMapping.getIcon()); + mappings.add(mapping); + }); + } + } + + private void addWidgetButtons(List buttons, @Nullable ModelButtonDefinitionList modelButtonList) { + if (modelButtonList != null) { + EList modelButtons = modelButtonList.getElements(); + modelButtons.forEach(modelButton -> { + ButtonDefinition button = sitemapFactory.createButtonDefinition(); + button.setRow(modelButton.getRow()); + button.setColumn(modelButton.getColumn()); + button.setCmd(modelButton.getCmd()); + button.setLabel(modelButton.getLabel()); + button.setIcon(modelButton.getIcon()); + buttons.add(button); + }); + } + } + + private void addWidgetVisibilityRules(List visibilityRules, + @Nullable ModelVisibilityRuleList modelVisibilityRuleList) { + if (modelVisibilityRuleList != null) { + EList modelVisibilityRules = modelVisibilityRuleList.getElements(); + modelVisibilityRules.forEach(modelVisibilityRule -> { + Rule visibilityRule = sitemapFactory.createRule(); + addRuleConditions(visibilityRule.getConditions(), modelVisibilityRule.getConditions()); + visibilityRules.add(visibilityRule); + }); + } + } + + private void addWidgetColorRules(List colorRules, @Nullable ModelColorArrayList modelColorRuleList) { + if (modelColorRuleList != null) { + EList modelColorRules = modelColorRuleList.getElements(); + modelColorRules.forEach(modelColorRule -> { + Rule colorRule = sitemapFactory.createRule(); + addRuleConditions(colorRule.getConditions(), modelColorRule.getConditions()); + colorRules.add(colorRule); + }); + } + } + + private void addWidgetIconRules(List iconRules, @Nullable ModelIconRuleList modelIconRuleList) { + if (modelIconRuleList != null) { + EList modelIconRules = modelIconRuleList.getElements(); + modelIconRules.forEach(modelIconRule -> { + Rule iconRule = sitemapFactory.createRule(); + iconRule.setArgument(modelIconRule.getArg()); + addRuleConditions(iconRule.getConditions(), modelIconRule.getConditions()); + iconRules.add(iconRule); + }); + } + } + + private void addRuleConditions(List conditions, EList modelConditions) { + modelConditions.forEach(modelCondition -> { + Condition condition = sitemapFactory.createCondition(); + condition.setItem(modelCondition.getItem()); + condition.setCondition(modelCondition.getCondition()); + String sign = modelCondition.getSign(); + String value = (sign != null ? sign : "") + modelCondition.getState(); + condition.setValue(value); + conditions.add(condition); + }); + } + @Override public Set getSitemapNames() { - return sitemapModelCache.keySet().stream() - .map(name -> name.substring(0, name.length() - SITEMAP_FILEEXT.length())).collect(Collectors.toSet()); + return sitemapCache.keySet(); } @Override public void modelChanged(String modelName, EventType type) { - if (modelName.endsWith(SITEMAP_FILEEXT)) { - if (type == EventType.REMOVED) { - sitemapModelCache.remove(modelName); - } else { - EObject sitemap = modelRepo.getModel(modelName); - // if the sitemap file is empty it will not be in the repo and thus there is no need to cache it here - if (sitemap instanceof Sitemap sitemap1) { - sitemapModelCache.put(modelName, sitemap1); - } + if (!modelName.endsWith(SITEMAP_FILEEXT)) { + return; + } + + Sitemap sitemap = null; + String sitemapName = modelName.substring(0, modelName.length() - SITEMAP_FILEEXT.length()); + Sitemap oldSitemap = sitemapRegistry.get(sitemapName); + + if (type == EventType.REMOVED) { + sitemapCache.remove(sitemapName); + } else { + EObject modelSitemapObject = modelRepo.getModel(modelName); + // if the sitemap file is empty it will not be in the repo and thus there is no need to cache it here + if (modelSitemapObject instanceof ModelSitemap modelSitemap) { + sitemap = parseModelSitemap(modelSitemap); + sitemapCache.put(sitemapName, sitemap); } } - for (ModelRepositoryChangeListener listener : modelChangeListeners) { - listener.modelChanged(modelName, type); + + switch (type) { + case EventType.ADDED: + if (sitemap != null) { + notifyListenersAboutAddedElement(sitemap); + } + break; + case EventType.REMOVED: + if (oldSitemap != null) { + notifyListenersAboutRemovedElement(oldSitemap); + } + break; + case EventType.MODIFIED: + if (sitemap != null && oldSitemap != null) { + notifyListenersAboutUpdatedElement(oldSitemap, sitemap); + } + break; } } - private void refreshSitemapModels() { - sitemapModelCache.clear(); - Iterable sitemapNames = modelRepo.getAllModelNamesOfType(SITEMAP_MODEL_NAME); - for (String sitemapName : sitemapNames) { - Sitemap sitemap = (Sitemap) modelRepo.getModel(sitemapName); - if (sitemap != null) { - sitemapModelCache.put(sitemapName, sitemap); - } - } + @Override + public Collection getAll() { + return sitemapCache.values(); } @Override - public void addModelChangeListener(ModelRepositoryChangeListener listener) { - modelChangeListeners.add(listener); + public void addProviderChangeListener(ProviderChangeListener listener) { + super.addProviderChangeListener(listener); + getAll().forEach(sitemap -> { + notifyListenersAboutAddedElement(sitemap); + }); } @Override - public void removeModelChangeListener(ModelRepositoryChangeListener listener) { - modelChangeListeners.remove(listener); + public void removeProviderChangeListener(ProviderChangeListener listener) { + super.removeProviderChangeListener(listener); + getAll().forEach(sitemap -> { + notifyListenersAboutRemovedElement(sitemap); + }); } } diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend index 3749f783bc6..6637b6163d4 100644 --- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend +++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend @@ -15,20 +15,25 @@ */ package org.openhab.core.model.sitemap.validation -import org.openhab.core.model.sitemap.sitemap.Button -import org.openhab.core.model.sitemap.sitemap.Buttongrid -import org.openhab.core.model.sitemap.sitemap.Colortemperaturepicker -import org.openhab.core.model.sitemap.sitemap.Frame -import org.openhab.core.model.sitemap.sitemap.LinkableWidget -import org.openhab.core.model.sitemap.sitemap.Setpoint -import org.openhab.core.model.sitemap.sitemap.Sitemap -import org.openhab.core.model.sitemap.sitemap.SitemapPackage -import org.openhab.core.model.sitemap.sitemap.Widget import org.eclipse.xtext.validation.Check import java.math.BigDecimal -import org.openhab.core.model.sitemap.sitemap.Input import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.openhab.core.model.sitemap.sitemap.Chart +import org.openhab.core.model.sitemap.sitemap.ModelWidget +import org.openhab.core.model.sitemap.sitemap.ModelFrame +import org.openhab.core.model.sitemap.sitemap.ModelText +import org.openhab.core.model.sitemap.sitemap.ModelImage +import org.openhab.core.model.sitemap.sitemap.ModelVideo +import org.openhab.core.model.sitemap.sitemap.ModelWebview +import org.openhab.core.model.sitemap.sitemap.ModelButtongrid +import org.openhab.core.model.sitemap.sitemap.ModelButton +import org.openhab.core.model.sitemap.sitemap.ModelLinkableWidget +import org.openhab.core.model.sitemap.sitemap.ModelSetpoint +import org.openhab.core.model.sitemap.sitemap.ModelSlider +import org.openhab.core.model.sitemap.sitemap.ModelColortemperaturepicker +import org.openhab.core.model.sitemap.sitemap.ModelInput +import org.openhab.core.model.sitemap.sitemap.ModelChart +import org.openhab.core.model.sitemap.sitemap.SitemapPackage +import org.openhab.core.model.sitemap.sitemap.ModelSitemap //import org.eclipse.xtext.validation.Check /** @@ -40,137 +45,207 @@ class SitemapValidator extends AbstractSitemapValidator { val ALLOWED_HINTS = #["text", "number", "date", "time", "datetime"] val ALLOWED_INTERPOLATION = #["linear", "step"] + + @Check + def void checkWidgetHasItem(ModelWidget w) { + if (!(w instanceof ModelFrame || w instanceof ModelText || w instanceof ModelImage || w instanceof ModelVideo || w instanceof ModelWebview || w instanceof ModelButtongrid) && w.item === null) { + val node = NodeModelUtils.getNode(w) + val line = node.getStartLine() + error("'" + w.getClass().getSimpleName() + "' widget doesn't have item defined at line " + line, + SitemapPackage.Literals.MODEL_INPUT.getEStructuralFeature(SitemapPackage.MODEL_WIDGET)) + } + } + + @Check + def void checkWidgetIcon(ModelWidget w) { + if ((w.icon !== null || w.iconRules !== null) && w.staticIcon !== null) { + val node = NodeModelUtils.getNode(w) + val line = node.getStartLine() + error("Widget '" + w.getClass().getSimpleName() + "' has icon '" + w.icon + "' and staticIcon '" + w.staticIcon + "' defined at the same time " + line, + SitemapPackage.Literals.MODEL_INPUT.getEStructuralFeature(SitemapPackage.MODEL_WIDGET)) + } + if (w.icon !== null && w.iconRules !== null) { + val node = NodeModelUtils.getNode(w) + val line = node.getStartLine() + error("Widget '" + w.getClass().getSimpleName() + "' has icon '" + w.icon + "' and icon rules '" + w.iconRules + "' defined at the same time " + line, + SitemapPackage.Literals.MODEL_INPUT.getEStructuralFeature(SitemapPackage.MODEL_WIDGET)) + } + } @Check - def void checkFramesInFrame(Frame frame) { - for (Widget w : frame.children) { - if (w instanceof Frame) { - error("Frames must not contain other frames", - SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN)); + def void checkFramesInFrame(ModelFrame frame) { + for (ModelWidget w : frame.children) { + val node = NodeModelUtils.getNode(w) + val line = node.getStartLine() + if (w instanceof ModelFrame) { + error("Frames must not contain other frames at line " + line, + SitemapPackage.Literals.MODEL_FRAME.getEStructuralFeature(SitemapPackage.MODEL_FRAME__CHILDREN)); return; } - if (w instanceof Button) { - error("Frames should not contain Button, Button is allowed only in Buttongrid", - SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN)); + if (w instanceof ModelButton) { + error("Frames should not contain Button, Button is allowed only in Buttongrid at line " + line, + SitemapPackage.Literals.MODEL_FRAME.getEStructuralFeature(SitemapPackage.MODEL_FRAME__CHILDREN)); return; } } } @Check - def void checkFramesInWidgetList(Sitemap sitemap) { + def void checkFramesInWidgetList(ModelSitemap sitemap) { var containsFrames = false var containsOtherWidgets = false - for (Widget w : sitemap.children) { - if (w instanceof Button) { - error("Sitemap should not contain Button, Button is allowed only in Buttongrid", - SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME)); + for (ModelWidget w : sitemap.children) { + val node = NodeModelUtils.getNode(w) + val line = node.getStartLine() + if (w instanceof ModelButton) { + error("Sitemap should not contain Button, Button is allowed only in Buttongrid at line " + line, + SitemapPackage.Literals.MODEL_SITEMAP.getEStructuralFeature(SitemapPackage.MODEL_SITEMAP__NAME)); return; } - if (w instanceof Frame) { + if (w instanceof ModelFrame) { containsFrames = true } else { containsOtherWidgets = true } if (containsFrames && containsOtherWidgets) { - error("Sitemap should contain either only frames or none at all", - SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME)); + error("Sitemap should contain either only frames or none at all at line " + line, + SitemapPackage.Literals.MODEL_SITEMAP.getEStructuralFeature(SitemapPackage.MODEL_SITEMAP__NAME)); return } } } @Check - def void checkFramesInWidgetList(LinkableWidget widget) { - if (widget instanceof Frame) { + def void checkFramesInWidgetList(ModelLinkableWidget widget) { + if (widget instanceof ModelFrame) { // we have a dedicated check for frames in place return; } - if (widget instanceof Buttongrid) { + if (widget instanceof ModelButtongrid) { // we have a dedicated check for Buttongrid in place return; } var containsFrames = false var containsOtherWidgets = false - for (Widget w : widget.children) { - if (w instanceof Button) { - error("Linkable widget should not contain Button, Button is allowed only in Buttongrid", - SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN)); + for (ModelWidget w : widget.children) { + val node = NodeModelUtils.getNode(w) + val line = node.getStartLine() + if (w instanceof ModelButton) { + error("Linkable widget should not contain Button, Button is allowed only in Buttongrid at line " + line, + SitemapPackage.Literals.MODEL_FRAME.getEStructuralFeature(SitemapPackage.MODEL_LINKABLE_WIDGET__CHILDREN)); return; } - if (w instanceof Frame) { + if (w instanceof ModelFrame) { containsFrames = true } else { containsOtherWidgets = true } if (containsFrames && containsOtherWidgets) { - error("Linkable widget should contain either only frames or none at all", - SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN)); + error("Linkable widget should contain either only frames or none at all at line " + line, + SitemapPackage.Literals.MODEL_FRAME.getEStructuralFeature(SitemapPackage.MODEL_LINKABLE_WIDGET__CHILDREN)); return } } } @Check - def void checkWidgetsInButtongrid(Buttongrid grid) { - val nb = grid.getButtons.size() + def void checkWidgetsInButtongrid(ModelButtongrid grid) { + val nb = grid.getButtons !== null ? grid.getButtons.getElements.size : 0 if (nb > 0 && grid.item === null) { - error("To use the \"buttons\" parameter in a Buttongrid, the \"item\" parameter is required", - SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__ITEM)); + val node = NodeModelUtils.getNode(grid) + val line = node.getStartLine() + error("To use the \"buttons\" parameter in a Buttongrid, the \"item\" parameter is required at line " + line, + SitemapPackage.Literals.MODEL_BUTTONGRID.getEStructuralFeature(SitemapPackage.MODEL_BUTTONGRID__ITEM)); } - for (Widget w : grid.children) { - if (!(w instanceof Button)) { - error("Buttongrid must contain only Button", - SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__CHILDREN)); + for (ModelWidget w : grid.children) { + if (!(w instanceof ModelButton)) { + val node = NodeModelUtils.getNode(w) + val line = node.getStartLine() + error("Buttongrid must contain only Button at line " + line, + SitemapPackage.Literals.MODEL_BUTTONGRID.getEStructuralFeature(SitemapPackage.MODEL_BUTTONGRID__CHILDREN)); return; } } } @Check - def void checkSetpoints(Setpoint sp) { + def void checkSetpointParameters(ModelSetpoint sp) { + val node = NodeModelUtils.getNode(sp) + val line = node.getStartLine() if (BigDecimal.ZERO == sp.step) { - error("Setpoint on item '" + sp.item + "' has step size of 0", - SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP)); + error("Setpoint widget has step size of '0' at line " + line, + SitemapPackage.Literals.MODEL_SETPOINT.getEStructuralFeature(SitemapPackage.MODEL_SETPOINT__STEP)); } - if (sp.step !== null && sp.step < BigDecimal.ZERO) { - error("Setpoint on item '" + sp.item + "' has negative step size", - SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP)); + error("Setpoint has negative step size of '" + sp.step + "' at line " + line, + SitemapPackage.Literals.MODEL_SETPOINT.getEStructuralFeature(SitemapPackage.MODEL_SETPOINT__STEP)); } - if (sp.minValue !== null && sp.maxValue !== null && sp.minValue > sp.maxValue) { - error("Setpoint on item '" + sp.item + "' has larger minValue than maxValue", - SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__MIN_VALUE)); + error("Setpoint on item has larger minValue '" + sp.minValue + "' than maxValue '" + sp.maxValue + "' at line " + line, + SitemapPackage.Literals.MODEL_SETPOINT.getEStructuralFeature(SitemapPackage.MODEL_SETPOINT__MIN_VALUE)); + } + } + + @Check + def void checkSliderParameters(ModelSlider s) { + val node = NodeModelUtils.getNode(s) + val line = node.getStartLine() + if (BigDecimal.ZERO == s.step) { + error("Slider widget has step size of '0' at line " + line, + SitemapPackage.Literals.MODEL_SLIDER.getEStructuralFeature(SitemapPackage.MODEL_SLIDER__STEP)); + } + if (s.step !== null && s.step < BigDecimal.ZERO) { + error("Slider has negative step size of '" + s.step + "' at line " + line, + SitemapPackage.Literals.MODEL_SLIDER.getEStructuralFeature(SitemapPackage.MODEL_SLIDER__STEP)); + } + if (s.minValue !== null && s.maxValue !== null && s.minValue > s.maxValue) { + error("Slider on item has larger minValue '" + s.minValue + "' than maxValue '" + s.maxValue + "' at line " + line, + SitemapPackage.Literals.MODEL_SLIDER.getEStructuralFeature(SitemapPackage.MODEL_SLIDER__MIN_VALUE)); } } @Check - def void checkColortemperaturepicker(Colortemperaturepicker ctp) { + def void checkColortemperaturepickerParameters(ModelColortemperaturepicker ctp) { if (ctp.minValue !== null && ctp.maxValue !== null && ctp.minValue > ctp.maxValue) { - error("Colortemperaturepicker on item '" + ctp.item + "' has larger minValue than maxValue", - SitemapPackage.Literals.COLORTEMPERATUREPICKER.getEStructuralFeature(SitemapPackage.COLORTEMPERATUREPICKER__MIN_VALUE)); + val node = NodeModelUtils.getNode(ctp) + val line = node.getStartLine() + error("Colortemperaturepicker widget has larger minValue '" + ctp.minValue + "' than maxValue '" + ctp.maxValue + "' at line " + line, + SitemapPackage.Literals.MODEL_COLORTEMPERATUREPICKER.getEStructuralFeature(SitemapPackage.MODEL_COLORTEMPERATUREPICKER__MIN_VALUE)); } } @Check - def void checkInputHintParameter(Input i) { + def void checkInputParameters(ModelInput i) { if (i.inputHint !== null && !ALLOWED_HINTS.contains(i.inputHint)) { val node = NodeModelUtils.getNode(i) val line = node.getStartLine() - error("Input on item '" + i.item + "' has invalid inputHint '" + i.inputHint + "' at line " + line, - SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.INPUT__INPUT_HINT)) + error("Input widget has invalid inputHint '" + i.inputHint + "' at line " + line, + SitemapPackage.Literals.MODEL_INPUT.getEStructuralFeature(SitemapPackage.MODEL_INPUT__INPUT_HINT)) } } @Check - def void checkInterpolationParameter(Chart i) { - if (i.interpolation !== null && !ALLOWED_INTERPOLATION.contains(i.interpolation)) { - val node = NodeModelUtils.getNode(i) + def void checkChartParameters(ModelChart c) { + val node = NodeModelUtils.getNode(c) + val line = node.getStartLine() + if (c.interpolation !== null && !ALLOWED_INTERPOLATION.contains(c.interpolation)) { + error("Chart widget has invalid interpolation '" + c.interpolation + "' at line " + line, + SitemapPackage.Literals.MODEL_INPUT.getEStructuralFeature(SitemapPackage.MODEL_CHART__INTERPOLATION)) + } + if (c.period === null) { + error("Chart widget doesn't have period defined at line " + line, + SitemapPackage.Literals.MODEL_INPUT.getEStructuralFeature(SitemapPackage.MODEL_CHART__PERIOD)) + } + } + + @Check + def void checkVideoParameters(ModelVideo v) { + if (v.url === null) { + val node = NodeModelUtils.getNode(v) val line = node.getStartLine() - error("Input on item '" + i.item + "' has invalid interpolation '" + i.interpolation + "' at line " + line, - SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.CHART__INTERPOLATION)) + error("Video widget doesn't have url defined at line " + line, + SitemapPackage.Literals.MODEL_INPUT.getEStructuralFeature(SitemapPackage.MODEL_VIDEO__URL)) } } } diff --git a/bundles/org.openhab.core.sitemap/.classpath b/bundles/org.openhab.core.sitemap/.classpath new file mode 100644 index 00000000000..84dfbfdf1df --- /dev/null +++ b/bundles/org.openhab.core.sitemap/.classpath @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.core.sitemap/.project b/bundles/org.openhab.core.sitemap/.project new file mode 100644 index 00000000000..584c1430e8c --- /dev/null +++ b/bundles/org.openhab.core.sitemap/.project @@ -0,0 +1,23 @@ + + + org.openhab.core.sitemap + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.openhab.core.sitemap/NOTICE b/bundles/org.openhab.core.sitemap/NOTICE new file mode 100644 index 00000000000..6c17d0d8a45 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/NOTICE @@ -0,0 +1,14 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-core + diff --git a/bundles/org.openhab.core.sitemap/pom.xml b/bundles/org.openhab.core.sitemap/pom.xml new file mode 100644 index 00000000000..b9af5ea135b --- /dev/null +++ b/bundles/org.openhab.core.sitemap/pom.xml @@ -0,0 +1,25 @@ + + + + 4.0.0 + + + org.openhab.core.bundles + org.openhab.core.reactor.bundles + 5.1.0-SNAPSHOT + + + org.openhab.core.sitemap + + openHAB Core :: Bundles :: Sitemap + + + + org.openhab.core.bundles + org.openhab.core + ${project.version} + + + + diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Button.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Button.java new file mode 100644 index 00000000000..6746fbc0629 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Button.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap {@link Button} widget. Button widgets should have a parent {@link Buttongrid} widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Button extends NonLinkableWidget { + + /** + * Get button row in grid. + * + * @return row + */ + int getRow(); + + /** + * Set button row in grid. + * + * @param row + */ + void setRow(int row); + + /** + * Get button column in grid. + * + * @return column + */ + int getColumn(); + + /** + * Set button column in grid. + * + * @param column + */ + void setColumn(int column); + + /** + * True if the button is stateless, by default a button is stateful. + * + * @return stateless + */ + boolean isStateless(); + + /** + * Set stateless parameter for button. + * + * @param stateless + */ + void setStateless(@Nullable Boolean stateless); + + /** + * Get button command, will be executed when the button is clicked. + * + * @return cmd + */ + String getCmd(); + + /** + * Set button command. + * + * @param cmd + */ + void setCmd(String cmd); + + /** + * Get button release command, will be executed when the button is released. + * + * @return releaseCmd + */ + @Nullable + String getReleaseCmd(); + + /** + * Set the button release command. + * + * @param releaseCmd + */ + void setReleaseCmd(@Nullable String releaseCmd); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/ButtonDefinition.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/ButtonDefinition.java new file mode 100644 index 00000000000..cf97c81a1a1 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/ButtonDefinition.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap {@link Buttongrid} button definition. All buttons will act on the same item defined in + * the button grid. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface ButtonDefinition { + + /** + * Get button row in grid. + * + * @return row + */ + int getRow(); + + /** + * Set button row in grid. + * + * @param row + */ + void setRow(int row); + + /** + * Get button column in grid. + * + * @return column + */ + int getColumn(); + + /** + * Set button column in grid. + * + * @param column + */ + void setColumn(int column); + + /** + * Get button command. + * + * @return cmd + */ + String getCmd(); + + /** + * Set button command. + * + * @param cmd + */ + void setCmd(String cmd); + + /** + * Get button label. + * + * @return label + */ + String getLabel(); + + /** + * Set button label. + * + * @param label + */ + void setLabel(String label); + + /** + * Get button icon. + * + * @return icon + */ + @Nullable + String getIcon(); + + /** + * Set button icon. + * + * @param icon + */ + void setIcon(@Nullable String icon); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java new file mode 100644 index 00000000000..b8fbcf0b014 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap Buttongrid widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Buttongrid extends LinkableWidget { + + /** + * Get the button grid buttons. This method should return a modifiable list, allowing updates to the list of + * buttons. + * + * @return buttons + */ + List getButtons(); + + /** + * Replace the button grid buttons a new list of buttons. + * + * @param buttons + */ + void setButtons(List buttons); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java new file mode 100644 index 00000000000..25c17d5505b --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Chart widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Chart extends NonLinkableWidget { + + /** + * Get the configured persistence service, if no service is configured, the default service should be used. + * + * @return service + */ + @Nullable + String getService(); + + /** + * Set the persistence service. + * + * @param service + */ + void setService(String service); + + /** + * Get the chart refresh interval in s. If no interval is set, 0 should be returned. + * + * @return refresh + */ + int getRefresh(); + + /** + * Set the chart refresh interval in s. + * + * @param refresh + */ + void setRefresh(@Nullable Integer refresh); + + /** + * Get the configured chart time period. See {@link setPeriod()}. + * + * @return period + */ + String getPeriod(); + + /** + * Set the chart time axis scale.The time axis can be either entirely in the past ending at the present time, + * entirely in the future starting at the present time, or partly in the past and partly in the future around the + * present time. To do this, the value can be composed of two parts separated by the "-" character, the value before + * the "-" is then the scale in the past and the value after the "-" is the scale in the future. Valid values before + * and after the central character "-" are h, 2h, 3h, ..., D, 2D, 3D, ..., W, 2W, 3W, ..., M, 2M, 3M, ..., Y, 2Y, + * ... and any valid duration following the ISO8601 duration notation such as P1Y6M for the last year and a half or + * PT1H30M for the last hour and a half. If only a period is provided, i.e. without the final "-" character or + * without anything after the "-" character, only a period in the past is taken into account. + * + * @param period + */ + void setPeriod(String period); + + /** + * Return true if legend should be shown. + * + * @return legend + */ + boolean hasLegend(); + + /** + * Set to true if legend should be shown. If not set, the legend will not be shown if there is only a single series + * in the chart. + * + * @param legend + */ + void setLegend(@Nullable Boolean legend); + + /** + * Return true if the group item will be shown instead of items in the group. + * + * @return forceAsItem + */ + boolean forceAsItem(); + + /** + * Set to true if group item should be shown in the chart instead of items in the group (default). + * + * @param forceAsItem + */ + void setForceAsItem(@Nullable Boolean forceAsItem); + + /** + * Get the y axis value format pattern. + * + * @return yAxisDecimalPattern + */ + @Nullable + String getYAxisDecimalPattern(); + + /** + * Set the format for values on the y axis. It accepts {@link java.text.DecimalFormat}. For example with #.## a + * number has 2 decimals. + * + * @param yAxisDecimalPattern + */ + void setYAxisDecimalPattern(@Nullable String yAxisDecimalPattern); + + /** + * Gets the interpolation parameter. See {@link setInterpolation()}. + * + * @return interpolation + */ + @Nullable + String getInterpolation(); + + /** + * Sets the interpolation parameter. The interpolation parameter is used to change how the line is drawn between 2 + * datapoints. By default, a horizontal line (step) will be drawn between 2 datapoints of Switch or Contact items. + * All other item types will have a line (linear) connecting the datapoints. With the "linear" or "step" value for + * this parameter, this default behaviour can be changed. + * + * @param interpolation + */ + void setInterpolation(@Nullable String interpolation); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colorpicker.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colorpicker.java new file mode 100644 index 00000000000..612448f60f4 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colorpicker.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap Colorpicker widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Colorpicker extends NonLinkableWidget { + +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colortemperaturepicker.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colortemperaturepicker.java new file mode 100644 index 00000000000..2f11b80f399 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Colortemperaturepicker.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Colortemperaturepicker widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Colortemperaturepicker extends NonLinkableWidget { + + /** + * Get minimum color temperature value. + * + * @return minValue + */ + @Nullable + BigDecimal getMinValue(); + + /** + * Set minimum color temperature value. + * + * @param minValue + */ + void setMinValue(@Nullable BigDecimal minValue); + + /** + * Get maximum color temperature value. + * + * @return maxValue + */ + @Nullable + BigDecimal getMaxValue(); + + /** + * Set maximum color temperature value. + * + * @param maxValue + */ + void setMaxValue(@Nullable BigDecimal maxValue); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java new file mode 100644 index 00000000000..a15be7e4bba --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap rule condition. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Condition { + + /** + * Get the item for which the state will be used in the condition evaluation. If no item is set (null returned), the + * item of + * the widget will be used. + * + * @return item + */ + @Nullable + String getItem(); + + /** + * Set the item for which the state will be used in the condition evaluation. + * + * @param item + */ + void setItem(@Nullable String item); + + /** + * Get the condition comparator. Valid values are: "==", ">", "<", ">=", "<=", "!=". The item in the condition will + * be compared against the value using this comparator. If no condition comparator is set, "==" is assumed. + * + * @return condition comparator + */ + @Nullable + String getCondition(); + + /** + * Set the condition comparator, see {@link getCondition()}. + * + * @param condition + */ + void setCondition(@Nullable String condition); + + /** + * Get the condition comparison value. + * + * @return value + */ + String getValue(); + + /** + * Set the condition comparison value. + * + * @param value + */ + void setValue(String value); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Default.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Default.java new file mode 100644 index 00000000000..3d0273d63ea --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Default.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Default widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Default extends NonLinkableWidget { + + /** + * Get the configured height of the widget. If no height is configured, 0 should be returned. + * + * @return height + */ + int getHeight(); + + /** + * Set the height of the widget, null means no height is configured. + * + * @param height + */ + void setHeight(@Nullable Integer height); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Frame.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Frame.java new file mode 100644 index 00000000000..2aae836a07a --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Frame.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap Frame widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Frame extends LinkableWidget { + +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Group.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Group.java new file mode 100644 index 00000000000..e014f3c4054 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Group.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap Group widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Group extends LinkableWidget { + +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Image.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Image.java new file mode 100644 index 00000000000..c73a789f3d7 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Image.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Image widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Image extends LinkableWidget { + + /** + * Get the url of the image. + * + * @return url + */ + @Nullable + String getUrl(); + + /** + * Set the url of the video. + * + * @param url + */ + void setUrl(@Nullable String url); + + /** + * Get the image refresh interval in s. If no interval is set, 0 should be returned. + * + * @return refresh + */ + int getRefresh(); + + /** + * Set the image refresh interval in s. + * + * @param refresh + */ + void setRefresh(@Nullable Integer refresh); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java new file mode 100644 index 00000000000..1aee018a321 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Input widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Input extends NonLinkableWidget { + + /** + * Get the input hint. This can be used by a UI to tailor the representation. See {@link setInputHint()}. + * + * @return input hint + */ + @Nullable + String getInputHint(); + + /** + * Set the input hint, allowed values are: "text", "number", "date", "time", "datetime". This can be used by a UI to + * tailor the representation. + * + * @param inputHint + */ + void setInputHint(@Nullable String inputHint); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/LinkableWidget.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/LinkableWidget.java new file mode 100644 index 00000000000..1df3de265f8 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/LinkableWidget.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap linkable widget (a widget that can have children). + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface LinkableWidget extends Widget, Parent { + + /** + * Get the child {@link Widget}s. This method should return a modifiable list, allowing updates to the child + * widgets. + * + * @return widgets + */ + List getWidgets(); + + /** + * Replace the child widgets with a new list of widgets. + * + * @param widgets + */ + void setWidgets(List widgets); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapping.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapping.java new file mode 100644 index 00000000000..42aa4c81169 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapping.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap widget Mapping. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Mapping { + + /** + * Get the click command mapped to the widget item state. + * + * @return cmd + */ + String getCmd(); + + /** + * Set the click command mapped to the widget item state. + * + * @param cmd + */ + void setCmd(String cmd); + + /** + * + * Get the release command mapped to the widget item state. + * + * @return releaseCmd + */ + @Nullable + String getReleaseCmd(); + + /** + * Set the release command mapped to the widget item state. + * + * @param releaseCmd + */ + void setReleaseCmd(@Nullable String releaseCmd); + + /** + * Get the label mapped to the widget item state. + * + * @return label + */ + String getLabel(); + + /** + * Set the label mapped to the widget item state. + * + * @param label + */ + void setLabel(String label); + + /** + * Get the icon mapped to the widget item state. + * + * @return icon + */ + @Nullable + String getIcon(); + + /** + * Set the label mapped to the widget item state. + * + * @param icon + */ + void setIcon(@Nullable String icon); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapview.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapview.java new file mode 100644 index 00000000000..fd9445b7746 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Mapview.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Mapview widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Mapview extends NonLinkableWidget { + + /** + * Get the configured height of the widget. If no height is configured, 0 should be returned. + * + * @return height + */ + int getHeight(); + + /** + * Set the height of the widget, null means no height is configured. + * + * @param height + */ + void setHeight(@Nullable Integer height); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/NonLinkableWidget.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/NonLinkableWidget.java new file mode 100644 index 00000000000..78efdd1512c --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/NonLinkableWidget.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap non-linkable widget (no children). + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface NonLinkableWidget extends Widget { + +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Parent.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Parent.java new file mode 100644 index 00000000000..36a013491f1 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Parent.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Interface representing all sitemap entities that can be parents, should be extended by Sitemap and LinkableWidget. + * This is a marker interface. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Parent { + +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Rule.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Rule.java new file mode 100644 index 00000000000..ba7972c0f6a --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Rule.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap widget icon, color or visibility rule. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Rule { + + /** + * Get the rule conditions. This method should return a modifiable list, allowing updates to conditions. + * + * @return conditions + */ + List getConditions(); + + /** + * Replace the rule conditions with a new list of conditions. + * + * @param conditions + */ + void setConditions(List conditions); + + /** + * Get the rule argument for icon or color rules. The rule argument is the resulting value if the rule is met. + * Visibility rules don't have an argument, always work on the full widget. + * + * @return argument + */ + @Nullable + String getArgument(); + + /** + * Set the rule argument. + * + * @param argument + */ + void setArgument(@Nullable String argument); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Selection.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Selection.java new file mode 100644 index 00000000000..49a27648ba0 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Selection.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap Selection widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Selection extends NonLinkableWidget { + + /** + * Get the switch {@link Mapping}s. This method should return a modifiable list, allowing updates to the mappings. + * + * @return mappings + */ + List getMappings(); + + /** + * Replace the widget mappings with a new list of mappings. + * + * @param mappings + */ + void setMappings(List mappings); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Setpoint.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Setpoint.java new file mode 100644 index 00000000000..eddabb28c2e --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Setpoint.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Setpoint widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Setpoint extends NonLinkableWidget { + + /** + * Get minimum setpoint value. + * + * @return minValue + */ + @Nullable + BigDecimal getMinValue(); + + /** + * Set minimum setpoint value. + * + * @param minValue + */ + void setMinValue(@Nullable BigDecimal minValue); + + /** + * Get maximum setpoint value. + * + * @return maxValue + */ + @Nullable + BigDecimal getMaxValue(); + + /** + * Set maximum setpoint value. + * + * @param maxValue + */ + void setMaxValue(@Nullable BigDecimal maxValue); + + /** + * Get setpoint step. + * + * @return step + */ + @Nullable + BigDecimal getStep(); + + /** + * Set setpoint step. + * + * @param step + */ + void setStep(@Nullable BigDecimal step); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java new file mode 100644 index 00000000000..9924de25278 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.Identifiable; + +/** + * A representation of a 'Sitemap'. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Sitemap extends Identifiable, Parent { + + /** + * Returns the sitemap name. + * + * @return sitemap name. + * @see #setName(String) + */ + String getName(); + + /** + * Sets the sitemap name. + * + * @param name the new sitemap name. + * @see #getName() + */ + void setName(String name); + + /** + * Returns the sitemap label. + * + * @return sitemap label. + * @see #setLabel(String) + */ + @Nullable + String getLabel(); + + /** + * Sets the sitemap label. + * + * @param label the new sitemap label. + * @see #getLabel() + */ + void setLabel(@Nullable String label); + + /** + * Returns the sitemap icon. + * + * @return sitemap icon. + * @see #setIcon(String) + */ + @Nullable + String getIcon(); + + /** + * Sets the sitemap icon. + * + * @param icon the new sitemap icon. + * @see #getIcon() + */ + void setIcon(@Nullable String icon); + + /** + * Returns the top level list of widgets in the sitemap. The returned list is a modifiable list. + * + * @return list of widgets. + */ + List getWidgets(); + + /** + * Replace the sitemap child widgets with a new list of widgets. + * + * @param widgets + */ + void setWidgets(List widgets); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Slider.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Slider.java new file mode 100644 index 00000000000..72d435b3610 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Slider.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Slider widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Slider extends NonLinkableWidget { + + /** + * Return true if the UI should render switch capabilities for the slider. + * + * @return true if switch enabled + */ + boolean isSwitchEnabled(); + + /** + * Set switch enabled. + * + * @param switchEnabled + */ + void setSwitchEnabled(@Nullable Boolean switchEnabled); + + /** + * Return true if the UI should only send updates to core on mouse release. + * + * @return true if release only + */ + boolean isReleaseOnly(); + + /** + * Set release only. + * + * @param releaseOnly + */ + void setReleaseOnly(@Nullable Boolean releaseOnly); + + /** + * Get minimum slider value. + * + * @return minValue + */ + @Nullable + BigDecimal getMinValue(); + + /** + * Set minimum slider value. + * + * @param minValue + */ + void setMinValue(@Nullable BigDecimal minValue); + + /** + * Get maximum slider value. + * + * @return maxValue + */ + @Nullable + BigDecimal getMaxValue(); + + /** + * Set maximum slider value. + * + * @param maxValue + */ + void setMaxValue(@Nullable BigDecimal maxValue); + + /** + * Get slider step. + * + * @return step + */ + @Nullable + BigDecimal getStep(); + + /** + * Set slider step. + * + * @param step + */ + void setStep(@Nullable BigDecimal step); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Switch.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Switch.java new file mode 100644 index 00000000000..b1e039772ae --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Switch.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap Switch widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Switch extends NonLinkableWidget { + + /** + * Get the switch {@link Mapping}s. This method should return a modifiable list, allowing updates to the mappings. + * + * @return mappings + */ + List getMappings(); + + /** + * Replace the widget mappings with a new list of mappings. + * + * @param mappings + */ + void setMappings(List mappings); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Text.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Text.java new file mode 100644 index 00000000000..3cfaa8107f9 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Text.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A representation of a sitemap Text widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Text extends LinkableWidget { + +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Video.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Video.java new file mode 100644 index 00000000000..2b8b2cd5be0 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Video.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Video widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Video extends NonLinkableWidget { + + /** + * Get the url of the video. + * + * @return url + */ + String getUrl(); + + /** + * Set the url of the video. + * + * @param url + */ + void setUrl(String url); + + /** + * Get the configured video encoding. + * + * @return encoding, null if no encoding is configured + */ + @Nullable + String getEncoding(); + + /** + * Set the video encoding. + * + * @param encoding + */ + void setEncoding(@Nullable String encoding); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Webview.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Webview.java new file mode 100644 index 00000000000..10ec91dfdad --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Webview.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap Webview widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault + +public interface Webview extends NonLinkableWidget { + + /** + * Get the configured height of the widget. If no height is configured, 0 should be returned. + * + * @return height + */ + int getHeight(); + + /** + * Set the height of the widget, null means no height is configured. + * + * @param height + */ + void setHeight(@Nullable Integer height); + + /** + * Get the url to be embedded in the {@link Webview} embedded frame. + * + * @return url + */ + String getUrl(); + + /** + * Set the url to be embedded in the {@link Webview} frame. + * + * @param url + */ + void setUrl(String url); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java new file mode 100644 index 00000000000..c689a95ca3c --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A representation of a sitemap widget. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface Widget { + + /** + * Get the direct parent {@link Widget} or {@link Sitemap}. + * + * @return parent + */ + @Nullable + Parent getParent(); + + /** + * Sets the parent {@link Widget} or {@link Sitemap}. + * Widgets in a sitemap should always have a parent. Implementations of {@link Widget} should have a constructor + * with {@link Parent} parameter to make building a sitemap easier. + * + * @param parent + */ + void setParent(Parent parent); + + /** + * Gets the item name for the widget. For specific widget type, the item is required and for these widgets, this + * method should not return null. + * + * @return item, or null if no item defined for the widget + */ + @Nullable + String getItem(); + + /** + * Sets the widget item. + * + * @param item + */ + void setItem(@Nullable String item); + + /** + * Get widget label. + * + * @return label + */ + @Nullable + String getLabel(); + + /** + * Set widget label. + * + * @param label + */ + void setLabel(@Nullable String label); + + /** + * Get widget icon. + * + * @return icon + */ + @Nullable + String getIcon(); + + /** + * Set widget icon. + * + * @param icon + */ + void setIcon(@Nullable String icon); + + /** + * Get the widget icon rules. This method should return a modifiable list, allowing updates to the icon rules. + * + * @return icon rules + */ + List getIconRules(); + + /** + * Replace the widget icon rules with a new list of icon rules. + * + * @param iconRules + */ + void setIconRules(List iconRules); + + /** + * True if the widget icon is static, false otherwise. + * + * @return static icon + */ + boolean isStaticIcon(); + + /** + * Set to true if the widget icon is static. + * + * @param staticIcon + */ + void setStaticIcon(@Nullable Boolean staticIcon); + + /** + * Get the widget label color rules. This method should return a modifiable list, allowing updates to the label + * color rules. + * + * @return label color rules + */ + List getLabelColor(); + + /** + * Replace the widget label color rules with a new list of label color rules. + * + * @param labelColor + */ + void setLabelColor(List labelColor); + + /** + * Get the widget value color rules. This method should return a modifiable list, allowing updates to the value + * color rules. + * + * @return value color rules + */ + List getValueColor(); + + /** + * Replace the widget value color rules with a new list of value color rules. + * + * @param valueColor + */ + void setValueColor(List valueColor); + + /** + * Get the widget icon color rules. This method should return a modifiable list, allowing updates to the icon + * color rules. + * + * @return icon color rules + */ + List getIconColor(); + + /** + * Replace the widget icon color rules with a new list of icon color rules. + * + * @param iconColor + */ + void setIconColor(List iconColor); + + /** + * Get the widget visibility rules. This method should return a modifiable list, allowing updates to the visibility + * rules. + * + * @return visibility rules + */ + List getVisibility(); + + /** + * Replace the widget visibility rules with a new list of visibility rules. + * + * @param visibility + */ + void setVisibility(List visiblity); + + /** + * Get type of widget. + * + * @return widget type + */ + String getWidgetType(); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonDefinitionImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonDefinitionImpl.java new file mode 100644 index 00000000000..dc04b30827c --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonDefinitionImpl.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.ButtonDefinition; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ButtonDefinitionImpl implements ButtonDefinition { + + private int row; + private int column; + private String cmd = ""; + private String label = ""; + private @Nullable String icon; + + @Override + public int getRow() { + return row; + } + + @Override + public void setRow(int row) { + this.row = row; + } + + @Override + public int getColumn() { + return column; + } + + @Override + public void setColumn(int column) { + this.column = column; + } + + @Override + public String getCmd() { + return cmd; + } + + @Override + public void setCmd(String cmd) { + this.cmd = cmd; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public void setLabel(String label) { + this.label = label; + } + + @Override + public @Nullable String getIcon() { + return icon; + } + + @Override + public void setIcon(@Nullable String icon) { + this.icon = icon; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonImpl.java new file mode 100644 index 00000000000..45d28552f08 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtonImpl.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Button; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ButtonImpl extends NonLinkableWidgetImpl implements Button { + + private int row; + private int column; + private @Nullable Boolean stateless; + private String cmd = ""; + private @Nullable String releaseCmd; + + public ButtonImpl() { + super(); + } + + public ButtonImpl(Parent parent) { + super(parent); + } + + @Override + public int getRow() { + return row; + } + + @Override + public void setRow(int row) { + this.row = row; + } + + @Override + public int getColumn() { + return column; + } + + @Override + public void setColumn(int column) { + this.column = column; + } + + @Override + public boolean isStateless() { + return stateless != null ? stateless : false; + } + + @Override + public void setStateless(@Nullable Boolean stateless) { + this.stateless = stateless; + } + + @Override + public String getCmd() { + return cmd; + } + + @Override + public void setCmd(String cmd) { + this.cmd = cmd; + } + + @Override + public @Nullable String getReleaseCmd() { + return releaseCmd; + } + + @Override + public void setReleaseCmd(@Nullable String releaseCmd) { + this.releaseCmd = releaseCmd; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtongridImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtongridImpl.java new file mode 100644 index 00000000000..c552b3f4b25 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ButtongridImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.ButtonDefinition; +import org.openhab.core.sitemap.Buttongrid; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ButtongridImpl extends LinkableWidgetImpl implements Buttongrid { + + private List buttons = new CopyOnWriteArrayList<>(); + + public ButtongridImpl() { + super(); + } + + public ButtongridImpl(Parent parent) { + super(parent); + } + + @Override + public List getButtons() { + return buttons; + } + + @Override + public void setButtons(List buttons) { + this.buttons = new CopyOnWriteArrayList<>(buttons); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ChartImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ChartImpl.java new file mode 100644 index 00000000000..24d35cf8db5 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ChartImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Chart; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ChartImpl extends NonLinkableWidgetImpl implements Chart { + + private @Nullable String service; + private @Nullable Integer refresh; + private String period = ""; + private @Nullable Boolean legend; + private @Nullable Boolean forceAsItem; + private @Nullable String yAxisDecimalPattern; + private @Nullable String interpolation; + + public ChartImpl() { + super(); + } + + public ChartImpl(Parent parent) { + super(parent); + } + + @Override + public @Nullable String getService() { + return service; + } + + @Override + public void setService(String service) { + this.service = service; + } + + @Override + public int getRefresh() { + return refresh != null ? refresh : 0; + } + + @Override + public void setRefresh(@Nullable Integer refresh) { + this.refresh = refresh; + } + + @Override + public String getPeriod() { + return period; + } + + @Override + public void setPeriod(String period) { + this.period = period; + } + + @Override + public boolean hasLegend() { + return legend != null ? legend : false; + } + + @Override + public void setLegend(@Nullable Boolean legend) { + this.legend = legend; + } + + @Override + public boolean forceAsItem() { + return forceAsItem != null ? forceAsItem : false; + } + + @Override + public void setForceAsItem(@Nullable Boolean forceAsItem) { + this.forceAsItem = forceAsItem; + } + + @Override + public @Nullable String getYAxisDecimalPattern() { + return yAxisDecimalPattern; + } + + @Override + public void setYAxisDecimalPattern(@Nullable String yAxisDecimalPattern) { + this.yAxisDecimalPattern = yAxisDecimalPattern; + } + + @Override + public @Nullable String getInterpolation() { + return interpolation; + } + + @Override + public void setInterpolation(@Nullable String interpolation) { + this.interpolation = interpolation; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColorpickerImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColorpickerImpl.java new file mode 100644 index 00000000000..3ceab871a3b --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColorpickerImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.Colorpicker; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ColorpickerImpl extends NonLinkableWidgetImpl implements Colorpicker { + + public ColorpickerImpl() { + super(); + } + + public ColorpickerImpl(Parent parent) { + super(parent); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColortemperaturepickerImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColortemperaturepickerImpl.java new file mode 100644 index 00000000000..2fa001d603c --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ColortemperaturepickerImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Colortemperaturepicker; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ColortemperaturepickerImpl extends NonLinkableWidgetImpl implements Colortemperaturepicker { + + private @Nullable BigDecimal minValue; + private @Nullable BigDecimal maxValue; + + public ColortemperaturepickerImpl() { + super(); + } + + public ColortemperaturepickerImpl(Parent parent) { + super(parent); + } + + @Override + public @Nullable BigDecimal getMinValue() { + return minValue; + } + + @Override + public void setMinValue(@Nullable BigDecimal minValue) { + this.minValue = minValue; + } + + @Override + public @Nullable BigDecimal getMaxValue() { + return maxValue; + } + + @Override + public void setMaxValue(@Nullable BigDecimal maxValue) { + this.maxValue = maxValue; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ConditionImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ConditionImpl.java new file mode 100644 index 00000000000..976e99f668e --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ConditionImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Condition; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ConditionImpl implements Condition { + + private @Nullable String item; + private @Nullable String condition; + private String value = ""; + + public ConditionImpl() { + } + + public ConditionImpl(Condition condition) { + this.item = condition.getItem(); + this.condition = condition.getCondition(); + this.value = condition.getValue(); + } + + @Override + public @Nullable String getItem() { + return item; + } + + @Override + public void setItem(@Nullable String item) { + this.item = item; + } + + @Override + public @Nullable String getCondition() { + return condition; + } + + @Override + public void setCondition(@Nullable String condition) { + this.condition = condition; + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/DefaultImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/DefaultImpl.java new file mode 100644 index 00000000000..985848e152f --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/DefaultImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Default; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class DefaultImpl extends NonLinkableWidgetImpl implements Default { + + private @Nullable Integer height; + + public DefaultImpl() { + super(); + } + + public DefaultImpl(Parent parent) { + super(parent); + } + + @Override + public int getHeight() { + return height != null ? height : 0; + } + + @Override + public void setHeight(@Nullable Integer height) { + this.height = height; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/FrameImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/FrameImpl.java new file mode 100644 index 00000000000..f2466d75e16 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/FrameImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.Frame; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class FrameImpl extends LinkableWidgetImpl implements Frame { + + public FrameImpl() { + super(); + } + + public FrameImpl(Parent parent) { + super(parent); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/GroupImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/GroupImpl.java new file mode 100644 index 00000000000..f4c01fb6838 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/GroupImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.Group; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class GroupImpl extends LinkableWidgetImpl implements Group { + + public GroupImpl() { + super(); + } + + public GroupImpl(Parent parent) { + super(parent); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ImageImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ImageImpl.java new file mode 100644 index 00000000000..8e8e86d15fd --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/ImageImpl.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Image; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class ImageImpl extends LinkableWidgetImpl implements Image { + + private @Nullable String url; + private @Nullable Integer refresh; + + public ImageImpl() { + super(); + } + + public ImageImpl(Parent parent) { + super(parent); + } + + @Override + public @Nullable String getUrl() { + return url; + } + + @Override + public void setUrl(@Nullable String url) { + this.url = url; + } + + @Override + public int getRefresh() { + return refresh != null ? refresh : 0; + } + + @Override + public void setRefresh(@Nullable Integer refresh) { + this.refresh = refresh; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/InputImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/InputImpl.java new file mode 100644 index 00000000000..ad4828a722b --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/InputImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Input; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class InputImpl extends NonLinkableWidgetImpl implements Input { + + private @Nullable String inputHint; + + public InputImpl() { + super(); + } + + public InputImpl(Parent parent) { + super(parent); + } + + @Override + public @Nullable String getInputHint() { + return inputHint; + } + + @Override + public void setInputHint(@Nullable String inputHint) { + this.inputHint = inputHint; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/LinkableWidgetImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/LinkableWidgetImpl.java new file mode 100644 index 00000000000..35236b3e1d4 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/LinkableWidgetImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.LinkableWidget; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Widget; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class LinkableWidgetImpl extends WidgetImpl implements LinkableWidget { + + private List widgets = new CopyOnWriteArrayList<>(); + + public LinkableWidgetImpl() { + super(); + } + + public LinkableWidgetImpl(Parent parent) { + super(parent); + } + + @Override + public List getWidgets() { + return widgets; + } + + @Override + public void setWidgets(List widgets) { + this.widgets = new CopyOnWriteArrayList<>(widgets); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MappingImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MappingImpl.java new file mode 100644 index 00000000000..9e8a6ac0a6d --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MappingImpl.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Mapping; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class MappingImpl implements Mapping { + + private String cmd = ""; + private @Nullable String releaseCmd; + private String label = ""; + private @Nullable String icon; + + @Override + public String getCmd() { + return cmd; + } + + @Override + public void setCmd(String cmd) { + this.cmd = cmd; + } + + @Override + public @Nullable String getReleaseCmd() { + return releaseCmd; + } + + @Override + public void setReleaseCmd(@Nullable String releaseCmd) { + this.releaseCmd = releaseCmd; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public void setLabel(String label) { + this.label = label; + } + + @Override + public @Nullable String getIcon() { + return icon; + } + + @Override + public void setIcon(@Nullable String icon) { + this.icon = icon; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MapviewImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MapviewImpl.java new file mode 100644 index 00000000000..e60df23c08a --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/MapviewImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Mapview; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class MapviewImpl extends NonLinkableWidgetImpl implements Mapview { + + private @Nullable Integer height; + + public MapviewImpl() { + super(); + } + + public MapviewImpl(Parent parent) { + super(parent); + } + + @Override + public int getHeight() { + return height != null ? height : 0; + } + + @Override + public void setHeight(@Nullable Integer height) { + this.height = height; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/NonLinkableWidgetImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/NonLinkableWidgetImpl.java new file mode 100644 index 00000000000..b7ef6be63d3 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/NonLinkableWidgetImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.NonLinkableWidget; +import org.openhab.core.sitemap.Parent; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class NonLinkableWidgetImpl extends WidgetImpl implements NonLinkableWidget { + + public NonLinkableWidgetImpl() { + super(); + } + + public NonLinkableWidgetImpl(Parent parent) { + super(parent); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/RuleImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/RuleImpl.java new file mode 100644 index 00000000000..5a8f809c21b --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/RuleImpl.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Rule; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class RuleImpl implements Rule { + + private List conditions = new CopyOnWriteArrayList<>(); + private @Nullable String argument; + + public RuleImpl() { + } + + public RuleImpl(Rule rule) { + this.conditions = rule.getConditions(); + this.argument = rule.getArgument(); + } + + @Override + public List getConditions() { + return conditions; + } + + @Override + public void setConditions(List conditions) { + this.conditions = new CopyOnWriteArrayList<>(conditions); + } + + @Override + public @Nullable String getArgument() { + return argument; + } + + @Override + public void setArgument(@Nullable String argument) { + this.argument = argument; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SelectionImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SelectionImpl.java new file mode 100644 index 00000000000..ce507f48d39 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SelectionImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Selection; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class SelectionImpl extends NonLinkableWidgetImpl implements Selection { + + private List mappings = new CopyOnWriteArrayList<>(); + + public SelectionImpl() { + super(); + } + + public SelectionImpl(Parent parent) { + super(parent); + } + + @Override + public List getMappings() { + return mappings; + } + + @Override + public void setMappings(List mappings) { + this.mappings = new CopyOnWriteArrayList<>(mappings); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SetpointImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SetpointImpl.java new file mode 100644 index 00000000000..bc0ab015c1b --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SetpointImpl.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Setpoint; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class SetpointImpl extends NonLinkableWidgetImpl implements Setpoint { + + private @Nullable BigDecimal minValue; + private @Nullable BigDecimal maxValue; + private @Nullable BigDecimal step; + + public SetpointImpl() { + super(); + } + + public SetpointImpl(Parent parent) { + super(parent); + } + + @Override + public @Nullable BigDecimal getMinValue() { + return minValue; + } + + @Override + public void setMinValue(@Nullable BigDecimal minValue) { + this.minValue = minValue; + } + + @Override + public @Nullable BigDecimal getMaxValue() { + return maxValue; + } + + @Override + public void setMaxValue(@Nullable BigDecimal maxValue) { + this.maxValue = maxValue; + } + + @Override + public @Nullable BigDecimal getStep() { + return step; + } + + @Override + public void setStep(@Nullable BigDecimal step) { + this.step = step; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SitemapImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SitemapImpl.java new file mode 100644 index 00000000000..4785436f7e5 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SitemapImpl.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Widget; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class SitemapImpl implements Sitemap { + + private String name = ""; + private @Nullable String label; + private @Nullable String icon; + private List widgets = new CopyOnWriteArrayList<>(); + + public SitemapImpl() { + } + + public SitemapImpl(String name) { + this.name = name; + } + + @Override + public String getUID() { + return name; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public @Nullable String getLabel() { + return label; + } + + @Override + public void setLabel(@Nullable String label) { + this.label = label; + } + + @Override + public @Nullable String getIcon() { + return icon; + } + + @Override + public void setIcon(@Nullable String icon) { + this.icon = icon; + } + + @Override + public List getWidgets() { + return widgets; + } + + @Override + public void setWidgets(List widgets) { + this.widgets = new CopyOnWriteArrayList<>(widgets); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SliderImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SliderImpl.java new file mode 100644 index 00000000000..32ba566cadc --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SliderImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Slider; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class SliderImpl extends NonLinkableWidgetImpl implements Slider { + + private @Nullable Boolean switchEnabled; + private @Nullable Boolean releaseOnly; + private @Nullable BigDecimal minValue; + private @Nullable BigDecimal maxValue; + private @Nullable BigDecimal step; + + public SliderImpl() { + super(); + } + + public SliderImpl(Parent parent) { + super(parent); + } + + @Override + public boolean isSwitchEnabled() { + return switchEnabled != null ? switchEnabled : false; + } + + @Override + public void setSwitchEnabled(@Nullable Boolean switchEnabled) { + this.switchEnabled = switchEnabled; + } + + @Override + public boolean isReleaseOnly() { + return releaseOnly != null ? releaseOnly : false; + } + + @Override + public void setReleaseOnly(@Nullable Boolean releaseOnly) { + this.releaseOnly = releaseOnly; + } + + @Override + public @Nullable BigDecimal getMinValue() { + return minValue; + } + + @Override + public void setMinValue(@Nullable BigDecimal minValue) { + this.minValue = minValue; + } + + @Override + public @Nullable BigDecimal getMaxValue() { + return maxValue; + } + + @Override + public void setMaxValue(@Nullable BigDecimal maxValue) { + this.maxValue = maxValue; + } + + @Override + public @Nullable BigDecimal getStep() { + return step; + } + + @Override + public void setStep(@Nullable BigDecimal step) { + this.step = step; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SwitchImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SwitchImpl.java new file mode 100644 index 00000000000..0d0ae361810 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/SwitchImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Switch; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class SwitchImpl extends NonLinkableWidgetImpl implements Switch { + + private List mappings = new CopyOnWriteArrayList<>(); + + public SwitchImpl() { + super(); + } + + public SwitchImpl(Parent parent) { + super(parent); + } + + @Override + public List getMappings() { + return mappings; + } + + @Override + public void setMappings(List mappings) { + this.mappings = new CopyOnWriteArrayList<>(mappings); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/TextImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/TextImpl.java new file mode 100644 index 00000000000..ef19158e78a --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/TextImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Text; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class TextImpl extends LinkableWidgetImpl implements Text { + + public TextImpl() { + super(); + } + + public TextImpl(Parent parent) { + super(parent); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/VideoImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/VideoImpl.java new file mode 100644 index 00000000000..8ed56ccb211 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/VideoImpl.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Video; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class VideoImpl extends NonLinkableWidgetImpl implements Video { + + private String url = ""; + private @Nullable String encoding; + + public VideoImpl() { + super(); + } + + public VideoImpl(Parent parent) { + super(parent); + } + + @Override + public String getUrl() { + return url; + } + + @Override + public void setUrl(String url) { + this.url = url; + } + + @Override + public @Nullable String getEncoding() { + return encoding; + } + + @Override + public void setEncoding(@Nullable String encoding) { + this.encoding = encoding; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WebviewImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WebviewImpl.java new file mode 100644 index 00000000000..e8d5f5f040c --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WebviewImpl.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Webview; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class WebviewImpl extends NonLinkableWidgetImpl implements Webview { + + private @Nullable Integer height; + private String url = ""; + + public WebviewImpl() { + super(); + } + + public WebviewImpl(Parent parent) { + super(parent); + } + + @Override + public int getHeight() { + return height != null ? height : 0; + } + + @Override + public void setHeight(@Nullable Integer height) { + this.height = height; + } + + @Override + public String getUrl() { + return url; + } + + @Override + public void setUrl(String url) { + this.url = url; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java new file mode 100644 index 00000000000..e53a5de3514 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Widget; + +/** + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public class WidgetImpl implements Widget { + + private @Nullable Parent parent; + + private @Nullable String item; + private @Nullable String label; + + private @Nullable String icon; + private List iconRules = new CopyOnWriteArrayList<>(); + private @Nullable Boolean staticIcon; + + private List labelColor = new CopyOnWriteArrayList<>(); + private List valueColor = new CopyOnWriteArrayList<>(); + private List iconColor = new CopyOnWriteArrayList<>(); + private List visibility = new CopyOnWriteArrayList<>(); + + public WidgetImpl() { + } + + public WidgetImpl(Parent parent) { + this.parent = parent; + } + + @Override + public @Nullable Parent getParent() { + return parent; + } + + @Override + public void setParent(Parent parent) { + this.parent = parent; + } + + @Override + public @Nullable String getItem() { + return item; + } + + @Override + public void setItem(@Nullable String item) { + this.item = item; + } + + @Override + public @Nullable String getLabel() { + return label; + } + + @Override + public void setLabel(@Nullable String label) { + this.label = label; + } + + @Override + public @Nullable String getIcon() { + return icon; + } + + @Override + public void setIcon(@Nullable String icon) { + this.icon = icon; + } + + @Override + public List getIconRules() { + return iconRules; + } + + @Override + public void setIconRules(List iconRules) { + this.iconRules = new CopyOnWriteArrayList<>(iconRules); + } + + @Override + public boolean isStaticIcon() { + return staticIcon == null ? false : staticIcon; + } + + @Override + public void setStaticIcon(@Nullable Boolean staticIcon) { + this.staticIcon = staticIcon; + } + + @Override + public List getLabelColor() { + return labelColor; + } + + @Override + public void setLabelColor(List labelColor) { + this.labelColor = new CopyOnWriteArrayList<>(labelColor); + } + + @Override + public List getValueColor() { + return valueColor; + } + + @Override + public void setValueColor(List valueColor) { + this.valueColor = new CopyOnWriteArrayList<>(valueColor); + } + + @Override + public List getIconColor() { + return iconColor; + } + + @Override + public void setIconColor(List iconColor) { + this.iconColor = new CopyOnWriteArrayList<>(iconColor); + } + + @Override + public List getVisibility() { + return visibility; + } + + @Override + public void setVisibility(List visibility) { + this.visibility = new CopyOnWriteArrayList<>(visibility); + } + + @Override + public String getWidgetType() { + return this.getClass().getInterfaces()[0].getSimpleName(); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java new file mode 100644 index 00000000000..5d3739a5480 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal.registry; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.ButtonDefinition; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.internal.ButtonDefinitionImpl; +import org.openhab.core.sitemap.internal.ButtonImpl; +import org.openhab.core.sitemap.internal.ButtongridImpl; +import org.openhab.core.sitemap.internal.ChartImpl; +import org.openhab.core.sitemap.internal.ColorpickerImpl; +import org.openhab.core.sitemap.internal.ColortemperaturepickerImpl; +import org.openhab.core.sitemap.internal.ConditionImpl; +import org.openhab.core.sitemap.internal.DefaultImpl; +import org.openhab.core.sitemap.internal.FrameImpl; +import org.openhab.core.sitemap.internal.GroupImpl; +import org.openhab.core.sitemap.internal.ImageImpl; +import org.openhab.core.sitemap.internal.InputImpl; +import org.openhab.core.sitemap.internal.MappingImpl; +import org.openhab.core.sitemap.internal.MapviewImpl; +import org.openhab.core.sitemap.internal.RuleImpl; +import org.openhab.core.sitemap.internal.SelectionImpl; +import org.openhab.core.sitemap.internal.SetpointImpl; +import org.openhab.core.sitemap.internal.SitemapImpl; +import org.openhab.core.sitemap.internal.SliderImpl; +import org.openhab.core.sitemap.internal.SwitchImpl; +import org.openhab.core.sitemap.internal.TextImpl; +import org.openhab.core.sitemap.internal.VideoImpl; +import org.openhab.core.sitemap.internal.WebviewImpl; +import org.openhab.core.sitemap.registry.SitemapFactory; +import org.openhab.core.sitemap.registry.SitemapRegistry; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link SitemapFactoryImpl} implements the {@link SitemapRegistry} + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +@Component(service = SitemapFactory.class, immediate = true) +public class SitemapFactoryImpl implements SitemapFactory { + + // Sitemap widget types + public static final String BUTTON = "Button"; + public static final String BUTTON_GRID = "Buttongrid"; + public static final String CHART = "Chart"; + public static final String COLOR_PICKER = "Colorpicker"; + public static final String COLOR_TEMPERATURE_PICKER = "Colortemperaturepicker"; + public static final String DEFAULT = "Default"; + public static final String FRAME = "Frame"; + public static final String GROUP = "Group"; + public static final String IMAGE = "Image"; + public static final String INPUT = "Input"; + public static final String MAPVIEW = "Mapview"; + public static final String SELECTION = "Selection"; + public static final String SETPOINT = "Setpoint"; + public static final String SLIDER = "Slider"; + public static final String SWITCH = "Switch"; + public static final String TEXT = "Text"; + public static final String VIDEO = "Video"; + public static final String WEBVIEW = "Webview"; + + private static final String[] WIDGET_TYPES = { BUTTON, BUTTON_GRID, CHART, COLOR_PICKER, COLOR_TEMPERATURE_PICKER, + DEFAULT, FRAME, GROUP, IMAGE, INPUT, MAPVIEW, SELECTION, SETPOINT, SLIDER, SWITCH, TEXT, VIDEO, WEBVIEW }; + + @Override + public Sitemap createSitemap(String sitemapName) { + return new SitemapImpl(sitemapName); + } + + @Override + public @Nullable Widget createWidget(String widgetTypeName) { + return switch (widgetTypeName) { + case BUTTON -> new ButtonImpl(); + case BUTTON_GRID -> new ButtongridImpl(); + case CHART -> new ChartImpl(); + case COLOR_PICKER -> new ColorpickerImpl(); + case COLOR_TEMPERATURE_PICKER -> new ColortemperaturepickerImpl(); + case DEFAULT -> new DefaultImpl(); + case FRAME -> new FrameImpl(); + case GROUP -> new GroupImpl(); + case IMAGE -> new ImageImpl(); + case INPUT -> new InputImpl(); + case MAPVIEW -> new MapviewImpl(); + case SELECTION -> new SelectionImpl(); + case SETPOINT -> new SetpointImpl(); + case SLIDER -> new SliderImpl(); + case SWITCH -> new SwitchImpl(); + case TEXT -> new TextImpl(); + case VIDEO -> new VideoImpl(); + case WEBVIEW -> new WebviewImpl(); + default -> null; + }; + } + + @Override + public @Nullable Widget createWidget(String widgetTypeName, Parent parent) { + return switch (widgetTypeName) { + case BUTTON -> new ButtonImpl(parent); + case BUTTON_GRID -> new ButtongridImpl(parent); + case CHART -> new ChartImpl(parent); + case COLOR_PICKER -> new ColorpickerImpl(parent); + case COLOR_TEMPERATURE_PICKER -> new ColortemperaturepickerImpl(parent); + case DEFAULT -> new DefaultImpl(parent); + case FRAME -> new FrameImpl(parent); + case GROUP -> new GroupImpl(parent); + case IMAGE -> new ImageImpl(parent); + case INPUT -> new InputImpl(parent); + case MAPVIEW -> new MapviewImpl(parent); + case SELECTION -> new SelectionImpl(parent); + case SETPOINT -> new SetpointImpl(parent); + case SLIDER -> new SliderImpl(parent); + case SWITCH -> new SwitchImpl(parent); + case TEXT -> new TextImpl(parent); + case VIDEO -> new VideoImpl(parent); + case WEBVIEW -> new WebviewImpl(parent); + default -> null; + }; + } + + @Override + public ButtonDefinition createButtonDefinition() { + return new ButtonDefinitionImpl(); + } + + @Override + public Mapping createMapping() { + return new MappingImpl(); + } + + @Override + public Rule createRule() { + return new RuleImpl(); + } + + @Override + public Condition createCondition() { + return new ConditionImpl(); + } + + @Override + public String[] getSupportedWidgetTypes() { + return WIDGET_TYPES; + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java new file mode 100644 index 00000000000..d83644bab94 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.internal.registry; + +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.common.registry.AbstractRegistry; +import org.openhab.core.common.registry.Provider; +import org.openhab.core.common.registry.RegistryChangeListener; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.registry.SitemapProvider; +import org.openhab.core.sitemap.registry.SitemapRegistry; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link SitemapRegistryImpl} implements the {@link SitemapRegistry} + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +@Component(service = SitemapRegistry.class, immediate = true) +public class SitemapRegistryImpl extends AbstractRegistry implements SitemapRegistry { + + public SitemapRegistryImpl() { + super(null); + } + + private final Set> registryChangeListeners = new CopyOnWriteArraySet<>(); + + @Override + protected void notifyListenersAboutAddedElement(Sitemap element) { + registryChangeListeners.forEach(listener -> listener.added(element)); + super.notifyListenersAboutAddedElement(element); + } + + @Override + protected void notifyListenersAboutRemovedElement(Sitemap element) { + registryChangeListeners.forEach(listener -> listener.removed(element)); + super.notifyListenersAboutRemovedElement(element); + } + + @Override + protected void notifyListenersAboutUpdatedElement(Sitemap oldElement, Sitemap element) { + registryChangeListeners.forEach(listener -> listener.updated(oldElement, element)); + } + + @Override + public void addRegistryChangeListener(RegistryChangeListener listener) { + registryChangeListeners.add(listener); + } + + @Override + public void removeRegistryChangeListener(RegistryChangeListener listener) { + registryChangeListeners.remove(listener); + } + + @Override + public void addSitemapProvider(Provider provider) { + addProvider(provider); + } + + @Override + public void removeSitemapProvider(Provider provider) { + removeProvider(provider); + } +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapFactory.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapFactory.java new file mode 100644 index 00000000000..0f4c667bf28 --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapFactory.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.registry; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.sitemap.ButtonDefinition; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Widget; + +/** + * The {@link SitemapFactory} is used to create {@link Sitemap}s and {@link Widget}s from their type string. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface SitemapFactory { + + /** + * Creates a new {@link Sitemap} instance with name sitemapName + * + * @param sitemapName + * @return a new Sitemap. + */ + Sitemap createSitemap(String sitemapName); + + /** + * Creates a new {@link Widget} instance of type widgetTypeName + * + * @param widgetTypeName + * @return a new Widget of type widgetTypeName or null if no matching class is known. + */ + @Nullable + Widget createWidget(String widgetTypeName); + + /** + * Creates a new {@link Widget} instance of type widgetTypeName and with {@link Parent} + * parent + * + * @param widgetTypeName + * @param parent + * @return a new Widget of type widgetTypeName or null if no matching class is known. + */ + @Nullable + Widget createWidget(String widgetTypeName, Parent parent); + + /** + * Creates a {@link ButtonDefinition} instance + * + * @return a new ButtonDefinition. + */ + ButtonDefinition createButtonDefinition(); + + /** + * Creates a {@link Mapping} instance + * + * @return a new Mapping. + */ + Mapping createMapping(); + + /** + * Creates a {@link Rule} instance + * + * @return a new Rule. + */ + Rule createRule(); + + /** + * Creates a {@link Rule} {@link Condition} instance + * + * @return a new Rule Condition. + */ + Condition createCondition(); + + /** + * Returns the list of all supported WidgetTypes of this Factory. + * + * @return the supported WidgetTypes + */ + String[] getSupportedWidgetTypes(); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapProvider.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapProvider.java new file mode 100644 index 00000000000..673cdbe721d --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.registry; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.Provider; +import org.openhab.core.sitemap.Sitemap; + +/** + * {@link SitemapProvider} should be implemented by any service that provides {@link Sitemap}s to a + * {@link SitemapRegistry}. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface SitemapProvider extends Provider { + + /** + * Get a sitemap from the provider. + * + * @param sitemapName + * @return sitemap + */ + @Nullable + Sitemap getSitemap(String sitemapName); + + /** + * Get the names of all sitemaps available from the provider + * + * @return sitemap names + */ + Set getSitemapNames(); +} diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapRegistry.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapRegistry.java new file mode 100644 index 00000000000..d39de996d7e --- /dev/null +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/registry/SitemapRegistry.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.sitemap.registry; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.common.registry.Provider; +import org.openhab.core.common.registry.Registry; +import org.openhab.core.sitemap.Sitemap; + +/** + * The {@link SitemapRegistry} is the central place to store sitemaps. + * Sitemaps are registered through {@link SitemapProvider}. + * + * @author Mark Herwege - Initial contribution + */ +@NonNullByDefault +public interface SitemapRegistry extends Registry { + + /** + * Add a sitemap provider to the registry. + * + * @param provider + */ + public void addSitemapProvider(Provider provider); + + /** + * Remove a sitemap provider from the registry. + * + * @param provider + */ + public void removeSitemapProvider(Provider provider); +} diff --git a/bundles/org.openhab.core.ui/pom.xml b/bundles/org.openhab.core.ui/pom.xml index 8a295495ca5..dbb90cfee16 100644 --- a/bundles/org.openhab.core.ui/pom.xml +++ b/bundles/org.openhab.core.ui/pom.xml @@ -70,19 +70,14 @@ org.openhab.core.bundles - org.openhab.core.transform + org.openhab.core.sitemap ${project.version} org.openhab.core.bundles - org.openhab.core.model.sitemap + org.openhab.core.transform ${project.version} - - org.openhab.core.bom - org.openhab.core.bom.compile-model - pom - diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java index 901c4152cb3..dd97321b07f 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java @@ -12,6 +12,7 @@ */ package org.openhab.core.ui.internal.components; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; @@ -19,60 +20,46 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.emf.common.util.EList; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.AbstractProvider; import org.openhab.core.common.registry.RegistryChangeListener; import org.openhab.core.config.core.ConfigUtil; -import org.openhab.core.model.core.EventType; -import org.openhab.core.model.core.ModelRepositoryChangeListener; -import org.openhab.core.model.sitemap.SitemapProvider; -import org.openhab.core.model.sitemap.sitemap.ButtonDefinition; -import org.openhab.core.model.sitemap.sitemap.ColorArray; -import org.openhab.core.model.sitemap.sitemap.IconRule; -import org.openhab.core.model.sitemap.sitemap.LinkableWidget; -import org.openhab.core.model.sitemap.sitemap.Mapping; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.SitemapFactory; -import org.openhab.core.model.sitemap.sitemap.SitemapPackage; -import org.openhab.core.model.sitemap.sitemap.VisibilityRule; -import org.openhab.core.model.sitemap.sitemap.Widget; -import org.openhab.core.model.sitemap.sitemap.impl.ButtonDefinitionImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ButtonImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ButtongridImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ChartImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ColorArrayImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ColorpickerImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ColortemperaturepickerImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ConditionImpl; -import org.openhab.core.model.sitemap.sitemap.impl.DefaultImpl; -import org.openhab.core.model.sitemap.sitemap.impl.FrameImpl; -import org.openhab.core.model.sitemap.sitemap.impl.GroupImpl; -import org.openhab.core.model.sitemap.sitemap.impl.IconRuleImpl; -import org.openhab.core.model.sitemap.sitemap.impl.ImageImpl; -import org.openhab.core.model.sitemap.sitemap.impl.InputImpl; -import org.openhab.core.model.sitemap.sitemap.impl.MappingImpl; -import org.openhab.core.model.sitemap.sitemap.impl.MapviewImpl; -import org.openhab.core.model.sitemap.sitemap.impl.SelectionImpl; -import org.openhab.core.model.sitemap.sitemap.impl.SetpointImpl; -import org.openhab.core.model.sitemap.sitemap.impl.SitemapImpl; -import org.openhab.core.model.sitemap.sitemap.impl.SliderImpl; -import org.openhab.core.model.sitemap.sitemap.impl.SwitchImpl; -import org.openhab.core.model.sitemap.sitemap.impl.TextImpl; -import org.openhab.core.model.sitemap.sitemap.impl.VideoImpl; -import org.openhab.core.model.sitemap.sitemap.impl.VisibilityRuleImpl; -import org.openhab.core.model.sitemap.sitemap.impl.WebviewImpl; -import org.openhab.core.model.sitemap.sitemap.impl.WidgetImpl; +import org.openhab.core.sitemap.Button; +import org.openhab.core.sitemap.ButtonDefinition; +import org.openhab.core.sitemap.Buttongrid; +import org.openhab.core.sitemap.Chart; +import org.openhab.core.sitemap.Colortemperaturepicker; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Default; +import org.openhab.core.sitemap.Image; +import org.openhab.core.sitemap.Input; +import org.openhab.core.sitemap.LinkableWidget; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Mapview; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Selection; +import org.openhab.core.sitemap.Setpoint; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Slider; +import org.openhab.core.sitemap.Video; +import org.openhab.core.sitemap.Webview; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.registry.SitemapFactory; +import org.openhab.core.sitemap.registry.SitemapProvider; +import org.openhab.core.sitemap.registry.SitemapRegistry; import org.openhab.core.ui.components.RootUIComponent; import org.openhab.core.ui.components.UIComponent; import org.openhab.core.ui.components.UIComponentRegistry; import org.openhab.core.ui.components.UIComponentRegistryFactory; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; @@ -90,82 +77,58 @@ * @author Mark Herwege - Make UI provided sitemaps compatible with enhanced syntax in conditions * @author Mark Herwege - Add support for Button element * @author Laurent Garnier - Added support for new sitemap element Colortemperaturepicker + * @author Mark Herwege - Implement sitemap registry */ @NonNullByDefault -@Component(service = SitemapProvider.class) -public class UIComponentSitemapProvider implements SitemapProvider, RegistryChangeListener { +@Component(service = SitemapProvider.class, immediate = true) +public class UIComponentSitemapProvider extends AbstractProvider + implements SitemapProvider, RegistryChangeListener { private final Logger logger = LoggerFactory.getLogger(UIComponentSitemapProvider.class); public static final String SITEMAP_NAMESPACE = "system:sitemap"; private static final String SITEMAP_PREFIX = "uicomponents_"; - private static final String SITEMAP_SUFFIX = ".sitemap"; private static final Pattern CONDITION_PATTERN = Pattern - .compile("((?[A-Za-z]\\w*)?\\s*(?==|!=|<=|>=|<|>))?\\s*(?\\+|-)?(?.+)"); + .compile("((?[A-Za-z]\\w*)?\\s*(?==|!=|<=|>=|<|>))?\\s*(?(\\+|-)?.+)"); private static final Pattern COMMANDS_PATTERN = Pattern.compile("^(?\"[^\"]*\"|[^\": ]*):(?.*)$"); private Map sitemaps = new HashMap<>(); private @Nullable UIComponentRegistryFactory componentRegistryFactory; private @Nullable UIComponentRegistry sitemapComponentRegistry; - private final Set modelChangeListeners = new CopyOnWriteArraySet<>(); + private final SitemapRegistry sitemapRegistry; + private final SitemapFactory sitemapFactory; + + @Activate + public UIComponentSitemapProvider(final @Reference SitemapRegistry sitemapRegistry, + final @Reference SitemapFactory sitemapFactory) { + this.sitemapRegistry = sitemapRegistry; + this.sitemapFactory = sitemapFactory; + sitemapRegistry.addSitemapProvider(this); + } + + @Deactivate + protected void deactivate() { + sitemapRegistry.removeSitemapProvider(this); + } @Override public @Nullable Sitemap getSitemap(String sitemapName) { - buildSitemap(sitemapName.replaceFirst(SITEMAP_PREFIX, "")); return sitemaps.get(sitemapName); } @Override public Set getSitemapNames() { - UIComponentRegistry registry = sitemapComponentRegistry; - if (registry == null) { - return Set.of(); - } - - sitemaps.clear(); - Collection rootComponents = registry.getAll(); - // try building all sitemaps to leave the invalid ones out - for (RootUIComponent rootComponent : rootComponents) { - try { - Sitemap sitemap = buildSitemap(rootComponent); - sitemaps.put(sitemap.getName(), sitemap); - } catch (Exception e) { - logger.error("Cannot build sitemap {}", rootComponent.getUID(), e); - } - } - return sitemaps.keySet(); } - protected @Nullable Sitemap buildSitemap(String sitemapName) { - UIComponentRegistry registry = sitemapComponentRegistry; - if (registry == null) { - return null; - } - - RootUIComponent rootComponent = registry.get(sitemapName); - if (rootComponent != null) { - try { - Sitemap sitemap = buildSitemap(rootComponent); - sitemaps.put(sitemap.getName(), sitemap); - return null; - } catch (Exception e) { - logger.error("Cannot build sitemap {}", rootComponent.getUID(), e); - } - } - - return null; - } - protected Sitemap buildSitemap(RootUIComponent rootComponent) { if (!"Sitemap".equals(rootComponent.getType())) { throw new IllegalArgumentException("Root component type is not Sitemap"); } - SitemapImpl sitemap = (SitemapImpl) SitemapFactory.eINSTANCE.createSitemap(); - sitemap.setName(SITEMAP_PREFIX + rootComponent.getUID()); + Sitemap sitemap = sitemapFactory.createSitemap(SITEMAP_PREFIX + rootComponent.getUID()); Object label = rootComponent.getConfig().get("label"); if (label != null) { sitemap.setLabel(label.toString()); @@ -173,168 +136,113 @@ protected Sitemap buildSitemap(RootUIComponent rootComponent) { if (rootComponent.getSlots() != null && rootComponent.getSlots().containsKey("widgets")) { for (UIComponent component : rootComponent.getSlot("widgets")) { - Widget widget = buildWidget(component); + Widget widget = buildWidget(component, sitemap); if (widget != null) { - sitemap.getChildren().add(widget); + sitemap.getWidgets().add(widget); } } } + sitemaps.put(sitemap.getName(), sitemap); return sitemap; } - protected @Nullable Widget buildWidget(UIComponent component) { - Widget widget = null; - - switch (component.getType()) { - case "Frame": - FrameImpl frameWidget = (FrameImpl) SitemapFactory.eINSTANCE.createFrame(); - widget = frameWidget; - break; - case "Text": - TextImpl textWidget = (TextImpl) SitemapFactory.eINSTANCE.createText(); - widget = textWidget; - break; - case "Group": - GroupImpl groupWidget = (GroupImpl) SitemapFactory.eINSTANCE.createGroup(); - widget = groupWidget; - break; - case "Image": - ImageImpl imageWidget = (ImageImpl) SitemapFactory.eINSTANCE.createImage(); - widget = imageWidget; - setWidgetPropertyFromComponentConfig(widget, component, "url", SitemapPackage.IMAGE__URL); - setWidgetPropertyFromComponentConfig(widget, component, "refresh", SitemapPackage.IMAGE__REFRESH); - break; - case "Video": - VideoImpl videoWidget = (VideoImpl) SitemapFactory.eINSTANCE.createVideo(); - widget = videoWidget; - setWidgetPropertyFromComponentConfig(widget, component, "url", SitemapPackage.VIDEO__URL); - setWidgetPropertyFromComponentConfig(widget, component, "encoding", SitemapPackage.VIDEO__ENCODING); - break; - case "Chart": - ChartImpl chartWidget = (ChartImpl) SitemapFactory.eINSTANCE.createChart(); - widget = chartWidget; - setWidgetPropertyFromComponentConfig(widget, component, "service", SitemapPackage.CHART__SERVICE); - setWidgetPropertyFromComponentConfig(widget, component, "refresh", SitemapPackage.CHART__REFRESH); - setWidgetPropertyFromComponentConfig(widget, component, "period", SitemapPackage.CHART__PERIOD); - setWidgetPropertyFromComponentConfig(widget, component, "legend", SitemapPackage.CHART__LEGEND); - setWidgetPropertyFromComponentConfig(widget, component, "forceAsItem", - SitemapPackage.CHART__FORCE_AS_ITEM); - setWidgetPropertyFromComponentConfig(widget, component, "yAxisDecimalPattern", - SitemapPackage.CHART__YAXIS_DECIMAL_PATTERN); - setWidgetPropertyFromComponentConfig(widget, component, "interpolation", - SitemapPackage.CHART__INTERPOLATION); - break; - case "Webview": - WebviewImpl webviewWidget = (WebviewImpl) SitemapFactory.eINSTANCE.createWebview(); - widget = webviewWidget; - setWidgetPropertyFromComponentConfig(widget, component, "height", SitemapPackage.WEBVIEW__HEIGHT); - setWidgetPropertyFromComponentConfig(widget, component, "url", SitemapPackage.WEBVIEW__URL); - break; - case "Switch": - SwitchImpl switchWidget = (SwitchImpl) SitemapFactory.eINSTANCE.createSwitch(); - addWidgetMappings(switchWidget.getMappings(), component); - widget = switchWidget; - break; - case "Mapview": - MapviewImpl mapviewWidget = (MapviewImpl) SitemapFactory.eINSTANCE.createMapview(); - widget = mapviewWidget; - setWidgetPropertyFromComponentConfig(widget, component, "height", SitemapPackage.MAPVIEW__HEIGHT); - break; - case "Slider": - SliderImpl sliderWidget = (SliderImpl) SitemapFactory.eINSTANCE.createSlider(); - widget = sliderWidget; - setWidgetPropertyFromComponentConfig(widget, component, "minValue", SitemapPackage.SLIDER__MIN_VALUE); - setWidgetPropertyFromComponentConfig(widget, component, "maxValue", SitemapPackage.SLIDER__MAX_VALUE); - setWidgetPropertyFromComponentConfig(widget, component, "step", SitemapPackage.SLIDER__STEP); - setWidgetPropertyFromComponentConfig(widget, component, "switchEnabled", - SitemapPackage.SLIDER__SWITCH_ENABLED); - setWidgetPropertyFromComponentConfig(widget, component, "releaseOnly", - SitemapPackage.SLIDER__RELEASE_ONLY); - break; - case "Selection": - SelectionImpl selectionWidget = (SelectionImpl) SitemapFactory.eINSTANCE.createSelection(); - addWidgetMappings(selectionWidget.getMappings(), component); - widget = selectionWidget; - break; - case "Input": - InputImpl inputWidget = (InputImpl) SitemapFactory.eINSTANCE.createInput(); - widget = inputWidget; - setWidgetPropertyFromComponentConfig(widget, component, "inputHint", SitemapPackage.INPUT__INPUT_HINT); - break; - case "Setpoint": - SetpointImpl setpointWidget = (SetpointImpl) SitemapFactory.eINSTANCE.createSetpoint(); - widget = setpointWidget; - setWidgetPropertyFromComponentConfig(widget, component, "minValue", SitemapPackage.SETPOINT__MIN_VALUE); - setWidgetPropertyFromComponentConfig(widget, component, "maxValue", SitemapPackage.SETPOINT__MAX_VALUE); - setWidgetPropertyFromComponentConfig(widget, component, "step", SitemapPackage.SETPOINT__STEP); - break; - case "Colorpicker": - ColorpickerImpl colorpickerWidget = (ColorpickerImpl) SitemapFactory.eINSTANCE.createColorpicker(); - widget = colorpickerWidget; - break; - case "Colortemperaturepicker": - ColortemperaturepickerImpl colortemperaturepickerWidget = (ColortemperaturepickerImpl) SitemapFactory.eINSTANCE - .createColortemperaturepicker(); - widget = colortemperaturepickerWidget; - setWidgetPropertyFromComponentConfig(widget, component, "minValue", - SitemapPackage.COLORTEMPERATUREPICKER__MIN_VALUE); - setWidgetPropertyFromComponentConfig(widget, component, "maxValue", - SitemapPackage.COLORTEMPERATUREPICKER__MAX_VALUE); - break; - case "Buttongrid": - ButtongridImpl buttongridWidget = (ButtongridImpl) SitemapFactory.eINSTANCE.createButtongrid(); - addWidgetButtons(buttongridWidget.getButtons(), component); - widget = buttongridWidget; - break; - case "Button": - ButtonImpl buttonWidget = (ButtonImpl) SitemapFactory.eINSTANCE.createButton(); - widget = buttonWidget; - setWidgetPropertyFromComponentConfig(widget, component, "row", SitemapPackage.BUTTON__ROW); - setWidgetPropertyFromComponentConfig(widget, component, "column", SitemapPackage.BUTTON__COLUMN); - setWidgetPropertyFromComponentConfig(widget, component, "stateless", SitemapPackage.BUTTON__STATELESS); - setWidgetPropertyFromComponentConfig(widget, component, "cmd", SitemapPackage.BUTTON__CMD); - setWidgetPropertyFromComponentConfig(widget, component, "releaseCmd", - SitemapPackage.BUTTON__RELEASE_CMD); - break; - case "Default": - DefaultImpl defaultWidget = (DefaultImpl) SitemapFactory.eINSTANCE.createDefault(); - widget = defaultWidget; - setWidgetPropertyFromComponentConfig(widget, component, "height", SitemapPackage.DEFAULT__HEIGHT); - break; - default: - logger.warn("Unknown sitemap component type {}", component.getType()); - break; - } + protected @Nullable Widget buildWidget(UIComponent component, Parent parent) { + Widget widget = sitemapFactory.createWidget(component.getType(), parent); if (widget != null) { - setWidgetPropertyFromComponentConfig(widget, component, "label", SitemapPackage.WIDGET__LABEL); - setWidgetIconPropertyFromComponentConfig(widget, component); - setWidgetPropertyFromComponentConfig(widget, component, "item", SitemapPackage.WIDGET__ITEM); + switch (widget) { + case Image imageWidget: + setWidgetPropertyFromComponentConfig(imageWidget, component, "url"); + setWidgetPropertyFromComponentConfig(imageWidget, component, "refresh"); + break; + case Video videoWidget: + setWidgetPropertyFromComponentConfig(videoWidget, component, "url"); + setWidgetPropertyFromComponentConfig(videoWidget, component, "encoding"); + break; + case Chart chartWidget: + setWidgetPropertyFromComponentConfig(chartWidget, component, "service"); + setWidgetPropertyFromComponentConfig(chartWidget, component, "refresh"); + setWidgetPropertyFromComponentConfig(chartWidget, component, "period"); + setWidgetPropertyFromComponentConfig(chartWidget, component, "legend"); + setWidgetPropertyFromComponentConfig(chartWidget, component, "forceAsItem"); + setWidgetPropertyFromComponentConfig(chartWidget, component, "yAxisDecimalPattern"); + setWidgetPropertyFromComponentConfig(chartWidget, component, "interpolation"); + break; + case Webview webviewWidget: + setWidgetPropertyFromComponentConfig(webviewWidget, component, "height"); + setWidgetPropertyFromComponentConfig(webviewWidget, component, "url"); + break; + case Mapview mapviewWidget: + setWidgetPropertyFromComponentConfig(mapviewWidget, component, "height"); + break; + case Slider sliderWidget: + setWidgetPropertyFromComponentConfig(sliderWidget, component, "minValue"); + setWidgetPropertyFromComponentConfig(sliderWidget, component, "maxValue"); + setWidgetPropertyFromComponentConfig(sliderWidget, component, "step"); + setWidgetPropertyFromComponentConfig(sliderWidget, component, "switchEnabled"); + setWidgetPropertyFromComponentConfig(sliderWidget, component, "releaseOnly"); + break; + case Selection selectionWidget: + addWidgetMappings(selectionWidget.getMappings(), component); + break; + case Input inputWidget: + setWidgetPropertyFromComponentConfig(inputWidget, component, "inputHint"); + break; + case Setpoint setpointWidget: + setWidgetPropertyFromComponentConfig(setpointWidget, component, "minValue"); + setWidgetPropertyFromComponentConfig(setpointWidget, component, "maxValue"); + setWidgetPropertyFromComponentConfig(setpointWidget, component, "step"); + break; + case Colortemperaturepicker colortemperaturepickerWidget: + setWidgetPropertyFromComponentConfig(colortemperaturepickerWidget, component, "minValue"); + setWidgetPropertyFromComponentConfig(colortemperaturepickerWidget, component, "maxValue"); + break; + case Buttongrid buttongridWidget: + addWidgetButtons(buttongridWidget.getButtons(), component); + break; + case Button buttonWidget: + setWidgetPropertyFromComponentConfig(buttonWidget, component, "row"); + setWidgetPropertyFromComponentConfig(buttonWidget, component, "column"); + setWidgetPropertyFromComponentConfig(buttonWidget, component, "stateless"); + setWidgetPropertyFromComponentConfig(buttonWidget, component, "cmd"); + setWidgetPropertyFromComponentConfig(buttonWidget, component, "releaseCmd"); + break; + case Default defaultWidget: + setWidgetPropertyFromComponentConfig(defaultWidget, component, "height"); + break; + default: + break; + } + + setWidgetPropertyFromComponentConfig(widget, component, "item"); + setWidgetPropertyFromComponentConfig(widget, component, "label"); + setWidgetPropertyFromComponentConfig(widget, component, "icon"); + setWidgetPropertyFromComponentConfig(widget, component, "staticIcon"); if (widget instanceof LinkableWidget linkableWidget) { if (component.getSlots() != null && component.getSlots().containsKey("widgets")) { for (UIComponent childComponent : component.getSlot("widgets")) { - Widget childWidget = buildWidget(childComponent); + Widget childWidget = buildWidget(childComponent, linkableWidget); if (childWidget != null) { - linkableWidget.getChildren().add(childWidget); + linkableWidget.getWidgets().add(childWidget); } } } } - addWidgetVisibility(widget.getVisibility(), component); - addLabelColor(widget.getLabelColor(), component); - addValueColor(widget.getValueColor(), component); - addIconColor(widget.getIconColor(), component); - addIconRules(widget.getIconRules(), component); + addWidgetRules(widget.getVisibility(), component, "visibility"); + addWidgetRules(widget.getLabelColor(), component, "labelColor"); + addWidgetRules(widget.getValueColor(), component, "valueColor"); + addWidgetRules(widget.getIconColor(), component, "iconColor"); + addWidgetRules(widget.getIconRules(), component, "iconRules"); } return widget; } private void setWidgetPropertyFromComponentConfig(Widget widget, @Nullable UIComponent component, - String configParamName, int feature) { + String configParamName) { if (component == null || component.getConfig() == null) { return; } @@ -343,39 +251,26 @@ private void setWidgetPropertyFromComponentConfig(Widget widget, @Nullable UICom return; } try { - WidgetImpl widgetImpl = (WidgetImpl) widget; + String setterName = "set" + configParamName.substring(0, 1).toUpperCase() + configParamName.substring(1); Object normalizedValue = ConfigUtil.normalizeType(value); - if (widgetImpl.eGet(feature, false, false) instanceof Integer) { + Class clazz = widget.getClass(); + Method method = List.of(clazz.getMethods()).stream().filter(m -> m.getName().equals(setterName)).findFirst() + .get(); + Class argumentType = (method.getParameters()[0].getType()); + if (argumentType.equals(Integer.class) || argumentType.equals(int.class)) { normalizedValue = (normalizedValue instanceof BigDecimal bd) ? bd.intValue() : Integer.parseInt(normalizedValue.toString()); - } else if (widgetImpl.eGet(feature, false, false) instanceof Boolean + } else if ((argumentType.equals(Boolean.class) || argumentType.equals(boolean.class)) && !(normalizedValue instanceof Boolean)) { normalizedValue = Boolean.valueOf(normalizedValue.toString()); } - widgetImpl.eSet(feature, normalizedValue); + method.invoke(widget, normalizedValue); } catch (Exception e) { logger.warn("Cannot set {} parameter for {} widget parameter: {}", configParamName, component.getType(), e.getMessage()); } } - private void setWidgetIconPropertyFromComponentConfig(Widget widget, @Nullable UIComponent component) { - if (component == null || component.getConfig() == null) { - return; - } - Object staticIcon = component.getConfig().get("staticIcon"); - if (staticIcon != null && Boolean.parseBoolean(ConfigUtil.normalizeType(staticIcon).toString())) { - setWidgetPropertyFromComponentConfig(widget, component, "icon", SitemapPackage.WIDGET__STATIC_ICON); - return; - } - - Object icon = component.getConfig().get("icon"); - if (icon == null) { - return; - } - setWidgetPropertyFromComponentConfig(widget, component, "icon", SitemapPackage.WIDGET__ICON); - } - private @Nullable String stripQuotes(@Nullable String input) { if ((input != null) && (input.length() >= 2) && (input.charAt(0) == '\"') && (input.charAt(input.length() - 1) == '\"')) { @@ -385,7 +280,7 @@ private void setWidgetIconPropertyFromComponentConfig(Widget widget, @Nullable U } } - private void addWidgetMappings(EList mappings, UIComponent component) { + private void addWidgetMappings(List mappings, UIComponent component) { if (component.getConfig() != null && component.getConfig().containsKey("mappings")) { Object sourceMappings = component.getConfig().get("mappings"); if (sourceMappings instanceof Collection sourceMappingsCollection) { @@ -403,10 +298,10 @@ private void addWidgetMappings(EList mappings, UIComponent component) { releaseCmd = stripQuotes(releaseCmd); String label = stripQuotes(splitMapping[1].trim()); String icon = splitMapping.length < 3 ? null : stripQuotes(splitMapping[2].trim()); - MappingImpl mapping = (MappingImpl) SitemapFactory.eINSTANCE.createMapping(); - mapping.setCmd(cmd); + Mapping mapping = sitemapFactory.createMapping(); + mapping.setCmd(cmd != null ? cmd : ""); mapping.setReleaseCmd(releaseCmd); - mapping.setLabel(label); + mapping.setLabel(label != null ? label : ""); mapping.setIcon(icon); mappings.add(mapping); } @@ -415,7 +310,7 @@ private void addWidgetMappings(EList mappings, UIComponent component) { } } - private void addWidgetButtons(EList buttons, UIComponent component) { + private void addWidgetButtons(List buttons, UIComponent component) { if (component.getConfig() != null && component.getConfig().containsKey("buttons")) { Object sourceButtons = component.getConfig().get("buttons"); if (sourceButtons instanceof Collection sourceButtonsCollection) { @@ -428,12 +323,15 @@ private void addWidgetButtons(EList buttons, UIComponent compo String cmd = stripQuotes(splitted2[0].trim()); String label = stripQuotes(splitted2[1].trim()); String icon = splitted2.length < 3 ? null : stripQuotes(splitted2[2].trim()); - ButtonDefinitionImpl button = (ButtonDefinitionImpl) SitemapFactory.eINSTANCE - .createButtonDefinition(); + ButtonDefinition button = sitemapFactory.createButtonDefinition(); button.setRow(row); button.setColumn(column); - button.setCmd(cmd); - button.setLabel(label); + if (cmd != null) { + button.setCmd(cmd); + } + if (label != null) { + button.setLabel(label); + } button.setIcon(icon); buttons.add(button); } @@ -442,84 +340,37 @@ private void addWidgetButtons(EList buttons, UIComponent compo } } - private void addWidgetVisibility(EList visibility, UIComponent component) { - if (component.getConfig() != null && component.getConfig().containsKey("visibility")) { - Object sourceVisibilities = component.getConfig().get("visibility"); - if (sourceVisibilities instanceof Collection sourceVisibilitiesCollection) { - for (Object sourceVisibility : sourceVisibilitiesCollection) { - if (sourceVisibility instanceof String) { - List conditionsString = getRuleConditions(sourceVisibility.toString(), null); - VisibilityRuleImpl visibilityRule = (VisibilityRuleImpl) SitemapFactory.eINSTANCE - .createVisibilityRule(); - List conditions = getConditions(conditionsString, component, "visibility"); - visibilityRule.eSet(SitemapPackage.VISIBILITY_RULE__CONDITIONS, conditions); - visibility.add(visibilityRule); - } - } - } - } - } - - private void addLabelColor(EList labelColor, UIComponent component) { - addColor(labelColor, component, "labelcolor"); - } - - private void addValueColor(EList valueColor, UIComponent component) { - addColor(valueColor, component, "valuecolor"); - } - - private void addIconColor(EList iconColor, UIComponent component) { - addColor(iconColor, component, "iconcolor"); - } - - private void addColor(EList color, UIComponent component, String key) { + private void addWidgetRules(List rules, UIComponent component, String key) { if (component.getConfig() != null && component.getConfig().containsKey(key)) { - Object sourceColors = component.getConfig().get(key); - if (sourceColors instanceof Collection sourceColorsCollection) { - for (Object sourceColor : sourceColorsCollection) { - if (sourceColor instanceof String) { - String argument = getRuleArgument(sourceColor.toString()); - List conditionsString = getRuleConditions(sourceColor.toString(), argument); - ColorArrayImpl colorArray = (ColorArrayImpl) SitemapFactory.eINSTANCE.createColorArray(); - colorArray.setArg(argument); - List conditions = getConditions(conditionsString, component, key); - colorArray.eSet(SitemapPackage.COLOR_ARRAY__CONDITIONS, conditions); - color.add(colorArray); + Object sourceRules = component.getConfig().get(key); + if (sourceRules instanceof Collection sourceRulesCollection) { + for (Object sourceRule : sourceRulesCollection) { + if (sourceRule instanceof String) { + String argument = !key.equals("visibility") ? getRuleArgument(sourceRule.toString()) : null; + List conditionsString = getRuleConditions(sourceRule.toString(), argument); + Rule rule = sitemapFactory.createRule(); + List conditions = getConditions(conditionsString, component, key); + rule.setConditions(conditions); + rules.add(rule); } } } } } - private void addIconRules(EList icon, UIComponent component) { - if (component.getConfig() != null && component.getConfig().containsKey("iconrules")) { - Object sourceIcons = component.getConfig().get("iconrules"); - if (sourceIcons instanceof Collection sourceIconsCollection) { - for (Object sourceIcon : sourceIconsCollection) { - if (sourceIcon instanceof String) { - String argument = getRuleArgument(sourceIcon.toString()); - List conditionsString = getRuleConditions(sourceIcon.toString(), argument); - IconRuleImpl iconRule = (IconRuleImpl) SitemapFactory.eINSTANCE.createIconRule(); - iconRule.setArg(argument); - List conditions = getConditions(conditionsString, component, "iconrules"); - iconRule.eSet(SitemapPackage.ICON_RULE__CONDITIONS, conditions); - icon.add(iconRule); - } - } - } - } - } - - private List getConditions(List conditionsString, UIComponent component, String key) { - List conditions = new ArrayList<>(); + private List getConditions(List conditionsString, UIComponent component, String key) { + List conditions = new ArrayList<>(); for (String conditionString : conditionsString) { Matcher matcher = CONDITION_PATTERN.matcher(conditionString); + String value = null; if (matcher.matches()) { - ConditionImpl condition = (ConditionImpl) SitemapFactory.eINSTANCE.createCondition(); + value = stripQuotes(matcher.group("value")); + } + if (matcher.matches() && value != null) { + Condition condition = sitemapFactory.createCondition(); condition.setItem(matcher.group("item")); condition.setCondition(matcher.group("condition")); - condition.setSign(matcher.group("sign")); - condition.setState(stripQuotes(matcher.group("state"))); + condition.setValue(value); conditions.add(condition); } else { logger.warn("Syntax error in {} rule condition '{}' for widget {}", key, conditionString, @@ -547,51 +398,70 @@ private List getRuleConditions(String rule, @Nullable String argument) { return conditionsList.stream().filter(Predicate.not(String::isBlank)).map(String::trim).toList(); } - @Override - public void addModelChangeListener(ModelRepositoryChangeListener listener) { - modelChangeListeners.add(listener); + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + protected void setComponentRegistryFactory(UIComponentRegistryFactory componentRegistryFactory) { + this.componentRegistryFactory = componentRegistryFactory; + UIComponentRegistry sitemapComponentRegistry = this.componentRegistryFactory.getRegistry(SITEMAP_NAMESPACE); + sitemapComponentRegistry.addRegistryChangeListener(this); + sitemapComponentRegistry.getAll().forEach(element -> added(element)); + this.sitemapComponentRegistry = sitemapComponentRegistry; + } + + protected void unsetComponentRegistryFactory(UIComponentRegistryFactory componentRegistryFactory) { + UIComponentRegistry registry = this.sitemapComponentRegistry; + if (registry != null) { + registry.removeRegistryChangeListener(this); + } + + this.sitemaps = new HashMap<>(); + this.componentRegistryFactory = null; + this.sitemapComponentRegistry = null; } @Override - public void removeModelChangeListener(ModelRepositoryChangeListener listener) { - modelChangeListeners.remove(listener); + public Collection getAll() { + return sitemaps.values(); } @Override public void added(RootUIComponent element) { - for (ModelRepositoryChangeListener listener : modelChangeListeners) { - listener.modelChanged(SITEMAP_PREFIX + element.getUID() + SITEMAP_SUFFIX, EventType.ADDED); + if ("Sitemap".equals(element.getType())) { + String sitemapName = SITEMAP_PREFIX + element.getUID(); + if (sitemaps.get(sitemapName) == null) { + Sitemap sitemap = buildSitemap(element); + sitemaps.put(sitemapName, sitemap); + notifyListenersAboutAddedElement(sitemap); + } } } @Override public void removed(RootUIComponent element) { - for (ModelRepositoryChangeListener listener : modelChangeListeners) { - listener.modelChanged(SITEMAP_PREFIX + element.getUID() + SITEMAP_SUFFIX, EventType.REMOVED); + if ("Sitemap".equals(element.getType())) { + String sitemapName = SITEMAP_PREFIX + element.getUID(); + Sitemap sitemap = sitemaps.remove(sitemapName); + if (sitemap != null) { + notifyListenersAboutRemovedElement(sitemap); + } } } @Override public void updated(RootUIComponent oldElement, RootUIComponent element) { - for (ModelRepositoryChangeListener listener : modelChangeListeners) { - listener.modelChanged(SITEMAP_PREFIX + element.getUID() + SITEMAP_SUFFIX, EventType.MODIFIED); - } - } - - @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) - protected void setComponentRegistryFactory(UIComponentRegistryFactory componentRegistryFactory) { - this.componentRegistryFactory = componentRegistryFactory; - this.sitemapComponentRegistry = this.componentRegistryFactory.getRegistry(SITEMAP_NAMESPACE); - this.sitemapComponentRegistry.addRegistryChangeListener(this); - } - - protected void unsetComponentRegistryFactory(UIComponentRegistryFactory componentRegistryFactory) { - UIComponentRegistry registry = this.sitemapComponentRegistry; - if (registry != null) { - registry.removeRegistryChangeListener(this); + if ("Sitemap".equals(oldElement.getType()) && "Sitemap".equals(element.getType())) { + String oldSitemapName = SITEMAP_PREFIX + oldElement.getUID(); + String sitemapName = SITEMAP_PREFIX + element.getUID(); + Sitemap oldSitemap = sitemaps.get(oldSitemapName); + Sitemap sitemap = buildSitemap(element); + if (!oldSitemapName.equals(sitemapName)) { + sitemaps.remove(oldSitemapName); + } + sitemaps.put(sitemapName, sitemap); + if (oldSitemap != null) { + notifyListenersAboutUpdatedElement(oldSitemap, sitemap); + } else { + notifyListenersAboutAddedElement(sitemap); + } } - - this.componentRegistryFactory = null; - this.sitemapComponentRegistry = null; } } diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java index f78dc067be8..68ce3abddfd 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java @@ -31,10 +31,6 @@ import javax.measure.Unit; -import org.eclipse.emf.common.util.BasicEList; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.common.registry.RegistryChangeListener; @@ -64,18 +60,17 @@ import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.PlayPauseType; import org.openhab.core.library.types.QuantityType; -import org.openhab.core.model.sitemap.sitemap.ColorArray; -import org.openhab.core.model.sitemap.sitemap.Default; -import org.openhab.core.model.sitemap.sitemap.Group; -import org.openhab.core.model.sitemap.sitemap.IconRule; -import org.openhab.core.model.sitemap.sitemap.LinkableWidget; -import org.openhab.core.model.sitemap.sitemap.Mapping; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.SitemapFactory; -import org.openhab.core.model.sitemap.sitemap.Slider; -import org.openhab.core.model.sitemap.sitemap.Switch; -import org.openhab.core.model.sitemap.sitemap.VisibilityRule; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.Default; +import org.openhab.core.sitemap.Group; +import org.openhab.core.sitemap.LinkableWidget; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Slider; +import org.openhab.core.sitemap.Switch; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.registry.SitemapFactory; import org.openhab.core.transform.TransformationException; import org.openhab.core.transform.TransformationHelper; import org.openhab.core.transform.TransformationService; @@ -112,6 +107,7 @@ * @author Laurent Garnier - new icon parameter based on conditional rules * @author Danny Baumann - widget label source support * @author Laurent Garnier - Consider Colortemperaturepicker element as possible default widget + * @author Mark Herwege - Implement sitemap registry */ @NonNullByDefault @Component(immediate = true, configurationPid = "org.openhab.sitemap", // @@ -137,6 +133,7 @@ public class ItemUIRegistryImpl implements ItemUIRegistry { protected final Set itemUIProviders = new HashSet<>(); private final ItemRegistry itemRegistry; + private final SitemapFactory sitemapFactory; private final TimeZoneProvider timeZoneProvider; private final Map defaultWidgets = Collections.synchronizedMap(new WeakHashMap<>()); @@ -155,8 +152,9 @@ public WidgetLabelWithSource(String l, WidgetLabelSource s) { @Activate public ItemUIRegistryImpl(final @Reference ItemRegistry itemRegistry, - final @Reference TimeZoneProvider timeZoneProvider) { + final @Reference SitemapFactory sitemapFactory, final @Reference TimeZoneProvider timeZoneProvider) { this.itemRegistry = itemRegistry; + this.sitemapFactory = sitemapFactory; this.timeZoneProvider = timeZoneProvider; } @@ -276,65 +274,65 @@ private void applyConfig(@Nullable Map config) { } if (GroupItem.class.equals(itemType)) { - return SitemapFactory.eINSTANCE.createGroup(); + return sitemapFactory.createWidget("Group"); } else if (CallItem.class.equals(itemType) // || ContactItem.class.equals(itemType) // || DateTimeItem.class.equals(itemType)) { - return SitemapFactory.eINSTANCE.createText(); + return sitemapFactory.createWidget("Text"); } else if (ColorItem.class.equals(itemType)) { - return SitemapFactory.eINSTANCE.createColorpicker(); + return sitemapFactory.createWidget("Colorpicker"); } else if (DimmerItem.class.equals(itemType)) { - Slider slider = SitemapFactory.eINSTANCE.createSlider(); + Slider slider = (Slider) sitemapFactory.createWidget("Slider"); slider.setSwitchEnabled(true); slider.setReleaseOnly(true); return slider; } else if (ImageItem.class.equals(itemType)) { - return SitemapFactory.eINSTANCE.createImage(); + return sitemapFactory.createWidget("Image"); } else if (LocationItem.class.equals(itemType)) { - return SitemapFactory.eINSTANCE.createMapview(); + return sitemapFactory.createWidget("Mapview"); } else if (NumberItem.class.isAssignableFrom(itemType) // || StringItem.class.equals(itemType)) { boolean isReadOnly = isReadOnly(itemName); int commandOptionsSize = getCommandOptionsSize(itemName); if (!isReadOnly && commandOptionsSize > 0) { - return commandOptionsSize <= MAX_BUTTONS ? SitemapFactory.eINSTANCE.createSwitch() - : SitemapFactory.eINSTANCE.createSelection(); + return commandOptionsSize <= MAX_BUTTONS ? sitemapFactory.createWidget("Switch") + : sitemapFactory.createWidget("Selection"); } if (!isReadOnly && hasStateOptions(itemName)) { - return SitemapFactory.eINSTANCE.createSelection(); + return sitemapFactory.createWidget("Selection"); } if (!isReadOnly && NumberItem.class.isAssignableFrom(itemType) && hasItemTag(itemName, "Setpoint")) { - return SitemapFactory.eINSTANCE.createSetpoint(); + return sitemapFactory.createWidget("Setpoint"); } else if (!isReadOnly && NumberItem.class.isAssignableFrom(itemType) && hasItemTag(itemName, "ColorTemperature")) { - return SitemapFactory.eINSTANCE.createColortemperaturepicker(); + return sitemapFactory.createWidget("Colortemperaturepicker"); } else { - return SitemapFactory.eINSTANCE.createText(); + return sitemapFactory.createWidget("Text"); } } else if (PlayerItem.class.equals(itemType)) { return createPlayerButtons(); } else if (RollershutterItem.class.equals(itemType) // || SwitchItem.class.equals(itemType)) { - return SitemapFactory.eINSTANCE.createSwitch(); + return sitemapFactory.createWidget("Switch"); } return null; } private Switch createPlayerButtons() { - final Switch playerItemSwitch = SitemapFactory.eINSTANCE.createSwitch(); + final Switch playerItemSwitch = (Switch) sitemapFactory.createWidget("Switch"); final List mappings = playerItemSwitch.getMappings(); Mapping commandMapping; - mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping()); + mappings.add(commandMapping = sitemapFactory.createMapping()); commandMapping.setCmd(NextPreviousType.PREVIOUS.name()); commandMapping.setLabel("<<"); - mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping()); + mappings.add(commandMapping = sitemapFactory.createMapping()); commandMapping.setCmd(PlayPauseType.PAUSE.name()); commandMapping.setLabel("||"); - mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping()); + mappings.add(commandMapping = sitemapFactory.createMapping()); commandMapping.setCmd(PlayPauseType.PLAY.name()); commandMapping.setLabel(">"); - mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping()); + mappings.add(commandMapping = sitemapFactory.createMapping()); commandMapping.setCmd(NextPreviousType.NEXT.name()); commandMapping.setLabel(">>"); return playerItemSwitch; @@ -391,7 +389,7 @@ private Switch createPlayerButtons() { } } } catch (ItemNotFoundException e) { - logger.warn("Cannot retrieve item '{}' for widget {}", itemName, w.eClass().getInstanceTypeName()); + logger.warn("Cannot retrieve item '{}' for widget {}", itemName, w.getClass().getSimpleName()); } boolean considerTransform = false; @@ -562,7 +560,7 @@ private QuantityType convertStateToWidgetUnit(QuantityType quantityState, : pattern.substring(0, pattern.indexOf(unit, matcherEnd) + unit.length()); } } catch (ItemNotFoundException e) { - logger.warn("Cannot retrieve item '{}' for widget {}", itemName, w.eClass().getInstanceTypeName()); + logger.warn("Cannot retrieve item '{}' for widget {}", itemName, w.getClass().getSimpleName()); } return pattern; @@ -685,8 +683,7 @@ private String transform(String label, boolean matchTransform, @Nullable String @Override public @Nullable String getCategory(Widget w) { - String widgetTypeName = w.eClass().getInstanceTypeName() - .substring(w.eClass().getInstanceTypeName().lastIndexOf(".") + 1); + String widgetTypeName = w.getWidgetType(); // the default is the widget type name, e.g. "switch" String category = widgetTypeName.toLowerCase(); @@ -695,8 +692,6 @@ private String transform(String label, boolean matchTransform, @Nullable String // if an icon is defined for the widget, use it if (w.getIcon() != null) { category = w.getIcon(); - } else if (w.getStaticIcon() != null) { - category = w.getStaticIcon(); } else if (conditionalIcon != null) { category = conditionalIcon; } else { @@ -720,7 +715,7 @@ private String transform(String label, boolean matchTransform, @Nullable String Item item = getItem(itemName); return convertState(w, item, item.getState()); } catch (ItemNotFoundException e) { - logger.warn("Cannot retrieve item '{}' for widget {}", itemName, w.eClass().getInstanceTypeName()); + logger.warn("Cannot retrieve item '{}' for widget {}", itemName, w.getClass().getSimpleName()); } } return UnDefType.UNDEF; @@ -777,12 +772,12 @@ private String transform(String label, boolean matchTransform, @Nullable String } else { try { int widgetID = Integer.parseInt(id.substring(0, 2)); - if (widgetID < sitemap.getChildren().size()) { - w = sitemap.getChildren().get(widgetID); + if (widgetID < sitemap.getWidgets().size()) { + w = sitemap.getWidgets().get(widgetID); for (int i = 2; i < id.length(); i += 2) { int childWidgetID = Integer.parseInt(id.substring(i, i + 2)); - if (childWidgetID < ((LinkableWidget) w).getChildren().size()) { - w = ((LinkableWidget) w).getChildren().get(childWidgetID); + if (childWidgetID < ((LinkableWidget) w).getWidgets().size()) { + w = ((LinkableWidget) w).getWidgets().get(childWidgetID); } } } @@ -797,25 +792,25 @@ private String transform(String label, boolean matchTransform, @Nullable String } @Override - public EList getChildren(Sitemap sitemap) { - EList widgets = sitemap.getChildren(); + public List getChildren(Sitemap sitemap) { + List widgets = sitemap.getWidgets(); - EList result = new BasicEList<>(); + List result = new ArrayList<>(); widgets.stream().map(this::resolveDefault).filter(Objects::nonNull).map(Objects::requireNonNull) .forEach(result::add); return result; } @Override - public EList getChildren(LinkableWidget w) { - EList widgets; - if (w instanceof Group group && w.getChildren().isEmpty()) { + public List getChildren(LinkableWidget w) { + List widgets; + if (w instanceof Group group && w.getWidgets().isEmpty()) { widgets = getDynamicGroupChildren(group); } else { - widgets = w.getChildren(); + widgets = w.getWidgets(); } - EList result = new BasicEList<>(); + List result = new ArrayList<>(); widgets.stream().map(this::resolveDefault).filter(Objects::nonNull).map(Objects::requireNonNull) .forEach(result::add); @@ -823,9 +818,9 @@ public EList getChildren(LinkableWidget w) { } @Override - public @Nullable EObject getParent(Widget w) { + public @Nullable Parent getParent(Widget w) { Widget w2 = defaultWidgets.get(w); - return (w2 == null) ? w.eContainer() : w2.eContainer(); + return (w2 == null) ? w.getParent() : w2.getParent(); } private @Nullable Widget resolveDefault(@Nullable Widget widget) { @@ -851,13 +846,28 @@ public EList getChildren(LinkableWidget w) { private void copyProperties(Widget source, Widget target) { target.setItem(source.getItem()); target.setIcon(source.getIcon()); - target.setStaticIcon(source.getStaticIcon()); + target.setStaticIcon(source.isStaticIcon()); target.setLabel(source.getLabel()); - target.getVisibility().addAll(EcoreUtil.copyAll(source.getVisibility())); - target.getLabelColor().addAll(EcoreUtil.copyAll(source.getLabelColor())); - target.getValueColor().addAll(EcoreUtil.copyAll(source.getValueColor())); - target.getIconColor().addAll(EcoreUtil.copyAll(source.getIconColor())); - target.getIconRules().addAll(EcoreUtil.copyAll(source.getIconRules())); + target.getVisibility().addAll(copyAll(source.getVisibility())); + target.getLabelColor().addAll(copyAll(source.getLabelColor())); + target.getValueColor().addAll(copyAll(source.getValueColor())); + target.getIconColor().addAll(copyAll(source.getIconColor())); + target.getIconRules().addAll(copyAll(source.getIconRules())); + } + + private Collection copyAll(List rules) { + return rules.stream().map(rule -> { + Rule ruleCopy = sitemapFactory.createRule(); + ruleCopy.getConditions().addAll(rule.getConditions().stream().map(condition -> { + org.openhab.core.sitemap.Condition conditionCopy = sitemapFactory.createCondition(); + conditionCopy.setItem(condition.getItem()); + conditionCopy.setCondition(condition.getCondition()); + conditionCopy.setValue(condition.getValue()); + return conditionCopy; + }).toList()); + ruleCopy.setArgument(rule.getArgument()); + return ruleCopy; + }).toList(); } /** @@ -868,8 +878,8 @@ private void copyProperties(Widget source, Widget target) { * @param group The group widget to get children for * @return a list of default widgets provided for the member items */ - private EList getDynamicGroupChildren(Group group) { - EList children = new BasicEList<>(); + private List getDynamicGroupChildren(Group group) { + List children = new ArrayList<>(); String itemName = group.getItem(); try { if (itemName != null) { @@ -1036,18 +1046,16 @@ public String getWidgetId(Widget widget) { String id = ""; Widget w = widget; - while (w.eContainer() instanceof Widget) { - Widget parent = (Widget) w.eContainer(); - String index = String.valueOf(((LinkableWidget) parent).getChildren().indexOf(w)); + while (w.getParent() instanceof LinkableWidget parent) { + String index = String.valueOf(parent.getWidgets().indexOf(w)); if (index.length() == 1) { index = "0" + index; // make it two digits } id = index + id; w = parent; } - if (w.eContainer() instanceof Sitemap) { - Sitemap sitemap = (Sitemap) w.eContainer(); - String index = String.valueOf(sitemap.getChildren().indexOf(w)); + if (w.getParent() instanceof Sitemap sitemap) { + String index = String.valueOf(sitemap.getWidgets().indexOf(w)); if (index.length() == 1) { index = "0" + index; // make it two digits } @@ -1056,8 +1064,9 @@ public String getWidgetId(Widget widget) { // if the widget is dynamically created and not available in the sitemap, // use the item name as the id - if (w.eContainer() == null) { - id = w.getItem(); + if (w.getParent() == null) { + String item = w.getItem(); + id = item != null ? item : id; } return id; } @@ -1207,7 +1216,7 @@ private boolean matchStateToValue(State state, String value, @Nullable String ma return matched; } - private @Nullable String processColorDefinition(Widget w, @Nullable List colorList, String colorType) { + private @Nullable String processColorDefinition(Widget w, @Nullable List colorList, String colorType) { // Sanity check if (colorList == null || colorList.isEmpty()) { return null; @@ -1219,10 +1228,10 @@ private boolean matchStateToValue(State state, String value, @Nullable String ma // Loop through all elements looking for the definition associated // with the supplied value - for (ColorArray rule : colorList) { + for (Rule rule : colorList) { if (allConditionsOk(rule.getConditions(), w)) { // We have the color for this value - break! - colorString = rule.getArg(); + colorString = rule.getArgument(); break; } } @@ -1259,14 +1268,14 @@ private boolean matchStateToValue(State state, String value, @Nullable String ma @Override public boolean getVisiblity(Widget w) { // Default to visible if parameters not set - List ruleList = w.getVisibility(); - if (ruleList == null || ruleList.isEmpty()) { + List rulList = w.getVisibility(); + if (rulList.isEmpty()) { return true; } logger.debug("Checking visiblity for widget '{}'.", w.getLabel()); - for (VisibilityRule rule : ruleList) { + for (Rule rule : rulList) { if (allConditionsOk(rule.getConditions(), w)) { return true; } @@ -1279,9 +1288,9 @@ public boolean getVisiblity(Widget w) { @Override public @Nullable String getConditionalIcon(Widget w) { - List ruleList = w.getIconRules(); + List rulList = w.getIconRules(); // Sanity check - if (ruleList == null || ruleList.isEmpty()) { + if (rulList.isEmpty()) { return null; } @@ -1291,10 +1300,10 @@ public boolean getVisiblity(Widget w) { // Loop through all elements looking for the definition associated // with the supplied value - for (IconRule rule : ruleList) { + for (Rule rule : rulList) { if (allConditionsOk(rule.getConditions(), w)) { // We have the icon for this value - break! - icon = rule.getArg(); + icon = rule.getArgument(); break; } } @@ -1313,14 +1322,13 @@ public boolean getVisiblity(Widget w) { return icon; } - private boolean allConditionsOk(@Nullable List conditions, - Widget w) { + private boolean allConditionsOk(@Nullable List conditions, Widget w) { boolean allConditionsOk = true; if (conditions != null) { State defaultState = getState(w); // Go through all AND conditions - for (org.openhab.core.model.sitemap.sitemap.Condition condition : conditions) { + for (org.openhab.core.sitemap.Condition condition : conditions) { // Use a local state variable in case it gets overridden below State state = defaultState; @@ -1335,18 +1343,11 @@ private boolean allConditionsOk(@Nullable List sitemapProviders = new CopyOnWriteArrayList<>(); + protected final SitemapRegistry sitemapRegistry; @Activate public ProxyServletService(@Reference HttpService httpService, @Reference ItemUIRegistry itemUIRegistry, - Map config) { + @Reference SitemapRegistry sitemapRegistry, Map config) { this.httpService = httpService; this.itemUIRegistry = itemUIRegistry; + this.sitemapRegistry = sitemapRegistry; Servlet servlet = getImpl(); @@ -127,15 +125,6 @@ protected void deactivate() { } } - @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) - protected void addSitemapProvider(SitemapProvider provider) { - sitemapProviders.add(provider); - } - - protected void removeSitemapProvider(SitemapProvider provider) { - sitemapProviders.remove(provider); - } - /** * Return the async in preference to the blocking proxy servlet, if possible. * Supported OSGi containers might only support Servlet API 2.4 (blocking only). @@ -269,7 +258,7 @@ URI uriFromRequest(HttpServletRequest request) { } try { - uri = createURIFromString(uriString); + uri = createURIFromString(uriString != null ? uriString : ""); request.setAttribute(ATTR_URI, uri); return uri; } catch (MalformedURLException | URISyntaxException ex) { @@ -283,14 +272,7 @@ URI uriFromRequest(HttpServletRequest request) { } private @Nullable Sitemap getSitemap(String sitemapName) { - Sitemap sitemap = null; - for (SitemapProvider sitemapProvider : sitemapProviders) { - sitemap = sitemapProvider.getSitemap(sitemapName); - if (sitemap != null) { - break; - } - } - return sitemap; + return sitemapRegistry.get(sitemapName); } private URI createURIFromString(String url) throws MalformedURLException, URISyntaxException { diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIProvider.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIProvider.java index 64a1598996f..00d65e6b789 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIProvider.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIProvider.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.Item; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.Widget; /** * This interface describes the methods that need to be implemented by a provider that diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIRegistry.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIRegistry.java index 7e37598f349..e5b731e57e2 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIRegistry.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/items/ItemUIRegistry.java @@ -12,18 +12,19 @@ */ package org.openhab.core.ui.items; +import java.util.List; + import javax.measure.Unit; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.ecore.EObject; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.Item; import org.openhab.core.items.ItemRegistry; import org.openhab.core.library.types.QuantityType; -import org.openhab.core.model.sitemap.sitemap.LinkableWidget; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.LinkableWidget; +import org.openhab.core.sitemap.Parent; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Widget; import org.openhab.core.types.State; /** @@ -37,6 +38,7 @@ * @author Mark Herwege - new method getFormatPattern * @author Laurent Garnier - new method getConditionalIcon * @author Danny Baumann - widget label source support + * @author Mark Herwege - Implement sitemap registry */ @NonNullByDefault public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { @@ -129,7 +131,7 @@ enum WidgetLabelSource { * @param sitemap the sitemap to retrieve the children for * @return the children of the sitemap */ - EList getChildren(Sitemap sitemap); + List getChildren(Sitemap sitemap); /** * this should be used instead of LinkableWidget.getChildren() as there @@ -139,7 +141,7 @@ enum WidgetLabelSource { * @param w the widget to retrieve the children for * @return the (dynamically or statically defined) children of the widget */ - EList getChildren(LinkableWidget w); + List getChildren(LinkableWidget w); /** * this should be used instead of Widget.eContainer() as as the concrete @@ -149,7 +151,7 @@ enum WidgetLabelSource { * @return the parent of the widget */ @Nullable - EObject getParent(Widget w); + Parent getParent(Widget w); /** * Gets the format pattern for the widget value, retrieved from widget label, item label or item state description diff --git a/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java b/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java index baef93ca74b..c0a13454e16 100644 --- a/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java +++ b/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java @@ -27,8 +27,6 @@ import javax.measure.quantity.Temperature; -import org.eclipse.emf.common.util.BasicEList; -import org.eclipse.emf.ecore.EClass; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,22 +59,20 @@ import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; -import org.openhab.core.model.sitemap.sitemap.ColorArray; -import org.openhab.core.model.sitemap.sitemap.Colorpicker; -import org.openhab.core.model.sitemap.sitemap.Condition; -import org.openhab.core.model.sitemap.sitemap.Group; -import org.openhab.core.model.sitemap.sitemap.IconRule; -import org.openhab.core.model.sitemap.sitemap.Image; -import org.openhab.core.model.sitemap.sitemap.Mapping; -import org.openhab.core.model.sitemap.sitemap.Mapview; -import org.openhab.core.model.sitemap.sitemap.Selection; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.SitemapFactory; -import org.openhab.core.model.sitemap.sitemap.Slider; -import org.openhab.core.model.sitemap.sitemap.Switch; -import org.openhab.core.model.sitemap.sitemap.Text; -import org.openhab.core.model.sitemap.sitemap.VisibilityRule; -import org.openhab.core.model.sitemap.sitemap.Widget; +import org.openhab.core.sitemap.Colorpicker; +import org.openhab.core.sitemap.Condition; +import org.openhab.core.sitemap.Group; +import org.openhab.core.sitemap.Image; +import org.openhab.core.sitemap.Mapping; +import org.openhab.core.sitemap.Mapview; +import org.openhab.core.sitemap.Rule; +import org.openhab.core.sitemap.Selection; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Slider; +import org.openhab.core.sitemap.Switch; +import org.openhab.core.sitemap.Text; +import org.openhab.core.sitemap.Widget; +import org.openhab.core.sitemap.registry.SitemapFactory; import org.openhab.core.types.CommandDescriptionBuilder; import org.openhab.core.types.CommandOption; import org.openhab.core.types.State; @@ -92,6 +88,7 @@ * @author Kai Kreuzer - Initial contribution * @author Laurent Garnier - Tests updated to consider multiple AND conditions + tests added for getVisiblity * @author Laurent Garnier - Tests added for getCategory + * @author Mark Herwege - Implement sitemap registry */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -101,24 +98,51 @@ public class ItemUIRegistryImplTest { // we need to get the decimal separator of the default locale for our tests private static final char SEP = (new DecimalFormatSymbols().getDecimalSeparator()); private static final String ITEM_NAME = "Item"; + private static final String SITEMAP_NAME = "Sitemap"; private static final String DEFAULT_TIME_ZONE = "GMT-6"; private @NonNullByDefault({}) ItemUIRegistryImpl uiRegistry; private @Mock @NonNullByDefault({}) ItemRegistry registryMock; + private @Mock @NonNullByDefault({}) SitemapFactory sitemapFactoryMock; private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock; private @Mock @NonNullByDefault({}) Widget widgetMock; private @Mock @NonNullByDefault({}) Item itemMock; + private @Mock @NonNullByDefault({}) Group groupMock; + private @Mock @NonNullByDefault({}) Text textMock; + private @Mock @NonNullByDefault({}) Colorpicker colorpickerMock; + private @Mock @NonNullByDefault({}) Image imageMock; + private @Mock @NonNullByDefault({}) Mapview mapviewMock; + private @Mock @NonNullByDefault({}) Slider sliderMock; + private @Mock @NonNullByDefault({}) Switch switchMock; + private @Mock @NonNullByDefault({}) Selection selectionMock; + private @Mock @NonNullByDefault({}) Mapping mappingMock; + @BeforeEach public void setup() throws Exception { - uiRegistry = new ItemUIRegistryImpl(registryMock, timeZoneProviderMock); + uiRegistry = new ItemUIRegistryImpl(registryMock, sitemapFactoryMock, timeZoneProviderMock); when(widgetMock.getItem()).thenReturn(ITEM_NAME); when(registryMock.getItem(ITEM_NAME)).thenReturn(itemMock); when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.of(DEFAULT_TIME_ZONE)); TimeZone.setDefault(TimeZone.getTimeZone(DEFAULT_TIME_ZONE)); + + setupSitemapFactoryMock(); + } + + private void setupSitemapFactoryMock() { + when(sitemapFactoryMock.createWidget("Group")).thenReturn(groupMock); + when(sitemapFactoryMock.createWidget("Text")).thenReturn(textMock); + when(sitemapFactoryMock.createWidget("Colorpicker")).thenReturn(colorpickerMock); + when(sitemapFactoryMock.createWidget("Image")).thenReturn(imageMock); + when(sitemapFactoryMock.createWidget("Mapview")).thenReturn(mapviewMock); + when(sitemapFactoryMock.createWidget("Slider")).thenReturn(sliderMock); + when(sitemapFactoryMock.createWidget("Switch")).thenReturn(switchMock); + when(sitemapFactoryMock.createWidget("Selection")).thenReturn(selectionMock); + + when(sitemapFactoryMock.createMapping()).thenReturn(mappingMock); } @Test @@ -535,7 +559,7 @@ public void getLabelItemNotFound() throws ItemNotFoundException { String testLabel = "Label [%s]"; when(widgetMock.getLabel()).thenReturn(testLabel); - when(widgetMock.eClass()).thenReturn(SitemapFactory.eINSTANCE.createText().eClass()); + when(widgetMock.getWidgetType()).thenReturn("Text"); when(registryMock.getItem(ITEM_NAME)).thenThrow(new ItemNotFoundException(ITEM_NAME)); String label = uiRegistry.getLabel(widgetMock); assertEquals("Label [-]", label); @@ -564,7 +588,7 @@ public void getLabelGroupLabelWithValue() { @Test public void getWidgetUnknownPageId() throws ItemNotFoundException { - Sitemap sitemap = SitemapFactory.eINSTANCE.createSitemap(); + Sitemap sitemap = sitemapFactoryMock.createSitemap(SITEMAP_NAME); when(registryMock.getItem("unknown")).thenThrow(new ItemNotFoundException("unknown")); Widget w = uiRegistry.getWidget(sitemap, "unknown"); assertNull(w); @@ -591,7 +615,7 @@ public void testStateConversionForSwitchWidgetThroughGetState() throws ItemNotFo Switch switchWidget = mock(Switch.class); when(switchWidget.getItem()).thenReturn("myItem"); - when(switchWidget.getMappings()).thenReturn(new BasicEList<>()); + when(switchWidget.getMappings()).thenReturn(new ArrayList<>()); State stateForSwitch = uiRegistry.getState(switchWidget); @@ -612,7 +636,7 @@ public void testStateConversionForSwitchWidgetWithMappingThroughGetState() throw when(switchWidget.getItem()).thenReturn("myItem"); Mapping mapping = mock(Mapping.class); - BasicEList mappings = new BasicEList<>(); + List mappings = new ArrayList<>(); mappings.add(mapping); when(switchWidget.getMappings()).thenReturn(mappings); @@ -859,14 +883,14 @@ public void getLabelColorLabelWithDecimalValue() { when(widgetMock.getLabel()).thenReturn(testLabel); Condition condition = mock(Condition.class); - when(condition.getState()).thenReturn("21"); + when(condition.getValue()).thenReturn("21"); when(condition.getCondition()).thenReturn("<"); - BasicEList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(condition); - ColorArray rule = mock(ColorArray.class); + Rule rule = mock(Rule.class); when(rule.getConditions()).thenReturn(conditions); - when(rule.getArg()).thenReturn("yellow"); - BasicEList rules = new BasicEList<>(); + when(rule.getArgument()).thenReturn("yellow"); + List rules = new ArrayList<>(); rules.add(rule); when(widgetMock.getLabelColor()).thenReturn(rules); @@ -888,14 +912,14 @@ public void getLabelColorLabelWithUnitValue() { when(widgetMock.getLabel()).thenReturn(testLabel); Condition condition = mock(Condition.class); - when(condition.getState()).thenReturn("20"); + when(condition.getValue()).thenReturn("20"); when(condition.getCondition()).thenReturn("=="); - BasicEList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(condition); - ColorArray rule = mock(ColorArray.class); + Rule rule = mock(Rule.class); when(rule.getConditions()).thenReturn(conditions); - when(rule.getArg()).thenReturn("yellow"); - BasicEList rules = new BasicEList<>(); + when(rule.getArgument()).thenReturn("yellow"); + List rules = new ArrayList<>(); rules.add(rule); when(widgetMock.getLabelColor()).thenReturn(rules); @@ -929,8 +953,8 @@ public void getDefaultWidgets() { defaultWidget = uiRegistry.getDefaultWidget(DimmerItem.class, ITEM_NAME); assertThat(defaultWidget, is(instanceOf(Slider.class))); - assertThat(((Slider) defaultWidget).isSwitchEnabled(), is(true)); - assertThat(((Slider) defaultWidget).isReleaseOnly(), is(true)); + verify(sliderMock).setSwitchEnabled(true); + verify(sliderMock).setReleaseOnly(true); defaultWidget = uiRegistry.getDefaultWidget(ImageItem.class, ITEM_NAME); assertThat(defaultWidget, is(instanceOf(Image.class))); @@ -938,15 +962,16 @@ public void getDefaultWidgets() { defaultWidget = uiRegistry.getDefaultWidget(LocationItem.class, ITEM_NAME); assertThat(defaultWidget, is(instanceOf(Mapview.class))); - defaultWidget = uiRegistry.getDefaultWidget(PlayerItem.class, ITEM_NAME); - assertThat(defaultWidget, is(instanceOf(Switch.class))); - assertThat(((Switch) defaultWidget).getMappings(), hasSize(4)); - defaultWidget = uiRegistry.getDefaultWidget(RollershutterItem.class, ITEM_NAME); assertThat(defaultWidget, is(instanceOf(Switch.class))); defaultWidget = uiRegistry.getDefaultWidget(SwitchItem.class, ITEM_NAME); assertThat(defaultWidget, is(instanceOf(Switch.class))); + + when(switchMock.getMappings()).thenReturn(new ArrayList()); + defaultWidget = uiRegistry.getDefaultWidget(PlayerItem.class, ITEM_NAME); + assertThat(defaultWidget, is(instanceOf(Switch.class))); + assertThat(((Switch) defaultWidget).getMappings(), hasSize(4)); } @Test @@ -1067,36 +1092,36 @@ public void getLabelColorDefaultColor() { when(widgetMock.getLabel()).thenReturn(testLabel); Condition condition = mock(Condition.class); - when(condition.getState()).thenReturn("18"); + when(condition.getValue()).thenReturn("18"); when(condition.getCondition()).thenReturn(">="); Condition condition2 = mock(Condition.class); - when(condition2.getState()).thenReturn("21"); + when(condition2.getValue()).thenReturn("21"); when(condition2.getCondition()).thenReturn("<"); - BasicEList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(condition); conditions.add(condition2); - ColorArray rule = mock(ColorArray.class); + Rule rule = mock(Rule.class); when(rule.getConditions()).thenReturn(conditions); - when(rule.getArg()).thenReturn("yellow"); - BasicEList rules = new BasicEList<>(); + when(rule.getArgument()).thenReturn("yellow"); + List rules = new ArrayList<>(); rules.add(rule); Condition condition3 = mock(Condition.class); - when(condition3.getState()).thenReturn("21"); + when(condition3.getValue()).thenReturn("21"); when(condition3.getCondition()).thenReturn(">="); Condition condition4 = mock(Condition.class); - when(condition4.getState()).thenReturn("24"); + when(condition4.getValue()).thenReturn("24"); when(condition4.getCondition()).thenReturn("<"); - BasicEList conditions2 = new BasicEList<>(); + List conditions2 = new ArrayList<>(); conditions2.add(condition3); conditions2.add(condition4); - ColorArray rule2 = mock(ColorArray.class); + Rule rule2 = mock(Rule.class); when(rule2.getConditions()).thenReturn(conditions2); - when(rule2.getArg()).thenReturn("red"); + when(rule2.getArgument()).thenReturn("red"); rules.add(rule2); - BasicEList conditions5 = new BasicEList<>(); - ColorArray rule3 = mock(ColorArray.class); + List conditions5 = new ArrayList<>(); + Rule rule3 = mock(Rule.class); when(rule3.getConditions()).thenReturn(conditions5); - when(rule3.getArg()).thenReturn("blue"); + when(rule3.getArgument()).thenReturn("blue"); rules.add(rule3); when(widgetMock.getLabelColor()).thenReturn(rules); @@ -1134,36 +1159,36 @@ public void getLabelColorDefaultColor() { @Test public void getValueColor() { Condition condition = mock(Condition.class); - when(condition.getState()).thenReturn("18"); + when(condition.getValue()).thenReturn("18"); when(condition.getCondition()).thenReturn(">="); Condition condition2 = mock(Condition.class); - when(condition2.getState()).thenReturn("21"); + when(condition2.getValue()).thenReturn("21"); when(condition2.getCondition()).thenReturn("<"); - BasicEList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(condition); conditions.add(condition2); - ColorArray rule = mock(ColorArray.class); + Rule rule = mock(Rule.class); when(rule.getConditions()).thenReturn(conditions); - when(rule.getArg()).thenReturn("yellow"); - BasicEList rules = new BasicEList<>(); + when(rule.getArgument()).thenReturn("yellow"); + List rules = new ArrayList<>(); rules.add(rule); Condition condition3 = mock(Condition.class); - when(condition3.getState()).thenReturn("21"); + when(condition3.getValue()).thenReturn("21"); when(condition3.getCondition()).thenReturn(">="); Condition condition4 = mock(Condition.class); - when(condition4.getState()).thenReturn("24"); + when(condition4.getValue()).thenReturn("24"); when(condition4.getCondition()).thenReturn("<"); - BasicEList conditions2 = new BasicEList<>(); + List conditions2 = new ArrayList<>(); conditions2.add(condition3); conditions2.add(condition4); - ColorArray rule2 = mock(ColorArray.class); + Rule rule2 = mock(Rule.class); when(rule2.getConditions()).thenReturn(conditions2); - when(rule2.getArg()).thenReturn("red"); + when(rule2.getArgument()).thenReturn("red"); rules.add(rule2); - BasicEList conditions5 = new BasicEList<>(); - ColorArray rule3 = mock(ColorArray.class); + List conditions5 = new ArrayList<>(); + Rule rule3 = mock(Rule.class); when(rule3.getConditions()).thenReturn(conditions5); - when(rule3.getArg()).thenReturn("blue"); + when(rule3.getArgument()).thenReturn("blue"); rules.add(rule3); when(widgetMock.getValueColor()).thenReturn(rules); @@ -1201,36 +1226,36 @@ public void getValueColor() { @Test public void getIconColor() { Condition condition = mock(Condition.class); - when(condition.getState()).thenReturn("18"); + when(condition.getValue()).thenReturn("18"); when(condition.getCondition()).thenReturn(">="); Condition condition2 = mock(Condition.class); - when(condition2.getState()).thenReturn("21"); + when(condition2.getValue()).thenReturn("21"); when(condition2.getCondition()).thenReturn("<"); - BasicEList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(condition); conditions.add(condition2); - ColorArray rule = mock(ColorArray.class); + Rule rule = mock(Rule.class); when(rule.getConditions()).thenReturn(conditions); - when(rule.getArg()).thenReturn("yellow"); - BasicEList rules = new BasicEList<>(); + when(rule.getArgument()).thenReturn("yellow"); + List rules = new ArrayList<>(); rules.add(rule); Condition condition3 = mock(Condition.class); - when(condition3.getState()).thenReturn("21"); + when(condition3.getValue()).thenReturn("21"); when(condition3.getCondition()).thenReturn(">="); Condition condition4 = mock(Condition.class); - when(condition4.getState()).thenReturn("24"); + when(condition4.getValue()).thenReturn("24"); when(condition4.getCondition()).thenReturn("<"); - BasicEList conditions2 = new BasicEList<>(); + List conditions2 = new ArrayList<>(); conditions2.add(condition3); conditions2.add(condition4); - ColorArray rule2 = mock(ColorArray.class); + Rule rule2 = mock(Rule.class); when(rule2.getConditions()).thenReturn(conditions2); - when(rule2.getArg()).thenReturn("red"); + when(rule2.getArgument()).thenReturn("red"); rules.add(rule2); - BasicEList conditions5 = new BasicEList<>(); - ColorArray rule3 = mock(ColorArray.class); + List conditions5 = new ArrayList<>(); + Rule rule3 = mock(Rule.class); when(rule3.getConditions()).thenReturn(conditions5); - when(rule3.getArg()).thenReturn("blue"); + when(rule3.getArgument()).thenReturn("blue"); rules.add(rule3); when(widgetMock.getIconColor()).thenReturn(rules); @@ -1268,17 +1293,17 @@ public void getIconColor() { @Test public void getVisibility() { Condition condition = mock(Condition.class); - when(condition.getState()).thenReturn("21"); + when(condition.getValue()).thenReturn("21"); when(condition.getCondition()).thenReturn(">="); Condition condition2 = mock(Condition.class); - when(condition2.getState()).thenReturn("24"); + when(condition2.getValue()).thenReturn("24"); when(condition2.getCondition()).thenReturn("<"); - BasicEList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(condition); conditions.add(condition2); - VisibilityRule rule = mock(VisibilityRule.class); + Rule rule = mock(Rule.class); when(rule.getConditions()).thenReturn(conditions); - BasicEList rules = new BasicEList<>(); + List rules = new ArrayList<>(); rules.add(rule); when(widgetMock.getVisibility()).thenReturn(rules); @@ -1301,13 +1326,10 @@ public void getVisibility() { @Test public void getCategoryWhenIconSetWithoutRules() { - EClass textEClass = mock(EClass.class); - when(textEClass.getName()).thenReturn("text"); - when(textEClass.getInstanceTypeName()).thenReturn("org.openhab.core.model.sitemap.Text"); - when(widgetMock.eClass()).thenReturn(textEClass); + when(widgetMock.getWidgetType()).thenReturn("Text"); when(widgetMock.getIcon()).thenReturn("temperature"); - when(widgetMock.getStaticIcon()).thenReturn(null); - when(widgetMock.getIconRules()).thenReturn(null); + when(widgetMock.isStaticIcon()).thenReturn(false); + when(widgetMock.getIconRules()).thenReturn(List.of()); String icon = uiRegistry.getCategory(widgetMock); assertEquals("temperature", icon); @@ -1315,30 +1337,27 @@ public void getCategoryWhenIconSetWithoutRules() { @Test public void getCategoryWhenIconSetWithRules() { - EClass textEClass = mock(EClass.class); - when(textEClass.getName()).thenReturn("text"); - when(textEClass.getInstanceTypeName()).thenReturn("org.openhab.core.model.sitemap.Text"); - when(widgetMock.eClass()).thenReturn(textEClass); + when(widgetMock.getWidgetType()).thenReturn("Text"); when(widgetMock.getIcon()).thenReturn(null); - when(widgetMock.getStaticIcon()).thenReturn(null); + when(widgetMock.isStaticIcon()).thenReturn(false); Condition condition = mock(Condition.class); - when(condition.getState()).thenReturn("21"); + when(condition.getValue()).thenReturn("21"); when(condition.getCondition()).thenReturn(">="); Condition condition2 = mock(Condition.class); - when(condition2.getState()).thenReturn("24"); + when(condition2.getValue()).thenReturn("24"); when(condition2.getCondition()).thenReturn("<"); - BasicEList conditions = new BasicEList<>(); + List conditions = new ArrayList<>(); conditions.add(condition); conditions.add(condition2); - IconRule rule = mock(IconRule.class); + Rule rule = mock(Rule.class); when(rule.getConditions()).thenReturn(conditions); - when(rule.getArg()).thenReturn("temperature"); - BasicEList rules = new BasicEList<>(); + when(rule.getArgument()).thenReturn("temperature"); + List rules = new ArrayList<>(); rules.add(rule); - BasicEList conditions2 = new BasicEList<>(); - IconRule rule2 = mock(IconRule.class); + List conditions2 = new ArrayList<>(); + Rule rule2 = mock(Rule.class); when(rule2.getConditions()).thenReturn(conditions2); - when(rule2.getArg()).thenReturn("humidity"); + when(rule2.getArgument()).thenReturn("humidity"); rules.add(rule2); when(widgetMock.getIconRules()).thenReturn(rules); @@ -1365,13 +1384,10 @@ public void getCategoryWhenIconSetWithRules() { @Test public void getCategoryWhenStaticIconSet() { - EClass textEClass = mock(EClass.class); - when(textEClass.getName()).thenReturn("text"); - when(textEClass.getInstanceTypeName()).thenReturn("org.openhab.core.model.sitemap.Text"); - when(widgetMock.eClass()).thenReturn(textEClass); - when(widgetMock.getIcon()).thenReturn(null); - when(widgetMock.getStaticIcon()).thenReturn("temperature"); - when(widgetMock.getIconRules()).thenReturn(null); + when(widgetMock.getWidgetType()).thenReturn("Text"); + when(widgetMock.getIcon()).thenReturn("temperature"); + when(widgetMock.isStaticIcon()).thenReturn(true); + when(widgetMock.getIconRules()).thenReturn(List.of()); String icon = uiRegistry.getCategory(widgetMock); assertEquals("temperature", icon); @@ -1379,13 +1395,10 @@ public void getCategoryWhenStaticIconSet() { @Test public void getCategoryWhenIconSetOnItem() { - EClass textEClass = mock(EClass.class); - when(textEClass.getName()).thenReturn("text"); - when(textEClass.getInstanceTypeName()).thenReturn("org.openhab.core.model.sitemap.Text"); - when(widgetMock.eClass()).thenReturn(textEClass); + when(widgetMock.getWidgetType()).thenReturn("Text"); when(widgetMock.getIcon()).thenReturn(null); - when(widgetMock.getStaticIcon()).thenReturn(null); - when(widgetMock.getIconRules()).thenReturn(null); + when(widgetMock.isStaticIcon()).thenReturn(false); + when(widgetMock.getIconRules()).thenReturn(List.of()); when(itemMock.getCategory()).thenReturn("temperature"); @@ -1395,13 +1408,10 @@ public void getCategoryWhenIconSetOnItem() { @Test public void getCategoryDefaultIcon() { - EClass textEClass = mock(EClass.class); - when(textEClass.getName()).thenReturn("text"); - when(textEClass.getInstanceTypeName()).thenReturn("org.openhab.core.model.sitemap.Text"); - when(widgetMock.eClass()).thenReturn(textEClass); + when(widgetMock.getWidgetType()).thenReturn("Text"); when(widgetMock.getIcon()).thenReturn(null); - when(widgetMock.getStaticIcon()).thenReturn(null); - when(widgetMock.getIconRules()).thenReturn(null); + when(widgetMock.isStaticIcon()).thenReturn(false); + when(widgetMock.getIconRules()).thenReturn(List.of()); when(itemMock.getCategory()).thenReturn(null); diff --git a/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/proxy/ProxyServletServiceTest.java b/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/proxy/ProxyServletServiceTest.java index 892bef52398..2eb8cb773bb 100644 --- a/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/proxy/ProxyServletServiceTest.java +++ b/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/proxy/ProxyServletServiceTest.java @@ -34,11 +34,11 @@ import org.mockito.quality.Strictness; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.StringType; -import org.openhab.core.model.sitemap.SitemapProvider; -import org.openhab.core.model.sitemap.sitemap.Image; -import org.openhab.core.model.sitemap.sitemap.Sitemap; -import org.openhab.core.model.sitemap.sitemap.Switch; -import org.openhab.core.model.sitemap.sitemap.Video; +import org.openhab.core.sitemap.Image; +import org.openhab.core.sitemap.Sitemap; +import org.openhab.core.sitemap.Switch; +import org.openhab.core.sitemap.Video; +import org.openhab.core.sitemap.registry.SitemapRegistry; import org.openhab.core.types.UnDefType; import org.openhab.core.ui.items.ItemUIRegistry; import org.osgi.service.http.HttpService; @@ -47,6 +47,7 @@ * Unit tests for the {@link ProxyServletService} class. * * @author Kai Kreuzer - Initial contribution + * @author Mark Herwege - Implement sitemap registry */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -76,7 +77,7 @@ public class ProxyServletServiceTest { private @Mock @NonNullByDefault({}) ItemUIRegistry itemUIRegistryMock; private @Mock @NonNullByDefault({}) HttpService httpServiceMock; - private @Mock @NonNullByDefault({}) SitemapProvider sitemapProviderMock; + private @Mock @NonNullByDefault({}) SitemapRegistry sitemapRegistryMock; private @Mock @NonNullByDefault({}) Sitemap sitemapMock; private @Mock @NonNullByDefault({}) HttpServletRequest requestMock; private @Mock @NonNullByDefault({}) Switch switchWidgetMock; @@ -85,11 +86,10 @@ public class ProxyServletServiceTest { @BeforeEach public void setUp() { - service = new ProxyServletService(httpServiceMock, itemUIRegistryMock, Map.of()); - service.sitemapProviders.add(sitemapProviderMock); + service = new ProxyServletService(httpServiceMock, itemUIRegistryMock, sitemapRegistryMock, Map.of()); sitemapMock = mock(Sitemap.class); - when(sitemapProviderMock.getSitemap(eq(SITEMAP_NAME))).thenReturn(sitemapMock); + when(sitemapRegistryMock.get(eq(SITEMAP_NAME))).thenReturn(sitemapMock); when(itemUIRegistryMock.getWidget(eq(sitemapMock), eq(SWITCH_WIDGET_ID))).thenReturn(switchWidgetMock); when(itemUIRegistryMock.getWidget(eq(sitemapMock), eq(IMAGE_WIDGET_ID))).thenReturn(imageWidgetMock); diff --git a/bundles/pom.xml b/bundles/pom.xml index c57372d5b39..3401cb42ac2 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -53,6 +53,7 @@ org.openhab.core.id org.openhab.core.persistence org.openhab.core.semantics + org.openhab.core.sitemap org.openhab.core.thing org.openhab.core.transform org.openhab.core.voice diff --git a/features/karaf/openhab-core/src/main/feature/feature.xml b/features/karaf/openhab-core/src/main/feature/feature.xml index 491d3129c27..9a8f3c28b97 100644 --- a/features/karaf/openhab-core/src/main/feature/feature.xml +++ b/features/karaf/openhab-core/src/main/feature/feature.xml @@ -53,6 +53,7 @@ mvn:org.openhab.core.bundles/org.openhab.core.id/${project.version} mvn:org.openhab.core.bundles/org.openhab.core.persistence/${project.version} mvn:org.openhab.core.bundles/org.openhab.core.semantics/${project.version} + mvn:org.openhab.core.bundles/org.openhab.core.sitemap/${project.version} asm mvn:org.openhab.core.bundles/org.openhab.core.thing/${project.version} mvn:org.openhab.core.bundles/org.openhab.core.transform/${project.version} @@ -201,7 +202,6 @@ openhab-core-base openhab-core-ui mvn:org.openhab.core.bundles/org.openhab.core.io.rest.sitemap/${project.version} - openhab-core-model-sitemap @@ -436,8 +436,6 @@ openhab-core-base - openhab-core-model-item - openhab-core-model-sitemap mvn:org.openhab.core.bundles/org.openhab.core.ui/${project.version} mvn:org.openhab.core.bundles/org.openhab.core.io.rest.ui/${project.version} diff --git a/itests/org.openhab.core.model.item.tests/itest.bndrun b/itests/org.openhab.core.model.item.tests/itest.bndrun index f889aea2923..316365ca933 100644 --- a/itests/org.openhab.core.model.item.tests/itest.bndrun +++ b/itests/org.openhab.core.model.item.tests/itest.bndrun @@ -112,6 +112,7 @@ Fragment-Host: org.openhab.core.model.item org.openhab.core.model.thing;version='[5.1.0,5.1.1)',\ org.openhab.core.persistence;version='[5.1.0,5.1.1)',\ org.openhab.core.semantics;version='[5.1.0,5.1.1)',\ + org.openhab.core.sitemap;version='[5.1.0,5.1.1)',\ org.openhab.core.test;version='[5.1.0,5.1.1)',\ org.openhab.core.thing;version='[5.1.0,5.1.1)',\ org.openhab.core.transform;version='[5.1.0,5.1.1)',\ diff --git a/itests/org.openhab.core.model.rule.tests/itest.bndrun b/itests/org.openhab.core.model.rule.tests/itest.bndrun index c1de543002b..ee3ccef6aa9 100644 --- a/itests/org.openhab.core.model.rule.tests/itest.bndrun +++ b/itests/org.openhab.core.model.rule.tests/itest.bndrun @@ -115,6 +115,7 @@ Fragment-Host: org.openhab.core.model.rule.runtime org.openhab.core.model.thing;version='[5.1.0,5.1.1)',\ org.openhab.core.persistence;version='[5.1.0,5.1.1)',\ org.openhab.core.semantics;version='[5.1.0,5.1.1)',\ + org.openhab.core.sitemap;version='[5.1.0,5.1.1)',\ org.openhab.core.test;version='[5.1.0,5.1.1)',\ org.openhab.core.thing;version='[5.1.0,5.1.1)',\ org.openhab.core.transform;version='[5.1.0,5.1.1)',\ diff --git a/itests/org.openhab.core.model.script.tests/itest.bndrun b/itests/org.openhab.core.model.script.tests/itest.bndrun index 5c64d6db9d2..272645ea884 100644 --- a/itests/org.openhab.core.model.script.tests/itest.bndrun +++ b/itests/org.openhab.core.model.script.tests/itest.bndrun @@ -119,6 +119,7 @@ Fragment-Host: org.openhab.core.model.script org.openhab.core.model.thing;version='[5.1.0,5.1.1)',\ org.openhab.core.persistence;version='[5.1.0,5.1.1)',\ org.openhab.core.semantics;version='[5.1.0,5.1.1)',\ + org.openhab.core.sitemap;version='[5.1.0,5.1.1)',\ org.openhab.core.test;version='[5.1.0,5.1.1)',\ org.openhab.core.thing;version='[5.1.0,5.1.1)',\ org.openhab.core.transform;version='[5.1.0,5.1.1)',\ diff --git a/itests/org.openhab.core.model.thing.tests/itest.bndrun b/itests/org.openhab.core.model.thing.tests/itest.bndrun index 28be91fdf93..77687adf333 100644 --- a/itests/org.openhab.core.model.thing.tests/itest.bndrun +++ b/itests/org.openhab.core.model.thing.tests/itest.bndrun @@ -121,6 +121,7 @@ Fragment-Host: org.openhab.core.model.thing org.openhab.core.model.thing.testsupport;version='[5.1.0,5.1.1)',\ org.openhab.core.persistence;version='[5.1.0,5.1.1)',\ org.openhab.core.semantics;version='[5.1.0,5.1.1)',\ + org.openhab.core.sitemap;version='[5.1.0,5.1.1)',\ org.openhab.core.test;version='[5.1.0,5.1.1)',\ org.openhab.core.thing;version='[5.1.0,5.1.1)',\ org.openhab.core.transform;version='[5.1.0,5.1.1)',\ diff --git a/itests/org.openhab.core.storage.json.tests/itest.bndrun b/itests/org.openhab.core.storage.json.tests/itest.bndrun index c102c7617bd..eb17dbda5a2 100644 --- a/itests/org.openhab.core.storage.json.tests/itest.bndrun +++ b/itests/org.openhab.core.storage.json.tests/itest.bndrun @@ -68,4 +68,6 @@ Fragment-Host: org.openhab.core.storage.json biz.aQute.tester.junit-platform;version='[7.1.0,7.1.1)',\ org.osgi.service.cm;version='[1.6.0,1.6.1)',\ org.ops4j.pax.logging.pax-logging-api;version='[2.3.0,2.3.1)',\ - org.openhab.core.semantics;version='[5.1.0,5.1.1)' + org.openhab.core.semantics;version='[5.1.0,5.1.1)', \ + org.openhab.core.sitemap;version='[5.1.0,5.1.1)' + \ No newline at end of file diff --git a/itests/org.openhab.core.thing.tests/itest.bndrun b/itests/org.openhab.core.thing.tests/itest.bndrun index 52b4c9ba834..af387bbe45f 100644 --- a/itests/org.openhab.core.thing.tests/itest.bndrun +++ b/itests/org.openhab.core.thing.tests/itest.bndrun @@ -75,4 +75,5 @@ Fragment-Host: org.openhab.core.thing biz.aQute.tester.junit-platform;version='[7.1.0,7.1.1)',\ org.ops4j.pax.logging.pax-logging-api;version='[2.3.0,2.3.1)',\ org.openhab.core.semantics;version='[5.1.0,5.1.1)',\ + org.openhab.core.sitemap;version='[5.1.0,5.1.1)',\ org.objectweb.asm;version='[9.8.0,9.8.1)' diff --git a/itests/org.openhab.core.voice.tests/itest.bndrun b/itests/org.openhab.core.voice.tests/itest.bndrun index 906dfcad4dc..b309095f118 100644 --- a/itests/org.openhab.core.voice.tests/itest.bndrun +++ b/itests/org.openhab.core.voice.tests/itest.bndrun @@ -81,4 +81,6 @@ Fragment-Host: org.openhab.core.voice org.openhab.core.voice.tests;version='[5.1.0,5.1.1)',\ biz.aQute.tester.junit-platform;version='[7.1.0,7.1.1)',\ org.ops4j.pax.logging.pax-logging-api;version='[2.3.0,2.3.1)',\ - org.openhab.core.semantics;version='[5.1.0,5.1.1)' + org.openhab.core.semantics;version='[5.1.0,5.1.1)', \ + org.openhab.core.sitemap;version='[5.1.0,5.1.1)' + \ No newline at end of file From f48ac160f1a3b00b3e70b0ba5001407194779bc0 Mon Sep 17 00:00:00 2001 From: Mark Herwege Date: Wed, 3 Sep 2025 16:46:28 +0200 Subject: [PATCH 2/6] fix javadoc Signed-off-by: Mark Herwege --- .../src/main/java/org/openhab/core/sitemap/Chart.java | 4 ++-- .../src/main/java/org/openhab/core/sitemap/Condition.java | 2 +- .../src/main/java/org/openhab/core/sitemap/Input.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java index 25c17d5505b..4417dbdc73a 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Chart.java @@ -53,7 +53,7 @@ public interface Chart extends NonLinkableWidget { void setRefresh(@Nullable Integer refresh); /** - * Get the configured chart time period. See {@link setPeriod()}. + * Get the configured chart time period. See {@link #setPeriod(String)}. * * @return period */ @@ -119,7 +119,7 @@ public interface Chart extends NonLinkableWidget { void setYAxisDecimalPattern(@Nullable String yAxisDecimalPattern); /** - * Gets the interpolation parameter. See {@link setInterpolation()}. + * Gets the interpolation parameter. See {@link #setInterpolation(String)}. * * @return interpolation */ diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java index a15be7e4bba..21568272c05 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java @@ -50,7 +50,7 @@ public interface Condition { String getCondition(); /** - * Set the condition comparator, see {@link getCondition()}. + * Set the condition comparator, see {@link #getCondition()}. * * @param condition */ diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java index 1aee018a321..a466ee77356 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Input.java @@ -24,7 +24,7 @@ public interface Input extends NonLinkableWidget { /** - * Get the input hint. This can be used by a UI to tailor the representation. See {@link setInputHint()}. + * Get the input hint. This can be used by a UI to tailor the representation. See {@link #setInputHint(String)}. * * @return input hint */ From 40a340a0bb88704ea7e16ed393390eda8f5e0eef Mon Sep 17 00:00:00 2001 From: Mark Herwege Date: Wed, 3 Sep 2025 16:53:14 +0200 Subject: [PATCH 3/6] fix javadoc Signed-off-by: Mark Herwege --- .../src/main/java/org/openhab/core/sitemap/Condition.java | 5 +++-- .../src/main/java/org/openhab/core/sitemap/Widget.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java index 21568272c05..2e13a2d28c6 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Condition.java @@ -41,8 +41,9 @@ public interface Condition { void setItem(@Nullable String item); /** - * Get the condition comparator. Valid values are: "==", ">", "<", ">=", "<=", "!=". The item in the condition will - * be compared against the value using this comparator. If no condition comparator is set, "==" is assumed. + * Get the condition comparator. Valid values are: "==", ">", "<", ">=", "<=", "!=". The item in the + * condition will be compared against the value using this comparator. If no condition comparator is set, "==" is + * assumed. * * @return condition comparator */ diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java index c689a95ca3c..440a735c410 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java @@ -174,7 +174,7 @@ public interface Widget { * * @param visibility */ - void setVisibility(List visiblity); + void setVisibility(List visibility); /** * Get type of widget. From af8145bc59700c0267be44874f0558b06e240887 Mon Sep 17 00:00:00 2001 From: Mark Herwege Date: Wed, 3 Sep 2025 17:22:13 +0200 Subject: [PATCH 4/6] copilot review Signed-off-by: Mark Herwege --- .../core/sitemap/internal/registry/SitemapFactoryImpl.java | 3 +-- .../core/ui/internal/proxy/ProxyServletService.java | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java index 5d3739a5480..2f9da8d1152 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapFactoryImpl.java @@ -45,11 +45,10 @@ import org.openhab.core.sitemap.internal.VideoImpl; import org.openhab.core.sitemap.internal.WebviewImpl; import org.openhab.core.sitemap.registry.SitemapFactory; -import org.openhab.core.sitemap.registry.SitemapRegistry; import org.osgi.service.component.annotations.Component; /** - * The {@link SitemapFactoryImpl} implements the {@link SitemapRegistry} + * The {@link SitemapFactoryImpl} implements the {@link SitemapFactory} * * @author Mark Herwege - Initial contribution */ diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/proxy/ProxyServletService.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/proxy/ProxyServletService.java index ca8ece15d2f..dcc097aad18 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/proxy/ProxyServletService.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/proxy/ProxyServletService.java @@ -258,7 +258,7 @@ URI uriFromRequest(HttpServletRequest request) { } try { - uri = createURIFromString(uriString != null ? uriString : ""); + uri = createURIFromString(uriString); request.setAttribute(ATTR_URI, uri); return uri; } catch (MalformedURLException | URISyntaxException ex) { @@ -275,7 +275,10 @@ URI uriFromRequest(HttpServletRequest request) { return sitemapRegistry.get(sitemapName); } - private URI createURIFromString(String url) throws MalformedURLException, URISyntaxException { + private URI createURIFromString(@Nullable String url) throws MalformedURLException, URISyntaxException { + if (url == null) { + throw new MalformedURLException(); + } URI uri = new URI(url); // URI in this context should be valid URL. Therefore before returning URI, create URL, // which validates the string. From f9bbe19e49a57c42b711839dd83792bcbee7efb8 Mon Sep 17 00:00:00 2001 From: Mark Herwege Date: Sat, 20 Sep 2025 19:00:45 +0200 Subject: [PATCH 5/6] review adjustments Signed-off-by: Mark Herwege --- .../internal/WidgetsChangeListener.java | 2 +- .../org/openhab/core/sitemap/Buttongrid.java | 2 +- .../org/openhab/core/sitemap/Sitemap.java | 6 ---- .../java/org/openhab/core/sitemap/Widget.java | 8 ++--- .../core/sitemap/internal/WidgetImpl.java | 32 +++++++++--------- .../registry/SitemapRegistryImpl.java | 33 ------------------- .../UIComponentSitemapProvider.java | 12 +++++-- .../ui/internal/items/ItemUIRegistryImpl.java | 6 ++-- 8 files changed, 35 insertions(+), 66 deletions(-) diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java index 82b60b91936..cf83cfeb6b8 100644 --- a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java +++ b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java @@ -247,7 +247,7 @@ private SitemapWidgetEvent constructSitemapEventForWidget(Item item, State state final Item itemToBeSent = itemBelongsToWidget ? item : getItemForWidget(widget); State stateToBeSent = null; if (itemToBeSent != null) { - String widgetTypeName = widget.getClass().getSimpleName(); + String widgetTypeName = widget.getWidgetType(); boolean drillDown = "mapview".equalsIgnoreCase(widgetTypeName); Predicate itemFilter = (i -> CoreItemFactory.LOCATION.equals(i.getType())); event.item = EnrichedItemDTOMapper.map(itemToBeSent, drillDown, itemFilter, null, null, diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java index b8fbcf0b014..439f6667476 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Buttongrid.java @@ -33,7 +33,7 @@ public interface Buttongrid extends LinkableWidget { List getButtons(); /** - * Replace the button grid buttons a new list of buttons. + * Replace the button grid buttons with a new list of buttons. * * @param buttons */ diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java index 9924de25278..903f23dcebf 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Sitemap.java @@ -30,7 +30,6 @@ public interface Sitemap extends Identifiable, Parent { * Returns the sitemap name. * * @return sitemap name. - * @see #setName(String) */ String getName(); @@ -38,7 +37,6 @@ public interface Sitemap extends Identifiable, Parent { * Sets the sitemap name. * * @param name the new sitemap name. - * @see #getName() */ void setName(String name); @@ -46,7 +44,6 @@ public interface Sitemap extends Identifiable, Parent { * Returns the sitemap label. * * @return sitemap label. - * @see #setLabel(String) */ @Nullable String getLabel(); @@ -55,7 +52,6 @@ public interface Sitemap extends Identifiable, Parent { * Sets the sitemap label. * * @param label the new sitemap label. - * @see #getLabel() */ void setLabel(@Nullable String label); @@ -63,7 +59,6 @@ public interface Sitemap extends Identifiable, Parent { * Returns the sitemap icon. * * @return sitemap icon. - * @see #setIcon(String) */ @Nullable String getIcon(); @@ -72,7 +67,6 @@ public interface Sitemap extends Identifiable, Parent { * Sets the sitemap icon. * * @param icon the new sitemap icon. - * @see #getIcon() */ void setIcon(@Nullable String icon); diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java index 440a735c410..d4ced85c3fa 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java @@ -129,7 +129,7 @@ public interface Widget { * * @param labelColor */ - void setLabelColor(List labelColor); + void setLabelColor(List labelColorRules); /** * Get the widget value color rules. This method should return a modifiable list, allowing updates to the value @@ -144,7 +144,7 @@ public interface Widget { * * @param valueColor */ - void setValueColor(List valueColor); + void setValueColor(List valueColorRules); /** * Get the widget icon color rules. This method should return a modifiable list, allowing updates to the icon @@ -159,7 +159,7 @@ public interface Widget { * * @param iconColor */ - void setIconColor(List iconColor); + void setIconColor(List iconColorRules); /** * Get the widget visibility rules. This method should return a modifiable list, allowing updates to the visibility @@ -174,7 +174,7 @@ public interface Widget { * * @param visibility */ - void setVisibility(List visibility); + void setVisibility(List visibilityRules); /** * Get type of widget. diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java index e53a5de3514..ac1b8e63b90 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/WidgetImpl.java @@ -36,10 +36,10 @@ public class WidgetImpl implements Widget { private List iconRules = new CopyOnWriteArrayList<>(); private @Nullable Boolean staticIcon; - private List labelColor = new CopyOnWriteArrayList<>(); - private List valueColor = new CopyOnWriteArrayList<>(); - private List iconColor = new CopyOnWriteArrayList<>(); - private List visibility = new CopyOnWriteArrayList<>(); + private List labelColorRules = new CopyOnWriteArrayList<>(); + private List valueColorRules = new CopyOnWriteArrayList<>(); + private List iconColorRules = new CopyOnWriteArrayList<>(); + private List visibilityRules = new CopyOnWriteArrayList<>(); public WidgetImpl() { } @@ -110,42 +110,42 @@ public void setStaticIcon(@Nullable Boolean staticIcon) { @Override public List getLabelColor() { - return labelColor; + return labelColorRules; } @Override - public void setLabelColor(List labelColor) { - this.labelColor = new CopyOnWriteArrayList<>(labelColor); + public void setLabelColor(List labelColorRules) { + this.labelColorRules = new CopyOnWriteArrayList<>(labelColorRules); } @Override public List getValueColor() { - return valueColor; + return valueColorRules; } @Override - public void setValueColor(List valueColor) { - this.valueColor = new CopyOnWriteArrayList<>(valueColor); + public void setValueColor(List valueColorRules) { + this.valueColorRules = new CopyOnWriteArrayList<>(valueColorRules); } @Override public List getIconColor() { - return iconColor; + return iconColorRules; } @Override - public void setIconColor(List iconColor) { - this.iconColor = new CopyOnWriteArrayList<>(iconColor); + public void setIconColor(List iconColorRules) { + this.iconColorRules = new CopyOnWriteArrayList<>(iconColorRules); } @Override public List getVisibility() { - return visibility; + return visibilityRules; } @Override - public void setVisibility(List visibility) { - this.visibility = new CopyOnWriteArrayList<>(visibility); + public void setVisibility(List visibilityRules) { + this.visibilityRules = new CopyOnWriteArrayList<>(visibilityRules); } @Override diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java index d83644bab94..c44a537af5d 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/internal/registry/SitemapRegistryImpl.java @@ -12,13 +12,9 @@ */ package org.openhab.core.sitemap.internal.registry; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.common.registry.AbstractRegistry; import org.openhab.core.common.registry.Provider; -import org.openhab.core.common.registry.RegistryChangeListener; import org.openhab.core.sitemap.Sitemap; import org.openhab.core.sitemap.registry.SitemapProvider; import org.openhab.core.sitemap.registry.SitemapRegistry; @@ -37,35 +33,6 @@ public SitemapRegistryImpl() { super(null); } - private final Set> registryChangeListeners = new CopyOnWriteArraySet<>(); - - @Override - protected void notifyListenersAboutAddedElement(Sitemap element) { - registryChangeListeners.forEach(listener -> listener.added(element)); - super.notifyListenersAboutAddedElement(element); - } - - @Override - protected void notifyListenersAboutRemovedElement(Sitemap element) { - registryChangeListeners.forEach(listener -> listener.removed(element)); - super.notifyListenersAboutRemovedElement(element); - } - - @Override - protected void notifyListenersAboutUpdatedElement(Sitemap oldElement, Sitemap element) { - registryChangeListeners.forEach(listener -> listener.updated(oldElement, element)); - } - - @Override - public void addRegistryChangeListener(RegistryChangeListener listener) { - registryChangeListeners.add(listener); - } - - @Override - public void removeRegistryChangeListener(RegistryChangeListener listener) { - registryChangeListeners.remove(listener); - } - @Override public void addSitemapProvider(Provider provider) { addProvider(provider); diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java index dd97321b07f..47ed22f1f9c 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java @@ -47,6 +47,7 @@ import org.openhab.core.sitemap.Setpoint; import org.openhab.core.sitemap.Sitemap; import org.openhab.core.sitemap.Slider; +import org.openhab.core.sitemap.Switch; import org.openhab.core.sitemap.Video; import org.openhab.core.sitemap.Webview; import org.openhab.core.sitemap.Widget; @@ -173,6 +174,9 @@ protected Sitemap buildSitemap(RootUIComponent rootComponent) { setWidgetPropertyFromComponentConfig(webviewWidget, component, "height"); setWidgetPropertyFromComponentConfig(webviewWidget, component, "url"); break; + case Switch switchWidget: + addWidgetMappings(switchWidget.getMappings(), component); + break; case Mapview mapviewWidget: setWidgetPropertyFromComponentConfig(mapviewWidget, component, "height"); break; @@ -299,9 +303,13 @@ private void addWidgetMappings(List mappings, UIComponent component) { String label = stripQuotes(splitMapping[1].trim()); String icon = splitMapping.length < 3 ? null : stripQuotes(splitMapping[2].trim()); Mapping mapping = sitemapFactory.createMapping(); - mapping.setCmd(cmd != null ? cmd : ""); + if (cmd != null) { + mapping.setCmd(cmd); + } mapping.setReleaseCmd(releaseCmd); - mapping.setLabel(label != null ? label : ""); + if (label != null) { + mapping.setLabel(label); + } mapping.setIcon(icon); mappings.add(mapping); } diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java index 68ce3abddfd..2122da2547b 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java @@ -1268,14 +1268,14 @@ private boolean matchStateToValue(State state, String value, @Nullable String ma @Override public boolean getVisiblity(Widget w) { // Default to visible if parameters not set - List rulList = w.getVisibility(); - if (rulList.isEmpty()) { + List ruleList = w.getVisibility(); + if (ruleList.isEmpty()) { return true; } logger.debug("Checking visiblity for widget '{}'.", w.getLabel()); - for (Rule rule : rulList) { + for (Rule rule : ruleList) { if (allConditionsOk(rule.getConditions(), w)) { return true; } From 7c2a8c5df2e2a111582a493bb2dbda3a12aee1c5 Mon Sep 17 00:00:00 2001 From: Mark Herwege Date: Sat, 20 Sep 2025 19:10:29 +0200 Subject: [PATCH 6/6] fix javadoc Signed-off-by: Mark Herwege --- .../src/main/java/org/openhab/core/sitemap/Widget.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java index d4ced85c3fa..c0639a62fc6 100644 --- a/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java +++ b/bundles/org.openhab.core.sitemap/src/main/java/org/openhab/core/sitemap/Widget.java @@ -127,7 +127,7 @@ public interface Widget { /** * Replace the widget label color rules with a new list of label color rules. * - * @param labelColor + * @param labelColorRules */ void setLabelColor(List labelColorRules); @@ -142,7 +142,7 @@ public interface Widget { /** * Replace the widget value color rules with a new list of value color rules. * - * @param valueColor + * @param valueColorRules */ void setValueColor(List valueColorRules); @@ -157,7 +157,7 @@ public interface Widget { /** * Replace the widget icon color rules with a new list of icon color rules. * - * @param iconColor + * @param iconColorRules */ void setIconColor(List iconColorRules); @@ -172,7 +172,7 @@ public interface Widget { /** * Replace the widget visibility rules with a new list of visibility rules. * - * @param visibility + * @param visibilityRules */ void setVisibility(List visibilityRules);