ext/nokogiri/xml_schema.c
#include <nokogiri.h>
VALUE cNokogiriXmlSchema;
static void
xml_schema_deallocate(void *data)
{
xmlSchemaPtr schema = data;
xmlSchemaFree(schema);
}
static const rb_data_type_t xml_schema_type = {
.wrap_struct_name = "xmlSchema",
.function = {
.dfree = xml_schema_deallocate,
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
* call-seq:
* validate_document(document)
*
* Validate a Nokogiri::XML::Document against this Schema.
*/
static VALUE
validate_document(VALUE self, VALUE document)
{
xmlDocPtr doc;
xmlSchemaPtr schema;
xmlSchemaValidCtxtPtr valid_ctxt;
VALUE errors;
TypedData_Get_Struct(self, xmlSchema, &xml_schema_type, schema);
doc = noko_xml_document_unwrap(document);
errors = rb_ary_new();
valid_ctxt = xmlSchemaNewValidCtxt(schema);
if (NULL == valid_ctxt) {
/* we have a problem */
rb_raise(rb_eRuntimeError, "Could not create a validation context");
}
#ifdef HAVE_XMLSCHEMASETVALIDSTRUCTUREDERRORS
xmlSchemaSetValidStructuredErrors(
valid_ctxt,
Nokogiri_error_array_pusher,
(void *)errors
);
#endif
xmlSchemaValidateDoc(valid_ctxt, doc);
xmlSchemaFreeValidCtxt(valid_ctxt);
return errors;
}
/*
* call-seq:
* validate_file(filename)
*
* Validate a file against this Schema.
*/
static VALUE
validate_file(VALUE self, VALUE rb_filename)
{
xmlSchemaPtr schema;
xmlSchemaValidCtxtPtr valid_ctxt;
const char *filename ;
VALUE errors;
TypedData_Get_Struct(self, xmlSchema, &xml_schema_type, schema);
filename = (const char *)StringValueCStr(rb_filename) ;
errors = rb_ary_new();
valid_ctxt = xmlSchemaNewValidCtxt(schema);
if (NULL == valid_ctxt) {
/* we have a problem */
rb_raise(rb_eRuntimeError, "Could not create a validation context");
}
#ifdef HAVE_XMLSCHEMASETVALIDSTRUCTUREDERRORS
xmlSchemaSetValidStructuredErrors(
valid_ctxt,
Nokogiri_error_array_pusher,
(void *)errors
);
#endif
xmlSchemaValidateFile(valid_ctxt, filename, 0);
xmlSchemaFreeValidCtxt(valid_ctxt);
return errors;
}
static VALUE
xml_schema_parse_schema(
VALUE klass,
xmlSchemaParserCtxtPtr c_parser_context,
VALUE rb_parse_options
)
{
VALUE rb_errors;
int parse_options_int;
xmlSchemaPtr c_schema;
xmlExternalEntityLoader old_loader = 0;
VALUE rb_schema;
if (NIL_P(rb_parse_options)) {
rb_parse_options = rb_const_get_at(
rb_const_get_at(mNokogiriXml, rb_intern("ParseOptions")),
rb_intern("DEFAULT_SCHEMA")
);
}
rb_errors = rb_ary_new();
xmlSetStructuredErrorFunc((void *)rb_errors, Nokogiri_error_array_pusher);
#ifdef HAVE_XMLSCHEMASETPARSERSTRUCTUREDERRORS
xmlSchemaSetParserStructuredErrors(
c_parser_context,
Nokogiri_error_array_pusher,
(void *)rb_errors
);
#endif
parse_options_int = (int)NUM2INT(rb_funcall(rb_parse_options, rb_intern("to_i"), 0));
if (parse_options_int & XML_PARSE_NONET) {
old_loader = xmlGetExternalEntityLoader();
xmlSetExternalEntityLoader(xmlNoNetExternalEntityLoader);
}
c_schema = xmlSchemaParse(c_parser_context);
if (old_loader) {
xmlSetExternalEntityLoader(old_loader);
}
xmlSetStructuredErrorFunc(NULL, NULL);
xmlSchemaFreeParserCtxt(c_parser_context);
if (NULL == c_schema) {
xmlErrorConstPtr error = xmlGetLastError();
if (error) {
Nokogiri_error_raise(NULL, error);
} else {
rb_raise(rb_eRuntimeError, "Could not parse document");
}
return Qnil;
}
rb_schema = TypedData_Wrap_Struct(klass, &xml_schema_type, c_schema);
rb_iv_set(rb_schema, "@errors", rb_errors);
rb_iv_set(rb_schema, "@parse_options", rb_parse_options);
return rb_schema;
}
/*
* call-seq:
* read_memory(string) → Nokogiri::XML::Schema
*
* Create a new schema parsed from the contents of +string+
*
* [Parameters]
* - +string+: String containing XML to be parsed as a schema
*
* [Returns] Nokogiri::XML::Schema
*/
static VALUE
read_memory(int argc, VALUE *argv, VALUE klass)
{
VALUE rb_content;
VALUE rb_parse_options;
xmlSchemaParserCtxtPtr c_parser_context;
rb_scan_args(argc, argv, "11", &rb_content, &rb_parse_options);
c_parser_context = xmlSchemaNewMemParserCtxt(
(const char *)StringValuePtr(rb_content),
(int)RSTRING_LEN(rb_content)
);
return xml_schema_parse_schema(klass, c_parser_context, rb_parse_options);
}
/*
* call-seq:
* from_document(document) → Nokogiri::XML::Schema
*
* Create a new schema parsed from the +document+.
*
* [Parameters]
* - +document+: Nokogiri::XML::Document to be parsed
*
* [Returns] Nokogiri::XML::Schema
*/
static VALUE
rb_xml_schema_s_from_document(int argc, VALUE *argv, VALUE klass)
{
VALUE rb_document;
VALUE rb_parse_options;
VALUE rb_schema;
xmlDocPtr c_document;
xmlSchemaParserCtxtPtr c_parser_context;
int defensive_copy_p = 0;
rb_scan_args(argc, argv, "11", &rb_document, &rb_parse_options);
if (!rb_obj_is_kind_of(rb_document, cNokogiriXmlNode)) {
rb_raise(rb_eTypeError,
"expected parameter to be a Nokogiri::XML::Document, received %"PRIsVALUE,
rb_obj_class(rb_document));
}
if (!rb_obj_is_kind_of(rb_document, cNokogiriXmlDocument)) {
xmlNodePtr deprecated_node_type_arg;
NOKO_WARN_DEPRECATION("Passing a Node as the first parameter to Schema.from_document is deprecated. Please pass a Document instead. This will become an error in Nokogiri v1.17.0."); // TODO: deprecated in v1.15.3, remove in v1.17.0
Noko_Node_Get_Struct(rb_document, xmlNode, deprecated_node_type_arg);
c_document = deprecated_node_type_arg->doc;
} else {
c_document = noko_xml_document_unwrap(rb_document);
}
if (noko_xml_document_has_wrapped_blank_nodes_p(c_document)) {
// see https://github.com/sparklemotion/nokogiri/pull/2001
c_document = xmlCopyDoc(c_document, 1);
defensive_copy_p = 1;
}
c_parser_context = xmlSchemaNewDocParserCtxt(c_document);
rb_schema = xml_schema_parse_schema(klass, c_parser_context, rb_parse_options);
if (defensive_copy_p) {
xmlFreeDoc(c_document);
c_document = NULL;
}
return rb_schema;
}
void
noko_init_xml_schema(void)
{
cNokogiriXmlSchema = rb_define_class_under(mNokogiriXml, "Schema", rb_cObject);
rb_undef_alloc_func(cNokogiriXmlSchema);
rb_define_singleton_method(cNokogiriXmlSchema, "read_memory", read_memory, -1);
rb_define_singleton_method(cNokogiriXmlSchema, "from_document", rb_xml_schema_s_from_document, -1);
rb_define_private_method(cNokogiriXmlSchema, "validate_document", validate_document, 1);
rb_define_private_method(cNokogiriXmlSchema, "validate_file", validate_file, 1);
}