Plugin: Watches for properties being changed and then fires change events off to the message bus.
| 18 | * Plugin: Watches for properties being changed and then fires change events off to the message bus. |
| 19 | */ |
| 20 | public class Watches extends Box { |
| 21 | |
| 22 | private final MessageQueue<Quad<Dict.Prop, Box, Object, Object>, String> messageQueue; |
| 23 | static public final Dict.Prop<Watches> watches = new Dict.Prop<>("_watches").type().toCanon(); |
| 24 | |
| 25 | public Watches(MessageQueue<Quad<Dict.Prop, Box, Object, Object>, String> messageQueue) { |
| 26 | this.messageQueue = messageQueue; |
| 27 | this.properties.putToMap(Boxes.insideRunLoop, "main.__watch_updator__", this::update); |
| 28 | this.properties.put(watches, this); |
| 29 | } |
| 30 | |
| 31 | public Watches() |
| 32 | { |
| 33 | this.messageQueue = new MessageQueue<Quad<Dict.Prop, Box, Object, Object>, String>() { |
| 34 | @Override |
| 35 | protected Consumer<Boolean> makeQueueServiceThread(BiConsumer<String, Quad<Dict.Prop, Box, Object, Object>> to) { |
| 36 | |
| 37 | CompletableFuture<Boolean> stop = new CompletableFuture<Boolean>(); |
| 38 | |
| 39 | RunLoop.main.getLoop().attach(0, (x) -> { |
| 40 | try { |
| 41 | if (this.queue.size() > 0) Log.log("debug.messages", ()->" message queue :" + this.queue.size()); |
| 42 | |
| 43 | while (this.queue.peek() != null) { |
| 44 | Pair<String, Quad<Dict.Prop, Box, Object, Object>> m = this.queue.poll(1, TimeUnit.SECONDS); |
| 45 | if (m != null && !stop.isDone()) to.accept(m.first, m.second); |
| 46 | } |
| 47 | } catch (InterruptedException e) { |
| 48 | e.printStackTrace(); |
| 49 | } |
| 50 | }); |
| 51 | |
| 52 | return x -> { |
| 53 | }; |
| 54 | } |
| 55 | }; |
| 56 | this.properties.putToMap(Boxes.insideRunLoop, "main.__watch_updator__", this::update); |
| 57 | this.properties.put(watches, this); |
| 58 | } |
| 59 | |
| 60 | static public final Dict.Prop<LinkedHashMap<Dict.Prop, Object>> watchedPrevious = new Dict.Prop<>("_watchedPrevious"); |
| 61 | |
| 62 | SetMultimap<Dict.Prop, String> allWatches = MultimapBuilder.linkedHashKeys().linkedHashSetValues().build(); |
| 63 | |
| 64 | protected boolean update() { |
| 65 | |
| 66 | breadthFirst(both()).forEach((x) -> { |
| 67 | LinkedHashMap<Dict.Prop, Object> previous = x.properties.computeIfAbsent(watchedPrevious, (k) -> new LinkedHashMap<>()); |
| 68 | for (Dict.Prop p : allWatches .keySet()) { |
| 69 | Object was = previous.get(p); |
| 70 | Object now = x.properties.get(p); |
| 71 | |
| 72 | if (!Util.safeEq(was, now)) |
| 73 | { |
| 74 | fire(p, x, was, now, allWatches .get(p)); |
| 75 | // fetch it again, fire can change the value of the property |
| 76 | now = x.properties.get(p); |
| 77 | previous.put(p, now instanceof Mutable ? ((Mutable)now).duplicate() : now); |