/** * A childless stran is a "problem" in general because they break the evenness of the tree There * are three types of such strands, extra nodes to the left, extra to the right, or extra in * between parents. This method places those strands in spot. * * @param childlessStrand - the childless node to be laid out. * @param parentLeft - the nearest parent on the left (or <value>null</value> if none such exists) * @param parentRight - the nearest parent on the right (or <value>null</value> if none such * exists) * @param yLoc - the vertical location to lay out the nodes on. */ private void placeChildless( List<GXMoreThanNode> childlessStrand, GXMoreThanNode parentLeft, GXMoreThanNode parentRight, int yLoc) { int startMark = 0; int spacing = (int) (childlessStrand.get(0).getNode().getLayoutEntity().getWidthInLayout() + horSpacing); // There's only a parent on the right if (parentLeft == null && parentRight != null) { startMark = parentRight.getX() - (spacing * childlessStrand.size()); } // there's a parent on the left else if (parentLeft != null) { startMark = parentLeft.getX() + spacing; // there's a parent on the right as well meaning the childless are between two parents // we need to make there's enough room to place them if (parentRight != null) { int endMark = startMark + (spacing * childlessStrand.size()); // if there isn't enough room to place the childless between the parents if (endMark > parentRight.getX()) { // shift everything on the right to the right by the missing amount of space. shiftTreesRightOfMark(parentRight, endMark - parentRight.getX()); } } } // now the room has been assured, place strand. placeStrand(childlessStrand, startMark, yLoc, spacing); }
/** * follows the root by all its children to wrap the all up with all the extra info needed and adds * the tree to the matrix. * * @param currRoot - root to go over tree from * @param entitiesList - entities available * @param relationshipsList - relationship between the given entities * @param currRow - the current row in the matrix we are working on * @param rows - the matrix. */ private void builtTreeFromRoot( GXMoreThanNode currRoot, List<InternalNode> entitiesList, List<InternalRelationship> relationshipsList, List<GXMoreThanNode> currRow, List<List<GXMoreThanNode>> rows) { // this is the first node that comes from the currRoot // we'll use the mark to know where to continue laying out from GXMoreThanNode mark = null; List<InternalRelationship> relationshipsListCopy = new ArrayList<InternalRelationship>(relationshipsList); // Orders the children of the currRoot in the given row (the row under it) for (InternalRelationship rel : relationshipsListCopy) { if (currRoot.getNode().equals(rel.getSource())) { InternalNode destNode = rel.getDestination(); // if the destination node hasn't been laid out yet if (entitiesList.contains(destNode)) { // place it in the row (as in lay it out) GXMoreThanNode currNode = new GXMoreThanNode(destNode, currRoot.getNode()); currRoot.addChild(currNode); currRow.add(currNode); currNode.addedToRow(currRow); entitiesList.remove(destNode); // if this is the first node, save it as a mark. if (mark == null) { mark = currNode; } // remove the relationship since both of its ends have been laid out. relationshipsList.remove(rel); } } } // if new children have been added if (mark != null) { // Create a next row if necessary if (rows.size() - 1 <= rows.indexOf(currRow)) { rows.add(new ArrayList<GXMoreThanNode>()); } List<GXMoreThanNode> nextRow = rows.get(rows.indexOf(currRow) + 1); for (int i = currRow.indexOf(mark); i < currRow.size(); i++) { builtTreeFromRoot(currRow.get(i), entitiesList, relationshipsList, nextRow, rows); } } }
/** * Shifts the trees right of mark node * * @param mark to shift from * @param shift - factor by which to move right by. */ private void shiftTreesRightOfMark(GXMoreThanNode mark, int shift) { mark.setLocation(mark.getX() + shift, mark.getY()); GXMoreThanNode leftMostChild = getRightMostChild(mark); List<GXMoreThanNode> treeRoots = leftMostChild .getRow() .subList(leftMostChild.getRow().indexOf(leftMostChild), leftMostChild.getRow().size()); for (GXMoreThanNode root : treeRoots) { shiftTree(root, shift); } }
/** * Returns the right most child of parent * * @param parent * @return the right most child of parent given. */ private GXMoreThanNode getRightMostChild(GXMoreThanNode parent) { GXMoreThanNode rightMost = parent.getChildren().get(0); // run through children for (GXMoreThanNode child : parent.getChildren()) { if (child.getX() < rightMost.getX()) { rightMost = child; } } return rightMost; }
/** * Lays out row with respect to it's children. * * @param yLocation - the vertical location to start placing the nodes. * @param row - the row who's nodes we'd like to lay out. */ private void placeRow(List<GXMoreThanNode> row, int yLocation) { List<GXMoreThanNode> childlessStrand = new ArrayList<GXMoreThanNode>(); GXMoreThanNode parentLeft = null; // for each parent in this parent row for (int j = 0; j < row.size(); j++) { GXMoreThanNode parentRight = row.get(j); // if the node has children if (!parentRight.getChildren().isEmpty()) { // place the node in the center above his children int parentX = 0; for (GXMoreThanNode child : parentRight.getChildren()) { parentX += child.getX(); } parentX /= parentRight.getChildren().size(); parentRight.setLocation(parentX, yLocation); // and layout the childless strand if (!childlessStrand.isEmpty()) { placeChildless(childlessStrand, parentLeft, parentRight, yLocation); childlessStrand.clear(); } parentLeft = parentRight; } else { // accumulate the childless nodes childlessStrand.add(parentRight); } } // place childless who are extra on the right. as in did not get taken care of when // the parents were laid out. if (!childlessStrand.isEmpty()) { placeChildless(childlessStrand, parentLeft, null, yLocation); } }
/** * Places the list of nodes horizontally at the given point and spaced on horizontally by given * spacing. * * @param strand - list of nodes to be laid out. * @param x - location to begin the placing * @param y - vertical location to lay out the entire list. * @param spacing the horizontal spacing between nodes. */ private void placeStrand(List<GXMoreThanNode> strand, int x, int y, int spacing) { for (GXMoreThanNode item : strand) { item.setLocation(x, y); x += spacing; } }
/** * Shifts the given tree by the shift factor given to the right. * * @param root - root of tree to shift * @param shift - factor to shirt by */ private void shiftTree(GXMoreThanNode root, int shift) { root.setLocation(root.getX() + shift, root.getY()); for (GXMoreThanNode child : root.getChildren()) { shiftTree(child, shift); } }