jsonbrowse.JsonBrowse.java Source code

Java tutorial

Introduction

Here is the source code for jsonbrowse.JsonBrowse.java

Source

/*
 * 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
}