/** * Creates all edges in the internal model * * @param layout reference to the layout algorithm * @param vertices the vertices whom are to have an internal representation created * @param internalVertices the blank internal vertices to have their information filled in using * the real vertices */ protected void createInternalCells( mxHierarchicalLayout layout, Object[] vertices, mxGraphHierarchyNode[] internalVertices) { mxGraph graph = layout.getGraph(); // Create internal edges for (int i = 0; i < vertices.length; i++) { internalVertices[i] = new mxGraphHierarchyNode(vertices[i]); vertexMapper.put(vertices[i], internalVertices[i]); // If the layout is deterministic, order the cells Object[] conns = graph.getConnections(vertices[i], parent); List<Object> outgoingCells = Arrays.asList(graph.getOpposites(conns, vertices[i])); internalVertices[i].connectsAsSource = new LinkedHashSet<mxGraphHierarchyEdge>(outgoingCells.size()); // Create internal edges, but don't do any rank assignment yet // First use the information from the greedy cycle remover to // invert the leftward edges internally Iterator<Object> iter = outgoingCells.iterator(); while (iter.hasNext()) { // Don't add self-loops Object cell = iter.next(); if (cell != vertices[i] && graph.getModel().isVertex(cell) && !layout.isVertexIgnored(cell)) { // Allow for parallel edges Object[] edges = graph.getEdgesBetween(vertices[i], cell, true); if (edges != null && edges.length > 0) { ArrayList<Object> listEdges = new ArrayList<Object>(edges.length); for (int j = 0; j < edges.length; j++) { listEdges.add(edges[j]); } mxGraphHierarchyEdge internalEdge = new mxGraphHierarchyEdge(listEdges); Iterator<Object> iter2 = listEdges.iterator(); while (iter2.hasNext()) { Object edge = iter2.next(); edgeMapper.put(edge, internalEdge); // Resets all point on the edge and disables the edge style // without deleting it from the cell style graph.resetEdge(edge); if (layout.isDisableEdgeStyle()) { layout.setEdgeStyleEnabled(edge, false); layout.setOrthogonalEdge(edge, true); } } internalEdge.source = internalVertices[i]; internalVertices[i].connectsAsSource.add(internalEdge); } } } // Ensure temp variable is cleared from any previous use internalVertices[i].temp[0] = 0; } }
/** * Creates an internal ordered graph model using the vertices passed in. If there are any, * leftward edge need to be inverted in the internal model * * @param layout the enclosing layout object * @param vertices the vertices for this hierarchy * @param ordered whether or not the vertices are already ordered * @param deterministic whether or not this layout should be deterministic on each * @param scanRanksFromSinks Whether the rank assignment is done from the sinks or sources. usage */ public mxGraphHierarchyModel( mxHierarchicalLayout layout, Object[] vertices, List<Object> roots, Object parent, boolean ordered, boolean deterministic, boolean scanRanksFromSinks) { mxGraph graph = layout.getGraph(); this.deterministic = deterministic; this.scanRanksFromSinks = scanRanksFromSinks; this.roots = roots; this.parent = parent; if (vertices == null) { vertices = graph.getChildVertices(parent); } if (ordered) { formOrderedHierarchy(layout, vertices, parent); } else { // map of cells to internal cell needed for second run through // to setup the sink of edges correctly. Guess size by number // of edges is roughly same as number of vertices. vertexMapper = new Hashtable<Object, mxGraphHierarchyNode>(vertices.length); edgeMapper = new Hashtable<Object, mxGraphHierarchyEdge>(vertices.length); if (scanRanksFromSinks) { maxRank = 0; } else { maxRank = SOURCESCANSTARTRANK; } mxGraphHierarchyNode[] internalVertices = new mxGraphHierarchyNode[vertices.length]; createInternalCells(layout, vertices, internalVertices); // Go through edges set their sink values. Also check the // ordering if and invert edges if necessary for (int i = 0; i < vertices.length; i++) { Collection<mxGraphHierarchyEdge> edges = internalVertices[i].connectsAsSource; Iterator<mxGraphHierarchyEdge> iter = edges.iterator(); while (iter.hasNext()) { mxGraphHierarchyEdge internalEdge = iter.next(); Collection<Object> realEdges = internalEdge.edges; Iterator<Object> iter2 = realEdges.iterator(); if (iter2.hasNext()) { Object realEdge = iter2.next(); Object targetCell = graph.getView().getVisibleTerminal(realEdge, false); mxGraphHierarchyNode internalTargetCell = vertexMapper.get(targetCell); if (internalTargetCell != null && internalVertices[i] != internalTargetCell) { internalEdge.target = internalTargetCell; if (internalTargetCell.connectsAsTarget.size() == 0) { internalTargetCell.connectsAsTarget = new LinkedHashSet<mxGraphHierarchyEdge>(4); } internalTargetCell.connectsAsTarget.add(internalEdge); } } } // Use the temp variable in the internal nodes to mark this // internal vertex as having been visited. internalVertices[i].temp[0] = 1; } } }
/** * Creates an internal ordered graph model using the vertices passed in. If there are any, * leftward edge need to be inverted in the internal model * * @param layout reference to the layout algorithm * @param vertices the vertices to be laid out */ public void formOrderedHierarchy(mxHierarchicalLayout layout, Object[] vertices, Object parent) { mxGraph graph = layout.getGraph(); // map of cells to internal cell needed for second run through // to setup the sink of edges correctly. Guess size by number // of edges is roughly same as number of vertices. vertexMapper = new Hashtable<Object, mxGraphHierarchyNode>(vertices.length * 2); edgeMapper = new Hashtable<Object, mxGraphHierarchyEdge>(vertices.length); maxRank = 0; mxGraphHierarchyNode[] internalVertices = new mxGraphHierarchyNode[vertices.length]; createInternalCells(layout, vertices, internalVertices); // Go through edges set their sink values. Also check the // ordering if and invert edges if necessary // Need a temporary list to store which of these edges have been // inverted in the internal model. If connectsAsSource were changed // in the following while loop we'd get a // ConcurrentModificationException List<mxGraphHierarchyEdge> tempList = new ArrayList<mxGraphHierarchyEdge>(); for (int i = 0; i < vertices.length; i++) { Collection<mxGraphHierarchyEdge> edges = internalVertices[i].connectsAsSource; Iterator<mxGraphHierarchyEdge> iter = edges.iterator(); while (iter.hasNext()) { mxGraphHierarchyEdge internalEdge = iter.next(); Collection<Object> realEdges = internalEdge.edges; Iterator<Object> iter2 = realEdges.iterator(); if (iter2.hasNext()) { Object realEdge = iter2.next(); Object targetCell = graph.getView().getVisibleTerminal(realEdge, false); mxGraphHierarchyNode internalTargetCell = vertexMapper.get(targetCell); if (internalTargetCell != null && internalVertices[i] != internalTargetCell) { internalEdge.target = internalTargetCell; if (internalTargetCell.connectsAsTarget.size() == 0) { internalTargetCell.connectsAsTarget = new ArrayList<mxGraphHierarchyEdge>(4); } // The vertices passed in were ordered, check that the // target cell has not already been marked as visited if (internalTargetCell.temp[0] == 1) { // Internal Edge is leftward, reverse it internalEdge.invert(); // There must be a connectsAsSource list already internalTargetCell.connectsAsSource.add(internalEdge); tempList.add(internalEdge); internalVertices[i].connectsAsTarget.add(internalEdge); } else { internalTargetCell.connectsAsTarget.add(internalEdge); } } } } // Remove the inverted edges as sources from this node Iterator<mxGraphHierarchyEdge> iter2 = tempList.iterator(); while (iter2.hasNext()) { internalVertices[i].connectsAsSource.remove(iter2.next()); } tempList.clear(); // Use the temp variable in the internal nodes to mark this // internal vertex as having been visited. internalVertices[i].temp[0] = 1; } }