/**
   * Layouts all lifelines. The lifelines are placed at a certain height and along the x-axis with a
   * specified distance between them. Lifelines that contain a stereotype in their name are placed
   * slightly higher such that the bottom of each name is flush among all lifelines.
   */
  private void layoutLifelines() {
    float currentLifelineX = LIFELINE_START_X;
    Set<Lifeline> layoutedLifelines = new HashSet<Lifeline>();

    // Layout lifelines if they have no position. Otherwise they might have been moved on the
    // x-axis,
    // which we allow to do.
    for (InteractionFragment fragment : specification.getFragments()) {
      // In case the message add event hasn't been received yet there will be no lifeline that is
      // covered.
      // This is the case when a message and a reply are added.
      // The structure exists, but not all commands have been executed.
      for (Lifeline lifeline : fragment.getCovered()) {
        if (!layoutedLifelines.contains(lifeline)) {
          LifelineView lifelineView = lifelines.get(lifeline);
          LayoutElement layoutElement = lifelineView.getLayoutElement();

          if (layoutElement.getX() == 0) {
            lifelineView.getLayoutElement().setX(currentLifelineX);
            lifelineView.getLayoutElement().setY(LIFELINE_Y);
          }

          /**
           * Lifelines of metaclasses are double as high as regular lifelines. This causes problems
           * when dealing with combined fragments or when drawing a message from a message view of a
           * create message to one (as the first message). Therefore, these lifelines are moved up
           * so that all lifelines are flush on the bottom of their name container.
           *
           * @see issue #230
           */
          if (lifeline.getRepresents() instanceof StructuralFeature) {
            StructuralFeature structuralFeature = (StructuralFeature) lifeline.getRepresents();

            if (structuralFeature.isStatic()) {
              float y = LIFELINE_Y - (lifelineView.getNameHeight() / 2);
              if (y != lifelineView.getLayoutElement().getY()) {
                lifelineView.getLayoutElement().setY(y);
              }
            }
          }

          currentLifelineX = currentLifelineX + lifelineView.getWidth() + LIFEINE_SPACING;
          layoutedLifelines.add(lifeline);
        }
      }
    }
  }
  /**
   * Layouts all combined fragments of this view. The width is determined by the position of the
   * covered lifelines.
   */
  private void layoutCombinedFragments() {
    for (Entry<CombinedFragment, CombinedFragmentView> entry : combinedFragments.entrySet()) {
      CombinedFragmentView combinedFragmentView = entry.getValue();

      float minX = Float.MAX_VALUE;
      float maxX = -1;

      for (Lifeline lifeline : entry.getKey().getCovered()) {
        LifelineView lifelineView = lifelines.get(lifeline);

        Vector3D position = lifelineView.getPosition(TransformSpace.GLOBAL);
        minX = Math.min(minX, position.getX());
        maxX = Math.max(maxX, position.getX() + lifelineView.getWidth() + LIFEINE_SPACING);

        /**
         * Take into consideration the size of other fragments as well, since they could be quite
         * wide.
         */
        for (InteractionFragment fragment : lifeline.getCoveredBy()) {
          /**
           * Check whether the fragment is part of this combined fragment in its containment
           * hierarchy.
           */
          if (EcoreUtil.isAncestor(entry.getKey(), fragment)) {
            RamRectangleComponent fragmentView = fragments.get(fragment);
            if (fragmentView != null) {
              Vector3D fragmentPosition = fragmentView.getPosition(TransformSpace.GLOBAL);
              maxX =
                  Math.max(
                      maxX, fragmentPosition.getX() + fragmentView.getWidth() + LIFEINE_SPACING);
            }
          }
        }
      }

      Vector3D position = combinedFragmentView.getPosition(TransformSpace.GLOBAL);
      position.setX(minX);
      combinedFragmentView.setPositionGlobal(position);
      combinedFragmentView.setMinimumWidth(maxX - minX);
    }
  }