@Override
  public void notifyUpdate(
      Side side, Direction direction, Tuple updateElement, Tuple signature, boolean change) {
    Collection<Tuple> opposites = retrieveOpposites(side, signature);

    if (opposites != null) {
      for (Tuple opposite : opposites) {
        propagateUpdate(direction, unify(side, updateElement, opposite));
      }
    }

    // compensate for coincidence of slots
    if (coincidence) {
      if (opposites != null) {
        for (Tuple opposite : opposites) {
          if (opposite.equals(updateElement)) continue; // INSERT: already joined with itself
          propagateUpdate(direction, unify(opposite, updateElement));
        }
      }
      if (direction == Direction.REVOKE) // missed joining with itself
      propagateUpdate(direction, unify(updateElement, updateElement));
      //
      // switch(direction) {
      // case INSERT:
      // opposites = new ArrayList<Tuple>(opposites);
      // opposites.remove(updateElement);
      // break;
      // case REVOKE:
      // opposites = (opposites == null) ? new ArrayList<Tuple>() : new ArrayList<Tuple>(opposites);
      // opposites.add(updateElement);
      // break;
      // }
      //
    }
  }
 @Override
 protected CoursesOfTeacherMatch tupleToMatch(final Tuple t) {
   try {
     return CoursesOfTeacherMatch.newMatch(
         (school.Teacher) t.get(POSITION_T), (school.Course) t.get(POSITION_C));
   } catch (ClassCastException e) {
     LOGGER.error("Element(s) in tuple not properly typed!", e);
     return null;
   }
 }
 public FindConstraint(PositivePatternCall findCons, IPartialPatternCacher treatPatternCacher) {
   this.treatPatternCacher = treatPatternCacher;
   this.innerFindCall = findCons;
   // and affecteds from tuple (order needed!)
   Tuple tup = findCons.getVariablesTuple();
   this.affectedVariables = new ArrayList<PVariable>();
   for (int i = 0; i < tup.getSize(); i++) {
     this.affectedVariables.add((PVariable) tup.get(i));
   }
 }
 @Override
 protected RecordRoleValueMatch tupleToMatch(final Tuple t) {
   try {
     return new RecordRoleValueMatch.Immutable(
         (org.eclipse.incquery.snapshot.EIQSnapshot.MatchRecord) t.get(POSITION_RECORD),
         (org.eclipse.incquery.snapshot.EIQSnapshot.RecordRole) t.get(POSITION_ROLE));
   } catch (ClassCastException e) {
     LOGGER.error("Element(s) in tuple not properly typed!", e);
     return null;
   }
 }
 @Override
 protected RouteSensorMatch tupleToMatch(final Tuple t) {
   try {
     return RouteSensorMatch.newMatch(
         (hu.bme.mit.trainbenchmark.ttc.railway.Route) t.get(POSITION_ROUTE),
         (hu.bme.mit.trainbenchmark.ttc.railway.Sensor) t.get(POSITION_SENSOR),
         (hu.bme.mit.trainbenchmark.ttc.railway.SwitchPosition) t.get(POSITION_SWITCHPOSITION),
         (hu.bme.mit.trainbenchmark.ttc.railway.Switch) t.get(POSITION_SW));
   } catch (ClassCastException e) {
     LOGGER.error("Element(s) in tuple not properly typed!", e);
     return null;
   }
 }