/*******************************************************************************
* 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.incquery.runtime.rete.index;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.incquery.runtime.rete.collections.CollectionsFactory;
import org.eclipse.incquery.runtime.rete.network.Direction;
import org.eclipse.incquery.runtime.rete.network.Node;
import org.eclipse.incquery.runtime.rete.network.ReteContainer;
import org.eclipse.incquery.runtime.rete.network.StandardNode;
import org.eclipse.incquery.runtime.rete.tuple.LeftInheritanceTuple;
import org.eclipse.incquery.runtime.rete.tuple.Tuple;
import org.eclipse.incquery.runtime.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;
int sourceWidth;
Map<Tuple, Object> mainAggregates;
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;
mainAggregates = //new HashMap<Tuple, Object>();
CollectionsFactory.getMap();
this.projection = projection;
this.sourceWidth = projection.getMask().indices.length;
for (Tuple signature : projection.getSignatures()) {
mainAggregates.put(signature, aggregateGroup(signature, projection.get(signature)));
}
projection.attachListener(new DefaultIndexerListener(this) {
@Override
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, having access to the previous aggregated value (before the update) and
* the update definition. Defaults to aggregateGroup(). Override to increase performance.
*/
public Object aggregateGroupAfterUpdate(Tuple signature, Collection<Tuple> currentGroup, Object oldAggregate,
Direction direction, Tuple updateElement, boolean change) {
return aggregateGroup(signature, currentGroup);
}
protected Tuple aggregateAndPack(Tuple signature, Collection<Tuple> group) {
return packResult(signature, aggregateGroup(signature, group));
}
public AggregatorOuterIndexer getAggregatorOuterIndexer() {
if (aggregatorOuterIndexer == null) {
aggregatorOuterIndexer = new AggregatorOuterIndexer();
// reteContainer.connectAndSynchronize(this, aggregatorOuterIndexer);
}
return aggregatorOuterIndexer;
}
public AggregatorOuterIdentityIndexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature) {
if (aggregatorOuterIdentityIndexers == null)
aggregatorOuterIdentityIndexers = new AggregatorOuterIdentityIndexer[sourceWidth + 1];
if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) {
aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer(
resultPositionInSignature);
// reteContainer.connectAndSynchronize(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]);
}
return aggregatorOuterIdentityIndexers[resultPositionInSignature];
}
@Override
public void pullInto(Collection<Tuple> collector) {
for (Tuple signature : mainAggregates.keySet()) {
collector.add(packResult(signature, mainAggregates.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);
// these will be null if group is empty
Object oldAggregate = mainAggregates.get(signature);
Object safeOldAggregate = oldAggregate == null ? aggregateGroup(signature, null) : oldAggregate;
boolean empty = currentGroup == null || currentGroup.isEmpty();
Object newAggregate = empty ? null : aggregateGroupAfterUpdate(signature, currentGroup, safeOldAggregate/*
* non-null
*/,
direction, updateElement, change);
if (!empty)
mainAggregates.put(signature, newAggregate);
else
mainAggregates.remove(signature);
Tuple oldTuple = packResult(signature, safeOldAggregate);
Tuple newTuple = packResult(signature, newAggregate == null ? aggregateGroup(signature, null) : newAggregate);
if (oldAggregate != null)
propagateUpdate(Direction.REVOKE, oldTuple); // direct outputs lack non-empty groups
if (newAggregate != null)
propagateUpdate(Direction.INSERT, newTuple); // direct outputs lack non-empty groups
if (aggregatorOuterIndexer != null)
aggregatorOuterIndexer.propagate(signature, oldTuple, newTuple);
if (aggregatorOuterIdentityIndexers != null)
for (AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers)
if (aggregatorOuterIdentityIndexer != null)
aggregatorOuterIdentityIndexer.propagate(signature, oldTuple, newTuple);
}
// 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);
// }
private Object getAggregate(Tuple signature) {
Object aggregate = mainAggregates.get(signature);
return aggregate == null ? aggregateGroup(signature, null) : aggregate;
}
/**
* 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 {
// private Map<Tuple,Tuple> localAggregates;
public AggregatorOuterIndexer() {
super(me.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1));
this.parent = me;
// this.localAggregates = new HashMap<Tuple, Tuple>();
//
// for (Tuple signature: projection.getSignatures()) {
// localAggregates.put(signature, aggregateGroup(signature, projection.get(signature)));
// }
}
@Override
public Collection<Tuple> get(Tuple signature) {
return Collections.singleton(packResult(signature, getAggregate(signature)));
}
public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) {
propagate(Direction.INSERT, newTuple, signature, false);
propagate(Direction.REVOKE, oldTuple, signature, false);
}
// @Override
// public void update(Direction direction, Tuple updateElement) {
// Tuple signature = mask.transform(updateElement);
// Tuple neutral = aggregateAndPack(signature, null);
// if (direction == Direction.INSERT) {
// propagate(Direction.INSERT, updateElement, signature, false);
// propagate(Direction.REVOKE, neutral, signature, false);
// localAggregates.put(signature, updateElement);
// } else {
// localAggregates.remove(signature);
// propagate(Direction.INSERT, neutral, signature, false);
// propagate(Direction.REVOKE, updateElement, signature, false);
// }
// }
// private Object getLocalAggregate(Tuple signature) {
// Tuple resultTuple = localAggregates.get(signature);
// return resultTuple == null? aggregateGroup(signature, null) : resultTuple.get(sourceWidth);
// }
@Override
public Node getActiveNode() {
return projection.getActiveNode();
}
}
/**
* 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 /* implements Receiver */{
// private Map<Tuple,Tuple> localAggregates;
int resultPositionInSignature;
TupleMask pruneResult;
TupleMask reorder;
public AggregatorOuterIdentityIndexer(int resultPositionInSignature) {
super(me.reteContainer, TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1));
this.parent = me;
// this.localAggregates = new HashMap<Tuple, Tuple>();
this.resultPositionInSignature = resultPositionInSignature;
this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1);
if (resultPositionInSignature == sourceWidth)
this.reorder = null;
else
this.reorder = mask;
}
@Override
public Collection<Tuple> get(Tuple signatureWithResult) {
Tuple prunedSignature = pruneResult.transform(signatureWithResult);
Object result = getAggregate(prunedSignature);
if (signatureWithResult.get(resultPositionInSignature).equals(result))
return Collections.singleton(signatureWithResult);
else
return null;
}
public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) {
propagate(Direction.INSERT, reorder(newTuple), signature, true);
propagate(Direction.REVOKE, reorder(oldTuple), signature, true);
}
// @Override
// public void update(Direction direction, Tuple signatureWithResult) {
// Tuple prunedSignature = pruneResult.transform(signatureWithResult);
// if (direction == Direction.INSERT)
// localAggregates.put(prunedSignature, signatureWithResult);
// else
// localAggregates.remove(prunedSignature);
// Tuple transformed = reorder(signatureWithResult);
// propagate(direction, transformed, transformed, true);
// }
private Tuple reorder(Tuple signatureWithResult) {
Tuple transformed;
if (reorder == null)
transformed = signatureWithResult;
else
transformed = reorder.transform(signatureWithResult);
return transformed;
}
// private Object getLocalAggregate(Tuple signature) {
// Tuple resultTuple = localAggregates.get(signature);
// return resultTuple == null? aggregateGroup(signature, null) : resultTuple.get(resultPositionInSignature);
// }
@Override
public Node getActiveNode() {
return projection.getActiveNode();
}
}
}