/**
     * Default Constructor
     *
     * @param parentModel the parent model
     * @param source the source
     * @param target the target
     * @param link the link
     * @param containsSource is the source in selection
     */
    public Broken(
        mxGraphModel parentModel,
        BasicPort source,
        BasicPort target,
        BasicLink link,
        boolean containsSource) {
      super();
      this.parentModel = parentModel;

      this.source = source;
      this.target = target;
      this.parentLink = link;
      this.containsSource = containsSource;

      final BasicPort terminal;
      if (containsSource) {
        terminal = target;
      } else {
        terminal = source;
      }

      /*
       * Update position
       */
      final mxGeometry pos = parentModel.getGeometry(terminal);
      final mxGeometry parent = parentModel.getGeometry(parentModel.getParent(terminal));
      if (pos != null && parent != null) {
        this.x = pos.getX() + parent.getX() + (pos.getWidth() / 2) - (parent.getWidth() / 2);
        this.y = pos.getY() + parent.getY() + (pos.getHeight() / 2) - (parent.getHeight() / 2);
      } else {
        this.x = 0.0;
        this.y = 0.0;
      }
    }
  /** Populate the stored JGraph 5 facade from the mxGraph data model. */
  public void syncJGraphFacade() {
    if (facade == null || graph == null) {
      return;
    }

    mxIGraphModel model = graph.getModel();

    if (vertices != null) {
      Set<Object> vertexKeys = vertices.keySet();
      for (Object vertex : vertexKeys) {
        Object mxVertex = vertices.get(vertex);
        mxGeometry geo = model.getGeometry(mxVertex);
        facade.setLocation(vertex, geo.getX(), geo.getY());
      }
    }

    if (edges != null) {
      Set<Object> edgeKeys = edges.keySet();
      for (Object edge : edgeKeys) {
        Object mxEdge = edges.get(edge);
        List<mxPoint> points = model.getGeometry(mxEdge).getPoints();

        if (points != null) {
          facade.setIntermediatePoints(edge, points);
        }
      }
    }
  }
  /**
   * Implements <mxGraphLayout.execute>.
   *
   * <p>If the parent has any connected edges, then it is used as the root of the tree. Else,
   * <mxGraph.findTreeRoots> will be used to find a suitable root node within the set of children of
   * the given parent.
   */
  public void execute(Object parent, Object root) {
    mxIGraphModel model = graph.getModel();

    if (root == null) {
      // Takes the parent as the root if it has outgoing edges
      if (graph.getEdges(parent, model.getParent(parent), invert, !invert, false).length > 0) {
        root = parent;
      }

      // Tries to find a suitable root in the parent's
      // children
      else {
        List<Object> roots = graph.findTreeRoots(parent, true, invert);

        if (roots != null && roots.size() > 0) {
          for (int i = 0; i < roots.size(); i++) {
            if (!isVertexIgnored(roots.get(i))
                && graph.getEdges(roots.get(i), null, invert, !invert, false).length > 0) {
              root = roots.get(i);
              break;
            }
          }
        }
      }
    }

    if (root != null) {
      parent = model.getParent(root);
      model.beginUpdate();

      try {
        TreeNode node = dfs(root, parent, null);

        if (node != null) {
          layout(node);

          double x0 = graph.getGridSize();
          double y0 = x0;

          if (!moveTree || model.getParent(parent) == model.getRoot()) {
            mxGeometry g = model.getGeometry(root);

            if (g != null) {
              x0 = g.getX();
              y0 = g.getY();
            }
          }

          mxRectangle bounds = null;

          if (horizontal) {
            bounds = horizontalLayout(node, x0, y0, null);
          } else {
            bounds = verticalLayout(node, null, x0, y0, null);
          }

          if (bounds != null) {
            double dx = 0;
            double dy = 0;

            if (bounds.getX() < 0) {
              dx = Math.abs(x0 - bounds.getX());
            }

            if (bounds.getY() < 0) {
              dy = Math.abs(y0 - bounds.getY());
            }

            if (parent != null) {
              mxRectangle size = graph.getStartSize(parent);
              dx += size.getWidth();
              dy += size.getHeight();

              // Resize parent swimlane
              if (resizeParent && !graph.isCellCollapsed(parent)) {
                mxGeometry g = model.getGeometry(parent);

                if (g != null) {
                  double width = bounds.getWidth() + size.getWidth() - bounds.getX() + 2 * x0;
                  double height = bounds.getHeight() + size.getHeight() - bounds.getY() + 2 * y0;

                  g = (mxGeometry) g.clone();

                  if (g.getWidth() > width) {
                    dx += (g.getWidth() - width) / 2;
                  } else {
                    g.setWidth(width);
                  }

                  if (g.getHeight() > height) {
                    if (horizontal) {
                      dy += (g.getHeight() - height) / 2;
                    }
                  } else {
                    g.setHeight(height);
                  }

                  model.setGeometry(parent, g);
                }
              }
            }

            moveNode(node, dx, dy);
          }
        }
      } finally {
        model.endUpdate();
      }
    }
  }