/**
     * Constructs a new {@code PortReAdder} holding the given {@link ConnectedSelfLoopComponent}s
     * given.
     *
     * @param components The {@link ConnectedSelfLoopComponent}s to be part of this PortReAdder.
     */
    PortReadder(final List<ConnectedSelfLoopComponent> components) {
      // Initialize the mapping. As the mapping is static, this needs to be done on every new run.
      for (LoopSide side : LoopSide.values()) {
        LISTS_OF_COMPONENTS.put(side, new ArrayList<ConnectedSelfLoopComponent>());
      }

      // First: group the components according to their loopSide
      for (final ConnectedSelfLoopComponent component : components) {
        allHiddenPorts.addAll(component.getHidablePorts());

        if (component.getNonLoopPorts().isEmpty()) {
          LISTS_OF_COMPONENTS.get(component.getLoopSide()).add(component);
        } else {
          withNonSelfLoop.add(component);
        }
      }

      // Second: sort the collections of components
      // (in revered order, biggest first)
      for (List<ConnectedSelfLoopComponent> list : LISTS_OF_COMPONENTS.values()) {
        Collections.sort(list, loopSorter);
      }
      // ports on the NW-border need to be reversed, again.
      Collections.reverse(LISTS_OF_COMPONENTS.get(LoopSide.NW));
    }
 /**
  * Assignes the least crowded corner loopSide to the given components. The crowding will be
  * recalculated after each added edge.
  *
  * @param components The components to assign a loopSide for.
  */
 private void assignCornerSide(final List<ConnectedSelfLoopComponent> components) {
   for (final ConnectedSelfLoopComponent component : components) {
     final LoopSide side = loopSides.getLeastCrowdedCorner();
     loopSides.addSize(side, component.getTextWidth(), component.getTextHeight());
     component.setLoopSide(side, true);
   }
 }
    /**
     * Adds all ports of given {@link LoopSide} of this component-holder to the current position of
     * the {@link ListIterator}. The ports are placed, so that source and target ports of an edge
     * are placed next to each other. The target port of an edge will be places before the source
     * port of an edge.
     *
     * @param loopSide The components of this {@link LoopSide} will be added.
     * @param itr The {@link ListIterator} to add the ports into.
     */
    public void addInlineTargetsFirst(final LoopSide loopSide, final ListIterator<LPort> itr) {
      final List<LPort> firstPart = Lists.newArrayList();
      final List<LPort> secondPart = Lists.newArrayList();

      final Iterator<ConnectedSelfLoopComponent> compItr =
          LISTS_OF_COMPONENTS.get(loopSide).iterator();

      while (compItr.hasNext()) {
        ConnectedSelfLoopComponent component = compItr.next();
        firstPart.addAll(0, component.getSourceLoopPorts());
        firstPart.addAll(0, component.getTargetLoopPortsReversed());

        if (compItr.hasNext()) {
          component = compItr.next();
          secondPart.addAll(component.getTargetLoopPortsReversed());
          secondPart.addAll(component.getSourceLoopPorts());
        }
      }

      setSideOfPorts(firstPart, loopSide.getTargetSide());
      setSideOfPorts(secondPart, loopSide.getSourceSide());

      for (final LPort port : firstPart) {
        itr.add(port);
      }
      for (final LPort port : secondPart) {
        itr.add(port);
      }
    }
  /**
   * (Re-)Adds the ports to the node of a component, having at least one port with a non-loop edge.
   * The ports are added as the direct neighbor of a port they are connected to via an edge.
   *
   * @param component The component whose ports have to get re-added.
   */
  public void addComponentWithNonLoopEdges(final ConnectedSelfLoopComponent component) {
    // the node we are working on
    final LNode node = component.getNode();
    // Set of all ports of the components that are hidden. Initially all ports that CAN be hidden.
    final Set<LPort> hiddenPorts = Sets.newHashSet(component.getHidablePorts());
    // Iterator holding all ports that already have a portSide specified. Initially these are
    // all ports with an non-loop edge connected to them. While processing the elements, we will
    // add new ports to this Iterator. So we need a ListIterator.
    final ListIterator<LPort> portsWithSideIter =
        Lists.newLinkedList(component.getNonLoopPorts()).listIterator();

    while (portsWithSideIter.hasNext()) {
      final LPort portWithSide = portsWithSideIter.next();
      if (portWithSide.getOutgoingEdges().isEmpty()) {
        // inbound port
        for (final LEdge edgeWithHiddenPort : portWithSide.getIncomingEdges()) {
          final LPort hiddenPort = edgeWithHiddenPort.getSource();
          if (hiddenPorts.contains(hiddenPort)) {
            final ListIterator<LPort> nodePortIter = node.getPorts().listIterator();
            LPort portOnNode = nodePortIter.next();
            // find the port with side ...
            while (!portOnNode.equals(portWithSide)) {
              portOnNode = nodePortIter.next();
            }
            // ... and add the hidden port on it's right side
            nodePortIter.add(hiddenPort);
            portsWithSideIter.add(hiddenPort);
            setSideOfPort(hiddenPort, portWithSide.getSide());
            // ensure the next element will be our new added element
            portsWithSideIter.previous();
            portsWithSideIter.previous();
            hiddenPorts.remove(hiddenPort);
          }
        }
      } else {
        // outbound port
        for (final LEdge edgeWithHiddenPort : portWithSide.getOutgoingEdges()) {
          final LPort hiddenPort = edgeWithHiddenPort.getTarget();
          if (hiddenPorts.contains(hiddenPort)) {
            final ListIterator<LPort> nodePortIter = node.getPorts().listIterator();
            LPort portOnNode = nodePortIter.next();
            // find the port with side ...
            while (!portOnNode.equals(portWithSide)) {
              portOnNode = nodePortIter.next();
            }
            // ... and add the hidden port on it's left side
            nodePortIter.previous();
            nodePortIter.add(hiddenPort);
            portsWithSideIter.add(hiddenPort);
            setSideOfPort(hiddenPort, portWithSide.getSide());
            // ensure the next element will be our new added element
            portsWithSideIter.previous();
            portsWithSideIter.previous();
            hiddenPorts.remove(hiddenPort);
          }
        }
      }
    }
  }
 /**
  * Adds all target ports of the components of given {@link LoopSide} of this component-holder to
  * the current position of the {@link ListIterator}.
  *
  * @param loopSide The components of this {@link LoopSide} will be added.
  * @param itr The {@link ListIterator} to add the ports into.
  */
 public void addTargetPorts(final LoopSide loopSide, final ListIterator<LPort> itr) {
   final PortSide portSide = loopSide.getTargetSide();
   for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) {
     for (final LPort port : component.getTargetLoopPorts()) {
       itr.add(port);
       setSideOfPort(port, portSide);
     }
   }
 }
  /**
   * Sets the loopSide of all self-loops of the component individually, depending on the side(s)
   * already defined for at least one port in the component. At least one portSide must be defined,
   * otherwise this method will run infinitely.
   *
   * @param component The connected component to process.
   * @return The LoopSide that is equal to the assigned portSides. Undefined, if there is not a
   *     common side for all loops.
   */
  private LoopSide setPortSideByConstraint(final ConnectedSelfLoopComponent component) {
    // Iterator over the edges of the component.
    // The iterator is cycling, as there is no guarantee, that (at least) one port of the current
    // edge has a portSide defined. If both portSides of the current edge are UNDEFINED, we will
    // return to it later.
    final Iterator<LEdge> iter = Iterators.cycle(Lists.newArrayList(component.getEdges()));
    // LoopSides in this component will be collected here
    final EnumSet<LoopSide> loopSidesInComponent = EnumSet.noneOf(LoopSide.class);

    while (iter.hasNext()) {
      final LEdge edge = iter.next();
      final PortSide sourceSide = edge.getSource().getSide();
      final PortSide targetSide = edge.getTarget().getSide();
      if (sourceSide == PortSide.UNDEFINED) {
        // source side is undefined
        if (targetSide != PortSide.UNDEFINED) {
          // only targetSide is defined. Edge becomes a sideLoop on the side of target.
          final LoopSide side = LoopSide.fromPortSides(targetSide);
          edge.setProperty(InternalProperties.SPLINE_LOOPSIDE, side);
          edge.getSource().setSide(targetSide);
          loopSidesInComponent.add(side);
          iter.remove();
        }
      } else {
        // source side is defined
        if (targetSide == PortSide.UNDEFINED) {
          // only sourceSide is defined. Edge becomes a sideLoop on the side of source.
          final LoopSide side = LoopSide.fromPortSides(sourceSide);
          edge.setProperty(InternalProperties.SPLINE_LOOPSIDE, side);
          edge.getTarget().setSide(sourceSide);
          loopSidesInComponent.add(side);
          iter.remove();
        } else {
          // both sides are defined, set edge side resulting from the combination
          final LoopSide side = LoopSide.fromPortSide(sourceSide, targetSide);
          edge.setProperty(InternalProperties.SPLINE_LOOPSIDE, side);
          loopSidesInComponent.add(side);
          iter.remove();
        }
      }
    }

    // Now all edges have a LoopSide assigned.
    // Check if we can find a LoopSide for the whole component.
    LoopSide side;
    if (loopSidesInComponent.size() == 1) {
      side = loopSidesInComponent.iterator().next();
    } else {
      side = LoopSide.UNDEFINED;
    }

    component.setLoopSide(side, false);
    return side;
  }
    /**
     * Adds all source ports of the components of given {@link LoopSide} of this component-holder to
     * the current position of the {@link ListIterator} in reversed order.
     *
     * @param loopSide The components of this {@link LoopSide} will be added.
     * @param itr The {@link ListIterator} to add the ports into.
     */
    public void addSourcePortsReversed(final LoopSide loopSide, final ListIterator<LPort> itr) {
      final List<LPort> sourcePorts = Lists.newArrayList();

      for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) {
        sourcePorts.addAll(component.getSourceLoopPorts());
      }

      Collections.reverse(sourcePorts);
      setSideOfPorts(sourcePorts, loopSide.getSourceSide());

      for (final LPort port : sourcePorts) {
        itr.add(port);
      }
    }
    /**
     * Adds all target ports and than all source ports of the components of given {@link LoopSide}
     * of this component-holder to the current position of the {@link ListIterator}. The source
     * ports will be added in reversed order.
     *
     * @param loopSide The components of this {@link LoopSide} will be added.
     * @param itr The {@link ListIterator} to add the ports into.
     */
    public void addAllPorts(
        final LoopSide loopSide, final ListIterator<LPort> itr, final boolean sourceFirst) {

      final List<LPort> secondPart = Lists.newArrayList();
      PortSide secondPartSide = null;

      if (sourceFirst) {
        for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) {
          for (final LPort port : component.getSourceLoopPorts()) {
            itr.add(port);
            setSideOfPort(port, loopSide.getSourceSide());
          }
          secondPart.addAll(component.getTargetLoopPorts());
          secondPartSide = loopSide.getTargetSide();
        }
      } else {
        for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) {
          for (final LPort port : component.getTargetLoopPorts()) {
            itr.add(port);
            setSideOfPort(port, loopSide.getTargetSide());
          }
          secondPart.addAll(component.getSourceLoopPorts());
          secondPartSide = loopSide.getSourceSide();
        }
      }

      Collections.reverse(secondPart);
      setSideOfPorts(secondPart, secondPartSide);
      for (final LPort port : secondPart) {
        itr.add(port);
      }
    }
    /**
     * Distributes the self-loops equally around the node.
     *
     * @param components
     */
    public void calculate(final List<ConnectedSelfLoopComponent> components) {
      final List<ConnectedSelfLoopComponent> withLongText = Lists.newArrayList();
      final List<ConnectedSelfLoopComponent> withShortText = Lists.newArrayList();
      final List<ConnectedSelfLoopComponent> withoutText = Lists.newArrayList();

      // first we are going to check for the size of possible edge labels
      for (final ConnectedSelfLoopComponent component : components) {
        if (component.getTextWidth() > MAX_TEXT_LENGTH) {
          withLongText.add(component);
        } else if (component.getTextWidth() > 0.0) {
          withShortText.add(component);
        } else {
          withoutText.add(component);
        }
      }

      // if there is only one loop with text, handle this loop as a long text loop
      if (withShortText.size() == 1 && withLongText.isEmpty()) {
        withLongText.addAll(withShortText);
        withShortText.clear();
      }

      // try to put the components with long text to one of the horizontal across loop sides.
      // if there are no such sides available, handle long text loops as short text loops.
      if (!withLongText.isEmpty()
          && loopSides.availableSides().contains(LoopSide.N)
          && loopSides.availableSides().contains(LoopSide.S)) {
        assignAcrossSide(withLongText);
      } else {
        withShortText.addAll(withLongText);
      }

      // put the components with short text to one of the corner loop sides
      if (!withShortText.isEmpty()) {
        assignCornerSide(withShortText);
      }

      // Last but not least: loops without text get spread equally.
      if (!withoutText.isEmpty()) {
        // ////////////////////////////////////////////////////////////
        // First of all we try to put those ConnectedSelfLoops who have more than one edge,
        // to one of the available straight sides.
        // ////////////////////////////////////////////////////////////

        final Set<LoopSide> availableStraights = loopSides.availableStraightSides();
        if (!availableStraights.isEmpty()) {
          final Iterator<ConnectedSelfLoopComponent> itrComponent = withoutText.iterator();
          final Iterator<LoopSide> itrAvailable = Iterables.cycle(availableStraights).iterator();

          while (itrComponent.hasNext()) {

            // look for a component with more than one edge
            ConnectedSelfLoopComponent component = itrComponent.next();
            while (itrComponent.hasNext() && component.getEdges().size() < 2) {
              component = itrComponent.next();
            }

            // If we have found a component with more than one edge, assign the next
            // straight side to the component.
            if (component.getEdges().size() > 1) {
              final LoopSide side = itrAvailable.next();
              component.setLoopSide(side, true);
              itrComponent.remove();
              loopSides.removeSide(side);
            }
          }
        }

        // ////////////////////////////////////////////////////////////
        // Now we can distribute the remaining components around the node.
        // ////////////////////////////////////////////////////////////

        // number of required sides
        final int number = withoutText.size();

        // Center of the assigned pattern.
        final LoopSide center = findCenter();

        // The list of portSides we assign to the set of connected components.
        final List<LoopSide> portSides = Lists.newArrayList();

        // How many times must a "full set" of LoopSide-elements be constructed?
        final int fullSets = number / loopSides.availableNotAcrossSides().size();

        // Construct the full sets.
        for (int i = 0; i < fullSets; i++) {
          portSides.addAll(loopSides.availableNotAcrossSides());
        }

        // How many elements are remaining to be constructed after the full sets have been
        // constructed?
        int remainer = number % loopSides.availableNotAcrossSides().size();

        // If more than three elements are remaining, the pattern includes all four corners
        // and the sides that would be included in the pattern for (n-4) elements.
        if (remainer > 3) {
          portSides.addAll(LoopSide.getAllCornerSides());
          remainer -= 4;
        }

        // Create the pattern for 1-3 elements.
        switch (remainer) {
          case 3:
            // opposed side (from center) and the the same sides as for two elements
            portSides.add(center.opposite());
          case 2:
            // the first AVAILABLE neighbors of the neighbors of the opposed side of the
            // center
            LoopSide tmpSide = center.opposite().left();
            do {
              tmpSide = tmpSide.left();
            } while (!loopSides.availableSides().contains(tmpSide));
            portSides.add(tmpSide);

            tmpSide = center.opposite().right();
            do {
              tmpSide = tmpSide.right();
            } while (!loopSides.availableSides().contains(tmpSide));
            portSides.add(tmpSide);
            break;
          case 1:
            // opposed side (from center)
            portSides.add(center.opposite());
            break;
          default:
            break;
        }

        // Now assign the remaining sides to the set of portSides we just have constructed.
        final Iterator<LoopSide> itrSides = portSides.iterator();
        final Iterator<ConnectedSelfLoopComponent> itrComponent = withoutText.iterator();
        while (itrSides.hasNext() && itrComponent.hasNext()) {
          itrComponent.next().setLoopSide(itrSides.next(), true);
        }
      }
    }
  /** {@inheritDoc} */
  public void process(final LGraph layeredGraph, final IElkProgressMonitor monitor) {
    monitor.begin("Spline SelfLoop positioning", 1);

    /** Stores which loop placement strategy to choose. */
    final SelfLoopPlacement loopPlacement =
        layeredGraph.getProperty(Properties.SPLINE_SELF_LOOP_PLACEMENT);

    ////////////////////////////////////////////////////////
    // There are two main jobs to be done:
    // 1) Find a loop-side for each component.
    // 2) Position ports in the correct order around the node.
    ////////////////////////////////////////////////////////

    // Process all nodes on all layers.
    for (final Layer layer : layeredGraph) {
      for (final LNode node : layer) {

        // Read self-loops components.
        final List<ConnectedSelfLoopComponent> components =
            node.getProperty(InternalProperties.SPLINE_SELFLOOP_COMPONENTS);

        // Components to be distributed by the placement strategy.
        final List<ConnectedSelfLoopComponent> componentsToBePlaced = Lists.newArrayList();

        for (final ConnectedSelfLoopComponent component : components) {
          // Re-Add all hidden edges to their ports.
          component.unhideEdges();

          if (component.getConstrainedPorts().isEmpty()) {
            // If there is no constraint on any port, we have to process this component later
            componentsToBePlaced.add(component);
          } else {
            // If there is at least one port with a constraint to it's port-side,
            // we will set the port- and loop-sides by this constraint. (Job 1)
            setPortSideByConstraint(component);
            if (!component.getNonLoopPorts().isEmpty()) {
              // Position and re-add all ports to the node, that are part of a component
              // with at least one non-loop edge. (Job 2)
              addComponentWithNonLoopEdges(component);
            }
          }
        }

        // Now we have to find a loop-side (job 1) for the remaining components. They are all
        // stored in componentsToBePlaced. All these components don't have a port with a
        // constraint on it's portSide, so we can arrange them accordingly to the cosen strategy.
        switch (loopPlacement) {
          case EQUALLY_DISTRIBUTED:
            setPortSideSpreadEqually(componentsToBePlaced, node);
            break;
          case NORTH_SEQUENCE:
            for (final ConnectedSelfLoopComponent component : componentsToBePlaced) {
              component.setLoopSide(LoopSide.N, true);
            }
            break;
          case NORTH_STACKED:
            for (final ConnectedSelfLoopComponent component : componentsToBePlaced) {
              component.setLoopSide(LoopSide.N, true);
            }
            break;
          default:
            // Unknown strategy chosen.
            assert false;
            break;
        }

        // Position and re-add all ports to the node.
        // This is job 2)
        switch (loopPlacement) {
          case EQUALLY_DISTRIBUTED:
          case NORTH_STACKED:
            portStackedPositioning(components);
            break;
          case NORTH_SEQUENCE:
            portLinedPositioning(components);
            break;
          default:
            break;
        }
      }
    }
    monitor.done();
  }
 /** {@inheritDoc} */
 public int compare(
     final ConnectedSelfLoopComponent loop0, final ConnectedSelfLoopComponent loop1) {
   return Double.compare(loop1.getTextWidth(), loop0.getTextWidth());
 }