/* Copyright 2009 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
HermiT 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
HermiT 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
package org.semanticweb.HermiT.debugger.commands;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import org.semanticweb.HermiT.debugger.Debugger;
import org.semanticweb.HermiT.debugger.Printing;
import org.semanticweb.HermiT.debugger.Debugger.NodeCreationInfo;
import org.semanticweb.HermiT.model.AtLeastConcept;
import org.semanticweb.HermiT.model.ExistentialConcept;
import org.semanticweb.HermiT.tableau.Node;
@SuppressWarnings("serial")
public class SubtreeViewer extends JFrame {
protected final Debugger m_debugger;
protected final SubtreeTreeModel m_subtreeTreeModel;
protected final JTextArea m_nodeInfoTextArea;
protected final JTree m_tableauTree;
protected final JTextField m_nodeIDField;
public SubtreeViewer(Debugger debugger,Node rootNode) {
super("Subtree for node "+rootNode.getNodeID());
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
m_debugger=debugger;
m_subtreeTreeModel=new SubtreeTreeModel(debugger,rootNode);
m_tableauTree=new JTree(m_subtreeTreeModel);
m_tableauTree.setLargeModel(true);
m_tableauTree.setShowsRootHandles(true);
m_tableauTree.setCellRenderer(new NodeCellRenderer(debugger));
m_tableauTree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
TreePath selectionPath=m_tableauTree.getSelectionPath();
if (selectionPath==null)
showNodeLabels(null);
else
showNodeLabels((Node)selectionPath.getLastPathComponent());
}
});
m_nodeInfoTextArea=new JTextArea();
m_nodeInfoTextArea.setFont(Debugger.s_monospacedFont);
JScrollPane modelScrollPane=new JScrollPane(m_tableauTree);
modelScrollPane.setPreferredSize(new Dimension(600,400));
JScrollPane nodeInfoScrollPane=new JScrollPane(m_nodeInfoTextArea);
nodeInfoScrollPane.setPreferredSize(new Dimension(400,400));
JSplitPane mainSplit=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,modelScrollPane,nodeInfoScrollPane);
JPanel commandsPanel=new JPanel(new FlowLayout(FlowLayout.LEFT,5,3));
commandsPanel.add(new JLabel("Node ID:"));
m_nodeIDField=new JTextField();
m_nodeIDField.setPreferredSize(new Dimension(200,m_nodeIDField.getPreferredSize().height));
commandsPanel.add(m_nodeIDField);
JButton button=new JButton("Search");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int nodeID;
String nodeIDText=m_nodeIDField.getText();
try {
nodeID=Integer.parseInt(nodeIDText);
}
catch (NumberFormatException error) {
JOptionPane.showMessageDialog(SubtreeViewer.this,"Invalid node ID '"+nodeIDText+"'.");
return;
}
Node node=m_debugger.getTableau().getNode(nodeID);
if (node==null) {
JOptionPane.showMessageDialog(SubtreeViewer.this,"Node with ID "+nodeID+" cannot be found.");
return;
}
findNode(node);
}
});
getRootPane().setDefaultButton(button);
commandsPanel.add(button);
button=new JButton("Refresh");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
refresh();
}
});
commandsPanel.add(button);
JPanel mainPanel=new JPanel(new BorderLayout());
mainPanel.add(mainSplit,BorderLayout.CENTER);
mainPanel.add(commandsPanel,BorderLayout.SOUTH);
setContentPane(mainPanel);
pack();
setLocation(200,200);
setVisible(true);
m_nodeIDField.requestFocusInWindow();
}
public void refresh() {
m_subtreeTreeModel.refresh();
}
public void findNode(Node node) {
List<Node> pathToRoot=new ArrayList<Node>();
Node currentNode=node;
while (currentNode!=null && currentNode!=m_subtreeTreeModel.getRoot()) {
pathToRoot.add(currentNode);
currentNode=m_debugger.getNodeCreationInfo(currentNode).m_createdByNode;
}
if (currentNode==null) {
JOptionPane.showMessageDialog(SubtreeViewer.this,"Node with ID "+node.getNodeID()+" is not present in the shown subtree.");
return;
}
TreePath treePath=new MyTreePath(null,m_subtreeTreeModel.getRoot());
for (int index=pathToRoot.size()-1;index>=0;--index)
treePath=new MyTreePath(treePath,pathToRoot.get(index));
m_tableauTree.expandPath(treePath);
m_tableauTree.setSelectionPath(treePath);
m_tableauTree.scrollPathToVisible(treePath);
}
public void showNodeLabels(Node node) {
if (node==null)
m_nodeInfoTextArea.setText("");
else {
CharArrayWriter buffer=new CharArrayWriter();
PrintWriter writer=new PrintWriter(buffer);
Printing.printNodeData(m_debugger,node,writer);
writer.flush();
m_nodeInfoTextArea.setText(buffer.toString());
m_nodeInfoTextArea.select(0,0);
}
}
protected static class SubtreeTreeModel implements TreeModel {
protected final EventListenerList m_eventListeners;
protected final Debugger m_debugger;
protected final Node m_root;
public SubtreeTreeModel(Debugger debugger,Node root) {
m_eventListeners=new EventListenerList();
m_debugger=debugger;
m_root=root;
}
public void addTreeModelListener(TreeModelListener listener) {
m_eventListeners.add(TreeModelListener.class,listener);
}
public void removeTreeModelListener(TreeModelListener listener) {
m_eventListeners.remove(TreeModelListener.class,listener);
}
public Node getChild(Object parent,int index) {
NodeCreationInfo nodeCreationInfo = null;
if (parent instanceof Node) {
nodeCreationInfo = m_debugger.getNodeCreationInfo((Node) parent);
}
if (nodeCreationInfo==null)
return null;
else
return nodeCreationInfo.m_children.get(index);
}
public int getChildCount(Object parent) {
NodeCreationInfo nodeCreationInfo = null;
if (parent instanceof Node) {
nodeCreationInfo = m_debugger.getNodeCreationInfo((Node) parent);
}
if (nodeCreationInfo==null) {
return 0;
} else {
return nodeCreationInfo.m_children.size();
}
}
public int getIndexOfChild(Object parent,Object child) {
NodeCreationInfo nodeCreationInfo = null;
if (parent instanceof Node) {
nodeCreationInfo = m_debugger.getNodeCreationInfo((Node) parent);
}
if (nodeCreationInfo==null) {
return -1;
} else {
return nodeCreationInfo.m_children.indexOf(child);
}
}
public Object getRoot() {
return m_root;
}
public boolean isLeaf(Object node) {
return getChildCount(node)==0;
}
public void valueForPathChanged(TreePath path,Object newValue) {
}
public void refresh() {
Object[] listeners=m_eventListeners.getListenerList();
TreeModelEvent e=new TreeModelEvent(this,new Object[] { getRoot() });
for (Object listener : listeners)
if (listener instanceof TreeModelListener)
((TreeModelListener)listener).treeStructureChanged(e);
}
}
protected static class NodeCellRenderer extends DefaultTreeCellRenderer {
protected static final Icon NOT_ACTIVE_ICON=new DotIcon(Color.LIGHT_GRAY);
protected static final Icon BLOCKED_ICON=new DotIcon(Color.CYAN);
protected static final Icon WITH_EXISTENTIALS_ICON=new DotIcon(Color.RED);
protected static final Icon NI_NODE_ICON=new DotIcon(Color.BLACK);
protected static final Icon TREE_NODE_ICON=new DotIcon(Color.GREEN);
protected static final Icon GRAPH_NODE_ICON=new DotIcon(Color.MAGENTA);
protected static final Icon CONCRETE_NODE_ICON=new DotIcon(Color.BLUE);
protected final Debugger m_debugger;
public NodeCellRenderer(Debugger debugger) {
m_debugger=debugger;
}
public Component getTreeCellRendererComponent(JTree tree,Object value,boolean selected,boolean expanded,boolean leaf,int row,boolean hasFocus) {
Node node=(Node)value;
StringBuffer buffer=new StringBuffer();
ExistentialConcept existentialConcept=m_debugger.getNodeCreationInfo(node).m_createdByExistential;
if (existentialConcept==null) {
buffer.append(node.getNodeID());
buffer.append(":(root)");
}
else if (existentialConcept instanceof AtLeastConcept) {
AtLeastConcept atLeastConcept=(AtLeastConcept)existentialConcept;
buffer.append(atLeastConcept.getOnRole().toString(m_debugger.getPrefixes()));
buffer.append(" --> ");
buffer.append(node.getNodeID());
buffer.append(":[");
buffer.append(atLeastConcept.getToConcept().toString(m_debugger.getPrefixes()));
buffer.append("]");
}
else {
// Do nothing for now.
}
super.getTreeCellRendererComponent(tree,buffer.toString(),selected,expanded,leaf,row,hasFocus);
if (!node.isActive())
setIcon(NOT_ACTIVE_ICON);
else if (node.isBlocked())
setIcon(BLOCKED_ICON);
else if (node.hasUnprocessedExistentials())
setIcon(WITH_EXISTENTIALS_ICON);
else {
switch (node.getNodeType()) {
case TREE_NODE:
setIcon(TREE_NODE_ICON);
break;
case GRAPH_NODE:
setIcon(GRAPH_NODE_ICON);
break;
case NI_NODE:
setIcon(NI_NODE_ICON);
break;
case CONCRETE_NODE:
case ROOT_CONSTANT_NODE:
default:
setIcon(CONCRETE_NODE_ICON);
break;
}
}
return this;
}
}
protected static class DotIcon implements Icon {
protected final Color m_color;
public DotIcon(Color color) {
m_color=color;
}
public int getIconHeight() {
return 16;
}
public int getIconWidth() {
return 16;
}
public void paintIcon(Component c,Graphics g,int x,int y) {
Color oldColor=g.getColor();
g.setColor(m_color);
g.fillOval(x+2,y+2,x+12,y+12);
g.setColor(oldColor);
}
}
protected static class MyTreePath extends TreePath {
public MyTreePath(Object object) {
super(object);
}
public MyTreePath(TreePath treePath,Object object) {
super(treePath,object);
}
}
}