aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java
blob: 9121fc44f2a92b6b0b31ffcf65171f4c0ed82cf4 (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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/*******************************************************************************
 * Copyright (c) 2004-2008 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.network;

import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper;
import tools.refinery.viatra.runtime.rete.index.Indexer;
import tools.refinery.viatra.runtime.rete.index.OnetimeIndexer;
import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand;
import tools.refinery.viatra.runtime.rete.recipes.*;
import tools.refinery.viatra.runtime.rete.recipes.helper.RecipeRecognizer;
import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper;
import tools.refinery.viatra.runtime.rete.remote.Address;
import tools.refinery.viatra.runtime.rete.remote.RemoteReceiver;
import tools.refinery.viatra.runtime.rete.remote.RemoteSupplier;
import tools.refinery.viatra.runtime.rete.traceability.ActiveNodeConflictTrace;
import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
import tools.refinery.viatra.runtime.rete.traceability.UserRequestTrace;
import tools.refinery.viatra.runtime.rete.util.Options;

import java.util.Map;
import java.util.Set;

/**
 * Stores the internal parts of a rete network. Nodes are stored according to type and parameters.
 *
 * @author Gabor Bergmann
 */
public class NodeProvisioner {

    // boolean activeStorage = true;

    ReteContainer reteContainer;
    NodeFactory nodeFactory;
    ConnectionFactory connectionFactory;
    InputConnector inputConnector;
    IQueryRuntimeContext runtimeContext;

    // TODO as recipe?
    Map<Supplier, RemoteReceiver> remoteReceivers = CollectionsFactory.createMap();
    Map<Address<? extends Supplier>, RemoteSupplier> remoteSuppliers = CollectionsFactory.createMap();

    private RecipeRecognizer recognizer;

    /**
     * PRE: NodeFactory, ConnectionFactory must exist
     *
     * @param reteContainer
     *            the ReteNet whose interior is to be mapped.
     */
    public NodeProvisioner(ReteContainer reteContainer) {
        super();
        this.reteContainer = reteContainer;
        this.nodeFactory = reteContainer.getNodeFactory();
        this.connectionFactory = reteContainer.getConnectionFactory();
        this.inputConnector = reteContainer.getInputConnectionFactory();
        runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext();
        recognizer = new RecipeRecognizer(runtimeContext);
    }

    public synchronized Address<? extends Node> getOrCreateNodeByRecipe(RecipeTraceInfo recipeTrace) {
        ReteNodeRecipe recipe = recipeTrace.getRecipe();
        Address<? extends Node> result = getNodesByRecipe().get(recipe);
        if (result != null) {
            // NODE ALREADY CONSTRUCTED FOR RECIPE, only needs to add trace
            if (getRecipeTraces().add(recipeTrace))
                result.getNodeCache().assignTraceInfo(recipeTrace);
        } else {
            // No node for this recipe object - but equivalent recipes still
            // reusable
            ReteNodeRecipe canonicalRecipe = recognizer.canonicalizeRecipe(recipe);
            if (canonicalRecipe != recipe) {
                // FOUND EQUIVALENT RECIPE
                result = getNodesByRecipe().get(canonicalRecipe);
                if (result != null) {
                    // NODE ALREADY CONSTRUCTED FOR EQUIVALENT RECIPE
                    recipeTrace.shadowWithEquivalentRecipe(canonicalRecipe);
                    getNodesByRecipe().put(recipe, result);
                    if (getRecipeTraces().add(recipeTrace))
                        result.getNodeCache().assignTraceInfo(recipeTrace);
                    // Bug 491922: ensure that recipe shadowing propagates to
                    // parent traces
                    // note that if equivalentRecipes() becomes more
                    // sophisticated
                    // and considers recipes with different parents, this might
                    // have to be changed
                    ensureParents(recipeTrace);
                } else {
                    // CONSTRUCTION IN PROGRESS FOR EQUIVALENT RECIPE
                    if (recipe instanceof IndexerRecipe) {
                        // this is allowed for indexers;
                        // go on with the construction, as the same indexer node
                        // will be obtained anyways
                    } else {
                        throw new IllegalStateException(
                                "This should not happen: " + "non-indexer nodes are are supposed to be constructed "
                                        + "as soon as they are designated as canonical recipes");
                    }
                }
            }
            if (result == null) {
                // MUST INSTANTIATE NEW NODE FOR RECIPE
                final Node freshNode = instantiateNodeForRecipe(recipeTrace, recipe);
                result = reteContainer.makeAddress(freshNode);
            }
        }
        return result;
    }

    private Set<RecipeTraceInfo> getRecipeTraces() {
        return reteContainer.network.recipeTraces;
    }

    private Node instantiateNodeForRecipe(RecipeTraceInfo recipeTrace, final ReteNodeRecipe recipe) {
        this.getRecipeTraces().add(recipeTrace);
        if (recipe instanceof IndexerRecipe) {

            // INSTANTIATE AND HOOK UP
            // (cannot delay hooking up, because parent determines indexer
            // implementation)
            ensureParents(recipeTrace);
            final ReteNodeRecipe parentRecipe = recipeTrace.getParentRecipeTraces().iterator().next().getRecipe();
            final Indexer result = nodeFactory.createIndexer(reteContainer, (IndexerRecipe) recipe,
                    asSupplier(
                            (Address<? extends Supplier>) reteContainer.network.getExistingNodeByRecipe(parentRecipe)),
                    recipeTrace);

            // REMEMBER
            if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) {
                getNodesByRecipe().put(recipe, reteContainer.makeAddress(result));
            }

            return result;
        } else {

            // INSTANTIATE
            Node result = nodeFactory.createNode(reteContainer, recipe, recipeTrace);

            // REMEMBER
            if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) {
                getNodesByRecipe().put(recipe, reteContainer.makeAddress(result));
            }

            // HOOK UP
            // (recursion-tolerant due to this delayed order of initialization)
            if (recipe instanceof InputRecipe) {
                inputConnector.connectInput((InputRecipe) recipe, result);
            } else {
                if (recipe instanceof InputFilterRecipe)
                    inputConnector.connectInputFilter((InputFilterRecipe) recipe, result);
                ensureParents(recipeTrace);
                connectionFactory.connectToParents(recipeTrace, result);
            }
            return result;
        }
    }

    private Map<ReteNodeRecipe, Address<? extends Node>> getNodesByRecipe() {
        return reteContainer.network.nodesByRecipe;
    }

    private void ensureParents(RecipeTraceInfo recipeTrace) {
        for (RecipeTraceInfo parentTrace : recipeTrace.getParentRecipeTraces()) {
            getOrCreateNodeByRecipe(parentTrace);
        }
    }

    //// Remoting - TODO eliminate?

    synchronized RemoteReceiver accessRemoteReceiver(Address<? extends Supplier> address) {
        throw new UnsupportedOperationException("Multi-container Rete not supported yet");
        // if (!reteContainer.isLocal(address))
        // return
        // address.getContainer().getProvisioner().accessRemoteReceiver(address);
        // Supplier localSupplier = reteContainer.resolveLocal(address);
        // RemoteReceiver result = remoteReceivers.get(localSupplier);
        // if (result == null) {
        // result = new RemoteReceiver(reteContainer);
        // reteContainer.connect(localSupplier, result); // stateless node, no
        // // synch required
        //
        // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
        // remoteReceivers.put(localSupplier, result);
        // }
        // return result;
    }

    /**
     * @pre: address is NOT local
     */
    synchronized RemoteSupplier accessRemoteSupplier(Address<? extends Supplier> address) {
        throw new UnsupportedOperationException("Multi-container Rete not supported yet");
        // RemoteSupplier result = remoteSuppliers.get(address);
        // if (result == null) {
        // result = new RemoteSupplier(reteContainer,
        // address.getContainer().getProvisioner()
        // .accessRemoteReceiver(address));
        // // network.connectAndSynchronize(supplier, result);
        //
        // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
        // remoteSuppliers.put(address, result);
        // }
        // return result;
    }

    /**
     * The powerful method for accessing any (supplier) Address as a local supplier.
     */
    public Supplier asSupplier(Address<? extends Supplier> address) {
        if (!reteContainer.isLocal(address))
            return accessRemoteSupplier(address);
        else
            return reteContainer.resolveLocal(address);
    }

    /** the composite key tuple is formed as (RecipeTraceInfo, TupleMask) */
    private Map<Tuple, UserRequestTrace> projectionIndexerUserRequests = CollectionsFactory.createMap();

    // local version
    // TODO remove?
    public synchronized ProjectionIndexer accessProjectionIndexer(RecipeTraceInfo productionTrace, TupleMask mask) {
        Tuple tableKey = Tuples.staticArityFlatTupleOf(productionTrace, mask);
        UserRequestTrace indexerTrace = projectionIndexerUserRequests.computeIfAbsent(tableKey, k -> {
            final ProjectionIndexerRecipe projectionIndexerRecipe = projectionIndexerRecipe(
                    productionTrace, mask);
            return new UserRequestTrace(projectionIndexerRecipe, productionTrace);
        });
        final Address<? extends Node> address = getOrCreateNodeByRecipe(indexerTrace);
        return (ProjectionIndexer) reteContainer.resolveLocal(address);
    }

    // local version
    public synchronized ProjectionIndexer accessProjectionIndexerOnetime(RecipeTraceInfo supplierTrace,
            TupleMask mask) {
        if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
            return accessProjectionIndexer(supplierTrace, mask);

        final Address<? extends Node> supplierAddress = getOrCreateNodeByRecipe(supplierTrace);
        Supplier supplier = (Supplier) reteContainer.resolveLocal(supplierAddress);

        OnetimeIndexer result = new OnetimeIndexer(reteContainer, mask);
        reteContainer.getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, result, reteContainer));

        return result;
    }

    // local, read-only version
    public synchronized ProjectionIndexer peekProjectionIndexer(RecipeTraceInfo supplierTrace, TupleMask mask) {
        final Address<? extends Node> address = getNodesByRecipe().get(projectionIndexerRecipe(supplierTrace, mask));
        return address == null ? null : (ProjectionIndexer) reteContainer.resolveLocal(address);
    }

    private ProjectionIndexerRecipe projectionIndexerRecipe(
            RecipeTraceInfo parentTrace, TupleMask mask) {
        final ReteNodeRecipe parentRecipe = parentTrace.getRecipe();
        Tuple tableKey = Tuples.staticArityFlatTupleOf(parentRecipe, mask);
        ProjectionIndexerRecipe projectionIndexerRecipe = resultSeedRecipes.computeIfAbsent(tableKey, k ->
            RecipesHelper.projectionIndexerRecipe(parentRecipe, CompilerHelper.toRecipeMask(mask))
        );
        return projectionIndexerRecipe;
    }

    /** the composite key tuple is formed as (ReteNodeRecipe, TupleMask) */
    private Map<Tuple, ProjectionIndexerRecipe> resultSeedRecipes = CollectionsFactory.createMap();

    // public synchronized Address<? extends Supplier>
    // accessValueBinderFilterNode(
    // Address<? extends Supplier> supplierAddress, int bindingIndex, Object
    // bindingValue) {
    // Supplier supplier = asSupplier(supplierAddress);
    // Object[] paramsArray = { supplier.getNodeId(), bindingIndex, bindingValue
    // };
    // Tuple params = new FlatTuple(paramsArray);
    // ValueBinderFilterNode result = valueBinderFilters.get(params);
    // if (result == null) {
    // result = new ValueBinderFilterNode(reteContainer, bindingIndex,
    // bindingValue);
    // reteContainer.connect(supplier, result); // stateless node, no synch
    // // required
    //
    // if (Options.nodeSharingOption == Options.NodeSharingOption.ALL)
    // valueBinderFilters.put(params, result);
    // }
    // return reteContainer.makeAddress(result);
    // }

    /**
     * Returns a copy of the given indexer that is an active node by itself (created if does not exist). (Convention:
     * attached with same mask to a transparent node that is attached to parent node.) Node is created if it does not
     * exist yet.
     *
     * @return an identical but active indexer
     */
    // TODO rethink traceability
    RecipeTraceInfo accessActiveIndexer(RecipeTraceInfo inactiveIndexerRecipeTrace) {
        final RecipeTraceInfo parentRecipeTrace = inactiveIndexerRecipeTrace.getParentRecipeTraces().iterator().next();
        final ProjectionIndexerRecipe inactiveIndexerRecipe = (ProjectionIndexerRecipe) inactiveIndexerRecipeTrace
                .getRecipe();

        final TransparentRecipe transparentRecipe = RecipesFactory.eINSTANCE.createTransparentRecipe();
        transparentRecipe.setParent(parentRecipeTrace.getRecipe());
        final ActiveNodeConflictTrace transparentRecipeTrace = new ActiveNodeConflictTrace(transparentRecipe,
                parentRecipeTrace, inactiveIndexerRecipeTrace);

        final ProjectionIndexerRecipe activeIndexerRecipe = RecipesFactory.eINSTANCE
                .createProjectionIndexerRecipe();
        activeIndexerRecipe.setParent(transparentRecipe);
        activeIndexerRecipe.setMask(inactiveIndexerRecipe.getMask());
        final ActiveNodeConflictTrace activeIndexerRecipeTrace = new ActiveNodeConflictTrace(activeIndexerRecipe,
                transparentRecipeTrace, inactiveIndexerRecipeTrace);

        return activeIndexerRecipeTrace;
    }

    // /**
    // * @param parent
    // * @return
    // */
    // private TransparentNode accessTransparentNodeInternal(Supplier parent) {
    // nodeFactory.
    // return null;
    // }

    // public synchronized void registerSpecializedProjectionIndexer(Node node,
    // ProjectionIndexer indexer) {
    // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) {
    // Object[] paramsArray = { node.getNodeId(), indexer.getMask() };
    // Tuple params = new FlatTuple(paramsArray);
    // projectionIndexers.put(params, indexer);
    // }
    // }

}