public function addNode(PersistentObject $other, $role=null, $forceSet=false, $trackChange=true, $updateOtherSide=true) {
    $mapper = $this->getMapper();

    // set role if missing
    if ($role == null) {
      $otherType = $other->getType();
      $relations = $mapper->getRelationsByType($otherType);
      $role = (sizeof($relations) > 0) ? $relations[0]->getOtherRole() : $otherType;
    }

    // get the relation description
    $relDesc = $mapper->getRelation($role);

    $value = $other;
    $oldValue = parent::getValue($role);
    $addedNodes = []; // this array contains the other node or nothing
    if (!$relDesc || $relDesc->isMultiValued()) {
      // check multiplicity if multivalued
      $maxMultiplicity = $relDesc->getOtherMaxMultiplicity();
      if ($relDesc->isMultiValued() && !($maxMultiplicity == 'unbounded') &&
          sizeof(oldValue) >= $maxMultiplicity) {
            throw new IllegalArgumentException("Maximum number of related objects exceeded: ".$role." (".(sizeof(oldValue)+1)." > ".$maxMultiplicity.")");
      }
      // make sure that the value is an array if multivalued
      $mergeResult = self::mergeObjectLists($oldValue, [$value]);
      $value = $mergeResult['result'];
      $addedNodes = $mergeResult['added'];
    }
    elseif ($oldValue == null && $value != null ||
            $oldValue->getOID()->__toString() != $value->getOID()->__toString()) {
      $addedNodes[] = $value;
    }
    $result1 = sizeof($addedNodes) > 0 && parent::setValue($role, $value, $forceSet, $trackChange);

    // remember the addition
    if (sizeof($addedNodes) > 0) {
      if (!isset($this->addedNodes[$role])) {
        $this->addedNodes[$role] = [];
      }
      $this->addedNodes[$role][] = $other;
    }

    // propagate add action to the other object
    $result2 = true;
    if ($updateOtherSide) {
      $thisRole = $relDesc ? $relDesc->getThisRole() : null;
      $result2 = $other->addNode($this, $thisRole, $forceSet, $trackChange, false);
    }
    return ($result1 & $result2);
  }