########################################################################
#
# File Name:            Serializer.py
#
# Documentation:        http://docs.4suite.org/4Rdf/Serializers/Serializer.py.html
#
"""
Serializer/Deserialize from a DOM Node in the form according to http://www.w3.org/TR/REC-rdf-syntax/
WWW: http://4suite.org/4RDF         e-mail: support@4suite.org

Copyright (c) 1999-2001 Fourthought Inc, USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""

import string, urllib, urlparse, cStringIO
from xml.dom import Node
from xml.dom.ext import Print

from Ft.Rdf import RDF_MS_BASE, RDF_SCHEMA_BASE, ANONYMOUS_FRAGMENT_BASE
from Ft.Rdf.Resource import Resource
from Ft.Rdf.Statement import Statement
from Ft.Rdf import Container, Model
from Ft.Rdf import RdfException, ParseException
from Ft.Lib import Set, Uuid
#from Ft.Rdf.Sequence import Sequence
#from Ft.Rdf.Alternative import Alternative
from Ft.Lib import pDomlette
from xml.xpath import Evaluate, Compile, Context
from xml.dom import XML_NAMESPACE, XMLNS_NAMESPACE


RDF_CONTAINER_EXPR = Compile(".//rdf:RDF")

CONTAINERS = ['Bag', 'Seq', 'Alt']

RDF_SUBJ_PREDICATE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#subject"
RDF_OBJ_PREDICATE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#object"
RDF_PRED_PREDICATE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate"
RDF_TYPE_PREDICATE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
RDF_VALUE_PREDICATE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#value"

RDF_PREDICATES = [
                  RDF_SUBJ_PREDICATE,
                  RDF_OBJ_PREDICATE,
                  RDF_PRED_PREDICATE,
                  RDF_TYPE_PREDICATE,
                  RDF_VALUE_PREDICATE,
                  ]


class Serializer:
    def __init__(self, reify=1):
        self.reify = 0
        
    def serialize(self, model, nsMap=None, selectUri=None, localResources=[],
                  implementation=None):

        implementation = implementation or pDomlette.DOMImplementation()
        doc = implementation.createDocument(RDF_MS_BASE, 'rdf:RDF', None)

        nsMap = nsMap or {}
        if not nsMap.has_key(RDF_MS_BASE):
            nsMap[RDF_MS_BASE] = 'rdf'

        stmts = model.statements()
        stmts = filter(lambda x: x.uri != RDF_SCHEMA_BASE, stmts)

        if selectUri:
            stmts = filter(lambda x, sel=selectUri: x.uri == sel, stmts)

        inlineResources = filter(lambda x, s=len(RDF_MS_BASE):
                                 x.predicate[:s] == x.object[:s] == RDF_MS_BASE
                                 and x.predicate[s:] == 'type'
                                 and x.object[s:] in CONTAINERS,
                                 stmts)

        inlineResources = map(lambda s: s.subject, inlineResources)

        for resource in inlineResources:
            used = self.exportInlineResource(doc, nsMap, resource, stmts)
            stmts = Set.Not(stmts, used)

        sorted = self.__sortStatementsForDump(stmts)
        for subject in sorted.keys():
            data = sorted[subject]
            id = ''
            if subject in localResources:
                id = subject
                if id[0] == '#': id = id[1:]
                subject = ''
            self.exportDescription(doc, nsMap, data, subject, id)
        return doc

    def exportInlineResource(self, doc, nsMap, subject, origStmts):
        #Get all statements with this as a subject
        stmts = filter(lambda x, s=subject: x.subject == s, origStmts)
        simpleStatements = {}
        containerStatements = {}
        used = []
        type = "Description"
        for stmt in stmts:
            predNs, predLocal  = self.splitUri(stmt.predicate)
            if (predNs, predLocal) == (RDF_MS_BASE, 'type'):
                #This has already been handled
                ns, testType = self.splitUri(stmt.object)
                if testType != 'Bag':
                    type = testType
                used.append(stmt)
            elif predNs == RDF_MS_BASE:
                #Must be a statement reference
                used.append(stmt)
                if predLocal[0] == '_':
                    #See what this points too:
                    nextUsed = filter(lambda x, s=stmt.object:
                                      x.subject == s,
                                      origStmts)
                    nextUsedType = filter(lambda x, n=RDF_MS_BASE+'Statement':
                                          x.object==n,
                                          nextUsed)
                    if len(nextUsedType):
                        #Yep, remove the reification
                        used = used + nextUsed
                    else:
                        if type == 'Description':
                            type = 'Bag'
                        containerStatements[predLocal[1:]] = stmt.object
                else:
                    #FIXME: We should serialize this back out as a regular statement
                    pass
            else:
                if not simpleStatements.has_key(stmt.predicate):
                    simpleStatements[stmt.predicate] = []
                simpleStatements[stmt.predicate].append(stmt.object)
                used.append(stmt)

        if type == 'Description':
            self.exportDescription(doc, nsMap, simpleStatements, id=subject)
        else:
            self.exportContainer(doc, doc.documentElement, nsMap, type,
                                 containerStatements, id=subject)
            if len(simpleStatements.keys()):
                self.exportDescription(doc, nsMap, simpleStatements, id=subject)
        return used

    def exportContainer(self, doc, parent, nsMap, type, stmts, id):
        cont = doc.createElementNS(RDF_MS_BASE, 'rdf:%s'%type)
        cont.setAttributeNS('', 'ID', id)

        keys = stmts.keys()
        keys.sort()

        for k in keys:
            li = doc.createElementNS(RDF_MS_BASE, "rdf:li")
            if self.isResource(stmts[k]):
                li.setAttributeNS('', 'resource', stmts[k])
            else:
                li.appendChild(doc.createTextNode(stmts[k]))
            cont.appendChild(li)

        parent.appendChild(cont)
        return

    def exportDescription(self, doc, nsMap, stmts, subject='', id=''):
        desc = doc.createElementNS(RDF_MS_BASE, 'rdf:Description')
        if subject:
            desc.setAttributeNS('', 'about', subject)
        if id:
            desc.setAttributeNS('', 'ID', id)
        doc.documentElement.appendChild(desc)
        self.exportSimpleStatements(doc, desc, nsMap, stmts)
        return

    def exportSimpleStatements(self, doc, parent, nsMap, stmts):
        for pred in stmts.keys():
            import os, urlparse
            urlparts = urlparse.urlparse(pred)
            ns = None
            if urlparts[5]:    #fragment
                ns = urlparse.urlunparse(urlparts[:5] + ("",)) + '#'
                if nsMap.has_key(ns):
                    prefix = nsMap[ns]
                else:
                    prefix = 'ns' + str(len(nsMap.keys()))
                    nsMap[ns] = prefix
                predicateName = prefix and prefix + ':' + urlparts[5] or urlparts[5]
            elif urlparts[2] and not urlparts[3] and not urlparts[4]:    #tail of path
                p1, p2 = os.path.split(urlparts[2])
                if p1:
                    ns = urlparse.urlunparse(urlparts[:2] + (p1,) + ('', '', ''))
                    if nsMap.has_key(ns):
                        prefix = nsMap[ns]
                    else:
                        prefix = 'ns' + str(len(nsMap.keys()))
                        nsMap[ns] = prefix
                    predicateName = prefix and prefix + ':' + p2 or p2
                else:
                    prefix = ''
                    predicateName = p2
            else:
                #FIXME: Need to specify the whole URI, then
                predicateName = pred
                pass

            for obj in stmts[pred]:
                predicate = doc.createElementNS(ns, predicateName)
                parent.appendChild(predicate)
                if self.isResource(obj):
                    if predicate.getAttributeNS(RDF_MS_BASE, 'resource'):
                        #parent = predicate.parentNode
                        new_pred = predicate.cloneNode(0)
                        new_pred.setAttributeNS('', 'resource', obj)
                        parent.appendChild(new_pred)
                    else:
                        predicate.setAttributeNS('', 'resource', obj)
                else:
                    object = doc.createTextNode(obj)
                    predicate.appendChild(object)

        return
            
    def isResource(self,res):
        fields = string.split(res,':')
        if len(fields) < 2:
            return 0
        return fields[0] in ['uri','urn','http','ftp','telnet','gopher','https']

    def __sortStatementsForDump(self, statements):
        sorted = {}
        for statement in statements:
            s = statement.subject
            p = statement.predicate
            o = statement.object
            if not sorted.has_key(s):
                sorted[s] = {}
            if not sorted[s].has_key(p):
                sorted[s][p] = []
            sorted[s][p].append(o)
        return sorted

    #6.1
    def deserialize(self, model, node, sourceUri):
        sourceUri = sourceUri or model.uri

        con = Context.Context(node, 1, 1, processorNss={'rdf': RDF_MS_BASE})
        rdf_nodes = RDF_CONTAINER_EXPR.evaluate(con)
        if not rdf_nodes: rdf_nodes = [node]
        for r in rdf_nodes:
            for child in r.childNodes:
                if child.nodeType != Node.ELEMENT_NODE: continue
                self.readObject(model, child, sourceUri)
        return

    #6.2
    def readObject(self, model, node, sourceUri):
        if node.namespaceURI == RDF_MS_BASE and node.localName in ['Bag', 'Seq', 'Alt']:
            return self.readContainer(model, node, sourceUri)
        else:
            return self.readDescription(model, node, sourceUri)

    #6.3
    def readDescription(self, model, node, sourceUri):
        # When we figure out to do with aboutEachPrefix
        #subject_prefixes = []
        id, about, about_each, about_each_prefix = self.readIdAboutAttr(
            sourceUri, node
            )
        bagId = self.readBagIdAttr(node)
        if bagId is not None:
            raise ParseException(ParseException.FEATURE_NOT_SUPPORTED, 'bagID')
        if about_each_prefix is not None:
            raise ParseException(ParseException.FEATURE_NOT_SUPPORTED, 'aboutEachPrefix')
        propertyAttrs = self.readPropertyAttrs(node)
        descUri = ''
        reify = 0
        if id is not None:
            #Note: if they don't specify a sourceUri, things can get confused.
            #Should we issue a warning?  We used to improperly issue an error.
            descUri = self.uriFromParts(sourceUri, '#', id)
            type_ = self.readTypeAttr(sourceUri, node)
            if type_:
                stmt = Statement(descUri, RDF_MS_BASE + 'type', type_,
                                 sourceUri=sourceUri)
                model.add(stmt)
            subjects = [descUri]
        elif about is not None:
            subjects = [about]
        elif about_each is not None:
            #To add support, retrieve bag according to URI and add each item to subjects
            raise ParseException(ParseException.FEATURE_NOT_SUPPORTED, 'aboutEach')
        else:
            subjects = [self.getAnonymousResourceId(sourceUri)]

        returnReference = subjects[0]
        
        if (node.namespaceURI, node.localName ) != (RDF_MS_BASE, 'Description'):
            #type node: an implicit "rdf:Description" + rdf:type
            type = self.uriFromParts(sourceUri, node.namespaceURI, node.localName)
            new_stmt = Statement(returnReference, RDF_MS_BASE+'type', type,
                                 sourceUri=sourceUri)
            model.add(new_stmt)

        for (namespaceURI, localName), value in propertyAttrs.items():
            for s in subjects:
                predicate = self.uriFromParts(sourceUri, namespaceURI, localName)
                model.add(Statement(s, predicate, value,
                                    sourceUri=sourceUri)
                          )

        self.readResource(model, node.childNodes, sourceUri, subjects)
        return returnReference

    #6.3
    def readResource(self, model, nodeList, sourceUri, subjects=None):
        subjects = subjects or [self.getAnonymousResourceId(sourceUri)]
        #print "readResource", nodeList, subjects
        statements = []
        for child in nodeList:
            if child.nodeType == Node.ELEMENT_NODE:
                statements.extend(self.readPropertyElt(model, child, subjects,
                                                       sourceUri)
                                  )

        #When bagID is supported "statements" should be used to reify added stataments

##        if reify:
##            stCtr = 1
##            for st in statements:
##                uri = st.reify(model)
##                predicate = RDF_MS_BASE + '_%s' % stCtr
##                stCtr = stCtr + 1
##                stmt = Statement(descUri, predicate, uri, sourceUri=sourceUri)
##                model.add(stmt)

        return subjects[0]

    #6.4
    def readContainer(self, model, node, sourceUri):
        id = self.readIdAttr(node)
        memberAttrs = self.readMemberAttrs(node,sourceUri)
        members = filter(lambda x:
                         (x.nodeType == Node.ELEMENT_NODE
                          and x.namespaceURI == RDF_MS_BASE
                          and x.localName == 'li'),
                         node.childNodes)

        #Make sure the the proper members are there based on type
        if node.localName == 'Bag':
            #A bag can have member* or memberAttrs*
            if len(memberAttrs.keys()) >0 and len(members) > 0:
                raise ParseException(ParseException.INVALID_CONTAINER_PROPERTY)
        elif node.localName == 'Seq':
            #A seq can have member* or memberAttrs*
            if len(memberAttrs.keys()) >0 and len(members) > 0:
                raise ParseException(ParseException.INVALID_CONTAINER_PROPERTY)
        elif node.localName == 'Alt':
            #A alt can have member+ or memberAttrs?
            if len(memberAttrs.keys()) >0 and len(members) > 0:
                raise ParseException(ParseException.INVALID_CONTAINER_PROPERTY)
            if len(memberAttrs.keys()) > 1:
                raise Exception("Only one memberAttr allowed in an Alt")
            elif not len(memberAttrs.keys()) and not len(members):
                raise Exception("More than one member required on an Alt")
        else:
            raise Exception(INVALID_CONTAINER_TYPE, node.localName)

        #if this doesn't have an id then we need to make one up
        if not id:
            id = self.getAnonymousResourceId(sourceUri)
        else:
            origId = id
            if not self.isResource(id):
                id = self.uriFromParts(sourceUri, '#', id)

        stmt = Statement(id, RDF_MS_BASE+'type', RDF_MS_BASE+node.localName, sourceUri=sourceUri)
        model.add(stmt)

        for attrId,value in memberAttrs.items():
            pred = RDF_MS_BASE + '_%s' % attrId
            stmt = Statement(id, pred, value, sourceUri=sourceUri)
            model.add(stmt)

        ctr = 1
        for m in members:
            object = self.readResourceAttr(sourceUri, m)
            children = m.childNodes
            if object and len(children):
                raise Exception("Only resource or children allowed in %s:%s" % (node.namespaceURI, node.localName))
            if not object and not len(children):
                raise Exception("Resource or children required in %s:%s" % (node.namespaceURI, node.localName))
            if len(children) >0 and len(children) != 1 and children[0].nodeType != Node.TEXT_NODE:
                raise Exception("Only one text node allowed as a child of %s:%s" % (node.namespaceURI, node.localName))
            if not object:
                object = children[0].data
            predicate = RDF_MS_BASE + '_%d' % ctr
            ctr = ctr + 1
            stmt = Statement(id, predicate, object, sourceUri=sourceUri)
            model.add(stmt)
        return id

    #6.5, 6.6
    def readIdAboutAttr(self, sourceUri, node):
        id = self.readRdfKeyAttribute(node, 'ID')
        if id is not None:
            self.validateIdSymbol(id)
        
        about = self.readRdfKeyAttribute(node, 'about')
        if about is not None:
            about = urlparse.urljoin(sourceUri, about)
            
        about_each = self.readRdfKeyAttribute(node, 'aboutEach')
        if about_each is not None:
            about_each = urlparse.urljoin(sourceUri, about_each)
            
        about_each_prefix = self.readRdfKeyAttribute(node, 'aboutEachPrefix')
        if about_each_prefix is not None:
            about_each_prefix = urlparse.urljoin(sourceUri, about_each_prefix)

        if (id != None) + \
           (about != None) + \
           (about_each != None)  + \
           (about_each_prefix != None) > 1:
            raise Exception("Only one id, about, aboutEach or aboutEachPrefix allowed on %s:%s" % (node.namespaceURI,node.localName))

        return (id, about, about_each, about_each_prefix)

    #6.6
    def readIdAttr(self, node):
        id = self.readRdfKeyAttribute(node, 'ID')
        if id is not None:
            self.validateIdSymbol(id)
        return id
        
    #6.9
    def readBagIdAttr(self, node): 
        bid = self.readRdfKeyAttribute(node, 'bagID')
        if bid is not None:
            self.validateIdSymbol(bid)
        return bid

    #6.10
    def readPropertyAttrs(self, node):
        props = {}
        for attr in node.attributes.values():
            if attr.namespaceURI == XMLNS_NAMESPACE:
                continue
            if attr.namespaceURI == RDF_MS_BASE:
                if attr.localName == 'type':
                    self.validateUriReference(attr.value)
                    props[(RDF_MS_BASE, "type")] = attr.value
                elif attr.localName == 'value':
                    props[(RDF_MS_BASE, "value")] = attr.value
            elif attr.namespaceURI == XML_NAMESPACE:
                #Section 6 says xml:lang can be ignored as a property attr
                if attr.localName == 'lang':
                    continue
            elif attr.namespaceURI:
                props[(attr.namespaceURI, attr.localName)] = attr.value
            elif attr.localName not in ['about','aboutEach','ID','BagID','resource','parseType','aboutEachPrefix']:
                props[(attr.namespaceURI, attr.localName)] = attr.value
        #print "readPropertyAttrs result:", props
        return props

    #6.11
    def readTypeAttr(self, sourceUri, node):
        type_ = self.readRdfKeyAttribute(node, 'type')
        if type_ is not None:
            type_ = urlparse.urljoin(sourceUri, type_)
        return type_

    #6.12
    def readPropertyElt(self, model, node, subjects, sourceUri):
        #< propName idAttr? > value </propName>
        #< propName idAttr? parseLiteral > literal </ propName >
        #< propName idAttr? parseResource > propertyElt*  </  propName >
        #< propName idRefAttr? bagIdAttr? propAttr* />

        statements = []
        predicate = None
        object = None
        parseType = self.readParseType(node)

        #The id representing the reification of the statement
        id = self.readIdAttr(node)

        ns, localName = self.readPropName(node)
        #print "propertyElt", subjects, ns, localName

        predicate = self.uriFromParts('', ns, localName)

        if ns == RDF_MS_BASE and predicate not in RDF_PREDICATES:
            raise ParseException(ParseException.INVALID_PREDICATE, predicate)

        reify = 0
        #Properties and values expressed in XML attribute form within
        #an empty XML element E by productions [6.10] and [6.12] are
        #equivalent to the same properties and values expressed as XML
        #content of a single Description element D which would become
        #the content of E. 
        propertyAttrs = self.readPropertyAttrs(node)
        if propertyAttrs:
            if node.hasChildNodes():
                raise ParseException(ParseException.NONEMPTY_PROPELT_WITH_PROPATTRS, node.nodeName)
            if id:
                subj = self.uriFromParts(sourceUri, '#', id)
            else:
                subj = self.readResourceAttr(sourceUri, node) or \
                       self.getAnonymousResourceId(sourceUri)
            for (a_nsuri, a_lname), a_value in propertyAttrs.items():
                pred = self.uriFromParts(sourceUri, a_nsuri, a_lname)
                stmt = Statement(subj, pred, a_value, sourceUri=sourceUri)
                model.add(stmt)
                statements.append(stmt)
            object = subj
        else:
            #NOTE: Arbitrary decision: resource attribs have precedence
            #over the property element body
            object = self.readResourceAttr(sourceUri, node)
            if not object:
                try:
                    object = self.readValue(model, node, sourceUri, parseType)
                except ParseException:
                    raise ParseException(ParseException.MULTIPLE_VALUE_OBJECTS, predicate)
            if id:
                reify = 1

        #Note the difference.  An resource object cannot be
        # an empty string (invalud URI Ref), but a literal object can.
        if object is None:
            return statements

        for s in subjects:
            stmt = Statement(s, predicate, object, sourceUri=sourceUri)
            model.add(stmt)
            statements.append(stmt)
            if reify:
                r_subj = Statement(id, RDF_SUBJ_PREDICATE, s, sourceUri=sourceUri)
                r_pred = Statement(id, RDF_OBJ_PREDICATE, object, sourceUri=sourceUri)
                r_obj = Statement(id, RDF_PRED_PREDICATE, predicate, sourceUri=sourceUri)
                model.add([r_subj, r_pred, r_obj])                
        return statements

    #6.14
    def readPropName(self,node):
        propName = node.tagName
        return (node.namespaceURI, node.localName)

    #6.17
    def readValue(self, model, node, sourceUri, parseType):
        # value          ::= obj | string
        #print 'readValue', node, parseType
        if parseType == 'Resource':
            return self.readResource(model, node.childNodes, sourceUri)
        elif parseType == 'Literal':
            stream = cStringIO.StringIO()
            for child in node.childNodes:
                Print(child, stream)
            return stream.getvalue()

        elementNodes = filter(lambda x: x.nodeType == Node.ELEMENT_NODE,
                              node.childNodes)
        if len(elementNodes) == 1:
            # object
            obj_elem = elementNodes[0]
            if obj_elem.namespaceURI == RDF_MS_BASE and obj_elem.localName in CONTAINERS:
                return self.readContainer(model, obj_elem, sourceUri)
            else:
                return self.readDescription(model, obj_elem, sourceUri)
        elif elementNodes:
            # error
            raise ParseException(ParseException.MULTIPLE_VALUE_OBJECTS, "")
        else:
            # string
            textNodes = filter(lambda x: x.nodeType == Node.TEXT_NODE,
                               node.childNodes)
            if not len(textNodes):
                return None
            rt = ''
            for t in textNodes:
                rt = rt + self.readString(t.data)
            return rt

    #6.18
    def readResourceAttr(self, sourceUri, node):
        #RDF M&S technically doesn't allow the qualified form,
        #But user/implementor consensus allows this under the
        #vague XML NS concept of global attrs
        resource = self.readRdfKeyAttribute(node, 'resource')
        if resource is not None:
            resource = urlparse.urljoin(sourceUri, resource)
        return resource

    def readRdfKeyAttribute(self, node, local, defaultNsAllowed=1):
        """
        Returns None if the attribute is not on the element in null or
        RDF namespace, otherwise returns the attribute value
        Note that RDF M&S technically doesn't allow the qualified form,
        But user/implementor consensu allows this under the
        XML NS concept of global attrs
        """
        val = None
        if node.hasAttributeNS(RDF_MS_BASE, local):
            val = node.getAttributeNS(RDF_MS_BASE, local)
        elif defaultNsAllowed and node.hasAttributeNS('', local):
            val = node.getAttributeNS('', local)
        return val

    #6.20
    def readUriReference(self, sourceUri, uri):
        return uri and urlparse.urljoin(sourceUri,uri)

    #6.24
    def readString(self, st):
        self.validateString(st)
        return st

    #6.31
    def readMemberAttrs(self, node, sourceUri):
        attrs = {}
        for attr in node.attributes.values():
            if attr.namespaceURI == XMLNS_NAMESPACE:
                continue
            if (attr.namespaceURI, attr.localName[0]) == (RDF_MS_BASE, '_'):
                attrs[attr.localName[1:]] = attr.value
        return attrs

    # 6.32, 6.33
    def readParseType(self, node):
        if node.hasAttributeNS('', 'parseType') or node.hasAttributeNS(RDF_MS_BASE, 'parseType'):
            parseType = node.getAttributeNS('', 'parseType') or node.getAttributeNS(RDF_MS_BASE, 'parseType')
            if parseType != 'Resource':
                # See paragarph 3 following BNF in section 6
                # for defaulting of parseType
                parseType == 'Literal'
            return parseType
        return ''

    def uriFromParts(self, baseUri, part1, part2):
        """
        From RDF-MS, part 6:
        
        '...the expansion of the namespace-qualified tag name
        (Generic Identifier) of E. This expansion is generated by
        concatenating the namespace name given in the namespace
        declaration with the LocalPart of the qualified name.'
        ...
        'URI-References are resolved to resource identifiers by first
        resolving the URI-reference to absolute form as specified by
        [RFC 2396] using the base URI of the document in which the RDF
        statements appear.'
        """
        initial = part1 + part2
        resolved = urlparse.urljoin(baseUri, initial)
        return resolved

    def splitUri(self, uri):
        index = string.find(uri, '#')
        if index == -1:
            return ('', uri)
        return (uri[:index+1], uri[index+1:])

    def validateIdSymbol(self, idSym):
        #FIXME Validate?
        return 1

    def validateUriReference(self, ref):
        #FIXME Validate?
        return 1

    def validateString(self, st):
        #FIXME Validate?
        return 1

    def getAnonymousResourceId(self, sourceUri):
        uuid = Uuid.UuidAsString(Uuid.GenerateUuid())
        return self.uriFromParts(sourceUri, '#',
                                 ANONYMOUS_FRAGMENT_BASE + uuid)

    #Deprecated
    def exportToDom(self, model, nsMap=None):
        serialize(self, model, nsMap)

    def importFromDom(self, model, node, sourceUri):
        deserialize(self, model, node, sourceUri)

