package cryodex.modules.xwing; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.UIManager; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import cryodex.widget.FilteredTree; import cryodex.xml.XMLUtils; import cryodex.xml.XMLUtils.Element; public class XWingRules extends JPanel implements TreeSelectionListener, HyperlinkListener { private static final long serialVersionUID = 1L; private JEditorPane htmlPane; private JEditorPane linkPane; private FilteredTree tree; private DefaultMutableTreeNode root; private HashMap<Integer, DefaultMutableTreeNode> idToArticle = new HashMap<Integer, DefaultMutableTreeNode>(); // Optionally play with line styles. Possible values are // "Angled" (the default), "Horizontal", and "None". private static boolean playWithLineStyle = true; private static String lineStyle = "None"; // Optionally set the look and feel. private static boolean useSystemLookAndFeel = false; public XWingRules() { super(new GridLayout(1, 0)); loadRules(); // Create a tree that allows one selection at a time. tree = new FilteredTree(root) { private static final long serialVersionUID = 1L; @Override protected boolean matches(DefaultMutableTreeNode node, String textToMatch) { Object nodeInfo = node.getUserObject(); textToMatch = textToMatch.toUpperCase(); if (nodeInfo instanceof ArticleNode) { ArticleNode article = (ArticleNode) nodeInfo; if (article.getName().toUpperCase().contains(textToMatch)) { return true; } if (article.getData().toUpperCase().contains(textToMatch)) { return true; } } return false; } }; // Listen for when the selection changes. tree.getTree().addTreeSelectionListener(this); if (playWithLineStyle) { tree.putClientProperty("JTree.lineStyle", lineStyle); } // Create the scroll pane and add the tree to it. JScrollPane treeView = new JScrollPane(tree); // Create the HTML viewing pane. htmlPane = new JEditorPane("text/html", ""); htmlPane.setEditable(false); htmlPane.addHyperlinkListener(this); linkPane = new JEditorPane("text/html", ""); linkPane.setEditable(false); linkPane.addHyperlinkListener(this); JPanel contentPane = new JPanel(new BorderLayout()); contentPane.add(htmlPane, BorderLayout.CENTER); contentPane.add(linkPane, BorderLayout.SOUTH); JScrollPane htmlView = new JScrollPane(contentPane); // Add the scroll panes to a split pane. JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitPane.setTopComponent(treeView); splitPane.setBottomComponent(htmlView); Dimension minimumSize = new Dimension(200, 50); htmlView.setMinimumSize(minimumSize); treeView.setMinimumSize(minimumSize); splitPane.setDividerLocation(200); splitPane.setPreferredSize(new Dimension(700, 400)); // Add the split pane to this panel. add(splitPane); } /** Required by TreeSelectionListener interface. */ public void valueChanged(TreeSelectionEvent e) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath() .getLastPathComponent(); if (node == null) { return; } Object nodeInfo = node.getUserObject(); if (nodeInfo instanceof ArticleNode) { ArticleNode article = (ArticleNode) nodeInfo; displayData(article); } else { displayData(null); } } private void displayData(ArticleNode node) { if (node == null) { htmlPane.setText(""); linkPane.setText(""); } else { htmlPane.setText(node.getData()); linkPane.setText(node.getLink()); } } @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) { return; } DefaultMutableTreeNode node = idToArticle.get(Integer.valueOf(e .getDescription())); if (node != null) { ArticleNode articleNode = (ArticleNode) node.getUserObject(); displayData(articleNode); } TreePath path = new TreePath(node.getPath()); tree.getTree().setSelectionPath(path); tree.getTree().scrollPathToVisible(path); } private void loadRules() { try { InputStream stream = XWingRules.class .getResourceAsStream("XWING.rules"); if (stream == null) { return; } BufferedReader reader = new BufferedReader(new InputStreamReader( stream, "UTF-8")); Element mainElement = XMLUtils.getItem(reader); if (mainElement == null) { return; } List<Element> articles = mainElement.getChildren(); List<ArticleNode> articleNodes = new ArrayList<ArticleNode>(); for (Element e : articles) { ArticleNode node = new ArticleNode(e); articleNodes.add(node); idToArticle.put(node.getId(), new DefaultMutableTreeNode(node)); } // /////////// // Build Tree // /////////// root = new DefaultMutableTreeNode("X-Wing"); for (DefaultMutableTreeNode node : idToArticle.values()) { ArticleNode an = (ArticleNode) node.getUserObject(); if (an.getParent() == 0) { root.add(node); } else { idToArticle.get(an.getParent()).add(node); } } // ////////// // Fix links // ////////// for (DefaultMutableTreeNode node : idToArticle.values()) { ArticleNode an = (ArticleNode) node.getUserObject(); String data = an.getData(); while (data.contains("#")) { int index1 = data.indexOf('#'); int index2 = index1; while (index2 < data.length()) { index2++; if (data.length() == index2 || data.charAt(index2) < '0' || data.charAt(index2) > '9') { break; } } String linkValue = data.substring(index1 + 1, index2); int linkInt = Integer.valueOf(linkValue); ArticleNode toNode = (ArticleNode) idToArticle.get(linkInt) .getUserObject(); String linkString = "<a href=\"" + toNode.getId() + "\">" + toNode.getName() + "</a>"; data = data.substring(0, index1) + linkString + data.substring(index2, data.length()); } an.setData(data); String link = an.getLink(); while (link.contains("#")) { int index1 = link.indexOf('#'); int index2 = index1; while (index2 < link.length()) { index2++; if (link.length() == index2 || link.charAt(index2) < '0' || link.charAt(index2) > '9') { break; } } String linkValue = link.substring(index1 + 1, index2); int linkInt = Integer.valueOf(linkValue); ArticleNode toNode = (ArticleNode) idToArticle.get(linkInt) .getUserObject(); String linkString = "<a href=\"" + toNode.getId() + "\">" + toNode.getName() + "</a>"; link = link.substring(0, index1) + linkString + link.substring(index2, link.length()); } an.setLink(link); } reader.close(); } catch (Exception e) { e.printStackTrace(); } } private class ArticleNode { public int id; public String name; public String data; public int parent; public String link; public ArticleNode(Element e) { name = e.getChild("name").getData(); id = Integer.valueOf(e.getChild("id").getData()); data = "<html>" + e.getChild("data").getData() + "</html>"; parent = Integer.valueOf(e.getChild("parent").getData()); link = "<html>" + e.getChild("related").getData() + "</html>"; } public int getId() { return id; } public String getName() { return name; } public String getData() { return data; } public void setData(String data) { this.data = data; } @Override public String toString() { return getName(); } public int getParent() { return parent; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } } /** * Create the GUI and show it. For thread safety, this method should be * invoked from the event dispatch thread. */ private static void createAndShowGUI() { if (useSystemLookAndFeel) { try { UIManager.setLookAndFeel(UIManager .getSystemLookAndFeelClassName()); } catch (Exception e) { System.err.println("Couldn't use system look and feel."); } } // Create and set up the window. JFrame frame = new JFrame("X-Wing Rules"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Add content to the window. frame.add(new XWingRules()); // Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } }