blob: e478a6fa338605fe0aef196a43b238b4262e50f6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Gabor Bergmann - initial API and implementation
*******************************************************************************/
package org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.index;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Direction;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.ReteContainer;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.StandardNode;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.LeftInheritanceTuple;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.Tuple;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.TupleMask;
/**
* A special node depending on a projection indexer to aggregate tuple groups with the same projection.
* Only propagates the aggregates of non-empty groups. Use the outer indexers to circumvent.
* @author Bergmann Gábor
*
*/
public abstract class AggregatorNode extends StandardNode {
ProjectionIndexer projection;
AggregatorNode me;
AggregatorOuterIndexer aggregatorOuterIndexer = null;
AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers = null;
/**
* @param reteContainer
* @param projection the projection indexer whose tuple groups should be aggregated
*/
public AggregatorNode(ReteContainer reteContainer, ProjectionIndexer projection) {
super(reteContainer);
this.me = this;
this.projection = projection;
projection.attachListener(new IndexerListener(){
public void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change) {
aggregateUpdate(direction, updateElement, signature, change);
}
});
}
/**
* Aggregates (reduces) a group of tuples.
* The group can be null.
*/
public abstract Object aggregateGroup(Tuple signature, Collection<Tuple> group);
/**
* Aggregates (reduces) a group of tuples, while also returning the previous aggregated value (before the update).
* @return an array of length 2: {previousAggregate, currentAggregate}
*/
public abstract Object[] aggregateGroupBeforeAndNow(Tuple signature, Collection<Tuple> currentGroup, Direction direction, Tuple updateElement, boolean change);
protected Tuple aggregateAndPack(Tuple signature, Collection<Tuple> group) {
return packResult(signature, aggregateGroup(signature, group));
}
public AggregatorOuterIndexer getAggregatorOuterIndexer() {
if (aggregatorOuterIndexer==null)
aggregatorOuterIndexer = new AggregatorOuterIndexer();
return aggregatorOuterIndexer;
}
public AggregatorOuterIdentityIndexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature) {
if (aggregatorOuterIdentityIndexers==null)
aggregatorOuterIdentityIndexers = new AggregatorOuterIdentityIndexer[projection.getMask().indices.length + 1];
if (aggregatorOuterIdentityIndexers[resultPositionInSignature]==null)
aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer(resultPositionInSignature);
return aggregatorOuterIdentityIndexers[resultPositionInSignature];
}
/* (non-Javadoc)
* @see org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.network.Supplier#pullInto(java.util.Collection)
*/
public void pullInto(Collection<Tuple> collector) {
reteContainer.flushUpdates();
for (Tuple signature: projection.getSignatures()) {
collector.add(aggregateAndPack(signature, projection.get(signature)));
}
}
protected Tuple packResult(Tuple signature, Object result) {
Object[] resultArray = {result};
return new LeftInheritanceTuple(signature, resultArray);
}
protected void aggregateUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change) {
Collection<Tuple> currentGroup = projection.get(signature);
Object[] aggregatedResults = aggregateGroupBeforeAndNow(signature, currentGroup, direction, updateElement, change);
propagate(Direction.REVOKE, packResult(signature, aggregatedResults[0]), signature);
propagate(Direction.INSERT, packResult(signature, aggregatedResults[1]), signature);
}
protected void propagate(Direction direction, Tuple packResult, Tuple signature) {
propagateUpdate(direction, packResult);
if (aggregatorOuterIndexer!=null) aggregatorOuterIndexer.propagate(direction, packResult, signature);
if (aggregatorOuterIdentityIndexers!=null)
for (AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers)
aggregatorOuterIdentityIndexer.propagate(direction, packResult, signature);
}
/**
* A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original signature.
* @author Bergmann Gábor
*/
class AggregatorOuterIndexer extends StandardIndexer {
public AggregatorOuterIndexer() {
super(me.reteContainer, projection.getMask());
this.parent = me.projection.getParent();
}
public Collection<Tuple> get(Tuple signature) {
return Collections.singleton(aggregateAndPack(signature, projection.get(signature)));
}
public void propagate(Direction direction, Tuple packResult, Tuple signature) {
propagate(direction, packResult, signature, true);
}
}
/**
* A special non-iterable index that checks a suspected aggregate value for a given signature.
* The signature for this index is the original signature of the projection index,
* with the suspected result inserted at position resultPositionInSignature.
* @author Bergmann Gábor
*/
class AggregatorOuterIdentityIndexer extends StandardIndexer {
int resultPositionInSignature;
TupleMask pruneResult;
TupleMask reorder;
public AggregatorOuterIdentityIndexer(int resultPositionInSignature) {
super(me.reteContainer, projection.getMask());
this.parent = me.projection.getParent();
this.resultPositionInSignature = resultPositionInSignature;
int sizeWithResult = projection.getMask().indices.length + 1;
this.pruneResult = TupleMask.omit(resultPositionInSignature, sizeWithResult);
if (resultPositionInSignature == sizeWithResult - 1)
this.reorder = null;
else
this.reorder = TupleMask.displace(sizeWithResult-1, resultPositionInSignature, sizeWithResult);
}
public Collection<Tuple> get(Tuple signatureWithResult) {
Tuple prunedSignature = pruneResult.transform(signatureWithResult);
Object result = aggregateGroup(prunedSignature, projection.get(prunedSignature));
if (signatureWithResult.get(resultPositionInSignature).equals(result))
return Collections.singleton(signatureWithResult);
else
return null;
}
public void propagate(Direction direction, Tuple packResult, Tuple signature) {
Tuple updateElement;
if (reorder == null)
updateElement = packResult;
else
updateElement = reorder.transform(packResult);
propagate(direction, updateElement, updateElement, true);
}
}
}