aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java
blob: 9c647aa905159f90c90c4e0f2115e90520640db7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*******************************************************************************
 * Copyright (c) 2004-2012 Gabor Bergmann and Daniel Varro
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-v20.html.
 * 
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/

package tools.refinery.viatra.runtime.rete.index;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
import tools.refinery.viatra.runtime.matchers.util.Direction;
import tools.refinery.viatra.runtime.rete.network.Node;
import tools.refinery.viatra.runtime.rete.network.ReteContainer;
import tools.refinery.viatra.runtime.rete.network.Supplier;
import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;

/**
 * A specialized projection indexer that can be memory-less (relying on an external source of information).
 * 
 * <p>
 * All specialized projection indexers of a single node will share the same listener list, so that notification order is
 * maintained (see Bug 518434).
 * 
 * @author Gabor Bergmann
 * @noimplement Rely on the provided implementations
 * @noreference Use only via standard Node and Indexer interfaces
 * @noinstantiate This class is not intended to be instantiated by clients.
 */
public abstract class SpecializedProjectionIndexer extends StandardIndexer implements ProjectionIndexer {

    protected Node activeNode;
    protected List<ListenerSubscription> subscriptions;

    /**
     * @since 1.7
     */
    public SpecializedProjectionIndexer(final ReteContainer reteContainer, final TupleMask mask, final Supplier parent,
            final Node activeNode, final List<ListenerSubscription> subscriptions) {
        super(reteContainer, mask);
        this.parent = parent;
        this.activeNode = activeNode;
        this.subscriptions = subscriptions;
    }

    public List<ListenerSubscription> getSubscriptions() {
        return subscriptions;
    }

    @Override
    public Node getActiveNode() {
        return activeNode;
    }

    @Override
    protected void propagate(final Direction direction, final Tuple updateElement, final Tuple signature,
            final boolean change, final Timestamp timestamp) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void attachListener(final IndexerListener listener) {
        super.attachListener(listener);
        final CommunicationTracker tracker = this.getCommunicationTracker();
        final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener);
        final ListenerSubscription subscription = new ListenerSubscription(this, proxy);
        tracker.registerDependency(this, proxy.getOwner());
        // See Bug 518434
        // Must add to the first position, so that the later listeners are notified earlier.
        // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite
        // slot,
        // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier,
        // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation
        subscriptions.add(0, subscription);
    }

    @Override
    public void detachListener(final IndexerListener listener) {
        final CommunicationTracker tracker = this.getCommunicationTracker();
        // obtain the proxy before the super call would unregister the dependency
        final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener);
        super.detachListener(listener);
        final ListenerSubscription subscription = new ListenerSubscription(this, proxy);
        final boolean wasContained = subscriptions.remove(subscription);
        assert wasContained;
        tracker.unregisterDependency(this, proxy.getOwner());
    }

    @Override
    public void networkStructureChanged() {
        super.networkStructureChanged();
        final List<ListenerSubscription> oldSubscriptions = new ArrayList<ListenerSubscription>();
        oldSubscriptions.addAll(subscriptions);
        subscriptions.clear();
        for (final ListenerSubscription oldSubscription : oldSubscriptions) {
            // there is no need to unregister and re-register the dependency between indexer and listener
            // because the owner of the listener is the same (even if it is proxified)
            final CommunicationTracker tracker = this.getCommunicationTracker();
            // the subscriptions are shared, so we MUST reuse the indexer of the subscription instead of simply 'this'
            final IndexerListener proxy = tracker.proxifyIndexerListener(oldSubscription.indexer, oldSubscription.listener);
            final ListenerSubscription newSubscription = new ListenerSubscription(oldSubscription.indexer, proxy);
            subscriptions.add(newSubscription);
        }
    }

    /**
     * @since 2.4
     */
    public abstract void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement,
            Timestamp timestamp);

    /**
     * Infrastructure to share subscriptions between specialized indexers of the same parent node.
     * 
     * @author Gabor Bergmann
     * @since 1.7
     */
    public static class ListenerSubscription {
        protected SpecializedProjectionIndexer indexer;
        protected IndexerListener listener;

        public ListenerSubscription(SpecializedProjectionIndexer indexer, IndexerListener listener) {
            super();
            this.indexer = indexer;
            this.listener = listener;
        }

        /**
         * @since 2.4
         */
        public SpecializedProjectionIndexer getIndexer() {
            return indexer;
        }

        /**
         * @since 2.4
         */
        public IndexerListener getListener() {
            return listener;
        }

        /**
         * Call this from parent node.
         * @since 2.4
         */
        public void propagate(Direction direction, Tuple updateElement, Timestamp timestamp) {
            indexer.propagateToListener(listener, direction, updateElement, timestamp);
        }

        @Override
        public int hashCode() {
            return Objects.hash(indexer, listener);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ListenerSubscription other = (ListenerSubscription) obj;
            return Objects.equals(listener, other.listener) && Objects.equals(indexer, other.indexer);
        }

    }

}