/*
name: workspace.XML.builder
alias: workspace.XML.cache
extends: workspace.XML
*/
workspace.XML.builder = {}
workspace.XML.builder._construct = function()
{

    // ======== Common functions =========

    //------
    function isString(value) { return value != null ? value.isString : false; }
    function isNumber(value) { return value != null ? value.isNumber : false; }

    //--------
    function protGetText(elemParent, name, defValue)
    {
        var elemChild

        elemChild = elemParent.selectSingleNode(name);

        if (elemChild != null) { return elemChild.text; }
        else { return defValue != null ? defValue : null; }
    }


    //-------
    function protPutText(element, name, value)
    {

        var oSubElem = element.selectSingleNode(name)

        if (value == null)
        {
            if (oSubElem != null)
            {
                element.removeChild(oSubElem)
                notifyChanged(element);
            }
        }
        else
        {

            if (oSubElem == null)
            {
                oSubElem = element.ownerDocument.createElement(name)
                element.appendChild(oSubElem)
            }

            oSubElem.text = formatValue(value)
            notifyChanged(element);
        }

        return oSubElem;
    }

    function protPutCData(element, name, value)
    {
        var oSubElem = element.selectSingleNode(name)

        if (value == null)
        {
            if (oSubElem != null)
            {
                element.removeChild(oSubElem)
                notifyChanged(element);
            }

        }
        else
        {
            if (oSubElem == null)
            {
                oSubElem = element.ownerDocument.createElement(name)
                element.appendChild(oSubElem)
            }

            putCDataValue(oSubElem, formatValue(value))

        }
    }

    function putCDataValue(element, value)
    {
        var oNode

        while (element.firstChild && !oNode)
        {
            if (element.firstChild.nodeTypeString == 'cdatasection')
                oNode = element.firstChild
            else
                element.removeChild(element.firstChild)
        }

        if (!oNode)
            element.appendChild(element.ownerDocument.createCDATASection(value))
        else
            oNode.data = value

        notifyChanged(element);
    }

    //--------
    function putAttributeValue(element, name, value)
    {
        if (value == null)
            element.removeAttribute(name)
        else
            element.setAttribute(name, formatValue(value))

        notifyChanged(element);

    }

    //-------
    function getElement(elemParent, name, create)
    {
        var elemChild = elemParent.selectSingleNode(name);

        if (elemChild == null && create)
        {
            elemChild = createElement(elemParent, name);
        }

        return elemChild;
    }

    //-------
    function getElementPath(roElem)
    {
        var sResult

        if (roElem.nodeTypeString == "element") sResult = getElementPath(roElem.parentNode) + "/"

        return sResult + roElem.tagName + "[" + elementIndex(roElem) + "]"
    }

    //-------
    function elementIndex(roElem)
    {
        var i = 0
        var e = new Enumerator(roElem.parentNode.selectNodes(roElem.tagName))

        for (; !e.atEnd() && (e.item() != roElem); e.moveNext()) { i += 1; }

        return i
    }

    //------
    function createElement(parent, name)
    {
        var elemChild;

        // Check if the parent IS the document node (ownerDocument) and if so reference directly to it 
        var ownerDocument = (parent.nodeName == "#document") ? parent : parent.ownerDocument;

        if (name.indexOf('[') > 0)
        {
            var match = /\[@(.*?)\s*=\s*["']?(.*?)["']?\]/.exec(name)
            if (match)
            {
                elemChild = ownerDocument.createElement(RegExp.leftContext)
                elemChild.setAttribute(match[1], match[2])
            }
        }
        else
        {
            elemChild = ownerDocument.createElement(name);
        }

        ownerDocument = null;

        /* Errors on the line below could be due to an XmlDataSource
        control on the aspx page not using the correct root node. Where using / at the start of
        XPath for the DataField attribute of controls referencing it, the root node in the 
        XmlDataSource control MUST match the root node in the XPath. */
        parent.appendChild(elemChild)
        return elemChild
    }

    //------
    function resolvePersona(persona)
    {
        if (persona == null) return new GenericPersona("item")
        else return persona
    }

    function formatValue(value)
    {
        if (value != null)
        {
            if (value.isDate)
                return value.hasLocaleTime() ? value.toString('dd MMM yyyy HH:mm:ss') : value.toString('dd MMM yyyy')
            else if (value.isBoolean)
                return value ? '-1' : '0'
            else
                return value.toString()
        }
        else
            return ''
    }



    //======= Generic XML Element =======
    function Generic(elem) { this.element = elem; }
    Generic.prototype = new workspace.BaseEventer()
    Generic.prototype.getID = function() { return Nz(this.element.getAttribute("ID"), ""); }
    Generic.prototype.putID = function(value)
    {
        this.element.setAttribute("ID", Nz(value, ""));
        notifyChanged(this.element);
    }
    Generic.prototype.getText = function() { return this.element.text; }
    Generic.prototype.putText = function(value)
    {
        if (this.isComplex()) throw "Can not assign text to a complex element"
        value = Nz(value, "")
        this.element.text = formatValue(value)
        notifyChanged(this.element);
    }
    Generic.prototype.getCData = function() { return this.element.text; }
    Generic.prototype.putCData = function(value) { putCDataValue(this.element, value); }
    Generic.prototype.getAttribute = function(name, def)
    {
        if (typeof (def) == 'undefined') def = ''
        return Nz(this.element.getAttribute(name), def);
    }
    Generic.prototype.putAttribute = function(name, value) { putAttributeValue(this.element, name, value); }
    Generic.prototype.getSimpleChild = function(name, def) { return protGetText(this.element, name, def); }
    Generic.prototype.putSimpleChild = function(name, value) { protPutText(this.element, name, value); }
    Generic.prototype.index = function() { return elementIndex(this.element); }
    Generic.prototype.path = function() { return getElementPath(this.element); }
    Generic.prototype.isComplex = function() { return (this.element.selectNodes("*").length > 0); }
    Generic.prototype.addChild = function(item, constructor)
    {

        var elem = null;

        if (!constructor) constructor = Generic

        if (item.isString)
        {
            elem = createElement(this.element, item)
        }
        else if (Generic.prototype.isPrototypeOf(item))
        {
            elem = this.element.appendChild(item.element)
        }
        else
        {
            elem = this.element.appendChild(item)
        }

        notifyChanged(this.element);

        return new constructor(elem);

    }
    Generic.prototype.addText = function(text)
    {
        this.element.appendChild(this.element.ownerDocument.createTextNode(text))
        notifyChanged(this.element);
    }
    Generic.prototype.getChild = function(name, create, constructor)
    {
        var oElem = getElement(this.element, name, create)
        if (oElem != null)
        {
            if (constructor == null) return new Generic(oElem)
            else return new constructor(oElem)
        }
        else
        {
            return null
        }
    }
    Generic.prototype.removeChild = function(child)
    {
        var oElem

        if (isString(child))
            oElem = getElement(this.element, child)
        else if (Generic.prototype.isPrototypeOf(child))
            oElem = child.element
        else
            oElem = child
        // end if

        if (oElem)
        {
            this.element.removeChild(oElem)
            notifyChanged(this.element);
        }
        return oElem != null
    }
    Generic.prototype.getParent = function(constructor)
    {
        if (!constructor) constructor = Generic

        var elem = this.element.parentNode
        if (elem && elem.nodeType == 1) return new constructor(elem);
    }
    Generic.prototype.filter = function(nameOrPersona)
    {
        if (nameOrPersona == null) return new Elements(this.element, new GenericPersona("item"))
        else if (nameOrPersona.isString) return new Elements(this.element, new GenericPersona(nameOrPersona))
        else if (typeof (nameOrPersona) == "object") return new Elements(this.element, resolvePersona(nameOrPersona))
        else throw "Type mismatch in Generic.getChildren"
    }
    Generic.prototype.childEnum = function(persona) { return new ElementsEnum(this.element.selectNodes("*"), persona); }
    Generic.prototype.xpathEnum = function(xpath, persona) { return new ElementsEnum(this.element.selectNodes(xpath), persona); }
    Generic.prototype.isValid = function() { return (this.element.selectSingleNode(".//brokenRule") == null); }
    Generic.prototype.getBrokenRules = function() { return this.filter("brokenRule"); }
    Generic.prototype.getClone = function(deep) { return new Generic(this.element.cloneNode(deep)); }
    Generic.prototype.copyFrom = function(src)
    {
        var genSrc = getGeneric(src)
        var oAttribs = genSrc.element.attributes
        for (var i = 0; i < oAttribs.length; i++)
            this.element.setAttributeNode(oAttribs.item(i).cloneNode(true))

        var oElems = genSrc.element.childNodes
        for (var oElem = oElems.nextNode(); oElem != null; oElem = oElems.nextNode())
            this.element.appendChild(oElem.cloneNode(true))

        notifyChanged(this.element);
    }
    Generic.prototype.getDocUseXPath = function()
    {
        var dom = this.element.ownerDocument

        try { return dom.getProperty("SelectionLanguage") == "XPath"; }
        catch (e) { return false; }
    }
    Generic.prototype.putDocUseXPath = function(vbDocXPath)
    {
        var dom = this.element.ownerDocument

        try
        {
            if (vbDocXPath)
                dom.setProperty("SelectionLanguage", "XPath")
            else
                dom.setProperty("SelectionLanguage", "XSLPattern")

            return vbDocXPath
        }
        catch (e) { return false; }
    }
    Generic.prototype.addSequence = function(xpath, attribName, startIndex)
    {
        var i = startIndex != null ? startIndex : 1

        var oElems = this.element.selectNodes(xpath)
        for (var oElem = oElems.nextNode(); oElem != null; oElem = oElems.nextNode(), i++)
            oElem.setAttribute(attribName, i)

        notifyChanged(this.element);

        return i
    }
    Generic.prototype.removeSequence = function(xpath, attribName)
    {
        var i = 0
        var oElems = this.element.selectNodes(xpath)
        for (var oElem = oElems.nextNode(); oElem != null; oElem = oElems.nextNode(), i++)
            oElem.removeAttribute(attribName)

        return i
    }
    Generic.prototype.slice = function(copy, constructor)
    {
        var elem = copy ? this.element.cloneNode(true) : this.element
        var dom = workspace.XML.getDOM()
        dom.appendChild(elem)
        if (!copy) notifyChanged(this.element);
        return constructor ? new constructor(elem) : copy ? new Generic(elem) : this
    }
    Generic.prototype.replace = function(source)
    {
        var gen = getGeneric(source)
        this.element.parentNode.replaceChild(gen.element, this.element)
        this.element = gen.element
        notifyChanged(this.element);
        return this
    }
    Generic.prototype.expand = function(processingInstruction)
    {
        var elem = this.isDocumentElement() ? this.element : this.slice(true).element
        var oExpanded = workspace.XML.expand(elem.ownerDocument, processingInstruction)
        if (oExpanded) this.replace(getGeneric(oExpanded))
    }
    Generic.prototype.isDocumentElement = function()
    { return this.element == this.element.ownerDocument.documentElement; }
    Generic.prototype.drop = function(xpath)
    {
        var e = this.element.selectNodes(xpath)
        var iDropCount = e.length //ensures all nodes selected first
        for (var elem = e.nextNode(); elem != null; elem = e.nextNode())
        {
            elem.parentNode.removeChild(elem)
        }
        if (iDropCount > 0) notifyChanged(this.element);
        return iDropCount
    }
    Generic.prototype.getFieldValue = function(xpath, def, bindProperties)
    {
        if (!bindProperties)
        {
            return protGetText(this.element, xpath, def)
        }
        else
        {
            elem = getElement(this.element, xpath)

            if (!elem)
            {
                return def
            }
            else
            {
                var oRet = new Object()

                for (var key in bindProperties)
                    oRet[key] = protGetText(elem, bindProperties[key], def ? def[key] : undefined)

                return oRet
            }
        }
    }
    Generic.prototype.putFieldValue = function(xpath, value, bindProperties)
    {
        var asPath = xpath.split('/')
        var elem = this.element

        for (var i = 0; i < asPath.length - 1; i++)
        {
            if (i == 0 && asPath[i] == '')
                elem = this.element.ownerDocument;
            else
                elem = getElement(elem, asPath[i], true)
        }

        if (!bindProperties || value == null)
        {
            if (asPath[i].substr(0, 1) == '@')
                putAttributeValue(elem, asPath[i].slice(1), value)
            else
                protPutText(elem, asPath[i], value)
        }
        else
        {
            if (asPath[i].substr(0, 1) == '@')
            {
                for (var key in bindProperties)
                {
                    if (bindProperties[key] = '.')
                    {
                        putAttributeValue(elem, asPath[i].slice(1), value[key])
                        break;
                    }
                }
            }
            else
            {
                elem = getElement(elem, asPath[i], true)

                for (var key in bindProperties)
                {
                    var sPath = bindProperties[key]
                    if (sPath.substr(0, 1) == '@')
                        putAttributeValue(elem, sPath.slice(1), value[key])
                    else
                        protPutText(elem, sPath, value[key])
                }
            }
        }
    }
    Generic.prototype.getData = function() { return this; }
    Generic.prototype.post = function(URL, CallBack, CallBackOnThis)
    {
        var oRes = workspace.XML.request("POST", URL, this.element.xml, CallBack, CallBackOnThis)
        if (oRes)
            return (CallBack && typeof (CallBack) == 'function') ? oRes : getGeneric(oRes)
    }

    //======= Generic Persona ======
    function GenericPersona(name)
    {
        if (typeof (name) == "undefined") name = "item"
        this.name = name;
    }
    GenericPersona.prototype.createChild = function(elem) { return new this.childConstructor(elem); }
    GenericPersona.prototype.createElements = function(elem) { return new Elements(elem, this); }
    GenericPersona.prototype.getPredicate = function(ID) { return '[@ID="' + ID + '"]'; }
    GenericPersona.prototype.setIDAttribs = function(elem, ID) { elem.setAttribute("ID", ID); }
    GenericPersona.prototype.childConstructor = Generic


    //======== Elements Class =======
    function Elements(elem, persona)
    {
        this.element = elem;
        this.persona = persona;

        if (persona.customize) persona.customize(this)
    }
    Elements.prototype.add = function(before)
    {
        var oElem = this.element.ownerDocument.createElement(this.persona.name)

        return this.insert(this.persona.createChild(oElem), before)
    }
    Elements.prototype.insert = function(item, before)
    {
        if (typeof (before) == "undefined") this.element.appendChild(item.element)
        else this.element.insertBefore(item.element, before.element)

        notifyChanged(this.element);

        return item
    }
    Elements.prototype.exists = function(indexKey)
    {
        return this.getElement(indexKey, false) != null;
    }
    Elements.prototype.getItem = function(indexKey, create)
    {
        var subElem = this.getElement(indexKey, create);

        if (subElem != null) return this.persona.createChild(subElem)
        else return null
    }
    Elements.prototype.count = function()
    {
        return this.element.selectNodes(this.persona.name).length
    }
    Elements.prototype.index = function(indexKey)
    {
        var oTarget = this.getElement(indexKey)
        if (oTarget != null) return elementIndex(oTarget)
        else return null
    }
    Elements.prototype.remove = function(indexKey)
    {
        var subElem = this.getElement(indexKey)
        if (subElem != null)
        {
            this.element.removeChild(subElem);
            notifyChanged(this.element);
        }
    }
    Elements.prototype.removeAll = function()
    {
        var e = new Enumerator(this.element.selectNodes(this.persona.name))

        for (; !e.atEnd(); e.moveNext())
        {
            this.element.removeChild(e.item());
        }

        notifyChanged(this.element);
    }
    Elements.prototype.merge = function(source, clone)
    {
        if (typeof (clone) == "undefined") clone = false

        for (var oItem = source.nextItem(); oItem != null; oItem = source.nextItem())
        {
            if (clone) this.element.appendChild(oItem.element.cloneNode(true))
            else this.element.appendChild(oItem.element)
        }

        notifyChanged(this.element);
    }
    Elements.prototype.mergeByID = function(source, clone, lastWinner)
    {

        if (typeof (clone) == "undefined") clone = false
        if (typeof (lastWinner) == "undefined") lastWinner = false

        var oNewNode
        var oOldNode

        for (var oItem = source.nextItem(); oItem != null; oItem = source.nextItem())
        {

            oOldNode = this.getElement(oItem.getID())

            if (!oOldNode || lastWinner)
            {

                if (clone) oNewNode = oItem.element.cloneNode(true)
                else oNewNode = oItem.element

                if (!oOldNode) this.element.appendChild(oNewNode)
                else this.element.replaceChild(oOldNode, oNewNode)

            }

        }

        notifyChanged(this.element)

    }
    Elements.prototype.replaceItem = function(indexKey, newItem)
    {
        var oOldNode = this.getElement(indexKey, false)
        var oNewNode

        if (Generic.prototype.isPrototypeOf(newItem)) oNewNode = newItem.element
        else oNewNode = newItem

        if (oOldNode != null) this.element.replaceChild(oNewNode, oOldNode)
        else this.element.appendChild(oNewNode)

        notifyChanged(this.element)
    }
    Elements.prototype.xml = function() { return this.element.xml; }
    Elements.prototype.descendantEnum = function()
    { return new ElementsEnum(this.element.selectNodes(".//" + this.persona.name), this.persona); }
    Elements.prototype.childEnum = function()
    { return new ElementsEnum(this.element.selectNodes(this.persona.name), this.persona); }
    Elements.prototype.xpathEnum = function(xpath)
    { return new ElementsEnum(this.element.selectNodes(xpath), this.persona); }
    Elements.prototype.getElement = function(indexKey, create)
    {
        if (typeof (create) == "undefined") create = false

        var subElem

        if (isString(indexKey))
        {
            if (indexKey.substring(0, 1) == "[")
                subElem = this.element.selectSingleNode(this.persona.name + indexKey)
            else
                subElem = this.element.selectSingleNode(this.persona.name + this.persona.getPredicate(indexKey))
        }
        else if (isNumber(indexKey))
        {
            subElem = this.element.selectNodes(this.persona.name).item(indexKey - 1);
        }
        else if (Generic.prototype.isPrototypeOf(indexKey))
        {
            subElem = indexKey.element
        }
        else if (typeOf(indexKey) == "object")
        {
            subElem = indexKey
        }

        if ((subElem == null) && create)
        {
            subElem = this.element.ownerDocument.createElement(this.persona.name);
            if (isString(indexKey)) this.persona.setIDAttribs(subElem, indexKey);
            this.element.appendChild(subElem);
        }

        return subElem;
    }
    Elements.prototype.isValid = function() { return (this.element.selectSingleNode(".//brokenRule") == null); }
    Elements.prototype.getDescendant = function(indexKey)
    {
        var oElem = this.element.selectSingleNode(".//" + this.persona.name + this.persona.getPredicate(indexKey))
        if (oElem) return this.persona.createChild(oElem)
    }

    //======= Elements Enumerator =======
    function ElementsEnum(list, persona)
    {
        this.list = list
        if (persona)
        {
            if (persona.createChild) this.persona = persona
            else this.childConstructor = persona
        }
    }
    ElementsEnum.prototype.count = function() { return this.list.length }
    ElementsEnum.prototype.reset = function() { this.list.reset() }
    ElementsEnum.prototype.getItem = function(index)
    {
        var oElem = this.list.item(index)
        if (oElem != null) return this.createChild(oElem)
        else return null
    }
    ElementsEnum.prototype.nextItem = function()
    {
        var oElem = this.list.nextNode()
        if (oElem != null) return this.createChild(oElem)
        else return null
    }
    ElementsEnum.prototype.seekItem = function(id)
    {
        for (var res = this.nextItem(); res != null; res = this.nextItem())
        {
            if (res.getID() = id) break;
        }

        return res
    }
    ElementsEnum.prototype.createChild = function(elem)
    {
        if (this.childConstructor) return new this.childConstructor(elem)
        else return this.persona.createChild(elem)
    }
    ElementsEnum.prototype.forEach = function(callback, thisObject)
    {
        for (var item = this.nextItem(), i = 0; item != null; item = this.nextItem(), i++)
        {
            callback.call(thisObject, item, i, this)
        }
    }
    ElementsEnum.prototype.persona = new GenericPersona()

    function getGeneric(source, constructor)
    {
        if (!constructor) constructor = Generic
        if (source) source = workspace.XML.getElem(source);
        return source ? new constructor(source) : null;
    }

    function getElements(source, persona) { return new Elements(workspace.XML.getElem(source), resolvePersona(persona)); }

    function MonitoredDocument(doc)
    {
        this.doc = doc;
    }
    MonitoredDocument.prototype = new workspace.BaseEventer();
    MonitoredDocument.prototype.notifyChanged = function()
    {
        this.fireEvent("onchanged");
    }
    MonitoredDocument.prototype.dispose = function()
    {
        this.doc = null;
        this.dropEvent("onchanged");
    }

    var monitoredDocuments = [];

    function notifyChanged(elem)
    {
        if (monitoredDocuments.length > 0)
        {
            var monitored = getMonitoredDocument(elem.ownerDocument);
            if (monitored)
                monitored.notifyChanged();
        }
    }


    function monitorDocument(doc)
    {
        var monitor = getMonitoredDocument(doc);
        if (!monitor)
        {
            monitor = new MonitoredDocument(doc);
            monitoredDocuments.push(monitor);
        }
        return monitor;
    }

    function unmonitorDocument(doc)
    {
        for (var i = 0; i < monitoredDocuments.length; i++)
        {
            if (monitoredDocuments[i].doc == doc)
            {
                monitoredDocuments[i].doc == null;

                monitoredDocuments.splice(i, 1);
                return;
            }
        }

    }

    function getMonitoredDocument(doc)
    {
        for (var i = 0; i < monitoredDocuments.length; i++)
        {
            if (monitoredDocuments[i].doc == doc)
                return monitoredDocuments[i];
        }
    }

    this.XmlManipulator = Generic
    this.Generic = Generic
    this.Elements = Elements
    this.getXmlManipulator = getGeneric
    this.getGeneric = getGeneric
    this.getElements = getElements
    this.XmlManipulatorPersona = GenericPersona;
    this.monitorDocument = monitorDocument;
    this.unmonitorDocument = unmonitorDocument;


}
workspace.XML.builder._construct()


workspace.XML.cache = {}
workspace.XML.cache._construct = function()
{

    function StoredXML(elem) { this.element = elem; }
    StoredXML.prototype = new workspace.XML.builder.XmlManipulator();
    StoredXML.prototype.getKey = function() { return this.getAttribute("_sharedXmlID") }
    StoredXML.prototype.putKey = function(value) { this.putAttribute("_sharedXmlID", value) }
    StoredXML.prototype.getUserID = function()
    {
        var result = this.getAttribute("_userID", null);
        return result != null ? parseInt(result) : null
    }
    StoredXML.prototype.putUserID = function(value) { this.putAttribute("_userID", value) }
    StoredXML.prototype.getCreated = function()
    {
        var result = this.getAttribute("_created", null);
        return result != null ? new Date(result) : null
    }
    StoredXML.prototype.putCreated = function(value) { this.putAttribute("_created", value) }
    StoredXML.prototype.getExpires = function()
    {
        var result = this.getAttribute("_expires", null);
        return result != null ? new Date(result) : null
    }
    StoredXML.prototype.putExpires = function(value) { this.putAttribute("_expires", value) }

    function fetch(key, callback)
    {
        if (callback)
        {
            return workspace.XML.get("/sharedxml.ashx?xml=" + key, fnCallback);
        }
        else
        {
            var xml = workspace.XML.get("/sharedxml.ashx?xml=" + key)
            if (xml)
            {
                return new StoredXML(xml.documentElement);
            }
        }

        function fnCallback(xhr)
        {
            var xml = workspace.XML.getDOMFromRequest(xhr);
            if (xml)
            {
                callback(new StoredXML(xml.documentElement));
            }
        }
    }

    function store(xml)
    {
        var previousKey = null;
        var anon = false;
        var lifeSpan = null;

        for (var i = 1; i < arguments.length; i++)
        {
            var arg = arguments[i];
            if (arg != null)
            {
                if (arg.isString)
                {
                    previousKey = arg;
                }
                else if (arg.isTimeSpan)
                {
                    lifeSpan = arg
                }
                else
                {
                    anon = arg
                }
            }
        }

        var delim = "?";
        var url = "/sharedxml.ashx"

        if (previousKey)
        {
            url += delim + "xml=" + previousKey
            delim = "&"
        }

        if (anon)
        {
            url += delim + "anon=true"
            delim = "&"
        }

        if (lifeSpan)
        {
            url += delim + "lifespan=" + lifeSpan.totalMinutes;
        }

        result = workspace.XML.builder.getXmlManipulator(xml).post(url)
        if (result)
            return result.getText();
    }

    function drop(key)
    {
        return workspace.XML.get("/sharedxml.ashx?function=drop&xml=" + key) ? true : false;
    }

    this.fetch = fetch
    this.store = store
    this.drop = drop
    workspace.XML.StoredXML = StoredXML;


}
workspace.XML.cache._construct()