2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mqtt.generic.tools;
17 import java.util.TreeMap;
18 import java.util.concurrent.CompletableFuture;
19 import java.util.function.Consumer;
20 import java.util.function.Function;
21 import java.util.stream.Collectors;
22 import java.util.stream.Stream;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
29 * In some MQTT conventions there are topics dedicated to list further subtopics.
30 * We need to watch those topics and maintain observable lists for those children.
34 * This class consists of a mapping ID->subtopic. Apply an array of subtopics
35 * via the {@link #apply(String[], Function, Function, Consumer)} method.
39 * Restore children from configuration by using {@link #put(String, Object)}.
42 * For example in homie 3.x these topics are meant to be watched:
45 * * homie/mydevice/$nodes
46 * * homie/mydevice/mynode/$properties
50 * An example value of "homie/mydevice/$nodes" could be "lamp1,lamp2,switch", which means there are
51 * "homie/mydevice/lamp1","homie/mydevice/lamp2" and "homie/mydevice/switch" existing and this map
52 * would contain 3 entries [lamp1->Node, lamp2->Node, switch->Node].
55 * @author David Graeff - Initial contribution
57 * @param <T> Any object
60 public class ChildMap<T> {
61 protected Map<String, T> map = new TreeMap<>();
63 public Stream<T> stream() {
64 return map.values().stream();
68 * Modifies the map in way that it matches the entries of the given childIDs.
70 * @param future A future that completes as soon as all children have their added-action performed.
71 * @param childIDs The list of IDs that should be in the map. Everything else currently in the map will be removed.
72 * @param addedAction A function where the newly added child is given as an argument to perform any actions on it.
73 * A future is expected as a return value that completes as soon as said action is performed.
74 * @param supplyNewChild A function where the ID of a new child is given and the created child is
77 * @param removedCallback A callback, that is called whenever a child got removed by the
78 * {@link #apply(CompletableFuture, String[], Function)} method.
79 * @return Complete successfully if all "addedAction" complete successfully, otherwise complete exceptionally.
81 public CompletableFuture<@Nullable Void> apply(String[] childIDs,
82 final Function<T, CompletableFuture<Void>> addedAction, final Function<String, T> supplyNewChild,
83 final Consumer<T> removedCallback) {
84 Set<String> arrayValues = Stream.of(childIDs).collect(Collectors.toSet());
86 // Add all entries to the map, that are not in there yet.
87 final Map<String, T> newSubnodes = arrayValues.stream().filter(entry -> !this.map.containsKey(entry))
88 .collect(Collectors.toMap(k -> k, supplyNewChild));
89 this.map.putAll(newSubnodes);
91 // Remove any entries that are not listed in the 'childIDs'.
92 this.map.entrySet().removeIf(entry -> {
93 if (!arrayValues.contains(entry.getKey())) {
94 removedCallback.accept(entry.getValue());
100 // Apply the 'addedAction' function for all new entries.
101 return CompletableFuture
102 .allOf(newSubnodes.values().stream().map(addedAction).toArray(CompletableFuture[]::new));
106 * Return the size of this map.
113 * Get the item with the given id
118 public T get(@Nullable String key) {
125 public void clear() {
130 * Use this method only to restore a child from configuration.
133 * @param value The subnode object
135 public void put(String key, T value) {