package ruc.irm.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.zip.GZIPInputStream;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
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.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import ruc.irm.similarity.word.hownet2.sememe.Sememe;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
/**
* 用于显示义原层次树的Tree组件,仅仅为了管理和查看方便,与相似度计算算法无关。 所有内容读自sememe.xml.gz压缩文件。
* 整个面板由两大部分构成,上面(North)为输入查询的文本框和查询按钮,下面(Center)为JSplitPane,JSplitPane又由
* JTextArea和JTree组成,JTextArea用于显示查询结果,JTree用于显示层次关系
*
* @author <a href="mailto:iamxiatian@gmail.com">夏天</a>
* @organization 中国人民大学信息资源管理学院 知识工程实验室
*/
public class SememeTreeUI extends JFrame {
private static final long serialVersionUID = 3270193057395104087L;
public static JPanel createPanel() {
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
try {
final DefaultMutableTreeNode root = load();
final JTree jtree = new JTree(root);
JScrollPane treeScrollPane = new JScrollPane(jtree);
// mainPanel.add(treeScrollPane, BorderLayout.SOUTH);
JPanel queryPanel = new JPanel();
// TODO加入查询义原路径的处理
final JTextField queryField = new JTextField();
queryField.setColumns(50);
queryPanel.add(new JLabel("输入义原:"));
queryPanel.add(queryField);
JButton queryButton = new JButton("查询");
queryPanel.add(queryButton);
mainPanel.add(queryPanel, BorderLayout.NORTH);
final JTextArea editor = new JTextArea();
editor.setLineWrap(true);
editor.setForeground(Color.RED);
editor.setRows(3);
JScrollPane editorScrollPane = new JScrollPane(editor);
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, editorScrollPane, treeScrollPane);
splitPane.setDividerSize(2);
mainPanel.add(splitPane, BorderLayout.CENTER);
// mainPanel.add(editorScrollPane, BorderLayout.CENTER);
queryButton.addActionListener(new ActionListener() {
/**
* 递归查询符合条件的结果,并把所有结果存入selectedNodes中
*
* @param node
* @param selectedNodes
*/
private void query(DefaultMutableTreeNode node, Collection<DefaultMutableTreeNode> selectedNodes) {
String queryString = queryField.getText();
String text = node.getUserObject().toString();
if (text.startsWith(queryString) || text.endsWith(queryString)) {
selectedNodes.add(node);
}
if (node.getChildCount() == 0)
return;
for (DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getFirstChild(); child != null; child = child
.getNextSibling()) {
query(child, selectedNodes);
}
}
/**
* 关闭所有树的节点
*
* @param parent
*/
@SuppressWarnings("unchecked")
private void collapseAll(TreePath parent) {
TreeNode node = (TreeNode) parent.getLastPathComponent();
if (node.getChildCount() > 0) {
for (Enumeration<TreeNode> e = node.children(); e.hasMoreElements();) {
TreeNode child = e.nextElement();
TreePath path = parent.pathByAddingChild(child);
collapseAll(path);
}
}
jtree.collapsePath(parent);
}
@Override
public void actionPerformed(ActionEvent e) {
String queryString = queryField.getText();
if (queryString.trim().equals("")) {
editor.setText("请输入查询义原");
return;
}
Collection<DefaultMutableTreeNode> selectedNodes = new ArrayList<DefaultMutableTreeNode>();
query(root, selectedNodes);
if (selectedNodes.size() == 0) {
editor.setText("查询结果:无匹配记录");
return;
}
jtree.setSelectionPath(null);
collapseAll(new TreePath(((DefaultTreeModel) jtree.getModel()).getPathToRoot(root)));
StringBuilder sb = new StringBuilder("共有" + selectedNodes.size() + "条匹配义原:\n");
for (DefaultMutableTreeNode node : selectedNodes) {
TreePath path = new TreePath(((DefaultTreeModel) jtree.getModel()).getPathToRoot(node));
jtree.expandPath(path);
jtree.addSelectionPath(path);
Object[] objs = path.getPath();
for (int i = objs.length - 1; i > 0; i--) {
if (i < objs.length - 1) {
sb.append(" -> ");
}
sb.append(objs[i].toString());
}
sb.append("\n\n");
}
editor.setText(sb.toString());
editor.setCaretPosition(0);
}
});
} catch (IOException e) {
e.printStackTrace();
}
return mainPanel;
}
private static void createNodes(Multimap<String, Sememe> sememes, DefaultMutableTreeNode top, String parentId) {
Collection<Sememe> children = sememes.get(parentId);
for (Sememe child : children) {
String text = child.getEnWord() + "|" + child.getCnWord();
if (child.getDefine() != null) {
text += " " + child.getDefine();
}
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(text);
top.add(childNode);
createNodes(sememes, childNode, child.getId());
}
}
/**
* 加载义原到Multimap中,并创建树的节点
*
* @return
* @throws IOException
*/
private static DefaultMutableTreeNode load() throws IOException {
/**
* 存放parentId和该parentId所隶属的义原,即Key为parentId,value为子义原
*/
Multimap<String, Sememe> sememes = ArrayListMultimap.create();
String sememeFile = Sememe.class.getPackage().getName().replaceAll("\\.", "/") + "/sememe.xml.gz";
InputStream input = Sememe.class.getClassLoader().getResourceAsStream(sememeFile);
input = new GZIPInputStream(input);
System.out.println("[" + SememeTreeUI.class.getSimpleName() + "]loading sememes into sememe tree...");
long time = System.currentTimeMillis();
try {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLEventReader xmlEventReader = inputFactory.createXMLEventReader(input);
while (xmlEventReader.hasNext()) {
XMLEvent event = xmlEventReader.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
if (startElement.getName().toString().equals("sememe")) {
String en = startElement.getAttributeByName(QName.valueOf("en")).getValue();
String cn = startElement.getAttributeByName(QName.valueOf("cn")).getValue();
String id = startElement.getAttributeByName(QName.valueOf("id")).getValue();
Attribute attr = startElement.getAttributeByName(QName.valueOf("define"));
String define = (attr == null ? null : attr.getValue());
Sememe sememe = new Sememe(id, en, cn, define);
int pos = id.lastIndexOf("-");
String parentId = "root";
if (pos > 0) {
parentId = id.substring(0, pos);
}
sememes.put(parentId, sememe);
}
}
}
input.close();
} catch (Exception e) {
throw new IOException(e);
}
time = System.currentTimeMillis() - time;
System.out.println("complete. time elapsed: " + (time / 1000) + "s");
DefaultMutableTreeNode top = new DefaultMutableTreeNode("知网义原层次关系树");
createNodes(sememes, top, "root");
return top;
}
public SememeTreeUI() {
this.setTitle("义原树演示程序");
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.getContentPane().add(createPanel());
this.pack();
setExtendedState(MAXIMIZED_BOTH);
}
public static void main(String[] args) {
new SememeTreeUI().setVisible(true);
}
}