########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Rdf/RdfsHandler.py,v 1.9 2005/03/04 15:03:04 mbrown Exp $
"""
An RDF schema validator for statements in a Model

Copyright 2005 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

# Obtained from http://www.w3.org/TR/rdf-schema/rdfs-namespace
# on 2005-03-03. French labels and xml:lang attrs added locally.
# owl:Ontology Class definition added locally, derived from
# http://www.w3.org/2002/07/owl, so that the schema itself will validate

RDFS_XML = """<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
   xmlns:owl="http://www.w3.org/2002/07/owl#"
   xmlns:dc="http://purl.org/dc/elements/1.1/">

  <owl:Ontology
     rdf:about="http://www.w3.org/2000/01/rdf-schema#"
     dc:title="The RDF Schema vocabulary (RDFS)"/>

  <rdfs:Class rdf:about="http://www.w3.org/2002/07/owl#Ontology">
    <rdfs:label>Ontology</rdfs:label>
  </rdfs:Class>

  <rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Resource">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">Resource</rdfs:label>
    <rdfs:label xml:lang="fr">Ressource</rdfs:label>
    <rdfs:comment>The class resource, everything.</rdfs:comment>
  </rdfs:Class>

  <rdf:Property rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#type">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">type</rdfs:label>
    <rdfs:label xml:lang="fr">type</rdfs:label>
    <rdfs:comment>The subject is an instance of a class.</rdfs:comment>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Class">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">Class</rdfs:label>
    <rdfs:label xml:lang="fr">Classe</rdfs:label>
    <rdfs:comment>The class of classes.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdfs:Class>

  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#subClassOf">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">subClassOf</rdfs:label>
    <rdfs:label xml:lang="fr">sousClasseDe</rdfs:label>
    <rdfs:comment>The subject is a subclass of a class.</rdfs:comment>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#subPropertyOf">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">subPropertyOf</rdfs:label>
    <rdfs:label xml:lang="fr">sousPropri&#233;t&#233;De</rdfs:label>
    <rdfs:comment>The subject is a subproperty of a property.</rdfs:comment>
    <rdfs:range rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </rdf:Property>

  <rdfs:Class rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">Property</rdfs:label>
    <rdfs:label xml:lang="fr">Propri&#233;t&#233;</rdfs:label>
    <rdfs:comment>The class of RDF properties.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdfs:Class>

  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#comment">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">comment</rdfs:label>
    <rdfs:label xml:lang="fr">commentaire</rdfs:label>
    <rdfs:comment>A description of the subject resource.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#label">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">label</rdfs:label>
    <rdfs:label xml:lang="fr">libell&#233;</rdfs:label>
    <rdfs:comment>A human-readable name for the subject.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
  </rdf:Property>

  <!-- was: rdfs:ConstraintProperty rdf:ID="domain" -->
  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#domain">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">domain</rdfs:label>
    <rdfs:label xml:lang="fr">domaine</rdfs:label>
    <rdfs:comment>A domain of the subject property.</rdfs:comment>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </rdf:Property>

  <!-- was: rdfs:ConstraintProperty rdf:ID="range" -->
  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#range">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">range</rdfs:label>
    <rdfs:label xml:lang="fr">&#233;tendue</rdfs:label>
    <rdfs:comment>A range of the subject property.</rdfs:comment>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#seeAlso">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">seeAlso</rdfs:label>
    <rdfs:label xml:lang="fr">voirAussi</rdfs:label>
    <rdfs:comment>Further information about the subject resource.</rdfs:comment>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#isDefinedBy">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:subPropertyOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#seeAlso"/>
    <rdfs:label xml:lang="en">isDefinedBy</rdfs:label>
    <rdfs:label xml:lang="fr">estD&#233;finiPar</rdfs:label>
    <rdfs:comment>The defininition of the subject resource.</rdfs:comment>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Literal">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">Literal</rdfs:label>
    <rdfs:label xml:lang="fr">Litt&#233;ral</rdfs:label>
    <rdfs:comment>The class of literal values, eg. textual strings and integers.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdfs:Class>

  <rdfs:Class rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">Statement</rdfs:label>
    <rdfs:label xml:lang="fr">D&#233;claration</rdfs:label>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:comment>The class of RDF statements.</rdfs:comment>
  </rdfs:Class>

  <rdf:Property rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#subject">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">subject</rdfs:label>
    <rdfs:label xml:lang="fr">sujet</rdfs:label>
    <rdfs:comment>The subject of the subject RDF statement.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">predicate</rdfs:label>
    <rdfs:label xml:lang="fr">pr&#233;dicat</rdfs:label>
    <rdfs:comment>The predicate of the subject RDF statement.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#object">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">object</rdfs:label>
    <rdfs:label xml:lang="fr">objet</rdfs:label>
    <rdfs:comment>The object of the subject RDF statement.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Container">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">Container</rdfs:label>
    <rdfs:label xml:lang="fr">Enveloppe</rdfs:label>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:comment>The class of RDF containers.</rdfs:comment>
  </rdfs:Class>

  <rdfs:Class rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">Bag</rdfs:label>
    <rdfs:label xml:lang="fr">Ensemble</rdfs:label>
    <rdfs:comment>The class of unordered containers.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Container"/>
  </rdfs:Class>

  <rdfs:Class rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">Sequence</rdfs:label>
    <rdfs:label xml:lang="fr">S&#233;quence</rdfs:label>
    <rdfs:comment>The class of ordered containers.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Container"/>
  </rdfs:Class>

  <rdfs:Class rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">Alt</rdfs:label>
    <rdfs:label xml:lang="fr">Choix</rdfs:label>
    <rdfs:comment>The class of containers of alternatives.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Container"/>
  </rdfs:Class>

  <rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">ContainerMembershipProperty</rdfs:label>
    <rdfs:comment>The class of container membership properties, rdf:_1, rdf:_2, ..., all of which are sub-properties of 'member'.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </rdfs:Class>

  <rdf:Property rdf:about="http://www.w3.org/2000/01/rdf-schema#member">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">member</rdfs:label>
    <rdfs:comment>A member of the subject resource.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#value">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">value</rdfs:label>
    <rdfs:label xml:lang="fr">valuer</rdfs:label>
    <rdfs:comment>Idiomatic property used for structured values.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <!-- the following are new additions, Nov 2002 -->

  <rdfs:Class rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#List">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">List</rdfs:label>
    <rdfs:comment>The class of RDF Lists.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/></rdfs:Class>

  <rdf:List rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">nil</rdfs:label>
    <rdfs:comment>The empty list, with no items in it. If the rest of a list is nil then the list has no more items in it.</rdfs:comment>
  </rdf:List>

  <rdf:Property rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#first">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">first</rdfs:label>
    <rdfs:comment>The first item in the subject RDF list.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#List"/>
    <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
  </rdf:Property>

  <rdf:Property rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#rest">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">rest</rdfs:label>
    <rdfs:comment>The rest of the subject RDF list after the first item.</rdfs:comment>
    <rdfs:domain rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#List"/>
    <rdfs:range rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#List"/>
  </rdf:Property>

  <rdfs:Class rdf:about="http://www.w3.org/2000/01/rdf-schema#Datatype">
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/2000/01/rdf-schema#"/>
    <rdfs:label xml:lang="en">Datatype</rdfs:label>
    <rdfs:comment>The class of RDF datatypes.</rdfs:comment>
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Class"/>
  </rdfs:Class>

  <rdfs:Datatype rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral">
    <rdfs:subClassOf rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
    <rdfs:isDefinedBy rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
    <rdfs:label xml:lang="en">XMLLiteral</rdfs:label>
    <rdfs:comment>The class of XML literal values.</rdfs:comment>
  </rdfs:Datatype>

  <rdf:Description rdf:about="http://www.w3.org/2000/01/rdf-schema#">
    <rdfs:seeAlso rdf:resource="http://www.w3.org/2000/01/rdf-schema-more"/>
  </rdf:Description>

</rdf:RDF>"""

from Ft import FtException
from Ft.Lib.Uri import OsPathToUri
from Ft.Rdf import Model, SchemaHandler, RdfException
from Ft.Rdf import RDF_MS_BASE, RDF_SCHEMA_BASE
from Ft.Rdf.Serializers.Dom import Serializer


class RdfsConstraintViolation(FtException):
    PROPERTY_HAS_MULTIPLE_RANGES = 1
    INVALID_DOMAIN_FOR_PROPERTY = 2
    INVALID_RANGE_FOR_PROPERTY = 3

    def __init__(self, errorCode, *args):
        import MessageSource
        FtException.__init__(self, errorCode, MessageSource.RDFS_ERROR_MESSAGES, args)


class RdfsHandler(SchemaHandler):
    """
    Processing of RDF schema information on behalf of a model instance.
    Note that a RdfsHandler is designed to be associated with
    a single model in its lifecycle, which is basically:
    init -> initModel -> <user's schema operations> -> reclaim
    Do not attempt to reuse a RdfsHandler with multiple models
    unless you really know what you're doing
    """

    def __init__(self):
        """Initializer for a RdfsHandler.  There are no parameters."""
        from Ft.Rdf.Drivers import Memory
        db = Memory.CreateDb('')
        self._coreRdfsModel = Model.Model(db, None)
        from Ft.Xml.Domlette import NonvalidatingReader
        self._reader = NonvalidatingReader
        self._serializer = Serializer()
        self._model = None
        self._active = False
        self._deserialized_URIs = {}
        return

    def _deserialize(self, model, isrc, scope=None):
        """
        Given a model, InputSource, and optional scope,
        deserializes the RDF/XML document wrapped by the InputSource
        into the model. Caches InputSource URIs so as not to deserialize
        the same document twice.
        """
        uri = isrc.uri
        if uri not in self._deserialized_URIs:
            doc = self._reader.parse(isrc)
            self._serializer.deserialize(model, doc, scope)
            self._deserialized_URIs[uri] = True
        return

    def initModel(self, model):
        """
        Make sure that the core schema statements are in the model
        """
        self._model = model
        SchemaHandler.initModel(self)
        #FIXME: Use faster init method than deserialization
        dummy_uri = OsPathToUri(__file__, attemptAbsolute=True)
        dummy_uri += '-INTERNAL-RDFS-XML-STRING'
        from Ft.Xml.InputSource import DefaultFactory
        isrc = DefaultFactory.fromString(RDFS_XML, dummy_uri)
        self._deserialize(self._model, isrc, scope=RDF_SCHEMA_BASE)
        #Keep around a copy for future reference
        isrc = DefaultFactory.fromString(RDFS_XML, dummy_uri)
        self._deserialize(self._coreRdfsModel, isrc, scope=RDF_SCHEMA_BASE)
        self._active = True
        return

    def _complete(self, subject, predicate, object, statementUri, scope, flags):
        if self._active and predicate:
            #FIXME: Deal with statementUri more smartly
            #Gather all sub-properties for equivalence in the complete
            sub_prop_flags = {}
            remain_flags = flags.copy()
            if flags.get("predicateFlags"):
                sub_prop_flags = {'subjectFlags': flags.get("predicateFlags")}
                del remain_flags["predicateFlags"]
            props = []
            new_props = self._model._complete(
                None, RDF_SCHEMA_BASE + 'subPropertyOf',
                predicate, statementUri, scope, sub_prop_flags
                )
            props.extend(new_props)
            while new_props:
                newer_props = []
                for prop in new_props:
                    newer_props.extend(self._model._complete(
                        None, RDF_SCHEMA_BASE + 'subPropertyOf',
                        prop.subject, statementUri, scope, {}))
                new_props = newer_props
                props.extend(new_props)
            matches =  self._model._complete(
                subject, predicate, object, statementUri, scope, flags
                )
            for prop in props:
                matches.extend(self._model._complete(
                    subject, prop.subject, object, statementUri, scope,
                    remain_flags))
            return matches
        else:
            return self._model._complete(subject, predicate, object,
                                         statementUri, scope, flags)

    def isCoreRdfs(self, stmt):
        """Checks whether a statement comes from the core RDF meta-model."""
        return self._coreRdfsModel.contains(stmt)

    def isInstance(self, obj, class_):
        """Checks whether a resource is an instance of a class.  Note that this is also true if the resource is an instance of any subclass of the given class."""
        class_stmts = self._model.complete(obj, RDF_MS_BASE + 'type', None)
        for cs in class_stmts:
            if self.isSubClass(cs.object, class_):
                return True
        return False

    def isSubClass(self, class1, class2):
        """Checks whether a class is an instance of another class."""

        #Recursive for now.  Is it worth moving to iteration?
        if class1 == class2: return True
        subclass_stmts = self._model.complete(class1, RDF_SCHEMA_BASE + 'subClassOf', None)
        if not subclass_stmts: return False
        if subclass_stmts[0].object == class2: return True
        return self.isSubClass(subclass_stmts[0].object, class2)

    def processNewStatements(self, newStmts):
        """Called by the Mode when a new statement is about to be added."""
        if self._active and newStmts:
            self.checkConstraints(newStmts)
            for stmt in newStmts:
                self._active = False
                if stmt.predicate == RDF_SCHEMA_BASE + 'isDefinedBy':
                    from Ft.Xml.InputSource import DefaultFactory
                    isrc = DefaultFactory.fromUri(stmt.object)
                    self._deserialize(self._model, isrc)
                self._active = True
        return

    def checkConstraints(self, newStmts):
        """
        Raises exception if constraint violation found, else returns normally
        Does not yet check Extended Constraints (provide hooks?)
        """
        domain_uri = RDF_SCHEMA_BASE + 'domain'
        range_uri = RDF_SCHEMA_BASE + 'range'
        property_uri = RDF_MS_BASE + 'Property'
        resource_uri = RDF_SCHEMA_BASE + 'Resource'
        literal_uri = RDF_SCHEMA_BASE + 'Literal'
        seen_domains = {}
        seen_ranges = {}
        for newStmt in newStmts:
            #First check whether the statement itself
            #is the introduction of a constraint
            predicate = newStmt.predicate
            if self.isSubClass(newStmt.subject, RDF_MS_BASE + 'Property'):
                if predicate in (domain_uri, range_uri):
                    continue
            #Now check whether the statement is governed by another constraint
            if self.isInstance(predicate, property_uri):
                if predicate not in seen_domains:
                    domains = self._model.complete(predicate, domain_uri, None)
                    seen_domains[predicate] = domains
                else:
                    domains = seen_domains[predicate]
                if predicate not in seen_ranges:
                    ranges = self._model.complete(predicate, range_uri, None)
                    if len(ranges) > 1:
                        raise RdfsConstraintViolation(RdfsConstraintViolation.PROPERTY_HAS_MULTIPLE_RANGES, predicate)
                    seen_ranges[predicate] = ranges
                else:
                    ranges = seen_ranges[predicate]
                if domains:
                    meets_constraints = False
                    for domain in domains:
                        if domain.object == resource_uri:
                            meets_constraints = True
                        if self.isInstance(newStmt.subject, domain.object):
                            meets_constraints = True
                    if not meets_constraints:
                        raise RdfsConstraintViolation(RdfsConstraintViolation.INVALID_DOMAIN_FOR_PROPERTY, str(newStmt.subject), str(predicate))
                if ranges:
                    if not self.isInstance(newStmt.object, ranges[0].object) and ranges[0].object not in (resource_uri, literal_uri):
                        raise RdfsConstraintViolation(RdfsConstraintViolation.INVALID_RANGE_FOR_PROPERTY, str(newStmt.object), str(newStmt.predicate))
        return

