summaryrefslogtreecommitdiff
path: root/src/lxml/xmlschema.pxi
blob: 84e75e5913369721db41fd67381b8e6b105f3c8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#  support for XMLSchema validation
cimport xmlschema

class XMLSchemaError(LxmlError):
    """Base class of all XML Schema errors
    """
    pass

class XMLSchemaParseError(XMLSchemaError):
    """Error while parsing an XML document as XML Schema.
    """
    pass

class XMLSchemaValidateError(XMLSchemaError):
    """Error while validating an XML document with an XML Schema.
    """
    pass

################################################################################
# XMLSchema

cdef class XMLSchema(_Validator):
    """Turn a document into an XML Schema validator.

    Either pass a schema as Element or ElementTree, or pass a file or
    filename through the ``file`` keyword argument.
    """
    cdef xmlschema.xmlSchema* _c_schema
    def __init__(self, etree=None, *, file=None):
        cdef _Document doc
        cdef _Element root_node
        cdef xmlDoc* fake_c_doc
        cdef xmlNode* c_node
        cdef char* c_href
        cdef xmlschema.xmlSchemaParserCtxt* parser_ctxt
        _Validator.__init__(self)
        self._c_schema = NULL
        fake_c_doc = NULL
        if etree is not None:
            doc = _documentOrRaise(etree)
            root_node = _rootNodeOrRaise(etree)

            # work around for libxml2 bug if document is not XML schema at all
            #if _LIBXML_VERSION_INT < 20624:
            c_node = root_node._c_node
            c_href = _getNs(c_node)
            if c_href is NULL or \
                   cstd.strcmp(c_href, 'http://www.w3.org/2001/XMLSchema') != 0:
                raise XMLSchemaParseError("Document is not XML Schema")

            fake_c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
            self._error_log.connect()
            parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(fake_c_doc)
        elif file is not None:
            if _isString(file):
                filename = _encodeFilename(file)
                self._error_log.connect()
                parser_ctxt = xmlschema.xmlSchemaNewParserCtxt(_cstr(filename))
            else:
                doc = _parseDocument(file, None)
                self._error_log.connect()
                parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(doc._c_doc)
        else:
            raise XMLSchemaParseError("No tree or file given")

        if parser_ctxt is not NULL:
            self._c_schema = xmlschema.xmlSchemaParse(parser_ctxt)
            if _LIBXML_VERSION_INT >= 20624:
                xmlschema.xmlSchemaFreeParserCtxt(parser_ctxt)

        self._error_log.disconnect()

        if fake_c_doc is not NULL:
            _destroyFakeDoc(doc._c_doc, fake_c_doc)

        if self._c_schema is NULL:
            raise XMLSchemaParseError(
                self._error_log._buildExceptionMessage(
                    "Document is not valid XML Schema"),
                self._error_log)

    def __dealloc__(self):
        xmlschema.xmlSchemaFree(self._c_schema)

    def __call__(self, etree):
        """Validate doc using XML Schema.

        Returns true if document is valid, false if not.
        """
        cdef xmlschema.xmlSchemaValidCtxt* valid_ctxt
        cdef _Document doc
        cdef _Element root_node
        cdef xmlDoc* c_doc
        cdef int ret

        doc = _documentOrRaise(etree)
        root_node = _rootNodeOrRaise(etree)

        self._error_log.connect()
        valid_ctxt = xmlschema.xmlSchemaNewValidCtxt(self._c_schema)
        if valid_ctxt is NULL:
            self._error_log.disconnect()
            return python.PyErr_NoMemory()

        c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
        with nogil:
            ret = xmlschema.xmlSchemaValidateDoc(valid_ctxt, c_doc)
        _destroyFakeDoc(doc._c_doc, c_doc)

        xmlschema.xmlSchemaFreeValidCtxt(valid_ctxt)

        self._error_log.disconnect()
        if ret == -1:
            raise XMLSchemaValidateError(
                "Internal error in XML Schema validation.",
                self._error_log)
        if ret == 0:
            return True
        else:
            return False

    cdef _ParserSchemaValidationContext _newSaxValidator(self):
        cdef _ParserSchemaValidationContext context
        context = NEW_SCHEMA_CONTEXT(_ParserSchemaValidationContext)
        context._schema = self
        context._valid_ctxt = NULL
        context._sax_plug = NULL
        return context

cdef class _ParserSchemaValidationContext:
    cdef XMLSchema _schema
    cdef xmlschema.xmlSchemaValidCtxt* _valid_ctxt
    cdef xmlschema.xmlSchemaSAXPlugStruct* _sax_plug

    def __dealloc__(self):
        if self._sax_plug:
            self.disconnect()
        if self._valid_ctxt:
            xmlschema.xmlSchemaFreeValidCtxt(self._valid_ctxt)

    cdef _ParserSchemaValidationContext copy(self):
        return self._schema._newSaxValidator()

    cdef int connect(self, xmlparser.xmlParserCtxt* c_ctxt) except -1:
        if self._valid_ctxt is NULL:
            self._valid_ctxt = xmlschema.xmlSchemaNewValidCtxt(
                self._schema._c_schema)
            if self._valid_ctxt is NULL:
                return python.PyErr_NoMemory()
        self._sax_plug = xmlschema.xmlSchemaSAXPlug(
            self._valid_ctxt, &c_ctxt.sax, &c_ctxt.userData)

    cdef void disconnect(self):
        xmlschema.xmlSchemaSAXUnplug(self._sax_plug)
        self._sax_plug = NULL

    cdef bint isvalid(self):
        if self._valid_ctxt is NULL:
            return 1 # valid
        return xmlschema.xmlSchemaIsValid(self._valid_ctxt)

cdef extern from "etree_defs.h":
    # macro call to 't->tp_new()' for fast instantiation
    cdef _ParserSchemaValidationContext NEW_SCHEMA_CONTEXT "PY_NEW" (object t)