/*******************************************************************************
* 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 org.eclipse.incquery.runtime.rete.network.Direction;
import org.eclipse.incquery.runtime.rete.network.ReteContainer;
import org.eclipse.incquery.runtime.rete.tuple.Tuple;
import org.eclipse.incquery.runtime.rete.tuple.TupleMask;
/**
* Performs a left outer join.
*
* @author Bergmann Gábor
*
*/
public class OuterJoinNode extends DualInputNode {
final Tuple defaults;
/**
* @param reteContainer
* @param primarySlot
* @param secondarySlot
* @param complementerSecondaryMask
* @param defaults
* the default line to use instead of missing elements if a left tuple has no match
*
*/
public OuterJoinNode(ReteContainer reteContainer, IterableIndexer primarySlot, Indexer secondarySlot,
TupleMask complementerSecondaryMask, Tuple defaults) {
super(reteContainer, primarySlot, secondarySlot, complementerSecondaryMask);
this.defaults = defaults;
}
@Override
public Tuple calibrate(Tuple primary, Tuple secondary) {
return unify(primary, secondary);
}
@Override
public void notifyUpdate(Side side, Direction direction, Tuple updateElement, Tuple signature, boolean change) {
Collection<Tuple> opposites = retrieveOpposites(side, signature);
switch (side) {
case PRIMARY:
if (opposites != null)
for (Tuple opposite : opposites) {
propagateUpdate(direction, unify(updateElement, opposite));
}
else
propagateUpdate(direction, unifyWithDefaults(updateElement));
break;
case SECONDARY:
if (opposites != null)
for (Tuple opposite : opposites) {
propagateUpdate(direction, unify(opposite, updateElement));
if (change)
propagateUpdate(direction.opposite(), unifyWithDefaults(opposite));
}
break;
case BOTH:
for (Tuple opposite : opposites) {
propagateUpdate(direction, unify(updateElement, opposite));
if (updateElement.equals(opposite))
continue;
propagateUpdate(direction, unify(opposite, updateElement));
}
if (direction == Direction.REVOKE) // missed joining with itself
propagateUpdate(direction, unify(updateElement, updateElement));
}
}
@Override
public void pullInto(Collection<Tuple> collector) {
reteContainer.flushUpdates();
for (Tuple signature : primarySlot.getSignatures()) {
Collection<Tuple> primaries = primarySlot.get(signature); // not null due to the contract of
// IterableIndex.getSignatures()
Collection<Tuple> opposites = secondarySlot.get(signature);
if (opposites != null)
for (Tuple ps : primaries)
for (Tuple opposite : opposites) {
collector.add(unify(ps, opposite));
}
else
for (Tuple ps : primaries) {
collector.add(unifyWithDefaults(ps));
}
}
}
private Tuple unifyWithDefaults(Tuple ps) {
return unify(ps, defaults);
}
}