########################################################################
#
# File name:            DocumentDefinitionSerialization.py
#
#
"""

WWW: http://4suite.org        e-mail: support@4suite.org

Copyright 2002 Fourthought Inc, USA.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""

import cStringIO
from Ft.Xml.Domlette import Print, GetAllNs
from Ft.Xml import XML_NAMESPACE, XMLNS_NAMESPACE, EMPTY_PREFIX, EMPTY_NAMESPACE

from Ft.Xml import Domlette, XPath
from Ft.Server import FTSERVER_NAMESPACE, RESERVED_NAMESPACE
from Ft import Rdf
import CreationParams

from Ft.Xml.XLink import XLINK_NAMESPACE
from Ft.Xml.XPath import Util

def Serialize(bns, nss, maps, cp):
    """Serialize a XPath Document Definition from discrete components"""

    doc = Domlette.implementation.createDocument(None, None, None)
    de = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:DocDef')
    doc.appendChild(de)
    _SerializeBaseNames(doc, bns)

    nsm = _SerializeNsMap(doc, nss)
    if nsm:
        doc.documentElement.appendChild(nsm)

    rm = _SerializeRdfMaps(doc, maps)
    if rm:
        doc.documentElement.appendChild(rm)

    params = CreationParams._Serialize(doc, cp)
    doc.documentElement.appendChild(params)

    st = cStringIO.StringIO()
    Print(doc, stream=st)

    return st.getvalue()


def Deserialize(dd_dom):
    """Deserialize a document definition into its pieces"""

    con = XPath.Context.Context(dd_dom, processorNss={'ftss': FTSERVER_NAMESPACE})
    creationParams = CreationParams._Deserialize(con)
    bns = _DeserializeBaseNames(con)
    rdf_maps = _DeserializeRdfMaps(con)
    ns_maps = _DeserializeNsMaps(con)
    return bns, ns_maps, rdf_maps, creationParams


def _SerializeBaseNames(doc, bns):
    root = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:BaseNames')
    for b in bns:
        base = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:Base')
        base.setAttributeNS(XLINK_NAMESPACE, u'xlink:type', u'simple')
        base.setAttributeNS(XLINK_NAMESPACE, u'xlink:href', b)
        base.setAttributeNS(XLINK_NAMESPACE, u'xlink:actuate', u'onLoad')
        base.setAttributeNS(XLINK_NAMESPACE, u'xlink:show', u'embed')
        root.appendChild(base)
    doc.documentElement.appendChild(root)


def _SerializeNsMap(doc, nss):
    root = None
    for prefix, ns in nss.items():
        if not prefix: continue
        name = 'xmlns:'+prefix
        if doc.documentElement.hasAttributeNS(XMLNS_NAMESPACE,name):
            if doc.documentElement.getAttributeNS(XMLNS_NAMESPACE,name) != ns:
                #What the heck do we do here
                raise "If you ever get this then we need to talk"
        else:
            doc.documentElement.setAttributeNS(XMLNS_NAMESPACE,name, ns)

    #Make sure there is nothing extra there
    keys = map(lambda x: str(x),nss.keys())
    for node in doc.documentElement.attributes.values():
	if (node.namespaceURI == XMLNS_NAMESPACE and not node.localName in keys):
	    doc.documentElement.removeAttributeNode(node)

    return root


def _SerializeRdfMaps(doc, maps):
    root = None
    if maps:
        root = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:RdfMappings')
    for map in maps:
        m = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:RdfMapping')
        s = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:Subject')
        s.appendChild(doc.createTextNode(map[0]))
        m.appendChild(s)
        p = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:Predicate')
        p.appendChild(doc.createTextNode(map[1]))
        m.appendChild(p)
        o = doc.createElementNS(FTSERVER_NAMESPACE, u'ftss:Object')
        o.appendChild(doc.createTextNode(map[2]))
	if (len(map) == 4):
            if map[3] == Rdf.OBJECT_TYPE_RESOURCE:
                o.setAttributeNS(EMPTY_NAMESPACE, u'type', u'rdf:Resource')
        m.appendChild(o)
        root.appendChild(m)
    return root


def _DeserializeBaseNames(con):
    res = XPath.Evaluate('/ftss:DocDef/ftss:BaseNames/ftss:Base/@xlink:href', context=con)
    return map(lambda x:x.value,res)


_nsMapExpression = XPath.Compile('ftss:DocDef/ftss:NsMappings/ftss:NsMapping')
_nsMapUriExpression = XPath.Compile('string(ftss:Uri)')
_nsMapPrefixExpression = XPath.Compile('string(ftss:Prefix)')


def _DeserializeNsMaps(con):
    ownerDoc = con.node.rootNode
    ns_maps = GetAllNs(ownerDoc.documentElement)
    maps = _nsMapExpression.evaluate(con)
    oNode = con.node
    for nsm in maps:
        con.node = nsm
        prefix = _nsMapPrefixExpression.evaluate(con)
        uri = _nsMapUriExpression.evaluate(con)
        ns_maps[prefix] = uri

    con.node = oNode
    return ns_maps


_rdfMapExpression = XPath.Compile('ftss:DocDef/ftss:RdfMappings/ftss:RdfMapping')
_rdfMapSubjectExpression = XPath.Compile('string(ftss:Subject)')
_rdfMapPredicateExpression = XPath.Compile('string(ftss:Predicate)')
_rdfMapObjectExpression = XPath.Compile('string(ftss:Object)')
_rdfMapObjectTypeExpression = XPath.Compile('string(ftss:Object/@type)')


from Ft.Rdf import RDF_MS_BASE, OBJECT_TYPE_UNKNOWN, OBJECT_TYPE_LITERAL, OBJECT_TYPE_RESOURCE


def _DeserializeRdfMaps(con):
    maps = _rdfMapExpression.evaluate(con)
    rdf_maps = []
    oNode = con.node
    for rdfm in maps:
        con.node = rdfm
        subj = _rdfMapSubjectExpression.evaluate(con)
        pred = _rdfMapPredicateExpression.evaluate(con)
        obj = _rdfMapObjectExpression.evaluate(con)
        obj_type = OBJECT_TYPE_UNKNOWN
        if _rdfMapObjectTypeExpression.evaluate(con):
            obj_type_str = _rdfMapObjectTypeExpression.evaluate(con)
            #For backward compat
            if obj_type_str in ["RESOURCE", "LITERAL"]:
		obj_type = OBJECT_TYPE_LITERAL
		if obj_type_str == "RESOURCE":
		    obj_type =  OBJECT_TYPE_RESOURCE
            else:
                exp_name = Util.ExpandQName(obj_type_str, refNode=con.node)
                if exp_name == (RDF_MS_BASE, u'Resource'):
                    obj_type = OBJECT_TYPE_RESOURCE
                elif exp_name == (RDF_MS_BASE, u'Literal'):
                    obj_type = OBJECT_TYPE_LITERAL
                else:
                    #For now
                    obj_type = OBJECT_TYPE_LITERAL
        rdf_maps.append((subj, pred, obj, obj_type))
    con.node = oNode
    return rdf_maps


