define('jls/util/XmlElement', ['jls/lang/Class', 'jls/lang/Exception'], function (Class, Exception) {

var XmlElement;
/*
 * E4X is not supported by google chrome, so could not use it by now.
 */
XmlElement = Class.create( /** @lends jls.util.XmlElement.prototype */
{
    /**
     * Creates an XML.
     * 
     * @param {Object} [parameters] The XML parameters.
     * @constructs
	 * @class This class represents an XML element.
     */
    initialize : function(parameters) {
        parameters = parameters || {};
        this.name = parameters.name || '';
        this.children = [];
        this.attributes = {};
        this.value = parameters.value || '';
        if (parameters.attributes) {
            this.setAttributes(parameters.attributes);
        }
        if (Object.isArray(parameters.children)) {
            this.addChildren(parameters.children);
        }
    },
    /**
     * Returns this XML element name.
     *
     * @returns {String} The XML element name.
     */
    getName : function() {
        return this.name;
    },
    /**
     * Sets this XML element name.
     *
     * @param {String} name The XML element name.
     */
    setName : function(name) {
    	this.name = name;
    	return this;
    },
    /**
     * Sets an attribute.
     *
     * @param {String} key The attribute key.
     * @param {String} [value] The attribute value.
     */
    setAttribute : function(key, value) {
        //jls.logger.trace('setAttribute(' + key + ', ' + value + ')');
    	if (typeof value != 'undefined') {
    		this.attributes[key] = value;
    	} else {
        	delete this.attributes[key];
    	}
    	return this;
    },
    /**
     * Returns an attribute value.
     *
     * @param {String} key The attribute key.
     * @param {String} [defaultValue] The default value to return if the key does not exist.
     * @returns {String} The attribute value.
     */
    getAttribute : function(key, defaultValue) {
        if (key in this.attributes) {
        	return this.attributes[key];
        }
        return typeof defaultValue == 'undefined' ? null : defaultValue;
    },
    /**
     * Sets some attributes.
     *
     * @param {Object} attributes The attributes to set.
     */
    setAttributes : function(attributes) {
        for ( var k in attributes) {
            this.setAttribute(k, attributes[k]);
        }
        return this;
    },
    addCData: Class.emptyFunction,
    addComment: Class.emptyFunction,
    addText : function(value) {
        //jls.logger.trace('addText(' + value + ')');
        this.value += value;
        return this;
    },
    getText : function() {
        return this.value;
    },
    /**
     * Adds a child.
     *
     * @param {Object} child The child to add.
     */
    addChild : function(child) {
        if (typeof child == 'object') {
            child = new XmlElement(child);
        } else if (! child instanceof XmlElement) {
            throw new Exception('don\'t know how to add "' + (typeof child) + '" child type');
        }
        //jls.logger.trace('addChild(' + child.getName() + ')');
        this.children.push(child);
        return child;
    },
    /**
     * Adds children.
     *
     * @param {Array} children The children to add.
     */
    addChildren : function(children) {
        for ( var i = 0; i < children.length; i++) {
            this.addChild(children[i]);
        }
        return this;
    },
    /**
     * Removes a child.
     *
     * @param {Object} child The child to remove.
     */
    removeChild : function(child) {
        var index = 0;
        if (typeof child == 'number') {
            index = child;
            child = this.children[index];
        } else if (child instanceof XmlElement) {
        	for (index = this.children.length - 1; (index >= 0) && (this.children[index] !== child); index--);
        	if (index < 0) {
        		throw new Exception('Child not found');
        	}
        } else {
            throw new Exception('don\'t know how to remove "' + (typeof child) + '" child type');
        }
        this.children.splice(index, 1);
        return child;
    },
    /**
     * Removes all children.
     *
     */
    removeChildren : function() {
        /*for (var i = this.children.length - 1; i >= 0; i--) {
            this.removeChild(i);
        }*/
        this.children = [];
    },
    /**
     * Returns all children.
     *
     */
    getChildren : function() {
        return this.children;
    },
    getChildCount : function() {
        return this.children.length;
    },
    /**
     * Gets a child.
     *
     * @param {Number} index The child index.
     * @returns {jls.util.XmlElement} The child.
     */
    getChild : function(index) {
        return this.children[index];
    },
    toString : function(encoding) {
        var s = '';
        if (encoding) {
            s += '<?xml version="1.0" encoding="' + encoding + '" ?>';
        }
    	s += '<' + this.name;
        for (var key in this.attributes) {
    		s += ' ' + key + '="' + XmlElement.escape(this.attributes[key]) + '"';
        }
    	if ((this.children.length == 0) && (this.value.length == 0)) {
    		s += ' />';
    	} else {
    		s += '>';
            for (var i = 0; i < this.children.length; i++) {
            	s += this.children[i].toString();
            }
            if (this.value.length > 0) {
                s += XmlElement.escape(this.value);
            }
        	s += '</' + this.name + '>';
    	}
        return s;
    }
});

Object.extend(XmlElement, /** @lends jls.util.XmlElement */
{
	ELEMENT_NODE : 1,
	ATTRIBUTE_NODE : 2,
	TEXT_NODE : 3,
	CDATA_SECTION_NODE : 4,
	ENTITY_REFERENCE_NODE : 5,
	ENTITY_NODE : 6,
	PROCESSING_INSTRUCTION_NODE : 7,
	COMMENT_NODE : 8,
	DOCUMENT_NODE : 9,
	DOCUMENT_TYPE_NODE : 10,
	DOCUMENT_FRAGMENT_NODE : 11,
	NOTATION_NODE : 12,
    /**
     * Creates an XML element from a DOM one.
     *
     * @param {Object} domNode The DOM Element.
     * @returns {jls.util.XmlElement} The created XML element.
     */
    createFromDOM : function(domNode) {
        /*
         * TODO do not create an XML: wrap the DOM object
         */
        return XmlElement.populateFromDOM(new XmlElement(), domNode);
    },
    escape : function(s) {
        if (s) {
            return s.toString().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').
                replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/\n/g, '&#x000a;').replace(/\t/g, '&#x0009;');
        }
        return s;
    },
    parseXMLString : function(value) {
        // &#xE9;
        return value.replace(/&#x([0-9A-F]+);/, function(str, u) {
            return String.fromCharCode(parseInt(u, 16));
        });
    },
    populateFromDOM : function(xml, domNode) {
        if ((! domNode) || (! 'nodeType' in domNode)) {
            throw new Exception('Invalid DOM node');
        } else if (domNode.nodeType == XmlElement.DOCUMENT_NODE) {
        	// Look for the first child element
            for (var i = 0; i < domNode.childNodes.length; i++) {
            	if (domNode.childNodes[i].nodeType == XmlElement.ELEMENT_NODE) {
                    return arguments.callee(xml, domNode.childNodes[i]);
            	}
            }
            throw new Exception('Invalid DOM node (no element found)');
        } else if (domNode.nodeType != XmlElement.ELEMENT_NODE) {
            throw new Exception('Invalid DOM node (' + domNode.nodeType + ')');
        }
        xml.setName(domNode.nodeName);
        if (domNode.attributes) {
            for (var i = 0; i < domNode.attributes.length; i++) {
            	var childNode = domNode.attributes[i];
                switch (childNode.nodeType) {
                case XmlElement.ATTRIBUTE_NODE:
                	//xml.setAttribute(childNode.nodeName, jls.util.XmlElement.parseXMLString(childNode.nodeValue));
                    xml.setAttribute(childNode.nodeName, childNode.nodeValue);
                    break;
                }
            }
        }
        if (domNode.childNodes) {
            for (var i = 0; i < domNode.childNodes.length; i++) {
            	var childNode = domNode.childNodes[i];
                switch (childNode.nodeType) {
                case XmlElement.ELEMENT_NODE:
                	xml.addChild(XmlElement.createFromDOM(childNode));
                    break;
                case XmlElement.TEXT_NODE:
                	xml.addText(childNode.nodeValue);
                    break;
                case XmlElement.CDATA_SECTION_NODE:
                	xml.addCData(childNode.nodeValue);
                    break;
                case XmlElement.COMMENT_NODE:
                	xml.addComment(childNode.nodeValue);
                    break;
                }
            }
        }
        if (domNode.nodeValue) {
        	xml.addText(domNode.nodeValue);
        }
        return xml;
    },
    /**
     * Creates an XML element from a E4X one.
     *
     * @param {Object} xmlNode The E4X Element.
     * @returns {jls.util.XmlElement} The created XML element.
     */
    createFromE4X : function(xmlNode) {
        /*
         * TODO do not create an XML: wrap the E4X object
         */
        return XmlElement.populateFromE4X(new XmlElement(), xmlNode);
    },
    populateFromE4X : function(xml, xmlNode) {
        xml.setName(xmlNode.name());
        var i, l;
        l = xmlNode.attributes();
        for (i = 0; i < l.length(); i++) {
            var a = l[i];
            xml.setAttribute(a.name(), a);
        }
        /*l = xmlNode.elements();
        for (i = 0; i < l.length(); i++) {
            xml.addChild(jls.util.XmlElement.createFromE4X(l[i]));
        }
        l = xmlNode.comments();
        for (i = 0; i < l.length(); i++) {
            xml.addComment(l[i]);
        }*/
        l = xmlNode.children();
        for (i = 0; i < l.length(); i++) {
            var n = l[i];
            switch (n.nodeKind()) {
            case 'element':
                xml.addChild(XmlElement.createFromE4X(n));
                break;
            case 'text':
                xml.addText(n);
                break;
            case 'comment':
                xml.addComment(n);
                break;
            }
        }
        return xml;
    }
});


return XmlElement;
});
