package edu.brown.gui.catalog;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import org.voltdb.catalog.PlanFragment;
import org.voltdb.plannodes.AbstractPlanNode;
import org.voltdb.utils.Pair;
import edu.brown.gui.AbstractViewer;
import edu.brown.gui.common.GraphVisualizationPanel;
import edu.brown.plannodes.PlanNodeGraph;
import edu.brown.plannodes.PlanNodeTreeWalker;
import edu.brown.plannodes.PlanNodeUtil;
import edu.brown.utils.EventObservable;
import edu.brown.utils.EventObserver;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.VisualizationServer.Paintable;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
public class PlanTreeCatalogNode {
private final String label;
private final JPanel mainPanel;
private JTextArea nodeField;
private JTabbedPane tabbedPane;
private final Collection<PlanFragment> fragments;
private final AbstractPlanNode root;
private final PlanNodeGraph graph;
private final GraphVisualizationPanel<AbstractPlanNode, PlanNodeGraph.Edge> visualizationPanel;
private boolean zoomed = false;
private EventObserver<AbstractPlanNode> vertex_observer = new EventObserver<AbstractPlanNode>() {
@Override
public void update(EventObservable<AbstractPlanNode> o, AbstractPlanNode arg) {
PlanTreeCatalogNode.this.selectNode((AbstractPlanNode)arg);
}
};
public PlanTreeCatalogNode(String label, Collection<PlanFragment> fragments, AbstractPlanNode root) {
this.label = label;
this.fragments = fragments;
this.root = root;
this.graph = new PlanNodeGraph(this.root);
this.mainPanel = new JPanel(new BorderLayout());
this.visualizationPanel = GraphVisualizationPanel.factory(this.graph, this.vertex_observer, null);
this.init();
}
private void init() {
// GraphVisualization
RenderContext<AbstractPlanNode, PlanNodeGraph.Edge> context = this.visualizationPanel.getRenderContext();
context.setEdgeShapeTransformer(new EdgeShape.Line<AbstractPlanNode, PlanNodeGraph.Edge>());
context.setVertexFontTransformer(new GraphVisualizationPanel.VertexFontTransformer<AbstractPlanNode>(true));
// PlanFragmentBoundaries boundaryPainter = new PlanFragmentBoundaries();
// this.visualizationPanel.addPostRenderPaintable(boundaryPainter);
// Full Plan Tab
JPanel textInfoPanel = new JPanel();
textInfoPanel.setLayout(new BorderLayout());
JTextArea textInfoTextArea = new JTextArea();
textInfoTextArea.setEditable(false);
textInfoTextArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
textInfoTextArea.setText(PlanNodeUtil.debug(this.root));
textInfoPanel.add(new JScrollPane(textInfoTextArea), BorderLayout.CENTER);
// Node Field Tab
this.nodeField = new JTextArea();
this.nodeField.setEditable(false);
this.nodeField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
this.nodeField.setText("");
JPanel textInfoPanel2 = new JPanel(new BorderLayout());
textInfoPanel2.add(new JScrollPane(this.nodeField), BorderLayout.CENTER);
this.tabbedPane = new JTabbedPane();
this.tabbedPane.add("Full Plan", textInfoPanel);
this.tabbedPane.add("Selected Node", textInfoPanel2);
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, visualizationPanel, this.tabbedPane);
splitPane.setDividerLocation(AbstractViewer.DEFAULT_WINDOW_HEIGHT - 500);
this.mainPanel.add(splitPane, BorderLayout.CENTER);
}
/**
*
*/
public void centerOnRoot() {
if (this.zoomed == false) {
this.visualizationPanel.zoom(1.2);
this.zoomed = true;
}
final int depth = PlanNodeUtil.getDepth(this.root) / 2;
new PlanNodeTreeWalker(false) {
@Override
protected void callback(AbstractPlanNode element) {
if (depth == this.getDepth()) {
PlanTreeCatalogNode.this.visualizationPanel.centerVisualization(element, true);
this.stop();
}
}
}.traverse(this.root);
}
/**
*
* @param node
*/
private void selectNode(AbstractPlanNode node) {
this.nodeField.setText(PlanNodeUtil.debugNode(node));
this.tabbedPane.setSelectedIndex(1);
// this.visualizationPanel.centerVisualization(node);
}
@Override
public String toString() {
return (this.label);
}
public JPanel getPanel() {
return (this.mainPanel);
}
// -----------------------------------------------------------------
// PLANFRAGMENT BOUNDARY BOXES
// -----------------------------------------------------------------
/**
* An attempt to draw PlanFragment boundaries
* @author pavlo
*/
@SuppressWarnings("unused")
private class PlanFragmentBoundaries implements Paintable {
private PlanFragment fragments[];
private Pair<AbstractPlanNode, AbstractPlanNode> fragment_boundaries[];
private AbstractPlanNode nodes[];
private int nodes_frags[];
final Font pfFont = new Font("Serif", Font.BOLD, 10);
final BasicStroke boundaryStroke = new BasicStroke(1.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f,
new float[]{5.0f},
0.0f);
@SuppressWarnings("unchecked")
PlanFragmentBoundaries() {
this.fragments = PlanTreeCatalogNode.this.fragments.toArray(new PlanFragment[0]);
this.fragment_boundaries = (Pair<AbstractPlanNode, AbstractPlanNode>[])new Pair<?,?>[this.fragments.length];
int num_nodes = graph.getVertexCount();
this.nodes = new AbstractPlanNode[num_nodes];
this.nodes_frags = new int[num_nodes];
int node_idx = 0;
for (AbstractPlanNode n : graph.getVertices()) {
this.nodes[node_idx] = n;
// Now figure out what fragment each AbstractPlanNode belongs to
this.nodes_frags[node_idx] = -1;
for (int frag_idx = 0; frag_idx < this.fragments.length; frag_idx++) {
if (PlanNodeUtil.containsPlanNode(this.fragments[frag_idx], n)) {
this.nodes_frags[node_idx] = frag_idx;
break;
}
} // FOR
assert(this.nodes_frags[node_idx] != -1);
node_idx++;
} // FOR
for (int frag_idx = 0; frag_idx < this.fragments.length; frag_idx++) {
AbstractPlanNode max = null;
int max_depth = -1;
AbstractPlanNode min = null;
int min_depth = Integer.MAX_VALUE;
for (node_idx = 0; node_idx < nodes.length; node_idx++) {
if (this.nodes_frags[node_idx] != frag_idx) continue;
int depth = PlanNodeUtil.getDepth(this.nodes[node_idx]);
if (depth > max_depth) {
max_depth = depth;
max = this.nodes[node_idx];
}
if (depth < min_depth) {
min_depth = depth;
min = this.nodes[node_idx];
}
// System.err.println(this.nodes[node_idx] + " => " + depth);
} // FOR
if (max == null) max = min;
if (min == null) min = max;
this.fragment_boundaries[frag_idx] = Pair.of(max, min);
// System.err.println(this.fragment_boundaries[frag_idx] + "\n");
} // FOR (fragments)
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setStroke(boundaryStroke);
int offsetLeft = 100;
int offsetRight = 50;
int box_y = 0;
float text_x = 5f;
float text_y = 10f;
Rectangle nodeSize = null;
// for (AbstractPlanNode node : this.nodes) {
// System.err.println(node + " -> " + visualizationPanel.getPosition(node));
// }
Font origFont = g2.getFont();
g2.setFont(pfFont);
for (int frag_idx = 0; frag_idx < this.fragments.length; frag_idx++) {
g2.setColor(frag_idx == 0 ? Color.BLUE : Color.BLACK);
// System.err.println(this.fragment_boundaries[frag_idx] + " => " + this.nodes_frags[frag_idx]);
Pair<AbstractPlanNode, AbstractPlanNode> p = this.fragment_boundaries[frag_idx];
AbstractPlanNode topNode = p.getFirst();
Point2D topPos = visualizationPanel.getPosition(topNode);
// System.err.println(topNode + " -> " + topPos);
AbstractPlanNode botNode = p.getSecond();
Point2D botPos = visualizationPanel.getPosition(botNode);
// System.err.println(botNode + " -> " + botPos);
if (nodeSize == null) {
Shape s = visualizationPanel.getRenderContext().getVertexShapeTransformer().transform(topNode);
nodeSize = s.getBounds();
// System.err.println("NodeSize: " + nodeSize.getBounds2D());
}
Point2D abs_topCorner = new Point2D.Double(topPos.getX() - offsetLeft,
topPos.getY() - 10);
// Point2D draw_topCorner = visualizationPanel.transform(abs_topCorner);
Point2D abs_botCorner = new Point2D.Double(botPos.getX() + offsetLeft,
botPos.getY() + nodeSize.getHeight());
// Point2D draw_botCorner = visualizationPanel.transform(abs_botCorner);
Rectangle2D.Double rect = new Rectangle2D.Double(abs_topCorner.getX(),
abs_topCorner.getY(),
Math.abs(abs_topCorner.getX() - abs_botCorner.getX()),
Math.abs(abs_topCorner.getY() - abs_botCorner.getY()));
Shape s = visualizationPanel.transform(rect);
g2.draw(s);
// System.err.println(s.getBounds2D());
// PlanFragment Label
Point2D text_pos = new Point2D.Float((float)abs_topCorner.getX() + text_x,
(float)abs_topCorner.getY() + text_y);
text_pos = visualizationPanel.transform(text_pos);
g2.setColor(Color.BLACK);
g2.drawString("#" + this.fragments[frag_idx].getName(),
(float)text_pos.getX(),
(float)text_pos.getY());
// System.err.println();
}
g2.setFont(origFont);
}
@Override
public boolean useTransform() {
return false;
}
} // END CLASS
}