Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright (C) 2014 Dell, Inc.
    * 
    * 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
    * 
    *     http://www.apache.org/licenses/LICENSE-2.0
    * 
   * 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.
   */
  
  package com.dell.doradus.common;
  
  import java.io.File;
  import java.io.Reader;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.w3c.dom.Attr;
  import org.w3c.dom.Node;
Represent a "Universal Node", which can be parsed from or generated to JSON or XML. Note that DOM trees parsed from JSON or XML are intended to be consumed by POJOs that expected a UNode tree, and UNode trees generated by POJOs can be used to directly generate JSON or XML. However, going from JSON to a UNode tree to XML is not intended since there are semantics about XML that are not known when parsing JSON.

This class uses manufacturing methods to create objects. Except for adding child nodes, objects are immutable once created.

  
  final public class UNode {
      // The supported UNode types. 
      private enum NodeType {
          ARRAY,
          MAP,
          VALUE
      }   // enum NodeType
      
      // Members:
      private final String      m_name;
      private final NodeType    m_type;
      private final String      m_value;
      private       List<UNodem_children;
      private       UNode       m_parent;
      
      // Hints for special XML/JSON handling
      private final boolean     m_bAttribute;
      private final String      m_tagName;
      private       boolean     m_bAltFormat;
      
      // Child node names also live in this map only when this UNode is a MAP.
      private Map<StringUNodem_childNodeMap;
  
      // Private constructor: All objects are created via manufacturers
      private UNode(String nameNodeType typeString valueboolean bAttributeString tagName) {
          // Name and type are required. Tag name can be empty but not null.
          assert name != null && name.length() > 0;
          assert type != null;
          assert tagName != null;
          
           = name;
           = type;
           = value;
           = bAttribute;
           = tagName;
      }   // constructor
      
      // Listener passed to JSONAnnie.parse() methods. Builds the UNode tree we want from a
      // JSON document.
      private static class SajListener implements JSONAnnie.SajListener {
          private static final int MAX_STACK_SIZE = 32;
          
          private UNode[] m_stack = new UNode[];
          private int     m_stackInx = 0;
          private UNode   m_rootNode;
          
          private void push(UNode node) {
              assert  < ;
              if ( == 0) {
                  = node;
             } else {
                 [ - 1].addChildNode(node);
             }
             [++] = node;
         }   // push
         
         private UNode pop() {
             assert  > 0;
             UNode node = [--];
             [ + 1] = null;
             return node;
         }   // pop
 
         private UNode getRootNode() {
             if ( == null) {
                 // JSON text was empty, etc.
                  = UNode.createMapNode("_unnamed");
             }
             return ;
         }   // getRootNode
 
         private UNode tos() {
             assert  > 0;
             return [ - 1];
         }   // tos
         
         @Override
         public void onStartObject(String name) {
             push(UNode.createMapNode(name));
         }   // onStartObject
 
         @Override
         public void onEndObject() {
             pop();
         }   // onEndObject
 
         @Override
         public void onStartArray(String name) {
             push(UNode.createArrayNode(name));
         }   // onStartArray
 
         @Override
         public void onEndArray() {
             pop();
         }   // onEndArray
 
         @Override
         public void onValue(String nameString value) {
             if ( == 0) {
                 // Outer object is a simple value
                  = UNode.createValueNode(namevalue);
             } else {
                 tos().addValueNode(namevalue);
             }
         }   // onValue
         
     }   // static class SajListener
     
     ////////// static Manufacturers
     
    
Create a MAP UNode with the given node name.

Parameters:
name Name for new MAP node.
 
     public static UNode createMapNode(String name) {
         return new UNode(name.nullfalse"");
     }   // createMapNode
     
    
Create a MAP UNode with the given node name and tag name.

Parameters:
name Name for new MAP node.
tagName Tag name.
 
     public static UNode createMapNode(String nameString tagName) {
         return new UNode(name.nullfalsetagName);
     }   // createMapNode
     
    
Create an ARRAY UNode with the given node name.

Parameters:
name Name for new ARRAY node.
 
     public static UNode createArrayNode(String name) {
         return new UNode(name.nullfalse"");
     }   // createArrayNode
     
    
Create an ARRAY UNode with the given node name and tag name.

Parameters:
name Name for new ARRAY node.
tagName Tag name.
 
     public static UNode createArrayNode(String nameString tagName) {
         return new UNode(name.nullfalsetagName);
     }   // createArrayNode
     
    
Create a VALUE UNode with the given node name and value.

Parameters:
name Node name.
value Node value.
 
     public static UNode createValueNode(String nameString value) {
         String nodeValue = value == null ? "" : value;
         return new UNode(name.nodeValuefalse"");
     }   // createValueNode
     
    
Create a VALUE UNode with the given node name and value, and optionally mark the node as an "attribute".

Parameters:
name Node name.
value Node value.
bAttribute True to mark the node as an attribute.
 
     public static UNode createValueNode(String nameString valueboolean bAttribute) {
         String nodeValue = value == null ? "" : value;
         return new UNode(name.nodeValuebAttribute"");
     }   // createValueNode
     
    
Create a VALUE UNode with the given node name, value, and tag name.

Parameters:
name Node name.
value Node value.
tagName Tag name.
 
     public static UNode createValueNode(String nameString valueString tagName) {
         String nodeValue = value == null ? "" : value;
         return new UNode(name.nodeValuefalsetagName);
     }   // createValueNode
     
     ////////// public static methods
     
    
Parse the given text, formatted with the given content-type, into a UNode tree and return the root node.

Parameters:
text Text to be parsed.
contentType ContentType of text. Only JSON and XML are supported.
Returns:
Root node of parsed node tree.
Throws:
java.lang.IllegalArgumentException If a parsing error occurs.
 
     public static UNode parse(String textContentType contentTypethrows IllegalArgumentException {
         UNode result = null;
         if (contentType.isJSON()) {
             result = parseJSON(text);
         } else if (contentType.isXML()) {
             result = parseXML(text);
         } else {
             Utils.require(false"Unsupported content-type: " + contentType);
         }
         return result;
     }   // parse 
     
    
Parse the text from the given character reader, formatted with the given content-type, into a UNode tree and return the root node. The reader is closed when finished.

Parameters:
reader Reader providing source characters.
contentType ContentType of text. Only JSON and XML are supported.
Returns:
Root node of parsed node tree.
Throws:
java.lang.IllegalArgumentException If a parsing error occurs.
 
     public static UNode parse(Reader readerContentType contentTypethrows IllegalArgumentException {
         UNode result = null;
         if (contentType.isJSON()) {
             result = parseJSON(reader);
         } else if (contentType.isXML()) {
             result = parseXML(reader);
         } else {
             Utils.require(false"Unsupported content-type: " + contentType);
         }
         return result;
     }   // parse 
     
    
Parse the text from the given file, formatted with the given content-type, into a UNode tree and return the root node. The file is read with a BufferedReader over a FileReader using the default encoding for the current system. Any I/O exception or parsing error is passed to the caller.

Parameters:
file File to read source from.
contentType ContentType of text. Only JSON and XML are supported.
Returns:
Root node of parsed node tree.
Throws:
java.lang.Exception If the file is not found, an I/O error occurs, or a parsing error occurs.
 
     public static UNode parse(File fileContentType contentTypethrows Exception {
         try (Reader reader = new BufferedReader(new FileReader(file))) {
             UNode result = null;
             if (contentType.isJSON()) {
                 result = parseJSON(reader);
             } else if (contentType.isXML()) {
                 result = parseXML(reader);
             } else {
                 Utils.require(false"Unsupported content-type: " + contentType);
             }
             return result;
         }
     }   // parse 
     
    
Parse the given JSON text and return the appropriate UNode object. The only JSON documents we allow are in the form:
      {"something": [value]}
 
This means that when we parse the JSON, we should see an object with a single member. The UNode returned is a MAP object whose name is the member name and whose elements are parsed from the [value].

Parameters:
text JSON text to parse
Returns:
UNode with type == UNode.NodeType.MAP.
Throws:
java.lang.IllegalArgumentException If the JSON text is malformed.
 
     public static UNode parseJSON(String textthrows IllegalArgumentException {
         assert text != null && text.length() > 0;
         
         SajListener listener = new SajListener();
         new JSONAnnie(text).parse(listener);
         return listener.getRootNode();
     }   // parseJSON
     
    
Parse the JSON text from the given character Reader and return the appropriate UNode object. If an error occurs reading from the reader, it is passed to the caller. The reader is closed when parsing is done. The only JSON documents we allow are in the form:
      {"something": [value]}
 
This means that when we parse the JSON, we should see an object with a single member. The UNode returned is a MAP object whose name is the member name and whose elements are parsed from the [value].

Parameters:
reader Character reader contain JSON text to parse. The reader is closed when reading is complete.
Returns:
UNode with type == UNode.NodeType.MAP.
Throws:
java.lang.IllegalArgumentException If the JSON text is malformed or an error occurs while reading from the reader.
 
     public static UNode parseJSON(Reader readerthrows IllegalArgumentException {
         assert reader != null;
         SajListener listener = new SajListener();
         try {
             new JSONAnnie(reader).parse(listener);
         } finally {
             Utils.close(reader);
         }
         return listener.getRootNode();
     }   // parseJSON
     
    
Parse the given XML text and return the appropriate UNode object. The UNode returned is a MAP whose child nodes are built from the attributes and child elements of the document's root element.

Parameters:
text XML text to be parsed.
Returns:
UNode with type == UNode.NodeType.MAP.
Throws:
java.lang.IllegalArgumentException If the XML is malformed.
 
     public static UNode parseXML(String textthrows IllegalArgumentException {
         assert text != null && text.length() > 0;
         
         // This throws if the XML is malformed.
         Element rootElem = Utils.parseXMLDocument(text);
         return parseXMLElement(rootElem);
     }   // parseXML
     
    
Parse XML from the given Reader and return the appropriate UNode object. The UNode returned is a MAP whose child nodes are built from the attributes and child elements of the document's root element.

Parameters:
reader Reader contain XML text to parse.
Returns:
UNode with type == UNode.NodeType.MAP.
Throws:
java.lang.IllegalArgumentException If the XML is malformed.
 
     public static UNode parseXML(Reader readerthrows IllegalArgumentException {
         assert reader != null;
         
         // This throws if the XML is malformed.
         Element rootElem = Utils.parseXMLDocument(reader);
         
         // Parse the root element and ensure it elligible as a map.
         UNode rootNode = parseXMLElement(rootElem);
         assert rootNode.isMap() : "Root node must be a map of unique names: " + rootNode.getName();
         return rootNode;
     }   // parseXML
     
     ////////// public member methods
     
     ///// Getters 
 
    
Get this node's name. All nodes have a name.

Returns:
This node's name.
 
     public String getName() {
         return ;
     }   // getName
     
    
Get this node's value. Only UNode.NodeType.VALUE nodes have a value.

Returns:
This node's value.
 
     public String getValue() {
         return ;
     }   // getValue
     
    
Return true if this UNode's type is UNode.NodeType.ARRAY.

Returns:
True if this UNode's type is UNode.NodeType.ARRAY.
 
     public boolean isArray() {
         return  == .;
     }   // isArray
     
    
Return true if this UNode's type is UNode.NodeType.ARRAY or UNode.NodeType.MAP.

Returns:
True if this UNode's type is UNode.NodeType.ARRAY or UNode.NodeType.MAP.
 
     public boolean isCollection() {
         return  == . ||  == .;
     }   // isCollection
     
    
Return true if this UNode's type is UNode.NodeType.MAP.

Returns:
True if this UNode's type is UNode.NodeType.MAP.
 
     public boolean isMap() {
         return  == .;
     }   // isMap
     
    
Return true if this UNode's type is UNode.NodeType.VALUE.

Returns:
True if this UNode's type is UNode.NodeType.VALUE.
 
     public boolean isValue() {
         return  == .;
     }   // isValue
 
     ///// Member access
 
    
Get the child member with the given index. The node must be a MAP or ARRAY. Child members are retained in the order they are added. If the given index is out of bounds, null is returned.

Parameters:
index Zero-relative child node index.
Returns:
The child member at the given index or null if there is no child node with the given index.
 
     public UNode getMember(int index) {
         assert isCollection();
         if ( == null || index >= .size()) {
             return null;
         }
         return .get(index);
     }   // getMember
     
    
Get the number of members (child nodes) owned by this node.

Returns:
The number of members (child nodes) owned by this node.
 
     public int getMemberCount() {
         return  == null ? 0 : .size();
     }   // getMemberCount
     
    
Get the member (child) names of this UNode, if any, as an Iterable<String> object. If this UNode is not a MAP or has no children, there will be no child names.

Returns:
An Iterable<String> object that returns this node's member names if it is a map. The result won't be null but there may not be any child members.
 
     public Iterable<StringgetMemberNames() {
         if ( == null) {
              = new LinkedHashMap<StringUNode>();
         }
         return .keySet();
     }   // getMemberNames
     
    
Get the value of the child VALUE node (member) of this UNode with the given name. If this is not a MAP, has no children, there is no child node with the given name, or the child node isn't a VALUE node, then null is returned.

Parameters:
name Candidate name of a child member node.
Returns:
Value of child VALUE UNode with the given name, if any, otherwise null.
 
     public String getMemberValue(String name) {
         if ( == null) {
             return null;
         }
         UNode childNode = .get(name);
         return childNode != null && childNode.isValue() ? childNode.getValue() : null;
     }   // getMemberValue
     
    
Get the child node (member) of this UNode with the given name. If this UNode isn't a MAP or there is no child node with the given name, null is returned. Note: the UNode returned is not copied.

Parameters:
name Candidate name of a child member node.
Returns:
Child UNode with the given name, if any, otherwise null.
 
     public UNode getMember(String name) {
         if ( == null) {
             return null;
         }
         return .get(name);
     }   // getMember
     
    
Get the list of child nodes of this collection UNode as an Iterable<UNode> object. The UNode must be a MAP or an ARRAY.

Returns:
An Iterable<UNode> object that iterates through this node's children. The result will never be null, but there might not be any child nodes.
 
     public Iterable<UNodegetMemberList() {
         assert  == . ||  == .;
         if ( == null) {
              = new ArrayList<UNode>();
         }
         return ;
     }   // getMemberList
     
    
Get this UNode's tag name, if set.

Returns:
This node's tag name, if set, otherwise null.
 
     public String getTagName() {
         return ;
     }   // getTagName
 
    
Return true if this is an ARRAY or MAP node with at least one child node.

Returns:
True if this is an ARRAY or MAP node with at least one child node.
 
     public boolean hasMembers() {
         return  != null && .size() > 0;
     }   // hasMembers
     
    
Format the DOM tree rooted at this UNode into text in the requested content-type.

Parameters:
contentType Desired text format. Only JSON and XML are supported.
Returns:
Text in the requested format.
 
     public String toString(ContentType contentType) {
         String result = null;
         if (contentType.isJSON()) {
             result = toJSON();
         } else if (contentType.isXML()) {
             result = toXML();
         } else {
             Utils.require(false"Unsupported content-type: " + contentType);
         }
         return result;
     }   // toString(ContentType)
 
    
Convert the DOM tree rooted at this UNode into a JSON document.

Returns:
JSON document for the DOM tree rooted at this UNode.
 
     public String toJSON() {
         JSONEmitter json = new JSONEmitter();
         json.startDocument();
         toJSON(json);
         json.endDocument();
         return json.toString();
     }   // toJSON
     
    
Convert the DOM tree rooted at this UNode into a JSON document. Optionally format the text with indenting to make it look pretty.

Parameters:
bPretty True to indent JSON output.
Returns:
JSON document for the DOM tree rooted at this UNode.
 
     public String toJSON(boolean bPretty) {
         int indent = bPretty ? 3 : 0;
         JSONEmitter json = new JSONEmitter(indent);
         json.startDocument();
         toJSON(json);
         json.endDocument();
         return json.toString();
     }   // toJSON
     
    
Convert the DOM tree rooted at this UNode into a JSON document compressed with GZIP.

Returns:
JSON document for the DOM tree rooted at this UNode compressed with GZIP.
Throws:
java.io.IOException If an error occurs writing to the GZIP stream.
 
     public byte[] toCompressedJSON() throws IOException {
         // Wrap a GZIPOuputStream around a ByteArrayOuputStream.
         ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
         GZIPOutputStream gzipOut = new GZIPOutputStream(bytesOut);
         
         // Wrap the GZIPOutputStream with an OutputStreamWriter that convers JSON Unicode
         // text to bytes using UTF-8.
         OutputStreamWriter writer = new OutputStreamWriter(gzipOut.);
         
         // Create a JSONEmitter that will write its output to the writer above and generate
         // the JSON output.
         JSONEmitter json = new JSONEmitter(writer);
         json.startDocument();
         toJSON(json);
         json.endDocument();
         
         // Ensure the output stream is flushed and the GZIP is finished, then the output
         // buffer is complete.
         writer.flush();
         gzipOut.finish();
         return bytesOut.toByteArray();
     }   // toCompressedJSON
     
    
Convert the DOM tree rooted at this UNode into an XML document.

Returns:
XML document for the DOM tree rooted at this UNode.
Throws:
java.lang.IllegalArgumentException If an XML construction error occurs.
 
     public String toXML() throws IllegalArgumentException {
         XMLBuilder xml = new XMLBuilder();
         xml.startDocument();
         toXML(xml);
         xml.endDocument();
         return xml.toString();
     }   // toXML
     
    
Convert the DOM tree rooted at this UNode into an XML document, optionally indenting each XML level to product a "pretty" structured output.

Parameters:
bPretty True to indent XML output.
Returns:
XML document for the DOM tree rooted at this UNode.
Throws:
java.lang.IllegalArgumentException If an XML construction error occurs.
 
     public String toXML(boolean bPrettythrows IllegalArgumentException {
         int indent = bPretty ? 3 : 0;
         XMLBuilder xml = new XMLBuilder(indent);
         xml.startDocument();
         toXML(xml);
         xml.endDocument();
         return xml.toString();
     }   // toXML
     
    
Add the XML required for this node to the given XMLBuilder.

Parameters:
xml An in-progress XMLBuilder.
Throws:
java.lang.IllegalArgumentException If an XML construction error occurs.
 
     public void toXML(XMLBuilder xmlthrows IllegalArgumentException {
         assert xml != null;
         
         // Determine what tag name to use for the generated element. 
         Map<StringStringattrMap = new LinkedHashMap<>(); 
         String elemName = ;
         if (.length() > 0) {
             // Place m_name into a "name" attribute and use m_tagName
             attrMap.put("name");
             elemName = ;
         }
         
         // Add child VALUE nodes marked as "attribute" in its own map.
         addXMLAttributes(attrMap);
         switch () {
         case :
             // Start an element with or without attributes.
             if (attrMap.size() > 0) {
                 xml.startElement(elemNameattrMap);
             } else {
                 xml.startElement(elemName);
             }
             
             // Add XML for non-attribute child nodes.
             if ( != null) {
                 for (UNode childNode : ) {
                     if (childNode.m_type != . || !childNode.m_bAttribute) {
                         childNode.toXML(xml);
                     }
                 }
             }
             xml.endElement();
             break;
         case :
             // Start an element with or without attributes.
             if (attrMap.size() > 0) {
                 xml.startElement(elemNameattrMap);
             } else {
                 xml.startElement(elemName);
             }
             
             // Add XML for non-attribute child nodes in name order.
             if ( != null) {
                 assert .size() == .size();
                 for (UNode childNode : .values()) {
                     if (childNode.m_type != . || !childNode.m_bAttribute) {
                         childNode.toXML(xml);
                     }
                 }
             }
             xml.endElement();
             break;
         case :
             // Map to a simple element.
             String value = ;
             if (Utils.containsIllegalXML(value)) {
                 value = Utils.base64FromString();
                 attrMap.put("encoding""base64");
             }
             if (attrMap.size() > 0) {
                 xml.addDataElement(elemNamevalueattrMap);
             } else {
                 xml.addDataElement(elemNamevalue);
             }
             break;
         default:
             assert false : "Unexpected NodeType: " + ;
         }
     }   // toXML
 
    
Return a diagnostic string representing this UNode in the form:
      UNode: {type=[type], name=[name], value=[value]}
 

Returns:
A diagnostic string representing this UNode.
 
     @Override
     public String toString() {
         return String.format("UNode: {type=%s, name=%s, value=%s}",
                              ( == null ? "<null>" : .toString()),
                              ( == null ? "<null>" : ),
                              ( == null ? "<null>" : )
                             );
     }   // toString
     
    
Return an indented diagnostic string of this UNode and the underlying tree. This method calls toString(), appends a newline, and the recurses to each child node, which prepends an indent and adds its node tree.

Returns:
An indented diagnostic string of this node and its UNode tree.
 
     public String toStringTree() {
         StringBuilder builder = new StringBuilder();
         toStringTree(builder, 0);
         return builder.toString();
     }   // printTree
     
     ///// Public update methods
 
    
Create a new ARRAY node with the given name and add it as a child of this node. This node must be a MAP or ARRAY. This is a convenience method that calls createArrayNode(java.lang.String) and then addChildNode(com.dell.doradus.common.UNode).

Parameters:
name Name of new ARRAY node.
Returns:
New ARRAY node, added as a child of this node.
 
     public UNode addArrayNode(String name) {
         return addChildNode(UNode.createArrayNode(name));
     }   // addArrayNode
     
    
Create a new ARRAY node with the given name and tag name and add it as a child of this node. This node must be a MAP or ARRAY. This is a convenience method that calls createArrayNode(java.lang.String,java.lang.String) and then addChildNode(com.dell.doradus.common.UNode).

Parameters:
name Name of new ARRAY node.
tagName Tag name of new ARRAY node (for XML).
Returns:
New ARRAY node, added as a child of this node.
 
     public UNode addArrayNode(String nameString tagName) {
         return addChildNode(UNode.createArrayNode(nametagName));
     }   // addArrayNode
     
    
Create a new MAP node with the given name and add it as a child of this node. This node must be a MAP or ARRAY. This is a convenience method that calls createMapNode(java.lang.String) and then addChildNode(com.dell.doradus.common.UNode).

Parameters:
name Name of new child MAP node.
Returns:
New MAP node, added as a child of this node.
 
     public UNode addMapNode(String name) {
         return addChildNode(UNode.createMapNode(name));
     }   // addMapNode
     
    
Create a new MAP node with the given name and tag name add it as a child of this node. This node must be a MAP or ARRAY. This is a convenience method that calls createMapNode(java.lang.String,java.lang.String) and then addChildNode(com.dell.doradus.common.UNode).

Parameters:
name Name of new MAP node.
tagName Tag name of new MAP node (for XML).
Returns:
New MAP node, added as a child of this node.
 
     public UNode addMapNode(String nameString tagName) {
         return addChildNode(UNode.createMapNode(nametagName));
     }   // addMapNode
     
    
Create a new VALUE node with the given name and value and add it as a child of this node. This node must be a MAP or ARRAY. This is convenience method that calls createValueNode(java.lang.String,java.lang.String) and then addChildNode(com.dell.doradus.common.UNode).

Parameters:
name Name of new VALUE node.
value Value of new VALUE node.
Returns:
New VALUE node.
 
     public UNode addValueNode(String nameString value) {
         return addChildNode(UNode.createValueNode(namevalue));
     }   // addValueNode
     
    
Create a new VALUE node with the given name, value, and attribute flag and add it as a child of this node. This node must be a MAP or ARRAY. This is convenience method that calls createValueNode(java.lang.String,java.lang.String,boolean) and then addChildNode(com.dell.doradus.common.UNode).

Parameters:
name Name of new VALUE node.
value Value of new VALUE node.
bAttribute True to mark the new VALUE node as an attribute (for XML).
Returns:
New VALUE node.
 
     public UNode addValueNode(String nameString valueboolean bAttribute) {
         return addChildNode(UNode.createValueNode(namevaluebAttribute));
     }   // addValueNode
     
    
Create a new VALUE node with the given name, value, and tag name and add it as a child of this node. This node must be a MAP or ARRAY. This is convenience method that calls createValueNode(java.lang.String,java.lang.String,java.lang.String) and then addChildNode(com.dell.doradus.common.UNode).

Parameters:
name Name of new VALUE node.
value Value of new VALUE node.
tagName Tag name of new VALUE node (for XML).
Returns:
New VALUE node.
 
     public UNode addValueNode(String nameString valueString tagName) {
         return addChildNode(UNode.createValueNode(namevaluetagName));
     }   // addValueNode
     
    
Add the given child node to this node, which must be a MAP or ARRAY. If this node is a MAP, the name of the child node must be unique among existing child nodes. The same node is returned as a result so the caller can do things like:
      UNode childNode = parentNode.addChildNode(UNode.createMapNode("foo"));
 

Parameters:
childNode Child node to add.
Returns:
Same child node object passed as a parameter.
Throws:
java.lang.IllegalArgumentException If the child node name is not unique and this is a map.
 
     public UNode addChildNode(UNode childNodethrows IllegalArgumentException {
         assert  == . ||  == .;
         assert childNode != null;
         assert childNode.m_parent == null;
         
         // Allocate on first child addition.
         if ( == null) {
              = new ArrayList<UNode>();
         }
         
         // Add to list first.
         .add(childNode);
         childNode.m_parent = this;
         
         // For MAPs, also add to the child name map and ensure the name is unique.
         if ( == .) {
             if ( == null) {
                  = new LinkedHashMap<StringUNode>();
             }
             UNode priorNode = .put(childNode.m_namechildNode);
             //assert priorNode == null : "Duplicate name ('" + childNode.m_name +
             //                           "') added to the same parent: " + m_name;
             if(priorNode != null) {
             	throw new RuntimeException("Duplicate name ('" + childNode.m_name +
                         "') added to the same parent: " + );
             }
         }
         return childNode;
     }   // addChildNode
     
    
Add all child nodes in the given collection to this node, which must be a MAP or ARRAY. This method merely iterates through the list and calls addChildNode(com.dell.doradus.common.UNode) for each one.

Parameters:
childNodes List of child UNode objects to add to this one.
 
     public void addChildNodes(Collection<UNodechildNodes) {
         for (UNode childNode : childNodes) {
             addChildNode(childNode);
         }
     }   // addChildNodes
 
    
Delete the child node of this MAP node with the given name, if it exists. This node must be a MAP. The child node name may or may not exist.

Parameters:
childName Name of child node to remove from this MAP node.
 
     public void removeMember(String childName) {
         assert isMap() : "'removeMember' allowed only for MAP nodes";
         if ( != null) {
             // Remove from child name map and then list if found.
             UNode removeNode = .remove(childName);
             if (removeNode != null) {
                 .remove(removeNode);
             }
         }
     }   // removeMember
 
    
Set the alternate-format option for this VALUE UNode. This option is currently only used for JSON formatting. The default syntax generated for a VALUE node in JSON is:
      "{name}": "{value}"
 
But if the parent node is an array and this node's name is "value", the VALUE node generates an unamed JSON value:
      "{name}"
 
However, if the VALUE node has a tag name and the alternate-format option is set, the following JSON is generarted instead:
      "{tag}: {"{name}": "{value}"}
 
This is used in cases where we want the XML to look like this:
      <field name="Tags">Customer</field>
 
But we want the JSON to look like this:
      "field": {"Tags": "Customer"}
 

Parameters:
bAltFormat True to enable the alternate format option for this UNode.
 
     public void setAltFormat(boolean bAltFormat) {
         assert isValue();
          = bAltFormat;
     }   // setAltFormat
 
     ////////// private JSON methods
     
     // Add the appropriate JSON syntax for this UNode to the given JSONEmitter.
     private void toJSON(JSONEmitter json) {
         switch () {
         case :
             json.startArray();
             if ( != null) {
                 for (UNode childNode : ) {
                     if (childNode.isMap()) {
                         json.startObject();
                         childNode.toJSON(json);
                         json.endObject();
                     } else {
                         childNode.toJSON(json);
                     }
                 }
             }
             json.endArray();
             break;
             
         case :
             // Return map child modes in name order.
             json.startGroup();
             if ( != null) {
                 assert .size() == .size();
                 for (UNode childNode : .values()) {
                     childNode.toJSON(json);
                 }
            }
            json.endGroup();
            break;
            
        case :
            if ( &&  != null) {
                // Generate as "<tag>: {"<name>": "<value>"}
                json.startGroup();
                json.addValue();
                json.endGroup();
            } else if ( != null && .isArray()) {
                if (.equals("value")) {
                    // nameless node: "<value>"
                    json.addValue();
                } else {
                    // value as an object: {"name": "value"}
                    json.addObject();
                }
            } else {
                // Simple case: "<name>": "<value>"
                json.addValue();
            }
            break;
            
        default:
            assert false : "Unknown NodeType: " + ;
        }
    }   // toJSON
    
    ////////// private XML methods
    // Parse the XML structure rooted at the given element and return the appropriate
    // UNode object. Note that element content is only allowed in "leaf" elements, hence
    // such elements cannot have child elements. The two rules that allow element
    // content are:
    // 
    // 1) If the element has no attributes and no child nodes, it becomes a VALUE node
    //    whose name is the tag name and whose value is the element's content. Example:
    //
    //          <key>Stellar1</key>
    //
    //    This becomes a VALUE UNode with name="key" and value="Stellar1".
    //
    // 2) If the element has a single attribute called "name" and no child elements, it
    //    becomes a VALUE node named with the attribute's value and the element content as
    //    the node value. Example:
    // 
    //          <field name="_ID">lollapalooza</field>
    // 
    //    This becomes a VALUE UNode with name="_ID" and value="lollapalooza". The element
    //    name ("field") is saved in the "tag name" member.
    //
    // A variant of rules 1) and 2) is that the attribute encoding="Base64" can be present
    // to denote that the value is Base64-encoded (and UTF-8 encoded under that). Hence,
    // the following two are identical to the previous examples except that the element
    // data is Base64 and UTF-8 decoded:
    //
    //          <key encoding="Base64">Stellar1</key>
    //          <field name="_ID" encoding="Base64">lollapalooza</field>
    //
    // The remaining rules cannot have element content:
    //
    // 3) If the element has exactly two attributes called "name" and "value", it becomes
    //    a VALUE node using the "name" and "value" attribute values respectively. Example:
    //
    //          <option name="AutoTables" value="false"/>
    //
    //    This becomes a VALUE UNode with name="AutoTables" and value="false". This case
    //    is not allowed to have child elements. The element name ("option") is saved in
    //    the "tag name" member. The attribute encoding="Base64" can be used for this
    //    case as well, causing the "value" attribute to be Base64 and UTF-8 decoded.
    //
    // 4) In all remaining cases, the element becomes a MAP or an ARRAY. A MAP is created
    //    if the node has no duplicate child element names, otherwise an ARRAY is created.
    //    If the element has a "name" attribute, its value is used for the node name.
    //    Otherwise, the tag name is used. All other attributes and child elements, if
    //    any, become child nodes of the MAP. Attributes are mapped to VALUE nodes; child
    //    elements are mapped on their own accord. Example:
    //
    //          <field name="Children" type="link" inverse="Parents"/>
    //
    //    This becomes a MAP named "Children" with two child members, both VALUE UNodes
    //    with the name/value pairs "type/link" and "inverse/Parents". Another example:
    //
    //          <add>
    //              <field name="_ID">123</field>
    //              <field name="_ID">456</field>
    //          </add>
    //
    //    This becomes an ARRAY named "add" with a child VALUE member for each <field>
    //    element (based on rule 2).
    private static UNode parseXMLElement(Element elem) {
        assert elem != null;
        
        // Get the element's content and attributes, if any.
        String content = Utils.getElementText(elem);
        NamedNodeMap attrMap = elem.getAttributes();
        
        // Map the element's child elements, if any, into a UNode list. This also tells us
        // if there are any child nodes.
        List<UNodechildUNodeList = new ArrayList<UNode>();
        boolean bDupNodeNames = parseXMLChildElems(elemchildUNodeList);
        
        // Decide what the element becomes as documented above.
        UNode result = null;
        
        // Detect base64 encoding attribute and remove it.
        String encoding = elem.getAttribute("encoding");
        if (!Utils.isEmpty(encoding) && encoding.equalsIgnoreCase("base64")) {
            attrMap.removeNamedItem("encoding");
            content = Utils.base64ToString(content);
        }
        
        // Case 1): <key>Stellar1</key>
        if (attrMap.getLength() == 0 && childUNodeList.size() == 0) {
            // No attributes and no child elements. Use the tag name and the element
            // content (which may be empty) as the node value.
            result = createValueNode(elem.getTagName(), content);
        
        // Case 2): <field name="_ID">lollapalooza</field>
        } else if (attrMap.getLength() == 1 &&
                        elem.getAttribute("name").length() > 0 &&
                        childUNodeList.size() == 0) {
            // Only a "name" attribute and no child elements. Use attribute value as the
            // name and content as the value.
            result = createValueNode(elem.getAttribute("name"), contentelem.getTagName());
            
        // Case 3): <option name="AutoTables" value="false"/>
        } else if (attrMap.getLength() == 2 &&
                        elem.getAttribute("name").length() > 0 &&
                        elem.getAttribute("value").length() > 0 &&
                        childUNodeList.size() == 0) {
            // Use values of "name" and "value" attributes. No content allowed.
            assert content.length() == 0 : "Content is not allowed for 'name/value' element: " + content;
            result = createValueNode(elem.getAttribute("name"), elem.getAttribute("value"), elem.getTagName());
            
        // Case 4): All other cases generate a MAP or an ARRAY.
        } else {
            // We shouldn't have any content for these nodes.
            assert content.length() == 0 : "Unexpected content for element '" +
                                           elem.getTagName() + "': " + content;
            
            // Use the "name" attribute value for the UNode name, if present. Otherwise,
            // use the element name.
            if (elem.getAttribute("name").length() > 0) {
                if (bDupNodeNames) {
                    result = createArrayNode(elem.getAttribute("name"), elem.getTagName());
                } else {
                    result = createMapNode(elem.getAttribute("name"), elem.getTagName());
                }
                attrMap.removeNamedItem("name");
            } else {
                if (bDupNodeNames) {
                    result = createArrayNode(elem.getTagName());
                } else {
                    result = createMapNode(elem.getTagName());
                }
            }
            
            // Map remaining attributes, if any, into child UNode objects and add all
            // child UNodes to this node's list.
            parseXMLAttributes(attrMapchildUNodeList);
            result.addChildNodes(childUNodeList);
        }
        return result;
    }   // parseXMLElement
    // Parse the given attribute map, creating a VALUE UNode for each attribute annd adding
    // if to the given child node list.
    private static void parseXMLAttributes(NamedNodeMap attrMapList<UNodechildUNodeList) {
        for (int index = 0; index < attrMap.getLength(); index++) {
            Attr attr = (Attr)attrMap.item(index);
            UNode childNode = createValueNode(attr.getName(), attr.getValue(), true);
            childUNodeList.add(childNode);
        }
    }   // parseXMLAttributes
    
    // Parse the given element's child elements, creating appropriate UNode objects for
    // each one and storing them in the given list. Indicate if any duplicate node names
    // are found while scanning.
    private static boolean parseXMLChildElems(Element elemList<UNodechildUNodeList) {
        assert elem != null;
        assert childUNodeList != null;
       
        // Scan for Element nodes (there could be Comment and other nodes).
        boolean bDupNodeNames = false;
        Set<StringnodeNameSet = new HashSet<String>();
        NodeList nodeList = elem.getChildNodes();
        for (int index = 0; index < nodeList.getLength(); index++) {
            Node childNode = nodeList.item(index);
            if (childNode instanceof Element) {
                // Create the appropriate child UNode for this element.
                UNode childUNode = parseXMLElement((Element)childNode);
                childUNodeList.add(childUNode);
                if (nodeNameSet.contains(childUNode.getName())) {
                    bDupNodeNames = true;
                } else {
                    nodeNameSet.add(childUNode.getName());
                }
            }
        }
        return bDupNodeNames;
    }   // parseXMLChildElems
    
    // Get the child nodes of this UNode that are VALUE nodes marked as attributes.
    private void addXMLAttributes(Map<StringStringattrMap) {
        if ( != null) {
            for (UNode childNode : ) {
                // A child node must not contain a tag name to be considered an attribute.
                if (childNode.m_type == . && childNode.m_bAttribute && Utils.isEmpty(childNode.m_tagName)) {
                    assert  != null && .length() > 0;
                    attrMap.put(childNode.m_namechildNode.m_value);
                }
            }
        }
    }   // addXMLAttributes
    
    ////////// Common private methods
    
    // Add this node's toString() the given buffer, indented by the given count, and
    // appended with a newline.
    private void toStringTree(StringBuilder builderint indent) {
        for (int count = 0; count < indentcount++) {
            builder.append(" ");
        }
        builder.append(this.toString());
        builder.append("\n");
        if ( != null) {
            for (UNode childNode : ) {
                childNode.toStringTree(builderindent + 3);
            }
        }
    }   // toStringTree
}   // class UNode
New to GrepCode? Check out our FAQ X