/* * 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 2 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * JSONObject.java * Copyright (C) 2009 University of Waikato, Hamilton, New Zealand */ package weka.core.json; import java.awt.BorderLayout; import java.io.Reader; import java_cup.runtime.DefaultSymbolFactory; import java_cup.runtime.SymbolFactory; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; /** * Container class for storing a <a href="http://www.json.org/" target="_blank">JSON</a> * data structure. * * @author FracPete (fracpete at waikato dot ac dot nz) * @version $Revision: 5786 $ */ public class JSONNode extends DefaultMutableTreeNode { /** for serialization. */ private static final long serialVersionUID = -3047440914507883491L; /** * The type of a node. * * @author FracPete (fracpete at waikato dot ac dot nz) * @version $Revision: 5786 $ */ public static enum NodeType { /** a primitive. */ PRIMITIVE, /** an object with nested key-value pairs. */ OBJECT, /** an array. */ ARRAY } /** the name of the node. */ protected String m_Name; /** the value of the node. */ protected Object m_Value; /** the type of the node. */ protected NodeType m_NodeType; /** * Initializes the root container. */ public JSONNode() { this(null, NodeType.OBJECT); } /** * Initializes the primitive container. * * @param name the name * @param value the primitive value */ public JSONNode(String name, Boolean value) { this(name, value, NodeType.PRIMITIVE); } /** * Initializes the primitive container. * * @param name the name * @param value the primitive value */ public JSONNode(String name, Integer value) { this(name, value, NodeType.PRIMITIVE); } /** * Initializes the primitive container. * * @param name the name * @param value the primitive value */ public JSONNode(String name, Double value) { this(name, value, NodeType.PRIMITIVE); } /** * Initializes the primitive container. * * @param name the name * @param value the primitive value */ public JSONNode(String name, String value) { this(name, value, NodeType.PRIMITIVE); } /** * Initializes the object container with null value. * * @param name the name * @param type the node type */ protected JSONNode(String name, NodeType type) { this(name, null, type); } /** * Initializes the container. * * @param name the name * @param value the primitive value * @param type the type of the node, null for primitives */ protected JSONNode(String name, Object value, NodeType type) { super(); m_Name = name; m_Value = value; m_NodeType = type; } /** * Checks whether the node is anonymous. * * @return true if no name available */ public boolean isAnonymous() { return (m_Name == null); } /** * Returns the name of the node. * * @return the name, null for anonymous nodes */ public String getName() { return m_Name; } /** * Returns the stored value. * * @return the stored value, can be null */ public Object getValue() { return getValue(null); } /** * Returns the stored value. * * @param defValue the default value, if value is null * @return the stored value, can be null */ public Object getValue(Object defValue) { if (m_Value == null) return defValue; else return m_Value; } /** * Returns whether the node stores a primitive value or a an array/object. * * @return true if a primitive, false in case of an array/object */ public boolean isPrimitive() { return (m_NodeType == NodeType.PRIMITIVE); } /** * Returns wether the node is an array. * * @return true if the node is array container */ public boolean isArray() { return (m_NodeType == NodeType.ARRAY); } /** * Returns wether the node is an object. * * @return true if the node is object container */ public boolean isObject() { return (m_NodeType == NodeType.OBJECT); } /** * Returns the type of the container. * * @return the type */ public NodeType getNodeType() { return m_NodeType; } /** * Adds a "null" child to the object. * * @param name the name of the null value * @return the new node, or null if none added */ public JSONNode addNull(String name) { return add(name, null, NodeType.PRIMITIVE); } /** * Adds a key-value child to the object. * * @param name the name of the pair * @param value the value * @return the new node, or null if none added */ public JSONNode addPrimitive(String name, Boolean value) { return add(name, value, NodeType.PRIMITIVE); } /** * Adds a key-value child to the object. * * @param name the name of the pair * @param value the value * @return the new node, or null if none added */ public JSONNode addPrimitive(String name, Integer value) { return add(name, value, NodeType.PRIMITIVE); } /** * Adds a key-value child to the object. * * @param name the name of the pair * @param value the value * @return the new node, or null if none added */ public JSONNode addPrimitive(String name, Double value) { return add(name, value, NodeType.PRIMITIVE); } /** * Adds a key-value child to the object. * * @param name the name of the pair * @param value the value * @return the new node, or null if none added */ public JSONNode addPrimitive(String name, String value) { return add(name, value, NodeType.PRIMITIVE); } /** * Adds an array child to the object. * * @param name the name of the pair * @return the new node, or null if none added */ public JSONNode addArray(String name) { return add(name, null, NodeType.ARRAY); } /** * Adds an array element child to the array. * * @param value the value of the element array * @return the new node, or null if none added */ public JSONNode addArrayElement(Object value) { NodeType type; if (getNodeType() != NodeType.ARRAY) return null; type = null; if (value != null) { if (value instanceof Boolean) type = NodeType.PRIMITIVE; else if (value instanceof Integer) type = NodeType.PRIMITIVE; else if (value instanceof Double) type = NodeType.PRIMITIVE; else if (value instanceof String) type = NodeType.PRIMITIVE; else if (value.getClass().isArray()) type = NodeType.ARRAY; else type = NodeType.OBJECT; } return add(null, value, type); } /** * Adds an object child to the object. * * @param name the name of the pair * @return the new node, or null if none added */ public JSONNode addObject(String name) { return add(name, null, NodeType.OBJECT); } /** * Adds a key-value child to the object. * * @param name the name of the pair * @param value the value * @param type the node type, null for primitives * @return the new node, or null if none added */ protected JSONNode add(String name, Object value, NodeType type) { JSONNode child; if (isPrimitive()) return null; child = new JSONNode(name, value, type); add(child); return child; } /** * Checks whether the node has a child with the given name. * * @param name the name of the child * @return true if child with that name is available */ public boolean hasChild(String name) { return (getChild(name) != null); } /** * Returns the child with the given name. * * @param name the name of the child * @return the child if available, null otherwise */ public JSONNode getChild(String name) { JSONNode result; JSONNode node; int i; result = null; for (i = 0; i < getChildCount(); i++) { node = (JSONNode) getChildAt(i); if (!node.isAnonymous() && node.getName().equals(name)) { result = node; break; } } return result; } /** * Generates the indentation string. * * @param level the level * @return the indentation string (tabs) */ protected String getIndentation(int level) { StringBuffer result; int i; result = new StringBuffer(); for (i = 0; i < level; i++) result.append("\t"); return result.toString(); } /** * Escapes ", \, /, \b, \f, \n, \r, \t in strings. * * @param o the object to process (only strings get processed) * @return the processed object */ protected Object escape(Object o) { if (o instanceof String) return escape((String) o); else return o; } /** * Escapes ", /, \b, \f, \n, \r, \t. * * @param s the string to process * @return the processed */ protected String escape(String s) { StringBuffer result; int i; char c; if ( (s.indexOf('\"') > -1) || (s.indexOf('\\') > -1) || (s.indexOf('\b') > -1) || (s.indexOf('\f') > -1) || (s.indexOf('\n') > -1) || (s.indexOf('\r') > -1) || (s.indexOf('\t') > -1) ) { result = new StringBuffer(); for (i = 0; i < s.length(); i++) { c = s.charAt(i); if (c == '\"') result.append("\\\""); else if (c == '\\') result.append("\\\\"); else if (c == '\b') result.append("\\b"); else if (c == '\f') result.append("\\f"); else if (c == '\n') result.append("\\n"); else if (c == '\r') result.append("\\r"); else if (c == '\t') result.append("\\t"); else result.append(c); } } else { result = new StringBuffer(s); } return result.toString(); } /** * Dumps the node structure into JSON format. * * @param buffer the buffer to add the data to */ public void toString(StringBuffer buffer) { int level; boolean isLast; String indent; int i; level = getLevel(); isLast = (getNextSibling() == null); indent = getIndentation(level); buffer.append(indent); if (m_Name != null) { buffer.append("\""); buffer.append(escape(m_Name)); buffer.append("\" : "); } if (isObject()) { buffer.append("{\n"); for (i = 0; i < getChildCount(); i++) ((JSONNode) getChildAt(i)).toString(buffer); buffer.append(indent); buffer.append("}"); } else if (isArray()) { buffer.append("[\n"); for (i = 0; i < getChildCount(); i++) ((JSONNode) getChildAt(i)).toString(buffer); buffer.append(indent); buffer.append("]"); } else { if (m_Value == null) { buffer.append("null"); } else if (m_Value instanceof String) { buffer.append("\""); buffer.append(escape((String) m_Value)); buffer.append("\""); } else { buffer.append(m_Value.toString()); } } if (!isLast) buffer.append(","); buffer.append("\n"); } /** * Returns a string representation of the node. * * @return the string representation */ public String toString() { String result; result = null; if (isObject()) { if (isRoot()) result = "JSON"; else if (m_Name == null) result = "<object>"; else result = escape(m_Name) + " (Object)"; } else if (isArray()) { if (m_Name == null) result = "<array>"; else result = escape(m_Name) + " (Array)"; } else { if (m_Name != null) result = escape(m_Name) + ": " + escape(m_Value); else result = "" + m_Value; } return result; } /** * Reads the JSON object from the given reader. * * @param reader the reader to read the JSON object from * @return the generated JSON object * @throws Exception if parsing fails */ public static JSONNode read(Reader reader) throws Exception { SymbolFactory sf; Parser parser; sf = new DefaultSymbolFactory(); parser = new Parser(new Scanner(reader, sf), sf); parser.parse(); return parser.getResult(); } /** * Only for testing. Generates a simple JSON object and displays it. * * @param args ignored * @throws Exception if something goes wrong */ public static void main(String[] args) throws Exception { // generates the example listed here: // http://en.wikipedia.org/wiki/JSON JSONNode person = new JSONNode(); person.addPrimitive("firstName", "John"); person.addPrimitive("lastName", "Smith"); JSONNode address = person.addObject("address"); address.addPrimitive("streetAddress", "21 2nd Street"); address.addPrimitive("city", "New York"); address.addPrimitive("state", "NY"); address.addPrimitive("postalCode", 10021); JSONNode phonenumbers = person.addArray("phoneNumbers"); phonenumbers.addArrayElement("212 555-1234"); phonenumbers.addArrayElement("646 555-4567"); // output in console StringBuffer buffer = new StringBuffer(); person.toString(buffer); System.out.println(buffer.toString()); // display GUI JTree tree = new JTree(person); JFrame frame = new JFrame("JSON"); frame.setSize(800, 600); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER); frame.setLocationRelativeTo(null); frame.setVisible(true); } }