/**
 * domparser.js - WebDAV Client dom parser.
 *
 * This is a Xml dom parser for WebDAV collection.
 *
 * @license Copyright 2011 The xhrdavclient library authors.
 * All rights reserved.
 */

goog.provide('xhrdav.parser.DomParser');
goog.require('goog.dom.xml');
goog.require('goog.functions');

/**
 * Dom parser for WebDAV Client API
 *
 * @constructor
 */
xhrdav.parser.DomParser = function() {
};

/**
 * Initialize parser
 *
 * @param {Object} xml XML Document.
 * @param {Object} handler Dom Handler.
 * @param {Object} options Parse options.
 * @return {xhrdav.DomParser} self object.
 */
xhrdav.parser.DomParser.prototype.initialize = function(xml, handler, options) {
  if (!goog.isDefAndNotNull(xml)) {
    goog.functions.error('Xml document is NOT GIVEN!!')();
  }
  if (!goog.isDefAndNotNull(handler)) {
    goog.functions.error('Parse handler is NOT GIVEN!!')();
  }

  /** @type {Object} */
  this.xml_ = goog.dom.xml.loadXml(
      goog.dom.xml.serialize(xml).split('\n').join('')).documentElement;
  /** @type {Object} */
  this.handler_ = handler;

  if (!goog.isDefAndNotNull(options)) options = {};
  /** @type {string} */
  this.nsSeparator_ = options.nsSeparator || '$';
  /** @type {string} */
  this.textNodeName_ = options.textNodeName || '$t';
  /** @type {string} */
  this.attributePrefix_ = options.attributePrefix || '';

  return this;
};

/**
 * Parse XML to Javascript Object
 *
 * @return {xhrdav.DomParser} self object.
 */
xhrdav.parser.DomParser.prototype.parse = function() {
  var xml = this.xml_;
  this.handler_.startDocument();
  this.handler_.execute(this, xml);
  this.handler_.endDocument();
  return this;
};

/**
 * Execute parse XML Document to Javascript Object
 *
 * @param {Object} xml  parsing xml.
 * @return {Object} parsed object for xml.
 */
xhrdav.parser.DomParser.prototype.parseDocument = function(xml) {
  var obj = {};

  // get nodeValue
  if (xml.nodeType == goog.dom.NodeType.ELEMENT) { // element
    // do attributes
    goog.array.forEach(xml.attributes, function(attr, index) {
      var attrKey = this.parseAttributeName_(attr.nodeName);
      obj[attrKey] = attr.nodeValue;
    }, this);
  } else if (xml.nodeType == goog.dom.NodeType.TEXT) { // text
    obj = xml.nodeValue;
  }

  // do children
  if (xml.hasChildNodes()) {
    goog.array.forEach(xml.childNodes, function(node, index) {
      var key = this.parseNodeName_(node.nodeName);
      if (goog.isDefAndNotNull(obj[key])) {
        if (goog.typeOf(obj[key]) != 'array') {
          var old = obj[key];
          obj[key] = [];
          obj[key].push(old);
        }
        obj[key].push(this.parseDocument(node));
      } else {
        obj[key] = this.parseDocument(node);
      }
    }, this);
  }
  return obj;
};

/**
 * XML attribute nodename to Javascript object key
 *
 * @private
 * @param {string} nodeName Xml attribute nodename.
 * @return {string} converted object key for XML attribute nodename.
 */
xhrdav.parser.DomParser.prototype.parseAttributeName_ = function(nodeName) {
  return this.attributePrefix_ + nodeName.replace(/:/, this.nsSeparator_);
};

/**
 * XML nodename to Javascript object key
 *
 * @private
 * @param {string} nodeName Xml nodename.
 * @return {string} converted nodename.
 */
xhrdav.parser.DomParser.prototype.parseNodeName_ = function(nodeName) {
  return nodeName
    .replace(/:/, this.nsSeparator_)
    .replace(/#text/, this.textNodeName_);
};

/* Entry Point for closure compiler */
goog.exportSymbol('xhrdav.parser.DomParser', xhrdav.parser.DomParser);
goog.exportProperty(xhrdav.parser.DomParser.prototype, 'initialize',
  xhrdav.parser.DomParser.prototype.initialize);
goog.exportProperty(xhrdav.parser.DomParser.prototype, 'parse',
  xhrdav.parser.DomParser.prototype.parse);
goog.exportProperty(xhrdav.parser.DomParser.prototype, 'parseDocument',
  xhrdav.parser.DomParser.prototype.parseDocument);