com.doitnext.swing.widgets.json.JSONEditPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.doitnext.swing.widgets.json.JSONEditPanel.java

Source

package com.doitnext.swing.widgets.json;

import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Enumeration;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonParser;

/**
 * Implements the embeddable swing widget for editing JSON.
 * 
 * This class is not thread safe.
 * 
 * @author Stephen Owens
 *
 *
 * <p>
 * Copyright 2011 Stephen P. Owens : steve@doitnext.com
 * </p>
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * </p>
 * <p>
 *   http://www.apache.org/licenses/LICENSE-2.0
 * </p>
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * </p>
 */
public class JSONEditPanel extends JPanel {
    /**
     * Using default serial version id.
     */
    private static final long serialVersionUID = 1L;

    // Default visibility to let classes in this module access this element directly.
    JTree jTree;

    public enum UpdateType {
        REPLACE, INSERT, APPEND, AS_CHILD
    };

    public enum AllowedOps {
        REPLACE, INSERT, APPEND, AS_CHILD, DELETE, RENAME, GET_JSON
    };

    /**
     * Default constructor for the JSONEditPanel object.
     * 
     * Creates an empty tree.
     */
    public JSONEditPanel() {
        setLayout(new BorderLayout());
        JSONJTreeNode root = new JSONJTreeNode(null, -1, new JsonNull());
        jTree = new JTree(root);
        jTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        add(new JScrollPane(jTree), BorderLayout.CENTER);
    }

    /**
     * Allow the owner of the component to listen for tree selection events.
     * 
     * @param tsl A handler for tree selection events
     */
    public void addTreeSelectionListener(TreeSelectionListener tsl) {
        jTree.addTreeSelectionListener(tsl);
    }

    /**
     * Replaces the current tree structure in the contained JTree component
     * with a new structure built from the JSON string provided.
     * 
     * @param json - the JSON to update the tree control with
     * @param updateType - if a node is selected when this method is called
     *    then the updateType indicates where the new json goes:
     *    REPLACE - replace current selected node with new JSON
     *    INSERT - place node before selected node
     *    APPEND - place node after selected node
     *    AS_CHILD - append to end of child nodes or insert new child node if no
     *            children present.  Selected node must be of type ARRAY or OBJECT
     */
    @SuppressWarnings("unchecked")
    public void setJson(String json, UpdateType updateType) {
        TreePath selection = jTree.getSelectionPath();
        if (selection == null) {
            if (updateType == UpdateType.REPLACE) {
                JsonElement root = new JsonParser().parse(json);
                JSONJTreeNode rootNode = new JSONJTreeNode(null, -1, root);
                jTree.setModel(new DefaultTreeModel(rootNode));
            } else {
                JOptionPane.showMessageDialog(this,
                        "Only replace JSON and get JSON are supported when no node is selected.", "Notice",
                        JOptionPane.INFORMATION_MESSAGE);
            }
        } else {
            JSONJTreeNode selectedNode = (JSONJTreeNode) selection.getLastPathComponent();
            JSONJTreeNode parent = (JSONJTreeNode) selectedNode.getParent();
            switch (updateType) {
            case REPLACE: {
                if (parent == null) {
                    JsonElement root = new JsonParser().parse(json);
                    JSONJTreeNode rootNode = new JSONJTreeNode(null, -1, root);
                    jTree.setModel(new DefaultTreeModel(rootNode));
                    return;
                }
                JsonElement root = new JsonParser().parse(json);
                JSONJTreeNode replacementNode = new JSONJTreeNode(selectedNode.fieldName, selectedNode.index, root);
                int index = selectedNode.getParent().getIndex(selectedNode);
                selectedNode.removeFromParent();
                parent.insert(replacementNode, index);
                ((DefaultTreeModel) jTree.getModel()).reload(parent);
            }
                break;
            case INSERT:
            case APPEND: {
                if (parent == null) {
                    JOptionPane.showMessageDialog(this, "You cannot append to the root element.", "Notice",
                            JOptionPane.INFORMATION_MESSAGE);
                    return;
                }
                JsonElement root = new JsonParser().parse(json);
                JSONJTreeNode replacementNode = new JSONJTreeNode(selectedNode.fieldName, selectedNode.index, root);
                int index = selectedNode.getParent().getIndex(selectedNode);
                if (updateType.equals(UpdateType.APPEND))
                    index++;
                parent.insert(replacementNode, index);
                ((DefaultTreeModel) jTree.getModel()).reload(parent);
            }
                break;
            case AS_CHILD: {
                JsonElement root = new JsonParser().parse(json);
                String fieldName = null;
                int arrayIndex = -1;
                if (selectedNode.dataType.equals(JSONJTreeNode.DataType.ARRAY)) {
                    Enumeration en = selectedNode.children();
                    int count = 0;
                    while (en.hasMoreElements()) {
                        en.nextElement();
                        count++;
                    }
                    arrayIndex = count;
                } else if (selectedNode.dataType.equals(JSONJTreeNode.DataType.OBJECT))
                    fieldName = "new-field";
                else {
                    JOptionPane.showMessageDialog(this, "Vaue type entities can not have children.", "Notice",
                            JOptionPane.INFORMATION_MESSAGE);
                    return;
                }
                JSONJTreeNode newNode = new JSONJTreeNode(fieldName, arrayIndex, root);
                selectedNode.add(newNode);
                ((DefaultTreeModel) jTree.getModel()).reload(selectedNode);
            }
                break;
            }
        }
    }

    /**
     * Renames the selected node if it is a renameable node.
     */
    public void renameNode() {
        TreePath selection = jTree.getSelectionPath();
        if (selection == null) {
            JOptionPane.showMessageDialog(this, "No node is selected for rename.", "Notice",
                    JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        JSONJTreeNode node = (JSONJTreeNode) selection.getLastPathComponent();
        JSONJTreeNode parent = (JSONJTreeNode) node.getParent();
        if (parent == null) {
            JOptionPane.showMessageDialog(this, "It is not possible to assign a name to the root node.", "Notice",
                    JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        if (!parent.dataType.equals(JSONJTreeNode.DataType.OBJECT)) {
            JOptionPane.showMessageDialog(this, "Only object fields may be renamed.", "Notice",
                    JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        String newName = JOptionPane.showInputDialog("Enter new name for " + node.fieldName);
        node.fieldName = newName;
        ((DefaultTreeModel) jTree.getModel()).nodeChanged(node);
    }

    /**
     * Deletes selected node or sets entire model to null if root node or no node is selected.
     */
    public void deleteNode() {
        TreePath selection = jTree.getSelectionPath();
        if (selection == null) {
            // Replace root with emptyness
            jTree.setModel(new DefaultTreeModel(new JSONJTreeNode(null, -1, new JsonNull())));
        } else {
            JSONJTreeNode node = (JSONJTreeNode) selection.getLastPathComponent();
            JSONJTreeNode parent = (JSONJTreeNode) node.getParent();
            if (parent == null) {
                // Replace root with emptyness
                jTree.setModel(new DefaultTreeModel(new JSONJTreeNode(null, -1, new JsonNull())));
            } else {
                node.removeFromParent();
                ((DefaultTreeModel) jTree.getModel()).reload(parent);
            }
        }
    }

    /**
     * Returns the current JSON root from the JTree
     * 
     * @return the JSON as it is represented by the current state of the Tree model
     */
    public String getRootJson() {
        ((DefaultTreeModel) jTree.getModel()).reload();
        JSONJTreeNode node = (JSONJTreeNode) jTree.getModel().getRoot();
        if (node != null)
            return node.asJsonElement().toString();
        else
            return null;
    }

    /**
     * Returns the current JSON from the JTree
     * 
     * @return the JSON as it is represented by the current state of the Tree model
     */
    public String getJson() {
        TreePath selection = jTree.getSelectionPath();
        JSONJTreeNode node = null;
        if (selection == null) {
            return getRootJson();
        } else {
            ((DefaultTreeModel) jTree.getModel()).reload(node);
            node = (JSONJTreeNode) selection.getLastPathComponent();
        }
        if (node != null)
            return node.asJsonElement().toString();
        else
            return null;
    }

    /**
     * Determine what can currently be asked of the component in terms of tree modifiation operations.
     * 
     * @return a list of AllowedOps enumerations to indicate the commands that are currently allowed upon the Component
     */
    public List<AllowedOps> getAllowedOperations() {
        List<AllowedOps> result = new ArrayList<AllowedOps>();
        result.add(AllowedOps.REPLACE);
        result.add(AllowedOps.GET_JSON);

        TreePath selection = jTree.getSelectionPath();
        if (selection == null)
            return result;

        JSONJTreeNode selectedNode = (JSONJTreeNode) selection.getLastPathComponent();
        JSONJTreeNode parentNode = null;

        if (selectedNode != null) {
            result.add(AllowedOps.DELETE);
            parentNode = (JSONJTreeNode) selectedNode.getParent();
        }
        if (parentNode != null) {
            result.add(AllowedOps.APPEND);
            result.add(AllowedOps.INSERT);
        }
        if (selectedNode.dataType.equals(JSONJTreeNode.DataType.ARRAY)
                || selectedNode.dataType.equals(JSONJTreeNode.DataType.OBJECT))
            result.add(AllowedOps.AS_CHILD);
        if ((parentNode != null) && (parentNode.dataType.equals(JSONJTreeNode.DataType.OBJECT)))
            result.add(AllowedOps.RENAME);
        return result;
    }
}