Java tutorial
/* * Copyright (C) 2014 Tim Vaughan <tgvaughan@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package jsonbrowse; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.Iterator; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JFileChooser; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; /** * @author Tim Vaughan <tgvaughan@gmail.com> */ public class JsonBrowse extends javax.swing.JFrame implements Runnable { private WatchService watcher; private Thread watchThread; private boolean watching; private final Path jsonFilePath; /** * Creates new form JsonBrowseApp * * @param jsonFilePath * @throws java.io.IOException * @throws java.lang.InterruptedException */ public JsonBrowse(Path jsonFilePath) throws IOException, InterruptedException { initComponents(); this.jsonFilePath = jsonFilePath; updateModel(); watching = true; startWatchThread(); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jScrollPane = new javax.swing.JScrollPane(); jTree = new javax.swing.JTree(); jButtonQuit = new javax.swing.JButton(); jWatchForChanges = new javax.swing.JCheckBox(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); jScrollPane.setViewportView(jTree); jButtonQuit.setText("Quit"); jButtonQuit.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButtonQuitActionPerformed(evt); } }); jWatchForChanges.setSelected(true); jWatchForChanges.setText("Watch file for changes"); jWatchForChanges.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jWatchForChangesActionPerformed(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup().addComponent(jWatchForChanges) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jButtonQuit))); layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 269, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jButtonQuit).addComponent(jWatchForChanges)))); pack(); }// </editor-fold>//GEN-END:initComponents private void jButtonQuitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonQuitActionPerformed setVisible(false); System.exit(0); }//GEN-LAST:event_jButtonQuitActionPerformed private void jWatchForChangesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jWatchForChangesActionPerformed // Toggle file change watching watching = !watching; }//GEN-LAST:event_jWatchForChangesActionPerformed /** * Update tree to reflect current JSON data in file. * * @param absoluteFileName *absolute* path of file. * @throws FileNotFoundException * @throws IOException */ private void updateModel() throws FileNotFoundException, IOException { ObjectMapper mapper = new ObjectMapper(); JsonNode rootJsonNode = mapper.readTree(new FileInputStream(jsonFilePath.toString())); // Construct tree model DefaultMutableTreeNode rootNode = buildTree("root", rootJsonNode); jTree.setModel(new DefaultTreeModel(rootNode)); } /** * Builds a tree of TreeNode objects using the tree under the * given JsonNode. * * @param name Text to be associated with node * @param node * @return root TreeNode */ private DefaultMutableTreeNode buildTree(String name, JsonNode node) { DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(name); Iterator<Entry<String, JsonNode>> it = node.fields(); while (it.hasNext()) { Entry<String, JsonNode> entry = it.next(); treeNode.add(buildTree(entry.getKey(), entry.getValue())); } if (node.isArray()) { for (int i = 0; i < node.size(); i++) { JsonNode child = node.get(i); if (child.isValueNode()) treeNode.add(new DefaultMutableTreeNode(child.asText())); else treeNode.add(buildTree(String.format("[%d]", i), child)); } } else if (node.isValueNode()) { treeNode.add(new DefaultMutableTreeNode(node.asText())); } return treeNode; } private void startWatchThread() throws IOException { watcher = jsonFilePath.getFileSystem().newWatchService(); watchThread = new Thread(this, "FileWatcher"); watchThread.start(); jsonFilePath.getParent().register(watcher, StandardWatchEventKinds.ENTRY_MODIFY); } @Override public void run() { try { WatchKey key = watcher.take(); while (key != null) { if (watching) { for (WatchEvent event : key.pollEvents()) { if (event.context() instanceof Path) { Path path = (Path) (event.context()); if (path.getFileName().equals(jsonFilePath.getFileName())) { if (path.toFile().length() > 0) updateModel(); } } } } key.reset(); key = watcher.take(); } } catch (InterruptedException | IOException ex) { Logger.getLogger(JsonBrowse.class.getName()).log(Level.SEVERE, null, ex); } System.out.println("Stopping thread."); } /** * @param args the command line arguments */ public static void main(String args[]) { /* Set the Nimbus look and feel */ //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html */ try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(JsonBrowse.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } //</editor-fold> String fileName; // Get name of JSON file Path jsonFilePath; if (args.length < 1) { JFileChooser fc = new JFileChooser(System.getProperty("user.dir")); fc.setDialogTitle("Select JSON file..."); fc.setFileFilter(new FileNameExtensionFilter("JSON files (*.json/*.txt)", "json", "txt")); if ((fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)) { jsonFilePath = fc.getSelectedFile().toPath().toAbsolutePath(); } else { return; } } else { Path path = Paths.get(args[0]); if (!path.isAbsolute()) jsonFilePath = Paths.get(System.getProperty("user.dir")).resolve(path); else jsonFilePath = path; } // Run app try { JsonBrowse app = new JsonBrowse(jsonFilePath); app.setVisible(true); } catch (FileNotFoundException ex) { System.out.println("Input file '" + jsonFilePath + "' not found."); System.exit(1); } catch (IOException ex) { System.out.println("Error reading from file '" + jsonFilePath + "'."); System.exit(1); } catch (InterruptedException ex) { Logger.getLogger(JsonBrowse.class.getName()).log(Level.SEVERE, null, ex); } } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButtonQuit; private javax.swing.JScrollPane jScrollPane; private javax.swing.JTree jTree; private javax.swing.JCheckBox jWatchForChanges; // End of variables declaration//GEN-END:variables }