/** {@inheritDoc} */
  public void process(final LGraph layeredGraph, final IKielerProgressMonitor monitor) {
    monitor.begin("Comment pre-processing", 1);

    Iterator<LNode> nodeIter = layeredGraph.getLayerlessNodes().iterator();
    while (nodeIter.hasNext()) {
      LNode node = nodeIter.next();
      if (node.getProperty(LayoutOptions.COMMENT_BOX)) {
        int edgeCount = 0;
        LEdge edge = null;
        LPort oppositePort = null;
        for (LPort port : node.getPorts()) {
          edgeCount += port.getDegree();
          if (port.getIncomingEdges().size() == 1) {
            edge = port.getIncomingEdges().get(0);
            oppositePort = edge.getSource();
          }
          if (port.getOutgoingEdges().size() == 1) {
            edge = port.getOutgoingEdges().get(0);
            oppositePort = edge.getTarget();
          }
        }

        if (edgeCount == 1
            && oppositePort.getDegree() == 1
            && !oppositePort.getNode().getProperty(LayoutOptions.COMMENT_BOX)) {
          // found a comment that has exactly one connection
          processBox(node, edge, oppositePort, oppositePort.getNode());
          nodeIter.remove();
        } else {
          // reverse edges that are oddly connected
          List<LEdge> revEdges = Lists.newArrayList();
          for (LPort port : node.getPorts()) {
            for (LEdge outedge : port.getOutgoingEdges()) {
              if (!outedge.getTarget().getOutgoingEdges().isEmpty()) {
                revEdges.add(outedge);
              }
            }

            for (LEdge inedge : port.getIncomingEdges()) {
              if (!inedge.getSource().getIncomingEdges().isEmpty()) {
                revEdges.add(inedge);
              }
            }
          }

          for (LEdge re : revEdges) {
            re.reverse(layeredGraph, true);
          }
        }
      }
    }

    monitor.done();
  }
  /**
   * Collects the positions and dimensions of {@link LNode}s and vertical segments in the layered
   * graph and writes them to the {@link CNode}s.
   */
  private void readNodes() {
    List<VerticalSegment> verticalSegments = Lists.newArrayList();
    // resetting to avoid problems if this is called repeatedly
    cGraph.cNodes.clear();

    // 1. collecting positions of graph elements
    for (Layer layer : layeredGraph) {
      for (LNode node : layer) {
        // add all nodes
        CLNode cNode = new CLNode(node, layeredGraph);
        cGraph.cNodes.add(cNode);

        // add vertical edge segments
        for (LEdge edge : node.getOutgoingEdges()) {

          Iterator<KVector> bends = edge.getBendPoints().iterator();

          // infer vertical segments from positions of bendpoints
          if (bends.hasNext()) {
            KVector bend1 = bends.next();

            // get segment of source n/s port
            if (edge.getSource().getSide() == PortSide.NORTH
                || edge.getSource().getSide() == PortSide.SOUTH) {

              verticalSegments.add(
                  new VerticalSegment(bend1, edge.getSource().getAbsoluteAnchor(), cNode, edge));
            }

            // get regular segments
            while (bends.hasNext()) {
              KVector bend2 = bends.next();
              if (!CompareFuzzy.eq(bend1.y, bend2.y)) {
                verticalSegments.add(new VerticalSegment(bend1, bend2, null, edge));
              }

              bend1 = bend2;
            }
          }
        }

        // same for incoming edges to get NSSegments on target side
        for (LEdge edge : node.getIncomingEdges()) {
          if (!edge.getBendPoints().isEmpty()) {

            // get segment of target n/s port
            if (edge.getTarget().getSide() == PortSide.NORTH
                || edge.getTarget().getSide() == PortSide.SOUTH) {

              KVector bend1 = edge.getBendPoints().getLast();
              verticalSegments.add(
                  new VerticalSegment(bend1, edge.getTarget().getAbsoluteAnchor(), cNode, edge));
            }
          }
        }
      }
    }

    // 2. combining intersecting segments in CLEdges to process them as one
    if (!verticalSegments.isEmpty()) {
      // sorting the segments by position in ascending order
      Collections.sort(verticalSegments);

      // merging intersecting segments in the same CLEdge
      VerticalSegment last = verticalSegments.get(0);
      CLEdge c = new CLEdge(last, layeredGraph);

      for (int i = 1; i < verticalSegments.size(); i++) {

        VerticalSegment verticalSegment = verticalSegments.get(i);

        if (verticalSegment.intersects(last)) {
          c.addSegment(verticalSegment);
        } else {
          cGraph.cNodes.add(c);
          c = new CLEdge(verticalSegment, layeredGraph);
        }

        last = verticalSegment;
      }
      cGraph.cNodes.add(c);
    }

    verticalSegments.clear();

    // 3. grouping nodes with their connected north/south segments
    groupCNodes();
  }