private void prepareLanes(BPMNElement lane, int level) {
   maxLaneDepth = Math.max(maxLaneDepth, level);
   List<BPMNElement> childs = new ArrayList<BPMNElement>();
   BPMNElement aChild = null;
   for (LayoutingElement c : diagram.getChildElementsOf(lane)) {
     BPMNElement child = (BPMNElement) c;
     if (BPMNType.isASwimlane(child.getType())) {
       prepareLanes(child, level + 1);
       childs.add(child);
     }
     aChild = child;
   }
   // Create Grid for lane (=
   // aChild.getParent())
   if (aChild != null) {
     getContextByElement(aChild);
   } else {
     // create empty grid for empty lanes
     // to prevent nullpointer-exception
     GridContext result = new GridContext();
     result.grid = new Grid<BPMNElement>();
     result.startCell = result.grid.getFirstRow().getFirstCell();
     superGrid.add(result.grid);
     parent2Context.put(lane, result);
   }
   lane2LaneChilds.put(lane, childs);
 }
  /**
   * @param lane
   * @param relY
   * @param level
   * @return height of lane
   */
  private double placeLane(BPMNElement lane, double relY, int level) {

    List<BPMNElement> childs = lane2LaneChilds.get(lane);
    double height = 0;
    for (BPMNElement child : childs) {
      height += placeLane(child, height, level + 1);
    }

    int width = poolWidth - level * LANE_HEAD_WIDTH;
    Grid<BPMNElement> myGrid = parent2Context.get(lane).grid;
    int firstRow = superGrid.findRow(myGrid.getFirstRow());
    int lastRow = superGrid.findRow(myGrid.getLastRow());
    for (int i = firstRow; i <= lastRow; i++) {
      height += heightOfRow[i];
    }

    double minHeight = lane.getGeometry().getHeight();
    if (level == 0) {
      minHeight += CELL_MARGIN / 2;
    }
    double diff = minHeight - height;
    if (diff > 1.0) {
      firstRow = superGrid.findRow(findFirstGridOfPool(lane).getFirstRow());
      double toAdd = diff / (lastRow - firstRow + 1.0);
      for (int i = firstRow; i <= lastRow; i++) {
        heightOfRow[i] += toAdd;
      }
      // Redo placement
      return placeLane(lane, relY, level);
    }
    if (level == 0) {
      // pool with magin
      lane.setGeometry(
          new LayoutingBoundsImpl(
              CELL_MARGIN, relY + (CELL_MARGIN / 4), width, height - (CELL_MARGIN / 2)));
    } else {
      // lane without margin
      lane.setGeometry(new LayoutingBoundsImpl(CELL_MARGIN, relY, width, height));
    }
    lane.updateDataModel();
    return height;
  }
  /** @param grids */
  private void calcGeometry(SuperGrid<BPMNElement> grids) {
    grids.pack();
    heightOfRow = new double[grids.getHeight()];
    widthOfColumn = new double[grids.getWidth()];
    // initialize with standard values
    Arrays.fill(heightOfRow, CELL_HEIGHT);
    Arrays.fill(widthOfColumn, CELL_WIDTH);
    // find biggest
    int row = 0;
    int column = 0;
    for (Row<BPMNElement> r : grids) {
      column = 0;
      for (Cell<BPMNElement> c : r) {
        if (c.isFilled()) {
          BPMNElement elem = c.getValue();
          LayoutingBounds geom = elem.getGeometry();
          widthOfColumn[column] = Math.max(widthOfColumn[column], geom.getWidth() + CELL_MARGIN);
          heightOfRow[row] = Math.max(heightOfRow[row], geom.getHeight() + CELL_MARGIN);
        }
        column++;
      }
      row++;
    }

    // calc width / height
    widthOfSuperGrid = 0;
    for (double columnWidth : widthOfColumn) {
      widthOfSuperGrid += columnWidth;
    }
    heightOfSuperGrid = 0;
    for (double rowHeight : heightOfRow) {
      heightOfSuperGrid += rowHeight;
    }

    poolWidth = maxLaneDepth * LANE_HEAD_WIDTH;
    poolWidth += widthOfSuperGrid;
  }
 private GridContext getContextByElement(BPMNElement el) {
   BPMNElement elParent = null;
   if (el != null) {
     elParent = (BPMNElement) el.getParent();
   }
   GridContext result = parent2Context.get(elParent);
   if (result == null) {
     result = new GridContext();
     result.grid = new Grid<BPMNElement>();
     result.startCell = result.grid.getFirstRow().getFirstCell();
     superGrid.add(result.grid);
     parent2Context.put(elParent, result);
   }
   return result;
 }
  /**
   * @param currentElement
   * @param precedingElements
   * @param context
   * @return cellOfElement
   */
  private Cell<BPMNElement> placeElement(
      BPMNElement currentElement, List<LayoutingElement> precedingElements, GridContext context) {
    Cell<BPMNElement> newCell;
    if (precedingElements.isEmpty()) {
      // StartEvents
      context.startCell.setValue(currentElement);
      newCell = context.startCell;
      context.startCell = context.startCell.beneath();
    } else {
      Cell<BPMNElement> leftCell;
      newCell = context.grid.getCellOfItem(currentElement); // not
      // null
      // if
      // join
      if (currentElement.isJoin()) {

        Point tmp;
        boolean splitFound = false;
        BPMNElement split = (BPMNElement) currentElement.prevSplit();
        if (split != null) {
          // get all close splits
          Queue<BPMNElement> splits =
              new PriorityQueue<BPMNElement>(
                  precedingElements.size() / 2, // should be a
                  // good rule of
                  // thumb
                  new BackwardDistanceComperator(currentElement));
          splits.add(split);
          for (LayoutingElement elem : precedingElements) {
            split = (BPMNElement) elem.prevSplit();
            if (split != null && !splits.contains(split)) {
              splits.add(split);
            }
          }
          split = null;
          // get split with most connections
          int maxCon = 0;
          for (BPMNElement target : splits) {
            if (target == currentElement) {
              // beeing my own splits only makes trouble
              continue;
            } else if (target.getParent() != currentElement.getParent()) {

              continue;
            }
            int curCon = 0;
            for (LayoutingElement elem : precedingElements) {
              if (elem.backwardDistanceTo(target) < Integer.MAX_VALUE) {
                curCon++;
              }
            }
            if (curCon > maxCon) {
              maxCon = curCon;
              split = target;
            }
          }
          splitFound = split != null;
        }

        int x = 0;
        int yAcc = 0;
        int yCnt = 0;
        for (LayoutingElement el : precedingElements) {
          BPMNElement elem = (BPMNElement) el;
          tmp = context.grid.find(context.grid.getCellOfItem(elem));
          if (tmp == null) {
            Grid<BPMNElement> preGrid = getContextByElement(elem).grid;
            tmp = preGrid.find(preGrid.getCellOfItem(elem));
            if (tmp == null) {
              tmp = new Point(0, 0);
            }
          } else {
            yAcc += tmp.y;
            yCnt++;
          }
          x = Math.max(x, tmp.x);
        }
        if (splitFound) {

          leftCell = context.grid.getCellOfItem(split).getParent().get(x);
          // set path to split unpackable
          for (Cell<BPMNElement> cCell = leftCell;
              cCell.getValue() != split;
              cCell = cCell.getPrevCell()) {
            cCell.setPackable(false);
          }

        } else {
          if (yCnt == 0) {
            leftCell = context.grid.getFirstRow().above().get(x);
          } else {
            leftCell = context.grid.get(yAcc / yCnt).get(x);
          }
        }
        if (newCell != null && newCell.getValue() == currentElement) {
          newCell.setValue(null);
        }
        newCell = leftCell.after();

        // set all incoming pathes unpackable
        for (LayoutingElement e : precedingElements) {
          BPMNElement el = (BPMNElement) e;
          Cell<BPMNElement> target = context.grid.getCellOfItem(el);
          if (target == null) {
            // don't set unpackable in other grids (other edge
            // layout)
            continue;
          }
          Cell<BPMNElement> start = target.getParent().get(x + 1);
          for (Cell<BPMNElement> cCell = start; cCell != target; cCell = cCell.getPrevCell()) {
            cCell.setPackable(false);
          }
        }

        // if not prelayouted
      } else if (newCell == null) {
        BPMNElement preElem = (BPMNElement) precedingElements.get(0);
        leftCell = context.grid.getCellOfItem(preElem);
        if (leftCell == null) {
          Grid<BPMNElement> preGrid = getContextByElement(preElem).grid;
          Cell<BPMNElement> preCell = preGrid.getCellOfItem(preElem);
          if (preCell == null) {
            System.err.println("Cannot find Cell for " + preElem);
          }

          List<Grid<BPMNElement>> grids = superGrid.getGrids();
          Row<BPMNElement> newRow = null;
          if (grids.indexOf(preGrid) < grids.indexOf(context.grid)) {
            newRow = context.grid.addFirstRow();
          } else {
            newRow = context.grid.addLastRow();
          }
          leftCell = newRow.get(Math.max(0, preCell.getParent().find(preCell)));
        }
        newCell = leftCell.after();
      }
      if (newCell.isFilled() && !newCell.getValue().equals(currentElement)) {
        newCell.getParent().insertRowBeneath();
        newCell = newCell.beneath();
      }
      newCell.setValue(currentElement);
    }
    return newCell;
  }
  public void doLayout() {
    superGrid = new SuperGrid<BPMNElement>();
    parent2Context.clear();
    lane2LaneChilds.clear();
    maxLaneDepth = 0;

    if (parent == null) {
      for (LayoutingElement pool : diagram.getElementsOfType(BPMNType.Pool)) {
        prepareLanes((BPMNElement) pool, 1);
      }
    }

    layoutElements();

    if (parent == null) {
      // set collapsed pools
      List<LayoutingElement> collapsedPools =
          this.diagram.getElementsOfType(BPMNType.CollapsedPool);
      Grid<BPMNElement> cpGrid = new Grid<BPMNElement>();
      superGrid.add(0, cpGrid);
      for (LayoutingElement collapsedPool : collapsedPools) {
        // make them small to not disturb finding the biggest ones in
        // each row / column

        collapsedPool.setGeometry(new LayoutingBoundsImpl(0, 0, 0, COLLAPSED_POOL_HEIGHT));
        for (Cell<BPMNElement> insertCell : cpGrid.addLastRow()) {
          insertCell.setValue((BPMNElement) collapsedPool);
        }
      }

      calcGeometry(superGrid);

      poolWidth = Math.max(poolWidth, COLLAPSED_POOL_MIN_WIDTH);

      // place Lanes
      for (LayoutingElement pool : diagram.getElementsOfType(BPMNType.Pool)) {
        Grid<BPMNElement> firstGrid = findFirstGridOfPool((BPMNElement) pool);
        int firstGridFirstRowIndex = superGrid.findRow(firstGrid.getFirstRow());

        double poolY = 0;
        for (int i = 0; i < firstGridFirstRowIndex; i++) {
          poolY += heightOfRow[i];
        }
        placeLane((BPMNElement) pool, poolY, 0);
      }

      writeGeometry(superGrid);

      // set pools to start at x = CELL_MARGIN & correct size

      for (LayoutingElement collapsedPool : collapsedPools) {
        collapsedPool.setGeometry(
            new LayoutingBoundsImpl(
                CELL_MARGIN, collapsedPool.getGeometry().getY(), poolWidth, COLLAPSED_POOL_HEIGHT));
        ((BPMNElement) collapsedPool).updateDataModel();
      }

      // convert Coordinates of Elements in Lanes from absolut to
      // realitive
      for (LayoutingElement pool : diagram.getElementsOfType(BPMNType.Pool)) {
        correctLaneElements((BPMNElement) pool, pool.getGeometry().getY(), 0);
      }

    } else {
      calcGeometry(superGrid);
      writeGeometry(superGrid);
    }
  }