blob: de6267a2c7da83a0b92877bbec4d30a39002422f [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.mrql;
import org.apache.mrql.gen.*;
import java.io.*;
import javax.swing.*;
import javax.swing.JTree.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
/* The MRQL debugger that uses the lineage generated in Provenance.gen */
public class Debugger extends JPanel implements TreeSelectionListener {
final MRData result;
final JTree tree;
final JTextField search = new JTextField(20);
static JFrame frame;
private static volatile boolean exit = false;
private static volatile boolean trace_nodes_only = false;
Trees exprs;
/** wrapped strings to be colored red */
private final static class TaggedString {
public String value;
TaggedString ( String value ) { this.value = value; }
public String toString () { return value; }
}
public Debugger ( MRData result_value, Trees exprs ) {
super(new GridLayout(1,0));
result = result_value;
this.exprs = exprs;
JToolBar toolBar = new JToolBar("");
toolBar.setPreferredSize(new Dimension(1000,40));
JButton button = new JButton();
button.setActionCommand("Exit");
button.setText("exit");
final JFrame nframe = frame;
button.addActionListener(new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
nframe.setVisible(false);
nframe.dispose();
exit = true;
}
});
button.setVisible(true);
toolBar.add(button);
toolBar.add(Box.createRigidArea(new Dimension(200,0)));
JCheckBox checkb = new JCheckBox();
checkb.setActionCommand("traceOnly");
checkb.setText("trace nodes only");
checkb.addActionListener(new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
trace_nodes_only = !trace_nodes_only;
if (trace_nodes_only) {
search.setText("");
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = createNode(result,"","");
root.setUserObject("results with trace and input nodes only");
model.setRoot(root);
model.reload(root);
} else reset();
}
});
checkb.setVisible(true);
toolBar.add(checkb);
button = new JButton();
button.setActionCommand("Previous");
button.setText("prev");
button.addActionListener(new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
}
});
button.setVisible(false);
//toolBar.add(button);
button = new JButton();
button.setActionCommand("Next");
button.setText("next");
button.addActionListener(new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
}
});
button.setVisible(false);
//toolBar.add(button);
toolBar.add(Box.createRigidArea(new Dimension(200,0)));
button = new JButton();
button.setActionCommand("Clear");
button.setText("clear");
button.addActionListener(new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
reset();
}
});
toolBar.add(button);
toolBar.add(search);
button = new JButton();
button.setActionCommand("SearchOutput");
button.setText("Search Output");
button.addActionListener(new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
if (!search.getText().equals("")) {
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = createNode(result,search.getText(),"");
root.setUserObject("search output results");
model.setRoot(root);
model.reload(root);
}
}
});
toolBar.add(button);
button = new JButton();
button.setActionCommand("SearchInput");
button.setText("Search Input");
button.addActionListener(new ActionListener() {
public void actionPerformed ( ActionEvent e ) {
if (!search.getText().equals("")) {
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = createNode(result,"",search.getText());
root.setUserObject("search input results");
model.setRoot(root);
model.reload(root);
}
}
});
toolBar.add(button);
tree = new JTree(createNode(result,"",""));
JScrollPane sp = new JScrollPane(tree,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
sp.setPreferredSize(new Dimension(1000,2000));
sp.getVerticalScrollBar().setPreferredSize(new Dimension(20,0));
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
Font currentFont = tree.getFont();
tree.setFont(new Font(currentFont.getName(),
currentFont.getStyle(),
(int)(currentFont.getSize()*1.5)));
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
public Component getTreeCellRendererComponent
( JTree tree, Object value, boolean sel, boolean exp,
boolean leaf, int row, boolean hasFocus ) {
super.getTreeCellRendererComponent(tree,value,sel,exp,leaf,row,hasFocus);
// tagged strings are red
if (((DefaultMutableTreeNode)value).getUserObject() instanceof TaggedString)
setForeground(Color.red);
return this;
}
};
renderer.setLeafIcon(null);
renderer.setOpenIcon(null);
renderer.setClosedIcon(null);
tree.setCellRenderer(renderer);
tree.addTreeSelectionListener(this);
setLayout(new BorderLayout());
add(toolBar,BorderLayout.NORTH);
add(sp,BorderLayout.CENTER);
}
public void valueChanged ( TreeSelectionEvent e ) { }
private void reset () {
search.setText("");
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = createNode(result,"","");
root.setUserObject("results");
model.setRoot(root);
model.reload(root);
}
private String exprNode ( Tree e ) {
match e {
case call(source,_,`path,...):
return path.stringValue();
case trace(`msg,_,_):
return msg.stringValue();
case call(`f,...):
return f.toString();
case cmap(lambda(`v,_),_):
return "cmap "+v;
case groupBy(_):
return "groupBy";
case coGroup(...):
return "coGroup";
case reduce(`m,...):
return "reduce("+m+")";
case project(`v,`a):
return exprNode(v)+"."+a;
case nth(`x,`n):
return exprNode(x)+"#"+n;
case `f(...):
return f;
};
return e.toString();
}
private boolean consider_node ( Tree e ) {
match e {
case call(source,...): return true;
case trace(...): return true;
};
return !trace_nodes_only;
}
private boolean existing_child ( DefaultMutableTreeNode node, DefaultMutableTreeNode parent ) {
Object no = node.getUserObject();
String ns = (no instanceof TaggedString) ? ((TaggedString)no).value : (String)no;
for ( int i = 0; i < parent.getChildCount(); i++ ) {
Object co = ((DefaultMutableTreeNode)parent.getChildAt(i)).getUserObject();
String cs = (co instanceof TaggedString) ? ((TaggedString)co).value : (String)co;
if (cs.equals(ns))
return true;
};
return false;
}
private void create_nodes ( MRData value, DefaultMutableTreeNode parent,
String inputSearch, boolean first ) {
Tuple p = ((Tuple)value);
MRData v = p.get(1);
Tree opr = exprs.nth(((MR_int)p.get(0)).get());
Tree tp = exprs.nth(((MR_int)p.get(0)).get()-1);
match tp {
case `T(`etp):
if (!Provenance.is_collection(T))
fail;
tp = etp;
}
DefaultMutableTreeNode node
= new DefaultMutableTreeNode((first) ? Printer.print(v,tp)
: exprNode(opr)+": "+Printer.print(v,tp));
if (first || consider_node(opr)) {
if (!existing_child(node,parent))
parent.add(node);
} else node = parent;
for ( int i = 2; i < p.size(); i++ )
if (p.get(i) instanceof Bag)
for ( MRData e: (Bag)p.get(i) )
create_nodes(e,node,inputSearch,false);
else create_nodes(p.get(i),node,inputSearch,false);
boolean matched = false;
for ( int i = 0; i < node.getChildCount(); i++ ) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getChildAt(i);
matched |= child.getUserObject() instanceof TaggedString;
};
if (matched && node.getUserObject() instanceof String)
node.setUserObject(new TaggedString((String)node.getUserObject()));
else match opr {
case call(source,...):
if (!inputSearch.equals("") && v.toString().contains(inputSearch)
&& node.getUserObject() instanceof String)
node.setUserObject(new TaggedString((String)node.getUserObject()));
}
}
private DefaultMutableTreeNode createNode ( MRData value, String outputSearch, String inputSearch ) {
if (value instanceof Bag) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode("results");
for ( MRData e: (Bag)value )
if (outputSearch.equals("") || ((Tuple)e).get(0).toString().contains(outputSearch))
create_nodes(((Tuple)e).get(1),node,inputSearch,true);
return node;
} else if (value instanceof MR_dataset) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode("results");
for ( MRData e: ((MR_dataset)value).dataset().take(Config.max_bag_size_print) )
if (outputSearch.equals("") || ((Tuple)e).get(0).toString().contains(outputSearch))
create_nodes(((Tuple)e).get(1),node,inputSearch,true);
return node;
} else {
DefaultMutableTreeNode node = new DefaultMutableTreeNode("value");
if (outputSearch.equals("") || ((Tuple)value).get(0).toString().contains(outputSearch))
create_nodes(((Tuple)value).get(1),node,inputSearch,true);
return node;
}
}
private static void createAndShowGUI ( MRData lineage, Tree tp, Trees exprs ) {
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowListener() {
public void windowClosing ( WindowEvent e ) {
frame.setVisible(false);
frame.dispose();
exit = true;
}
public void windowActivated ( WindowEvent e ) {}
public void windowClosed ( WindowEvent e ) {}
public void windowDeactivated ( WindowEvent e ) {}
public void windowDeiconified ( WindowEvent e ) {}
public void windowIconified ( WindowEvent e ) {}
public void windowOpened ( WindowEvent e ) {}
});
frame.add(new Debugger(lineage,exprs));
frame.pack();
frame.setVisible(true);
}
/** use the MRQL debugger on a query result
* @param lineage the query result extended with lineage
* @param type the result type
* @param exprs the expr terms used in the lineage
*/
public static void debug ( final MRData lineage, final Tree type, final Trees exprs ) {
try {
exit = false;
trace_nodes_only = false;
frame = new JFrame("MRQL debugger");
SwingUtilities.invokeLater(new Runnable() {
public void run () {
createAndShowGUI(lineage,type,exprs);
}
});
while (!exit)
Thread.sleep(1000);
} catch (Exception ex) {
}
}
}