/******************************************************************************
* Copyright: GPL v3 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
******************************************************************************/
package dba.gui.auxClasses.jGraph;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.view.mxGraph;
import dbaCore.data.Attribute;
import dbaCore.data.FunctionalDependency;
import dbaCore.data.RelationSchema;
import java.util.ArrayList;
/**
* Updates / Draws the RelationDetailsView
*/
public class RelationDetailsGraphUpdater extends RelationUpdater {
private mxGraph graph;
private Object parentPane;
private ArrayList<RelationSchema> dbRelations;
public RelationDetailsGraphUpdater(mxGraph graph, ArrayList<RelationSchema> relations) {
super();
dbRelations = relations;
parentPane = graph.getDefaultParent();
this.graph = graph;
graph.setAutoSizeCells(true);
}
public void run() {
graph.getModel().beginUpdate();
try {
display();
} finally {
graph.getModel().endUpdate();
}
}
/**
* Removes all cells from the Graph
*/
private void removeAllRelations() {
graph.removeCells(graph.getChildVertices(graph.getDefaultParent()));
}
/**
* Displays all relations with their functional dependencies
*/
private void display() {
int offset = 5;
mxCell relationCell;
removeAllRelations();
for (RelationSchema relation : dbRelations) {
relationCell = (mxCell) insertRelation(graph, relation, offset);
offset += relationCell.getGeometry().getHeight() + (50 + 30 * relation.getFunctionalDependencies().size());
}
}
/**
* Inserts a given Relation in a graph
*
* @param graph the graph as target for insertion
* @param relation the relation that should be inserted
* @param verticalOffset the vertical offset of the relation
* @return the mxCell representing the Relation
*/
private Object insertRelation(mxGraph graph, RelationSchema relation, int verticalOffset) {
int attributeOffset;
int horizontalOffset = 0;
ArrayList<mxCell> attributeCells = new ArrayList<>();
mxCell relationVertex = (mxCell) graph.insertVertex(parentPane, relation.getName(), relation, horizontalOffset,
verticalOffset, 15 * relation.getName().length(), 25, "RELATION_HEADER");
attributeOffset = (int) (relationVertex.getGeometry().getY() + 25);
// Add attributes
for (Attribute attr : relation.getAttributes()) {
attributeCells.add((mxCell) graph.insertVertex(parentPane, attr.getName(), attr, horizontalOffset,
attributeOffset, 30, 25, super.getAttributeStyle(attr, getImageSizeClass(attr))));
graph.updateCellSize(attributeCells.get(attributeCells.size() - 1));
horizontalOffset += 30;
}
double currentXPos = 0;
for (int i = 0; i < attributeCells.size(); i++) {
mxGeometry geo = attributeCells.get(i).getGeometry();
if (i > 0) {
currentXPos += attributeCells.get(i - 1).getGeometry().getWidth();
geo.setX(currentXPos);
}
geo.setHeight(25);
geo.setWidth(geo.getWidth() + 5);
}
drawFunctionalDependencies(relation.getAttributes(), attributeCells, relation.getFunctionalDependencies());
return relationVertex;
}
/**
* draws all given functionalDependencies
*
* @param attributes the attributes of the relation
* @param attributeCells the mxCells containing the attributes
* @param fds the functionalDependencies to display
*/
private void drawFunctionalDependencies(ArrayList<Attribute> attributes, ArrayList<mxCell> attributeCells,
ArrayList<FunctionalDependency> fds) {
int offset = 2;
for (FunctionalDependency fd : fds) {
drawFunctionalDependency(attributes, attributeCells, fd, offset);
offset += 25;
}
}
/**
* draws a single functionalDependency
*
* @param attributes the attributes of the relation
* @param attributeCells the mxCells containing the attributes
* @param fd the functionalDependency to display
* @param verticalOffset the offset between two functionalDependencies
*/
private void drawFunctionalDependency(ArrayList<Attribute> attributes, ArrayList<mxCell> attributeCells,
FunctionalDependency fd, int verticalOffset) {
// draws all the near nodes
ArrayList<mxCell> nearNodes = drawNodes(attributeCells, verticalOffset + 2);
// draws all the far nodes
ArrayList<mxCell> farNodes = drawNodes(attributeCells, verticalOffset + 20);
// draws all the arrows between the nodes
drawAttributeArrows(attributes, fd, nearNodes, farNodes);
}
/**
* Draws a node beneath each Attribute-mxCell (horizontal-Center)
*
* @param attributes the cells to draw a node beneath them
* @param offset the vertical offset between the cell and the node
* @return returns a ArrayList containing all inserted nodes
*/
private ArrayList<mxCell> drawNodes(ArrayList<mxCell> attributes, int offset) {
ArrayList<mxCell> nodes = new ArrayList<>();
for (mxCell cell : attributes) {
nodes.add((mxCell) graph.insertVertex(parentPane, null, null, cell.getGeometry().getCenterX(),
getCellLowestYPoint(cell) + offset, 1, 1, "NODE"));
}
return nodes;
}
/**
* Returns the lower end of the cell (max y-coordinate)
*
* @param cell the cell to work with
* @return the y-coordinate
*/
private double getCellLowestYPoint(mxCell cell) {
return cell.getGeometry().getY() + cell.getGeometry().getHeight();
}
/**
* Draws the arrows between the nodes
*
* @param attributes the attributes of the relation
* @param fd the functionalDependency to display
* @param nearNodes the Array of Nodes which are closer to the
* attribute-Nodes
* @param farNodes the Array of Nodes which are further away of the
* attribute-Nodes
*/
private void drawAttributeArrows(ArrayList<Attribute> attributes, FunctionalDependency fd,
ArrayList<mxCell> nearNodes, ArrayList<mxCell> farNodes) {
int index;
ArrayList<Integer> indices = new ArrayList<>();
// draw arrows for the SourceAttributes
for (Attribute sourceAttr : fd.getSourceAttributes()) {
index = attributes.indexOf(sourceAttr);
graph.insertEdge(parentPane, null, fd, nearNodes.get(index), farNodes.get(index), "EDGE_PLAIN");
indices.add(index);
}
// draw arrows for the TargetAttributes
for (Attribute targetAttr : fd.getTargetAttributes()) {
index = attributes.indexOf(targetAttr);
graph.insertEdge(parentPane, null, fd, farNodes.get(index), nearNodes.get(index), "EDGE_ARROW");
indices.add(index);
}
// Connect arrows
java.util.Collections.sort(indices);
graph.insertEdge(parentPane, null, fd, farNodes.get(indices.get(0)), farNodes.get(indices.get(indices.size() - 1)), "EDGE_PLAIN");
}
}