All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sakaiproject.tool.assessment.qti.util.XmlMapper Maven / Gradle / Ivy

There is a newer version: 23.3
Show newest version
/**********************************************************************************
 * $URL$
 * $Id$
 ***********************************************************************************
 *
 * Copyright (c) 2005, 2006, 2008, 2009 The Sakai Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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 org.sakaiproject.tool.assessment.qti.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.sakaiproject.tool.assessment.qti.util.XmlUtil;

/**
 * Utility class.  Maps  XML elements and attribute under a given node
 * to a Map, or populates bean.
 *
 * Note this now supports deep copy.
 *
 * @author @author Ed Smiley
 * @version $Id$
 */
public class XmlMapper
{
  private static Log log = LogFactory.getLog(XmlMapper.class);

  public static final String ATTRIBUTE_PREFIX = "attribute_";

  /**
   * Maps each element node and attribute under a given node to a Map.
   * It associates each element's text value with its name, and
   * each attribute value with a key of "attribute_" + the attribute name.
   *
   * If node is a document it processes it as if it were the root node.
   *
   * If there are multiple nodes with the same element name they will be stored
   * in a List.
   *
   * NOTE:
   * This was DESIGNED to ignore elements at more depth than root +1.
   * It has now been modified to deep copy under the nodes, but it
   * WILL NOT recurse and assign key value pairs below,
   * this is by design.  The elements below (e.g. XML snippets, XHTML text)
   * are all put into the value.
   *
   * @param node Node
   * @param indent String
   * @return HashMap
   */
  static public Map map(Document doc)
  {
    return hashNode(doc);
  }

  /**
   * Maps each element node to a bean property.
   * Supports only simple types such as String, int and long, plus Lists.
   *
   * If node is a document it processes it as if it were the root node.
   *
   * If there are multiple nodes with the same element name they will be stored
   * in a List.
   *
   * NOTE:
   * This is DESIGNED to ignore elements at more depth so that simple
   * String key value pairs are used.  This WILL NOT recurse to more depth,
   * by design.  If it did so, you would have to use maps of maps.
   *
   * @param bean Serializable object which has the appropriate setters/getters
   * @param doc the document
   */
  static public void populateBeanFromDoc(Serializable bean, Document doc)
  {
    try
    {
      Map m = map(doc);
      BeanUtils.populate(bean, m);
    }
    catch(Exception e)
    {
      log.error(e); 
      throw new RuntimeException(e);
    }
  }

  /**
   * utility class, hides the implementation as a HashMap
   * @param node
   * @return HashMap
   */
  private static HashMap hashNode(Node node)
  {
    HashMap hNode = new HashMap();

    int nType = node.getNodeType();
    NodeList nodes = node.getChildNodes();
    NamedNodeMap attributes = node.getAttributes();
    String name = node.getNodeName();

    // node is a document, recurse
    if(nType == Node.DOCUMENT_NODE)
    {
      // find root node
      if(nodes != null)
      {
        for(int i = 0; i < nodes.getLength(); i++)
        {
          // find  and process root node
          Node rnode = nodes.item(i);
          if(rnode.getNodeType() == Node.ELEMENT_NODE)
          {
            hNode = hashNode(rnode);

            break;
          }
        }
      }
    }

    //end if Node.DOCUMENT_NODE
    if(nType == Node.ELEMENT_NODE)
    {
      // add in child elements
      if(nodes != null)
      {
        for(int j = 0; j < nodes.getLength(); j++)
        {
          Node cnode = nodes.item(j);
          if(cnode.getNodeType() == Node.ELEMENT_NODE)
          {
            String cname = cnode.getNodeName();
            String ctext = ""; //textValue(cnode);
            String ctype = getTypeAttribute(cnode);
//            log.debug(cname + "=" + ctype);

            StringBuilder ctextbuf = new StringBuilder();
            // if we have multiple identical entries store them in a List
            if("list".equals(ctype))
            {
              ArrayList list;
              // if this element name already has a list
              if(hNode.get(cname) instanceof ArrayList)
              {
                list = (ArrayList) hNode.get(cname);
              }
              else // put it in a new list
              {
                list = new ArrayList();
              }

              // support for deep copy

//              list.add(ctext);
              NodeList ccnodes = cnode.getChildNodes();
                
              for (int n = 0; n < ccnodes.getLength(); n++) {
                ctextbuf.append(XmlUtil.getDOMString(ccnodes.item(n)));
              }
              ctext = ctextbuf.toString();		 
              list.add(ctext);
              hNode.put(cname, list);
            }
            else // scalar (default)
            {
              // support for deep copy
              NodeList ccnodes = cnode.getChildNodes();
              for (int n = 0; n < ccnodes.getLength(); n++) {
                ctextbuf.append(XmlUtil.getDOMString(ccnodes.item(n)));
              }
              ctext = ctextbuf.toString();		
              hNode.put(cname, ctext);
            }
          }
        }
      }

      // add in attributes
      if(attributes != null)
      {
        for(int i = 0; i < attributes.getLength(); i++)
        {
          Node current = attributes.item(i);
          hNode.put(
            ATTRIBUTE_PREFIX + current.getNodeName(), current.getNodeValue());
        }
      }
    }

    return hNode;
  }

  /**
   * utility method
   *
   * @param nd node
   *
   * @return text value of node
   */
  
  /*
  private static String textValue(Node nd)
  {
    
    NodeList nodes = nd.getChildNodes();
    
    StringBuilder textbuf = new StringBuilder(); 
    
    for(int i = 0; i < nodes.getLength(); i++)
    {
      Node cnode = nodes.item(i);
      if(cnode.getNodeType() == Node.TEXT_NODE)
      {
        //text += cnode.getNodeValue();
        textbuf.append(cnode.getNodeValue());
      }
    }
    String text = textbuf.toString();
    return text;
  }
  
  */

  /**
   * If there is a type attribute for the element node, return its value,
   * otherwise return "scalar".
   * @param node
   * @return
   */
  private static String getTypeAttribute(Node node){
    NamedNodeMap attributes = node.getAttributes();
    if(attributes != null)
    {
      for(int i = 0; i < attributes.getLength(); i++)
      {
        Node current = attributes.item(i);
        if ("type".equals(current.getNodeName())){
          return current.getNodeValue();
        }
      }
    }

    return "scalar";
  }

}








© 2015 - 2025 Weber Informatics LLC | Privacy Policy