2005-07-23 19:00:22 +04:00
/*
2005-07-31 01:09:12 +04:00
* schematron . c : implementation of the Schematron schema validity checking
2005-07-23 19:00:22 +04:00
*
* See Copyright for the status of this software .
*
2005-07-31 01:09:12 +04:00
* Daniel Veillard < daniel @ veillard . com >
*/
/*
* TODO :
* + double check the semantic , especially
* - multiple rules applying in a single pattern / node
* - the semantic of libxml2 patterns vs . XSLT production referenced
* by the spec .
* + export of results in SVRL
* + full parsing and coverage of the spec , conformance of the input to the
* spec
* + divergences between the draft and the ISO proposed standard : - (
* + hook and test include
* + try and compare with the XSLT version
2005-07-23 19:00:22 +04:00
*/
# define IN_LIBXML
# include "libxml.h"
# ifdef LIBXML_SCHEMATRON_ENABLED
# include <string.h>
# include <libxml/parser.h>
# include <libxml/tree.h>
# include <libxml/uri.h>
# include <libxml/xpath.h>
# include <libxml/xpathInternals.h>
# include <libxml/pattern.h>
# include <libxml/schematron.h>
# define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
2005-07-31 01:09:12 +04:00
# define SCT_OLD_NS BAD_CAST "http: //www.ascc.net/xml/schematron"
# define XML_SCHEMATRON_NS BAD_CAST "http: //purl.oclc.org/dsdl/schematron"
2005-07-23 19:00:22 +04:00
static const xmlChar * xmlSchematronNs = XML_SCHEMATRON_NS ;
2005-07-31 01:09:12 +04:00
static const xmlChar * xmlOldSchematronNs = SCT_OLD_NS ;
2005-07-23 19:00:22 +04:00
# define IS_SCHEMATRON(node, elem) \
( ( node ! = NULL ) & & ( node - > type = = XML_ELEMENT_NODE ) & & \
( node - > ns ! = NULL ) & & \
( xmlStrEqual ( node - > name , ( const xmlChar * ) elem ) ) & & \
2005-07-31 01:09:12 +04:00
( ( xmlStrEqual ( node - > ns - > href , xmlSchematronNs ) ) | | \
( xmlStrEqual ( node - > ns - > href , xmlOldSchematronNs ) ) ) )
2005-07-23 19:00:22 +04:00
# define NEXT_SCHEMATRON(node) \
while ( node ! = NULL ) { \
2012-09-11 09:26:36 +04:00
if ( ( node - > type = = XML_ELEMENT_NODE ) & & ( node - > ns ! = NULL ) & & \
2005-07-31 01:09:12 +04:00
( ( xmlStrEqual ( node - > ns - > href , xmlSchematronNs ) ) | | \
( xmlStrEqual ( node - > ns - > href , xmlOldSchematronNs ) ) ) ) \
2005-07-23 19:00:22 +04:00
break ; \
node = node - > next ; \
}
/**
* TODO :
*
* macro to flag unimplemented blocks
*/
2012-09-11 09:26:36 +04:00
# define TODO \
2005-07-23 19:00:22 +04:00
xmlGenericError ( xmlGenericErrorContext , \
" Unimplemented block at %s:%d \n " , \
__FILE__ , __LINE__ ) ;
2005-07-31 01:09:12 +04:00
typedef enum {
XML_SCHEMATRON_ASSERT = 1 ,
XML_SCHEMATRON_REPORT = 2
} xmlSchematronTestType ;
2005-07-23 19:00:22 +04:00
/**
* _xmlSchematronTest :
*
* A Schematrons test , either an assert or a report
*/
typedef struct _xmlSchematronTest xmlSchematronTest ;
typedef xmlSchematronTest * xmlSchematronTestPtr ;
struct _xmlSchematronTest {
xmlSchematronTestPtr next ; /* the next test in the list */
2005-07-31 01:09:12 +04:00
xmlSchematronTestType type ; /* the test type */
2005-07-23 19:00:22 +04:00
xmlNodePtr node ; /* the node in the tree */
xmlChar * test ; /* the expression to test */
xmlXPathCompExprPtr comp ; /* the compiled expression */
2005-07-24 18:27:16 +04:00
xmlChar * report ; /* the message to report */
2005-07-23 19:00:22 +04:00
} ;
/**
* _xmlSchematronRule :
*
* A Schematrons rule
*/
typedef struct _xmlSchematronRule xmlSchematronRule ;
typedef xmlSchematronRule * xmlSchematronRulePtr ;
struct _xmlSchematronRule {
xmlSchematronRulePtr next ; /* the next rule in the list */
2005-07-31 01:09:12 +04:00
xmlSchematronRulePtr patnext ; /* the next rule in the pattern list */
2005-07-23 19:00:22 +04:00
xmlNodePtr node ; /* the node in the tree */
xmlChar * context ; /* the context evaluation rule */
xmlSchematronTestPtr tests ; /* the list of tests */
xmlPatternPtr pattern ; /* the compiled pattern associated */
2005-07-24 18:27:16 +04:00
xmlChar * report ; /* the message to report */
2005-07-23 19:00:22 +04:00
} ;
2005-07-31 01:09:12 +04:00
/**
* _xmlSchematronPattern :
*
* A Schematrons pattern
*/
typedef struct _xmlSchematronPattern xmlSchematronPattern ;
typedef xmlSchematronPattern * xmlSchematronPatternPtr ;
struct _xmlSchematronPattern {
xmlSchematronPatternPtr next ; /* the next pattern in the list */
xmlSchematronRulePtr rules ; /* the list of rules */
xmlChar * name ; /* the name of the pattern */
} ;
2005-07-23 19:00:22 +04:00
/**
* _xmlSchematron :
*
* A Schematrons definition
*/
struct _xmlSchematron {
const xmlChar * name ; /* schema name */
2005-07-31 17:43:14 +04:00
int preserve ; /* was the document passed by the user */
2005-07-23 19:00:22 +04:00
xmlDocPtr doc ; /* pointer to the parsed document */
int flags ; /* specific to this schematron */
void * _private ; /* unused by the library */
2016-04-13 17:56:07 +03:00
xmlDictPtr dict ; /* the dictionary used internally */
2005-07-23 19:00:22 +04:00
const xmlChar * title ; /* the title if any */
int nbNs ; /* the number of namespaces */
int nbPattern ; /* the number of patterns */
2005-07-31 01:09:12 +04:00
xmlSchematronPatternPtr patterns ; /* the patterns found */
2005-07-23 19:00:22 +04:00
xmlSchematronRulePtr rules ; /* the rules gathered */
int nbNamespaces ; /* number of namespaces in the array */
int maxNamespaces ; /* size of the array */
const xmlChar * * namespaces ; /* the array of namespaces */
} ;
/**
* xmlSchematronValidCtxt :
*
* A Schematrons validation context
*/
struct _xmlSchematronValidCtxt {
int type ;
int flags ; /* an or of xmlSchematronValidOptions */
xmlDictPtr dict ;
int nberrors ;
int err ;
xmlSchematronPtr schema ;
xmlXPathContextPtr xctxt ;
FILE * outputFile ; /* if using XML_SCHEMATRON_OUT_FILE */
xmlBufferPtr outputBuffer ; /* if using XML_SCHEMATRON_OUT_BUFFER */
2013-06-15 00:20:37 +04:00
# ifdef LIBXML_OUTPUT_ENABLED
2005-07-23 19:00:22 +04:00
xmlOutputWriteCallback iowrite ; /* if using XML_SCHEMATRON_OUT_IO */
xmlOutputCloseCallback ioclose ;
2013-06-15 00:20:37 +04:00
# endif
2005-07-23 19:00:22 +04:00
void * ioctx ;
2008-03-14 15:46:42 +03:00
/* error reporting data */
void * userData ; /* user specific data block */
xmlSchematronValidityErrorFunc error ; /* the callback in case of errors */
xmlSchematronValidityWarningFunc warning ; /* callback in case of warning */
xmlStructuredErrorFunc serror ; /* the structured function */
2005-07-23 19:00:22 +04:00
} ;
struct _xmlSchematronParserCtxt {
int type ;
const xmlChar * URL ;
xmlDocPtr doc ;
int preserve ; /* Whether the doc should be freed */
const char * buffer ;
int size ;
2016-04-13 17:56:07 +03:00
xmlDictPtr dict ; /* dictionary for interned string names */
2005-07-23 19:00:22 +04:00
int nberrors ;
int err ;
xmlXPathContextPtr xctxt ; /* the XPath context used for compilation */
xmlSchematronPtr schema ;
int nbNamespaces ; /* number of namespaces in the array */
int maxNamespaces ; /* size of the array */
const xmlChar * * namespaces ; /* the array of namespaces */
int nbIncludes ; /* number of includes in the array */
int maxIncludes ; /* size of the array */
xmlNodePtr * includes ; /* the array of includes */
2008-03-14 15:46:42 +03:00
/* error reporting data */
2005-07-23 19:00:22 +04:00
void * userData ; /* user specific data block */
xmlSchematronValidityErrorFunc error ; /* the callback in case of errors */
xmlSchematronValidityWarningFunc warning ; /* callback in case of warning */
xmlStructuredErrorFunc serror ; /* the structured function */
} ;
# define XML_STRON_CTXT_PARSER 1
# define XML_STRON_CTXT_VALIDATOR 2
/************************************************************************
* *
* Error reporting *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* xmlSchematronPErrMemory :
* @ node : a context node
* @ extra : extra informations
*
* Handle an out of memory condition
*/
static void
xmlSchematronPErrMemory ( xmlSchematronParserCtxtPtr ctxt ,
const char * extra , xmlNodePtr node )
{
if ( ctxt ! = NULL )
ctxt - > nberrors + + ;
__xmlSimpleError ( XML_FROM_SCHEMASP , XML_ERR_NO_MEMORY , node , NULL ,
extra ) ;
}
/**
* xmlSchematronPErr :
* @ ctxt : the parsing context
* @ node : the context node
* @ error : the error code
* @ msg : the error message
* @ str1 : extra data
* @ str2 : extra data
2012-09-11 09:26:36 +04:00
*
2005-07-23 19:00:22 +04:00
* Handle a parser error
*/
2016-05-13 10:13:17 +03:00
static void LIBXML_ATTR_FORMAT ( 4 , 0 )
2005-07-23 19:00:22 +04:00
xmlSchematronPErr ( xmlSchematronParserCtxtPtr ctxt , xmlNodePtr node , int error ,
const char * msg , const xmlChar * str1 , const xmlChar * str2 )
{
xmlGenericErrorFunc channel = NULL ;
xmlStructuredErrorFunc schannel = NULL ;
void * data = NULL ;
if ( ctxt ! = NULL ) {
ctxt - > nberrors + + ;
channel = ctxt - > error ;
data = ctxt - > userData ;
schannel = ctxt - > serror ;
}
__xmlRaiseError ( schannel , channel , data , ctxt , node , XML_FROM_SCHEMASP ,
error , XML_ERR_ERROR , NULL , 0 ,
( const char * ) str1 , ( const char * ) str2 , NULL , 0 , 0 ,
msg , str1 , str2 ) ;
}
/**
* xmlSchematronVTypeErrMemory :
* @ node : a context node
* @ extra : extra informations
*
* Handle an out of memory condition
*/
static void
xmlSchematronVErrMemory ( xmlSchematronValidCtxtPtr ctxt ,
const char * extra , xmlNodePtr node )
{
if ( ctxt ! = NULL ) {
ctxt - > nberrors + + ;
ctxt - > err = XML_SCHEMAV_INTERNAL ;
}
__xmlSimpleError ( XML_FROM_SCHEMASV , XML_ERR_NO_MEMORY , node , NULL ,
extra ) ;
}
/************************************************************************
* *
* Parsing and compilation of the Schematrontrons *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* xmlSchematronAddTest :
* @ ctxt : the schema parsing context
2005-07-31 01:09:12 +04:00
* @ type : the type of test
* @ rule : the parent rule
2005-07-23 19:00:22 +04:00
* @ node : the node hosting the test
2005-07-31 01:09:12 +04:00
* @ test : the associated test
2005-07-24 18:27:16 +04:00
* @ report : the associated report string
2005-07-23 19:00:22 +04:00
*
* Add a test to a schematron
*
* Returns the new pointer or NULL in case of error
*/
static xmlSchematronTestPtr
2005-07-31 01:09:12 +04:00
xmlSchematronAddTest ( xmlSchematronParserCtxtPtr ctxt ,
xmlSchematronTestType type ,
2005-07-23 19:00:22 +04:00
xmlSchematronRulePtr rule ,
2005-07-24 18:27:16 +04:00
xmlNodePtr node , xmlChar * test , xmlChar * report )
2005-07-23 19:00:22 +04:00
{
xmlSchematronTestPtr ret ;
xmlXPathCompExprPtr comp ;
if ( ( ctxt = = NULL ) | | ( rule = = NULL ) | | ( node = = NULL ) | |
( test = = NULL ) )
return ( NULL ) ;
/*
* try first to compile the test expression
*/
comp = xmlXPathCtxtCompile ( ctxt - > xctxt , test ) ;
if ( comp = = NULL ) {
xmlSchematronPErr ( ctxt , node ,
XML_SCHEMAP_NOROOT ,
" Failed to compile test expression %s " ,
test , NULL ) ;
return ( NULL ) ;
}
ret = ( xmlSchematronTestPtr ) xmlMalloc ( sizeof ( xmlSchematronTest ) ) ;
if ( ret = = NULL ) {
xmlSchematronPErrMemory ( ctxt , " allocating schema test " , node ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematronTest ) ) ;
ret - > type = type ;
ret - > node = node ;
ret - > test = test ;
ret - > comp = comp ;
2005-07-24 18:27:16 +04:00
ret - > report = report ;
2005-07-31 16:17:24 +04:00
ret - > next = NULL ;
if ( rule - > tests = = NULL ) {
rule - > tests = ret ;
} else {
xmlSchematronTestPtr prev = rule - > tests ;
while ( prev - > next ! = NULL )
prev = prev - > next ;
prev - > next = ret ;
}
2005-07-23 19:00:22 +04:00
return ( ret ) ;
}
/**
* xmlSchematronFreeTests :
* @ tests : a list of tests
*
* Free a list of tests .
*/
static void
xmlSchematronFreeTests ( xmlSchematronTestPtr tests ) {
xmlSchematronTestPtr next ;
while ( tests ! = NULL ) {
next = tests - > next ;
if ( tests - > test ! = NULL )
xmlFree ( tests - > test ) ;
if ( tests - > comp ! = NULL )
xmlXPathFreeCompExpr ( tests - > comp ) ;
2005-07-24 18:27:16 +04:00
if ( tests - > report ! = NULL )
xmlFree ( tests - > report ) ;
2005-07-23 19:00:22 +04:00
xmlFree ( tests ) ;
tests = next ;
}
}
/**
* xmlSchematronAddRule :
* @ ctxt : the schema parsing context
* @ schema : a schema structure
* @ node : the node hosting the rule
* @ context : the associated context string
2005-07-24 18:27:16 +04:00
* @ report : the associated report string
2005-07-23 19:00:22 +04:00
*
* Add a rule to a schematron
*
* Returns the new pointer or NULL in case of error
*/
static xmlSchematronRulePtr
xmlSchematronAddRule ( xmlSchematronParserCtxtPtr ctxt , xmlSchematronPtr schema ,
2005-07-31 01:09:12 +04:00
xmlSchematronPatternPtr pat , xmlNodePtr node ,
xmlChar * context , xmlChar * report )
2005-07-23 19:00:22 +04:00
{
xmlSchematronRulePtr ret ;
xmlPatternPtr pattern ;
if ( ( ctxt = = NULL ) | | ( schema = = NULL ) | | ( node = = NULL ) | |
( context = = NULL ) )
return ( NULL ) ;
/*
* Try first to compile the pattern
*/
pattern = xmlPatterncompile ( context , ctxt - > dict , XML_PATTERN_XPATH ,
ctxt - > namespaces ) ;
if ( pattern = = NULL ) {
xmlSchematronPErr ( ctxt , node ,
XML_SCHEMAP_NOROOT ,
" Failed to compile context expression %s " ,
context , NULL ) ;
}
ret = ( xmlSchematronRulePtr ) xmlMalloc ( sizeof ( xmlSchematronRule ) ) ;
if ( ret = = NULL ) {
xmlSchematronPErrMemory ( ctxt , " allocating schema rule " , node ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematronRule ) ) ;
ret - > node = node ;
ret - > context = context ;
ret - > pattern = pattern ;
2005-07-24 18:27:16 +04:00
ret - > report = report ;
2005-07-31 16:17:24 +04:00
ret - > next = NULL ;
if ( schema - > rules = = NULL ) {
schema - > rules = ret ;
} else {
xmlSchematronRulePtr prev = schema - > rules ;
while ( prev - > next ! = NULL )
prev = prev - > next ;
prev - > next = ret ;
}
ret - > patnext = NULL ;
if ( pat - > rules = = NULL ) {
pat - > rules = ret ;
} else {
xmlSchematronRulePtr prev = pat - > rules ;
while ( prev - > patnext ! = NULL )
prev = prev - > patnext ;
prev - > patnext = ret ;
}
2005-07-23 19:00:22 +04:00
return ( ret ) ;
}
/**
* xmlSchematronFreeRules :
* @ rules : a list of rules
*
* Free a list of rules .
*/
static void
xmlSchematronFreeRules ( xmlSchematronRulePtr rules ) {
xmlSchematronRulePtr next ;
while ( rules ! = NULL ) {
next = rules - > next ;
if ( rules - > tests )
xmlSchematronFreeTests ( rules - > tests ) ;
if ( rules - > context ! = NULL )
xmlFree ( rules - > context ) ;
if ( rules - > pattern )
xmlFreePattern ( rules - > pattern ) ;
2005-07-24 18:27:16 +04:00
if ( rules - > report ! = NULL )
xmlFree ( rules - > report ) ;
2005-07-23 19:00:22 +04:00
xmlFree ( rules ) ;
rules = next ;
}
}
2005-07-31 01:09:12 +04:00
/**
* xmlSchematronAddPattern :
* @ ctxt : the schema parsing context
* @ schema : a schema structure
* @ node : the node hosting the pattern
* @ id : the id or name of the pattern
*
* Add a pattern to a schematron
*
* Returns the new pointer or NULL in case of error
*/
static xmlSchematronPatternPtr
xmlSchematronAddPattern ( xmlSchematronParserCtxtPtr ctxt ,
xmlSchematronPtr schema , xmlNodePtr node , xmlChar * name )
{
xmlSchematronPatternPtr ret ;
if ( ( ctxt = = NULL ) | | ( schema = = NULL ) | | ( node = = NULL ) | | ( name = = NULL ) )
return ( NULL ) ;
ret = ( xmlSchematronPatternPtr ) xmlMalloc ( sizeof ( xmlSchematronPattern ) ) ;
if ( ret = = NULL ) {
xmlSchematronPErrMemory ( ctxt , " allocating schema pattern " , node ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematronPattern ) ) ;
ret - > name = name ;
2005-07-31 16:17:24 +04:00
ret - > next = NULL ;
if ( schema - > patterns = = NULL ) {
schema - > patterns = ret ;
} else {
xmlSchematronPatternPtr prev = schema - > patterns ;
while ( prev - > next ! = NULL )
prev = prev - > next ;
prev - > next = ret ;
}
2005-07-31 01:09:12 +04:00
return ( ret ) ;
}
/**
* xmlSchematronFreePatterns :
* @ patterns : a list of patterns
*
* Free a list of patterns .
*/
static void
xmlSchematronFreePatterns ( xmlSchematronPatternPtr patterns ) {
xmlSchematronPatternPtr next ;
while ( patterns ! = NULL ) {
next = patterns - > next ;
if ( patterns - > name ! = NULL )
xmlFree ( patterns - > name ) ;
xmlFree ( patterns ) ;
patterns = next ;
}
}
2005-07-23 19:00:22 +04:00
/**
* xmlSchematronNewSchematron :
* @ ctxt : a schema validation context
*
* Allocate a new Schematron structure .
*
* Returns the newly allocated structure or NULL in case or error
*/
static xmlSchematronPtr
xmlSchematronNewSchematron ( xmlSchematronParserCtxtPtr ctxt )
{
xmlSchematronPtr ret ;
ret = ( xmlSchematronPtr ) xmlMalloc ( sizeof ( xmlSchematron ) ) ;
if ( ret = = NULL ) {
xmlSchematronPErrMemory ( ctxt , " allocating schema " , NULL ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematron ) ) ;
ret - > dict = ctxt - > dict ;
xmlDictReference ( ret - > dict ) ;
return ( ret ) ;
}
/**
* xmlSchematronFree :
* @ schema : a schema structure
*
* Deallocate a Schematron structure .
*/
void
xmlSchematronFree ( xmlSchematronPtr schema )
{
if ( schema = = NULL )
return ;
if ( ( schema - > doc ! = NULL ) & & ( ! ( schema - > preserve ) ) )
xmlFreeDoc ( schema - > doc ) ;
if ( schema - > namespaces ! = NULL )
2005-10-07 06:33:00 +04:00
xmlFree ( ( char * * ) schema - > namespaces ) ;
2012-09-11 09:26:36 +04:00
2005-07-23 19:00:22 +04:00
xmlSchematronFreeRules ( schema - > rules ) ;
2005-07-31 01:09:12 +04:00
xmlSchematronFreePatterns ( schema - > patterns ) ;
2005-07-23 19:00:22 +04:00
xmlDictFree ( schema - > dict ) ;
xmlFree ( schema ) ;
}
/**
* xmlSchematronNewParserCtxt :
* @ URL : the location of the schema
*
* Create an XML Schematrons parse context for that file / resource expected
* to contain an XML Schematrons file .
*
* Returns the parser context or NULL in case of error
*/
xmlSchematronParserCtxtPtr
xmlSchematronNewParserCtxt ( const char * URL )
{
xmlSchematronParserCtxtPtr ret ;
if ( URL = = NULL )
return ( NULL ) ;
ret =
( xmlSchematronParserCtxtPtr )
xmlMalloc ( sizeof ( xmlSchematronParserCtxt ) ) ;
if ( ret = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating schema parser context " ,
NULL ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematronParserCtxt ) ) ;
ret - > type = XML_STRON_CTXT_PARSER ;
ret - > dict = xmlDictCreate ( ) ;
ret - > URL = xmlDictLookup ( ret - > dict , ( const xmlChar * ) URL , - 1 ) ;
2005-07-29 03:49:35 +04:00
ret - > includes = NULL ;
2005-07-23 19:00:22 +04:00
ret - > xctxt = xmlXPathNewContext ( NULL ) ;
if ( ret - > xctxt = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating schema parser XPath context " ,
NULL ) ;
xmlSchematronFreeParserCtxt ( ret ) ;
return ( NULL ) ;
}
ret - > xctxt - > flags = XML_XPATH_CHECKNS ;
return ( ret ) ;
}
/**
* xmlSchematronNewMemParserCtxt :
* @ buffer : a pointer to a char array containing the schemas
* @ size : the size of the array
*
* Create an XML Schematrons parse context for that memory buffer expected
* to contain an XML Schematrons file .
*
* Returns the parser context or NULL in case of error
*/
xmlSchematronParserCtxtPtr
xmlSchematronNewMemParserCtxt ( const char * buffer , int size )
{
xmlSchematronParserCtxtPtr ret ;
if ( ( buffer = = NULL ) | | ( size < = 0 ) )
return ( NULL ) ;
ret =
( xmlSchematronParserCtxtPtr )
xmlMalloc ( sizeof ( xmlSchematronParserCtxt ) ) ;
if ( ret = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating schema parser context " ,
NULL ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematronParserCtxt ) ) ;
ret - > buffer = buffer ;
ret - > size = size ;
ret - > dict = xmlDictCreate ( ) ;
ret - > xctxt = xmlXPathNewContext ( NULL ) ;
if ( ret - > xctxt = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating schema parser XPath context " ,
NULL ) ;
xmlSchematronFreeParserCtxt ( ret ) ;
return ( NULL ) ;
}
return ( ret ) ;
}
/**
* xmlSchematronNewDocParserCtxt :
* @ doc : a preparsed document tree
*
* Create an XML Schematrons parse context for that document .
* NB . The document may be modified during the parsing process .
*
* Returns the parser context or NULL in case of error
*/
xmlSchematronParserCtxtPtr
xmlSchematronNewDocParserCtxt ( xmlDocPtr doc )
{
xmlSchematronParserCtxtPtr ret ;
if ( doc = = NULL )
return ( NULL ) ;
ret =
( xmlSchematronParserCtxtPtr )
xmlMalloc ( sizeof ( xmlSchematronParserCtxt ) ) ;
if ( ret = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating schema parser context " ,
NULL ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematronParserCtxt ) ) ;
ret - > doc = doc ;
ret - > dict = xmlDictCreate ( ) ;
/* The application has responsibility for the document */
ret - > preserve = 1 ;
ret - > xctxt = xmlXPathNewContext ( doc ) ;
if ( ret - > xctxt = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating schema parser XPath context " ,
NULL ) ;
xmlSchematronFreeParserCtxt ( ret ) ;
return ( NULL ) ;
}
return ( ret ) ;
}
/**
* xmlSchematronFreeParserCtxt :
* @ ctxt : the schema parser context
*
* Free the resources associated to the schema parser context
*/
void
xmlSchematronFreeParserCtxt ( xmlSchematronParserCtxtPtr ctxt )
{
if ( ctxt = = NULL )
return ;
if ( ctxt - > doc ! = NULL & & ! ctxt - > preserve )
xmlFreeDoc ( ctxt - > doc ) ;
if ( ctxt - > xctxt ! = NULL ) {
xmlXPathFreeContext ( ctxt - > xctxt ) ;
}
if ( ctxt - > namespaces ! = NULL )
2005-10-07 06:33:00 +04:00
xmlFree ( ( char * * ) ctxt - > namespaces ) ;
2005-07-23 19:00:22 +04:00
xmlDictFree ( ctxt - > dict ) ;
xmlFree ( ctxt ) ;
}
2007-05-22 20:00:06 +04:00
#if 0
2005-07-23 19:00:22 +04:00
/**
* xmlSchematronPushInclude :
* @ ctxt : the schema parser context
* @ doc : the included document
* @ cur : the current include node
*
* Add an included document
*/
static void
xmlSchematronPushInclude ( xmlSchematronParserCtxtPtr ctxt ,
xmlDocPtr doc , xmlNodePtr cur )
{
if ( ctxt - > includes = = NULL ) {
ctxt - > maxIncludes = 10 ;
ctxt - > includes = ( xmlNodePtr * )
xmlMalloc ( ctxt - > maxIncludes * 2 * sizeof ( xmlNodePtr ) ) ;
if ( ctxt - > includes = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating parser includes " ,
NULL ) ;
return ;
}
ctxt - > nbIncludes = 0 ;
} else if ( ctxt - > nbIncludes + 2 > = ctxt - > maxIncludes ) {
xmlNodePtr * tmp ;
tmp = ( xmlNodePtr * )
xmlRealloc ( ctxt - > includes , ctxt - > maxIncludes * 4 *
sizeof ( xmlNodePtr ) ) ;
if ( tmp = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating parser includes " ,
NULL ) ;
return ;
}
ctxt - > includes = tmp ;
ctxt - > maxIncludes * = 2 ;
}
ctxt - > includes [ 2 * ctxt - > nbIncludes ] = cur ;
ctxt - > includes [ 2 * ctxt - > nbIncludes + 1 ] = ( xmlNodePtr ) doc ;
ctxt - > nbIncludes + + ;
}
/**
* xmlSchematronPopInclude :
* @ ctxt : the schema parser context
*
* Pop an include level . The included document is being freed
*
* Returns the node immediately following the include or NULL if the
* include list was empty .
*/
static xmlNodePtr
xmlSchematronPopInclude ( xmlSchematronParserCtxtPtr ctxt )
{
xmlDocPtr doc ;
xmlNodePtr ret ;
if ( ctxt - > nbIncludes < = 0 )
return ( NULL ) ;
ctxt - > nbIncludes - - ;
doc = ( xmlDocPtr ) ctxt - > includes [ 2 * ctxt - > nbIncludes + 1 ] ;
ret = ctxt - > includes [ 2 * ctxt - > nbIncludes ] ;
xmlFreeDoc ( doc ) ;
if ( ret ! = NULL )
ret = ret - > next ;
if ( ret = = NULL )
return ( xmlSchematronPopInclude ( ctxt ) ) ;
return ( ret ) ;
}
2007-05-22 20:00:06 +04:00
# endif
2005-07-23 19:00:22 +04:00
/**
* xmlSchematronAddNamespace :
* @ ctxt : the schema parser context
* @ prefix : the namespace prefix
* @ ns : the namespace name
*
* Add a namespace definition in the context
*/
static void
xmlSchematronAddNamespace ( xmlSchematronParserCtxtPtr ctxt ,
const xmlChar * prefix , const xmlChar * ns )
{
if ( ctxt - > namespaces = = NULL ) {
ctxt - > maxNamespaces = 10 ;
ctxt - > namespaces = ( const xmlChar * * )
xmlMalloc ( ctxt - > maxNamespaces * 2 * sizeof ( const xmlChar * ) ) ;
if ( ctxt - > namespaces = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating parser namespaces " ,
NULL ) ;
return ;
}
ctxt - > nbNamespaces = 0 ;
} else if ( ctxt - > nbNamespaces + 2 > = ctxt - > maxNamespaces ) {
const xmlChar * * tmp ;
tmp = ( const xmlChar * * )
2005-10-07 06:33:00 +04:00
xmlRealloc ( ( xmlChar * * ) ctxt - > namespaces , ctxt - > maxNamespaces * 4 *
2005-07-23 19:00:22 +04:00
sizeof ( const xmlChar * ) ) ;
if ( tmp = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating parser namespaces " ,
NULL ) ;
return ;
}
ctxt - > namespaces = tmp ;
ctxt - > maxNamespaces * = 2 ;
}
2012-09-11 09:26:36 +04:00
ctxt - > namespaces [ 2 * ctxt - > nbNamespaces ] =
2005-07-23 19:00:22 +04:00
xmlDictLookup ( ctxt - > dict , ns , - 1 ) ;
2012-09-11 09:26:36 +04:00
ctxt - > namespaces [ 2 * ctxt - > nbNamespaces + 1 ] =
2005-07-23 19:00:22 +04:00
xmlDictLookup ( ctxt - > dict , prefix , - 1 ) ;
ctxt - > nbNamespaces + + ;
ctxt - > namespaces [ 2 * ctxt - > nbNamespaces ] = NULL ;
ctxt - > namespaces [ 2 * ctxt - > nbNamespaces + 1 ] = NULL ;
}
/**
* xmlSchematronParseRule :
* @ ctxt : a schema validation context
* @ rule : the rule node
*
* parse a rule element
*/
static void
2005-07-31 01:09:12 +04:00
xmlSchematronParseRule ( xmlSchematronParserCtxtPtr ctxt ,
xmlSchematronPatternPtr pattern ,
xmlNodePtr rule )
2005-07-23 19:00:22 +04:00
{
xmlNodePtr cur ;
int nbChecks = 0 ;
xmlChar * test ;
xmlChar * context ;
2005-07-24 18:27:16 +04:00
xmlChar * report ;
2005-07-23 19:00:22 +04:00
xmlSchematronRulePtr ruleptr ;
xmlSchematronTestPtr testptr ;
if ( ( ctxt = = NULL ) | | ( rule = = NULL ) ) return ;
context = xmlGetNoNsProp ( rule , BAD_CAST " context " ) ;
if ( context = = NULL ) {
xmlSchematronPErr ( ctxt , rule ,
XML_SCHEMAP_NOROOT ,
" rule has no context attribute " ,
NULL , NULL ) ;
return ;
} else if ( context [ 0 ] = = 0 ) {
xmlSchematronPErr ( ctxt , rule ,
XML_SCHEMAP_NOROOT ,
" rule has an empty context attribute " ,
NULL , NULL ) ;
xmlFree ( context ) ;
return ;
} else {
2005-07-31 01:09:12 +04:00
ruleptr = xmlSchematronAddRule ( ctxt , ctxt - > schema , pattern ,
rule , context , NULL ) ;
2005-07-23 19:00:22 +04:00
if ( ruleptr = = NULL ) {
xmlFree ( context ) ;
return ;
}
}
cur = rule - > children ;
NEXT_SCHEMATRON ( cur ) ;
while ( cur ! = NULL ) {
if ( IS_SCHEMATRON ( cur , " assert " ) ) {
nbChecks + + ;
test = xmlGetNoNsProp ( cur , BAD_CAST " test " ) ;
if ( test = = NULL ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" assert has no test attribute " ,
NULL , NULL ) ;
} else if ( test [ 0 ] = = 0 ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" assert has an empty test attribute " ,
NULL , NULL ) ;
xmlFree ( test ) ;
} else {
2005-07-24 18:27:16 +04:00
/* TODO will need dynamic processing instead */
report = xmlNodeGetContent ( cur ) ;
2005-07-23 19:00:22 +04:00
testptr = xmlSchematronAddTest ( ctxt , XML_SCHEMATRON_ASSERT ,
2005-07-24 18:27:16 +04:00
ruleptr , cur , test , report ) ;
2005-07-23 19:00:22 +04:00
if ( testptr = = NULL )
xmlFree ( test ) ;
}
} else if ( IS_SCHEMATRON ( cur , " report " ) ) {
nbChecks + + ;
test = xmlGetNoNsProp ( cur , BAD_CAST " test " ) ;
if ( test = = NULL ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" assert has no test attribute " ,
NULL , NULL ) ;
} else if ( test [ 0 ] = = 0 ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" assert has an empty test attribute " ,
NULL , NULL ) ;
xmlFree ( test ) ;
} else {
2005-07-24 18:27:16 +04:00
/* TODO will need dynamic processing instead */
report = xmlNodeGetContent ( cur ) ;
2005-07-23 19:00:22 +04:00
testptr = xmlSchematronAddTest ( ctxt , XML_SCHEMATRON_REPORT ,
2005-07-24 18:27:16 +04:00
ruleptr , cur , test , report ) ;
2005-07-23 19:00:22 +04:00
if ( testptr = = NULL )
xmlFree ( test ) ;
}
} else {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" Expecting an assert or a report element instead of %s " ,
cur - > name , NULL ) ;
}
cur = cur - > next ;
NEXT_SCHEMATRON ( cur ) ;
}
if ( nbChecks = = 0 ) {
xmlSchematronPErr ( ctxt , rule ,
XML_SCHEMAP_NOROOT ,
" rule has no assert nor report element " , NULL , NULL ) ;
}
}
/**
* xmlSchematronParsePattern :
* @ ctxt : a schema validation context
* @ pat : the pattern node
*
* parse a pattern element
*/
static void
xmlSchematronParsePattern ( xmlSchematronParserCtxtPtr ctxt , xmlNodePtr pat )
{
xmlNodePtr cur ;
2005-07-31 01:09:12 +04:00
xmlSchematronPatternPtr pattern ;
2005-07-23 19:00:22 +04:00
int nbRules = 0 ;
2005-07-31 01:09:12 +04:00
xmlChar * id ;
2005-07-23 19:00:22 +04:00
if ( ( ctxt = = NULL ) | | ( pat = = NULL ) ) return ;
2005-07-31 01:09:12 +04:00
id = xmlGetNoNsProp ( pat , BAD_CAST " id " ) ;
if ( id = = NULL ) {
id = xmlGetNoNsProp ( pat , BAD_CAST " name " ) ;
}
pattern = xmlSchematronAddPattern ( ctxt , ctxt - > schema , pat , id ) ;
if ( pattern = = NULL ) {
if ( id ! = NULL )
xmlFree ( id ) ;
return ;
}
2005-07-23 19:00:22 +04:00
cur = pat - > children ;
NEXT_SCHEMATRON ( cur ) ;
while ( cur ! = NULL ) {
if ( IS_SCHEMATRON ( cur , " rule " ) ) {
2005-07-31 01:09:12 +04:00
xmlSchematronParseRule ( ctxt , pattern , cur ) ;
2005-07-23 19:00:22 +04:00
nbRules + + ;
} else {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" Expecting a rule element instead of %s " , cur - > name , NULL ) ;
}
cur = cur - > next ;
NEXT_SCHEMATRON ( cur ) ;
}
if ( nbRules = = 0 ) {
xmlSchematronPErr ( ctxt , pat ,
XML_SCHEMAP_NOROOT ,
" Pattern has no rule element " , NULL , NULL ) ;
}
}
2007-05-22 20:00:06 +04:00
#if 0
2005-07-23 19:00:22 +04:00
/**
* xmlSchematronLoadInclude :
* @ ctxt : a schema validation context
* @ cur : the include element
*
* Load the include document , Push the current pointer
*
* Returns the updated node pointer
*/
static xmlNodePtr
xmlSchematronLoadInclude ( xmlSchematronParserCtxtPtr ctxt , xmlNodePtr cur )
{
xmlNodePtr ret = NULL ;
xmlDocPtr doc = NULL ;
xmlChar * href = NULL ;
xmlChar * base = NULL ;
xmlChar * URI = NULL ;
if ( ( ctxt = = NULL ) | | ( cur = = NULL ) )
return ( NULL ) ;
href = xmlGetNoNsProp ( cur , BAD_CAST " href " ) ;
if ( href = = NULL ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" Include has no href attribute " , NULL , NULL ) ;
return ( cur - > next ) ;
}
/* do the URI base composition, load and find the root */
base = xmlNodeGetBase ( cur - > doc , cur ) ;
URI = xmlBuildURI ( href , base ) ;
doc = xmlReadFile ( ( const char * ) URI , NULL , SCHEMATRON_PARSE_OPTIONS ) ;
if ( doc = = NULL ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_FAILED_LOAD ,
" could not load include '%s'. \n " ,
URI , NULL ) ;
goto done ;
}
ret = xmlDocGetRootElement ( doc ) ;
if ( ret = = NULL ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_FAILED_LOAD ,
" could not find root from include '%s'. \n " ,
URI , NULL ) ;
goto done ;
}
/* Success, push the include for rollback on exit */
xmlSchematronPushInclude ( ctxt , doc , cur ) ;
done :
if ( ret = = NULL ) {
if ( doc ! = NULL )
xmlFreeDoc ( doc ) ;
}
2006-03-10 03:36:23 +03:00
xmlFree ( href ) ;
if ( base ! = NULL )
2005-07-23 19:00:22 +04:00
xmlFree ( base ) ;
2006-03-10 03:36:23 +03:00
if ( URI ! = NULL )
2005-07-23 19:00:22 +04:00
xmlFree ( URI ) ;
return ( ret ) ;
}
2007-05-22 20:00:06 +04:00
# endif
2005-07-23 19:00:22 +04:00
/**
* xmlSchematronParse :
* @ ctxt : a schema validation context
*
* parse a schema definition resource and build an internal
* XML Shema struture which can be used to validate instances .
*
* Returns the internal XML Schematron structure built from the resource or
* NULL in case of error
*/
xmlSchematronPtr
xmlSchematronParse ( xmlSchematronParserCtxtPtr ctxt )
{
xmlSchematronPtr ret = NULL ;
xmlDocPtr doc ;
xmlNodePtr root , cur ;
int preserve = 0 ;
if ( ctxt = = NULL )
return ( NULL ) ;
ctxt - > nberrors = 0 ;
/*
* First step is to parse the input document into an DOM / Infoset
*/
if ( ctxt - > URL ! = NULL ) {
doc = xmlReadFile ( ( const char * ) ctxt - > URL , NULL ,
SCHEMATRON_PARSE_OPTIONS ) ;
if ( doc = = NULL ) {
xmlSchematronPErr ( ctxt , NULL ,
XML_SCHEMAP_FAILED_LOAD ,
" xmlSchematronParse: could not load '%s'. \n " ,
ctxt - > URL , NULL ) ;
return ( NULL ) ;
}
2005-07-31 17:43:14 +04:00
ctxt - > preserve = 0 ;
2005-07-23 19:00:22 +04:00
} else if ( ctxt - > buffer ! = NULL ) {
doc = xmlReadMemory ( ctxt - > buffer , ctxt - > size , NULL , NULL ,
SCHEMATRON_PARSE_OPTIONS ) ;
if ( doc = = NULL ) {
xmlSchematronPErr ( ctxt , NULL ,
XML_SCHEMAP_FAILED_PARSE ,
" xmlSchematronParse: could not parse. \n " ,
NULL , NULL ) ;
return ( NULL ) ;
}
doc - > URL = xmlStrdup ( BAD_CAST " in_memory_buffer " ) ;
ctxt - > URL = xmlDictLookup ( ctxt - > dict , BAD_CAST " in_memory_buffer " , - 1 ) ;
2005-07-31 17:43:14 +04:00
ctxt - > preserve = 0 ;
2005-07-23 19:00:22 +04:00
} else if ( ctxt - > doc ! = NULL ) {
doc = ctxt - > doc ;
preserve = 1 ;
2005-07-31 17:43:14 +04:00
ctxt - > preserve = 1 ;
2005-07-23 19:00:22 +04:00
} else {
xmlSchematronPErr ( ctxt , NULL ,
XML_SCHEMAP_NOTHING_TO_PARSE ,
" xmlSchematronParse: could not parse. \n " ,
NULL , NULL ) ;
return ( NULL ) ;
}
/*
* Then extract the root and Schematron parse it
*/
root = xmlDocGetRootElement ( doc ) ;
if ( root = = NULL ) {
xmlSchematronPErr ( ctxt , ( xmlNodePtr ) doc ,
XML_SCHEMAP_NOROOT ,
" The schema has no document element. \n " , NULL , NULL ) ;
if ( ! preserve ) {
xmlFreeDoc ( doc ) ;
}
return ( NULL ) ;
}
if ( ! IS_SCHEMATRON ( root , " schema " ) ) {
xmlSchematronPErr ( ctxt , root ,
XML_SCHEMAP_NOROOT ,
" The XML document '%s' is not a XML schematron document " ,
ctxt - > URL , NULL ) ;
goto exit ;
}
ret = xmlSchematronNewSchematron ( ctxt ) ;
if ( ret = = NULL )
goto exit ;
ctxt - > schema = ret ;
/*
* scan the schema elements
*/
cur = root - > children ;
NEXT_SCHEMATRON ( cur ) ;
if ( IS_SCHEMATRON ( cur , " title " ) ) {
xmlChar * title = xmlNodeGetContent ( cur ) ;
if ( title ! = NULL ) {
ret - > title = xmlDictLookup ( ret - > dict , title , - 1 ) ;
xmlFree ( title ) ;
}
cur = cur - > next ;
NEXT_SCHEMATRON ( cur ) ;
}
while ( IS_SCHEMATRON ( cur , " ns " ) ) {
xmlChar * prefix = xmlGetNoNsProp ( cur , BAD_CAST " prefix " ) ;
xmlChar * uri = xmlGetNoNsProp ( cur , BAD_CAST " uri " ) ;
if ( ( uri = = NULL ) | | ( uri [ 0 ] = = 0 ) ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" ns element has no uri " , NULL , NULL ) ;
}
if ( ( prefix = = NULL ) | | ( prefix [ 0 ] = = 0 ) ) {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" ns element has no prefix " , NULL , NULL ) ;
}
if ( ( prefix ) & & ( uri ) ) {
xmlXPathRegisterNs ( ctxt - > xctxt , prefix , uri ) ;
xmlSchematronAddNamespace ( ctxt , prefix , uri ) ;
ret - > nbNs + + ;
}
if ( uri )
xmlFree ( uri ) ;
if ( prefix )
xmlFree ( prefix ) ;
cur = cur - > next ;
NEXT_SCHEMATRON ( cur ) ;
}
while ( cur ! = NULL ) {
if ( IS_SCHEMATRON ( cur , " pattern " ) ) {
xmlSchematronParsePattern ( ctxt , cur ) ;
ret - > nbPattern + + ;
} else {
xmlSchematronPErr ( ctxt , cur ,
XML_SCHEMAP_NOROOT ,
" Expecting a pattern element instead of %s " , cur - > name , NULL ) ;
}
cur = cur - > next ;
NEXT_SCHEMATRON ( cur ) ;
}
if ( ret - > nbPattern = = 0 ) {
xmlSchematronPErr ( ctxt , root ,
XML_SCHEMAP_NOROOT ,
" The schematron document '%s' has no pattern " ,
ctxt - > URL , NULL ) ;
goto exit ;
}
2005-07-31 17:43:14 +04:00
/* the original document must be kept for reporting */
ret - > doc = doc ;
2007-11-13 23:27:52 +03:00
if ( preserve ) {
ret - > preserve = 1 ;
}
2005-07-31 17:43:14 +04:00
preserve = 1 ;
2005-07-23 19:00:22 +04:00
exit :
if ( ! preserve ) {
xmlFreeDoc ( doc ) ;
}
2006-03-10 03:36:23 +03:00
if ( ret ! = NULL ) {
if ( ctxt - > nberrors ! = 0 ) {
xmlSchematronFree ( ret ) ;
ret = NULL ;
} else {
ret - > namespaces = ctxt - > namespaces ;
ret - > nbNamespaces = ctxt - > nbNamespaces ;
ctxt - > namespaces = NULL ;
}
2005-07-23 19:00:22 +04:00
}
return ( ret ) ;
}
/************************************************************************
* *
* Schematrontron Reports handler *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-07-31 20:49:51 +04:00
static xmlNodePtr
xmlSchematronGetNode ( xmlSchematronValidCtxtPtr ctxt ,
xmlNodePtr cur , const xmlChar * xpath ) {
xmlNodePtr node = NULL ;
xmlXPathObjectPtr ret ;
if ( ( ctxt = = NULL ) | | ( cur = = NULL ) | | ( xpath = = NULL ) )
return ( NULL ) ;
ctxt - > xctxt - > doc = cur - > doc ;
ctxt - > xctxt - > node = cur ;
ret = xmlXPathEval ( xpath , ctxt - > xctxt ) ;
if ( ret = = NULL )
return ( NULL ) ;
if ( ( ret - > type = = XPATH_NODESET ) & &
( ret - > nodesetval ! = NULL ) & & ( ret - > nodesetval - > nodeNr > 0 ) )
node = ret - > nodesetval - > nodeTab [ 0 ] ;
xmlXPathFreeObject ( ret ) ;
return ( node ) ;
}
2005-07-24 18:27:16 +04:00
/**
* xmlSchematronReportOutput :
* @ ctxt : the validation context
* @ cur : the current node tested
* @ msg : the message output
*
* Output part of the report to whatever channel the user selected
*/
static void
xmlSchematronReportOutput ( xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED ,
xmlNodePtr cur ATTRIBUTE_UNUSED ,
2005-07-31 16:17:24 +04:00
const char * msg ) {
2005-07-24 18:27:16 +04:00
/* TODO */
fprintf ( stderr , " %s " , msg ) ;
}
2005-07-31 17:43:14 +04:00
/**
* xmlSchematronFormatReport :
* @ ctxt : the validation context
* @ test : the test node
* @ cur : the current node tested
*
* Build the string being reported to the user .
*
* Returns a report string or NULL in case of error . The string needs
* to be deallocated by teh caller
*/
static xmlChar *
2012-09-11 09:26:36 +04:00
xmlSchematronFormatReport ( xmlSchematronValidCtxtPtr ctxt ,
2005-07-31 17:43:14 +04:00
xmlNodePtr test , xmlNodePtr cur ) {
xmlChar * ret = NULL ;
2005-07-31 20:49:51 +04:00
xmlNodePtr child , node ;
2005-07-31 17:43:14 +04:00
if ( ( test = = NULL ) | | ( cur = = NULL ) )
return ( ret ) ;
child = test - > children ;
while ( child ! = NULL ) {
if ( ( child - > type = = XML_TEXT_NODE ) | |
( child - > type = = XML_CDATA_SECTION_NODE ) )
ret = xmlStrcat ( ret , child - > content ) ;
else if ( IS_SCHEMATRON ( child , " name " ) ) {
2005-07-31 20:49:51 +04:00
xmlChar * path ;
path = xmlGetNoNsProp ( child , BAD_CAST " path " ) ;
node = cur ;
if ( path ! = NULL ) {
node = xmlSchematronGetNode ( ctxt , cur , path ) ;
if ( node = = NULL )
node = cur ;
xmlFree ( path ) ;
}
2012-09-11 09:26:36 +04:00
if ( ( node - > ns = = NULL ) | | ( node - > ns - > prefix = = NULL ) )
2005-07-31 20:49:51 +04:00
ret = xmlStrcat ( ret , node - > name ) ;
2005-07-31 17:43:14 +04:00
else {
2005-07-31 20:49:51 +04:00
ret = xmlStrcat ( ret , node - > ns - > prefix ) ;
2005-07-31 17:43:14 +04:00
ret = xmlStrcat ( ret , BAD_CAST " : " ) ;
2005-07-31 20:49:51 +04:00
ret = xmlStrcat ( ret , node - > name ) ;
2005-07-31 17:43:14 +04:00
}
} else {
child = child - > next ;
continue ;
}
/*
* remove superfluous \ n
*/
if ( ret ! = NULL ) {
int len = xmlStrlen ( ret ) ;
xmlChar c ;
if ( len > 0 ) {
c = ret [ len - 1 ] ;
if ( ( c = = ' ' ) | | ( c = = ' \n ' ) | | ( c = = ' \r ' ) | | ( c = = ' \t ' ) ) {
while ( ( c = = ' ' ) | | ( c = = ' \n ' ) | |
( c = = ' \r ' ) | | ( c = = ' \t ' ) ) {
len - - ;
if ( len = = 0 )
break ;
c = ret [ len - 1 ] ;
}
ret [ len ] = ' ' ;
ret [ len + 1 ] = 0 ;
}
}
}
child = child - > next ;
}
return ( ret ) ;
}
2005-07-24 18:27:16 +04:00
/**
* xmlSchematronReportSuccess :
* @ ctxt : the validation context
* @ test : the compiled test
* @ cur : the current node tested
* @ success : boolean value for the result
*
* called from the validation engine when an assert or report test have
* been done .
*/
2005-07-23 19:00:22 +04:00
static void
2012-09-11 09:26:36 +04:00
xmlSchematronReportSuccess ( xmlSchematronValidCtxtPtr ctxt ,
2008-03-14 15:46:42 +03:00
xmlSchematronTestPtr test , xmlNodePtr cur , xmlSchematronPatternPtr pattern , int success ) {
2005-07-24 18:27:16 +04:00
if ( ( ctxt = = NULL ) | | ( cur = = NULL ) | | ( test = = NULL ) )
return ;
/* if quiet and not SVRL report only failures */
if ( ( ctxt - > flags & XML_SCHEMATRON_OUT_QUIET ) & &
2005-07-31 01:09:12 +04:00
( ( ctxt - > flags & XML_SCHEMATRON_OUT_XML ) = = 0 ) & &
2005-07-31 16:17:24 +04:00
( test - > type = = XML_SCHEMATRON_REPORT ) )
2005-07-24 18:27:16 +04:00
return ;
if ( ctxt - > flags & XML_SCHEMATRON_OUT_XML ) {
TODO
} else {
xmlChar * path ;
char msg [ 1000 ] ;
long line ;
2005-07-31 17:43:14 +04:00
const xmlChar * report = NULL ;
2005-07-24 18:27:16 +04:00
2005-07-31 01:09:12 +04:00
if ( ( ( test - > type = = XML_SCHEMATRON_REPORT ) & ( ! success ) ) | |
( ( test - > type = = XML_SCHEMATRON_ASSERT ) & ( success ) ) )
2005-07-24 18:27:16 +04:00
return ;
line = xmlGetLineNo ( cur ) ;
path = xmlGetNodePath ( cur ) ;
if ( path = = NULL )
path = ( xmlChar * ) cur - > name ;
2005-07-31 17:43:14 +04:00
#if 0
2005-07-24 18:27:16 +04:00
if ( ( test - > report ! = NULL ) & & ( test - > report [ 0 ] ! = 0 ) )
report = test - > report ;
2005-07-31 17:43:14 +04:00
# endif
if ( test - > node ! = NULL )
report = xmlSchematronFormatReport ( ctxt , test - > node , cur ) ;
if ( report = = NULL ) {
if ( test - > type = = XML_SCHEMATRON_ASSERT ) {
2008-03-14 15:46:42 +03:00
report = xmlStrdup ( ( const xmlChar * ) " node failed assert " ) ;
2005-07-31 17:43:14 +04:00
} else {
2008-03-14 15:46:42 +03:00
report = xmlStrdup ( ( const xmlChar * ) " node failed report " ) ;
}
2005-07-31 17:43:14 +04:00
}
snprintf ( msg , 999 , " %s line %ld: %s \n " , ( const char * ) path ,
line , ( const char * ) report ) ;
2008-03-14 15:46:42 +03:00
if ( ctxt - > flags & XML_SCHEMATRON_OUT_ERROR ) {
xmlStructuredErrorFunc schannel = NULL ;
xmlGenericErrorFunc channel = NULL ;
void * data = NULL ;
if ( ctxt ! = NULL ) {
if ( ctxt - > serror ! = NULL )
schannel = ctxt - > serror ;
else
channel = ctxt - > error ;
data = ctxt - > userData ;
2005-07-24 18:27:16 +04:00
}
2008-03-14 15:46:42 +03:00
__xmlRaiseError ( schannel , channel , data ,
NULL , cur , XML_FROM_SCHEMATRONV ,
( test - > type = = XML_SCHEMATRON_ASSERT ) ? XML_SCHEMATRONV_ASSERT : XML_SCHEMATRONV_REPORT ,
XML_ERR_ERROR , NULL , line ,
( pattern = = NULL ) ? NULL : ( ( const char * ) pattern - > name ) ,
( const char * ) path ,
( const char * ) report , 0 , 0 ,
2009-06-04 13:22:45 +04:00
" %s " , msg ) ;
2008-03-14 15:46:42 +03:00
} else {
2005-07-24 18:27:16 +04:00
xmlSchematronReportOutput ( ctxt , cur , & msg [ 0 ] ) ;
2008-03-14 15:46:42 +03:00
}
xmlFree ( ( char * ) report ) ;
2005-07-24 18:27:16 +04:00
if ( ( path ! = NULL ) & & ( path ! = ( xmlChar * ) cur - > name ) )
xmlFree ( path ) ;
}
2005-07-23 19:00:22 +04:00
}
2005-07-31 16:17:24 +04:00
/**
* xmlSchematronReportPattern :
* @ ctxt : the validation context
* @ pattern : the current pattern
*
* called from the validation engine when starting to check a pattern
*/
static void
2012-09-11 09:26:36 +04:00
xmlSchematronReportPattern ( xmlSchematronValidCtxtPtr ctxt ,
2005-07-31 16:17:24 +04:00
xmlSchematronPatternPtr pattern ) {
if ( ( ctxt = = NULL ) | | ( pattern = = NULL ) )
return ;
2008-03-14 15:46:42 +03:00
if ( ( ctxt - > flags & XML_SCHEMATRON_OUT_QUIET ) | | ( ctxt - > flags & XML_SCHEMATRON_OUT_ERROR ) ) /* Error gives pattern name as part of error */
2005-07-31 16:17:24 +04:00
return ;
if ( ctxt - > flags & XML_SCHEMATRON_OUT_XML ) {
TODO
} else {
char msg [ 1000 ] ;
if ( pattern - > name = = NULL )
return ;
snprintf ( msg , 999 , " Pattern: %s \n " , ( const char * ) pattern - > name ) ;
xmlSchematronReportOutput ( ctxt , NULL , & msg [ 0 ] ) ;
}
}
2005-07-23 19:00:22 +04:00
/************************************************************************
* *
* Validation against a Schematrontron *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-03-14 15:46:42 +03:00
/**
* xmlSchematronSetValidStructuredErrors :
* @ ctxt : a Schematron validation context
* @ serror : the structured error function
* @ ctx : the functions context
*
* Set the structured error callback
*/
void
xmlSchematronSetValidStructuredErrors ( xmlSchematronValidCtxtPtr ctxt ,
xmlStructuredErrorFunc serror , void * ctx )
{
if ( ctxt = = NULL )
return ;
ctxt - > serror = serror ;
ctxt - > error = NULL ;
ctxt - > warning = NULL ;
ctxt - > userData = ctx ;
}
2005-07-23 19:00:22 +04:00
/**
* xmlSchematronNewValidCtxt :
* @ schema : a precompiled XML Schematrons
* @ options : a set of xmlSchematronValidOptions
*
* Create an XML Schematrons validation context based on the given schema .
*
* Returns the validation context or NULL in case of error
*/
xmlSchematronValidCtxtPtr
xmlSchematronNewValidCtxt ( xmlSchematronPtr schema , int options )
{
int i ;
xmlSchematronValidCtxtPtr ret ;
2005-07-31 16:17:24 +04:00
ret = ( xmlSchematronValidCtxtPtr ) xmlMalloc ( sizeof ( xmlSchematronValidCtxt ) ) ;
2005-07-23 19:00:22 +04:00
if ( ret = = NULL ) {
xmlSchematronVErrMemory ( NULL , " allocating validation context " ,
NULL ) ;
return ( NULL ) ;
}
memset ( ret , 0 , sizeof ( xmlSchematronValidCtxt ) ) ;
ret - > type = XML_STRON_CTXT_VALIDATOR ;
ret - > schema = schema ;
ret - > xctxt = xmlXPathNewContext ( NULL ) ;
2005-07-24 18:27:16 +04:00
ret - > flags = options ;
2005-07-23 19:00:22 +04:00
if ( ret - > xctxt = = NULL ) {
xmlSchematronPErrMemory ( NULL , " allocating schema parser XPath context " ,
NULL ) ;
xmlSchematronFreeValidCtxt ( ret ) ;
return ( NULL ) ;
}
for ( i = 0 ; i < schema - > nbNamespaces ; i + + ) {
if ( ( schema - > namespaces [ 2 * i ] = = NULL ) | |
( schema - > namespaces [ 2 * i + 1 ] = = NULL ) )
break ;
xmlXPathRegisterNs ( ret - > xctxt , schema - > namespaces [ 2 * i + 1 ] ,
schema - > namespaces [ 2 * i ] ) ;
}
return ( ret ) ;
}
/**
* xmlSchematronFreeValidCtxt :
* @ ctxt : the schema validation context
*
* Free the resources associated to the schema validation context
*/
void
xmlSchematronFreeValidCtxt ( xmlSchematronValidCtxtPtr ctxt )
{
if ( ctxt = = NULL )
return ;
if ( ctxt - > xctxt ! = NULL )
xmlXPathFreeContext ( ctxt - > xctxt ) ;
if ( ctxt - > dict ! = NULL )
xmlDictFree ( ctxt - > dict ) ;
xmlFree ( ctxt ) ;
}
static xmlNodePtr
xmlSchematronNextNode ( xmlNodePtr cur ) {
if ( cur - > children ! = NULL ) {
/*
* Do not descend on entities declarations
*/
if ( cur - > children - > type ! = XML_ENTITY_DECL ) {
cur = cur - > children ;
/*
* Skip DTDs
*/
if ( cur - > type ! = XML_DTD_NODE )
return ( cur ) ;
}
}
while ( cur - > next ! = NULL ) {
cur = cur - > next ;
if ( ( cur - > type ! = XML_ENTITY_DECL ) & &
( cur - > type ! = XML_DTD_NODE ) )
return ( cur ) ;
}
2012-09-11 09:26:36 +04:00
2005-07-23 19:00:22 +04:00
do {
cur = cur - > parent ;
2006-03-10 03:36:23 +03:00
if ( cur = = NULL ) break ;
2005-07-23 19:00:22 +04:00
if ( cur - > type = = XML_DOCUMENT_NODE ) return ( NULL ) ;
if ( cur - > next ! = NULL ) {
cur = cur - > next ;
return ( cur ) ;
}
} while ( cur ! = NULL ) ;
return ( cur ) ;
}
/**
* xmlSchematronRunTest :
* @ ctxt : the schema validation context
* @ test : the current test
2012-09-11 09:26:36 +04:00
* @ instance : the document instace tree
2005-07-23 19:00:22 +04:00
* @ cur : the current node in the instance
*
* Validate a rule against a tree instance at a given position
*
* Returns 1 in case of success , 0 if error and - 1 in case of internal error
*/
static int
xmlSchematronRunTest ( xmlSchematronValidCtxtPtr ctxt ,
2008-03-14 15:46:42 +03:00
xmlSchematronTestPtr test , xmlDocPtr instance , xmlNodePtr cur , xmlSchematronPatternPtr pattern )
2005-07-23 19:00:22 +04:00
{
xmlXPathObjectPtr ret ;
int failed ;
failed = 0 ;
ctxt - > xctxt - > doc = instance ;
ctxt - > xctxt - > node = cur ;
ret = xmlXPathCompiledEval ( test - > comp , ctxt - > xctxt ) ;
if ( ret = = NULL ) {
failed = 1 ;
2005-07-31 17:43:14 +04:00
} else {
switch ( ret - > type ) {
case XPATH_XSLT_TREE :
case XPATH_NODESET :
if ( ( ret - > nodesetval = = NULL ) | |
( ret - > nodesetval - > nodeNr = = 0 ) )
failed = 1 ;
break ;
case XPATH_BOOLEAN :
failed = ! ret - > boolval ;
break ;
case XPATH_NUMBER :
if ( ( xmlXPathIsNaN ( ret - > floatval ) ) | |
( ret - > floatval = = 0.0 ) )
failed = 1 ;
break ;
case XPATH_STRING :
if ( ( ret - > stringval = = NULL ) | |
( ret - > stringval [ 0 ] = = 0 ) )
failed = 1 ;
break ;
case XPATH_UNDEFINED :
case XPATH_POINT :
case XPATH_RANGE :
case XPATH_LOCATIONSET :
case XPATH_USERS :
2005-07-23 19:00:22 +04:00
failed = 1 ;
2005-07-31 17:43:14 +04:00
break ;
}
xmlXPathFreeObject ( ret ) ;
2005-07-23 19:00:22 +04:00
}
2005-07-31 17:43:14 +04:00
if ( ( failed ) & & ( test - > type = = XML_SCHEMATRON_ASSERT ) )
2005-07-24 18:27:16 +04:00
ctxt - > nberrors + + ;
2005-07-31 17:43:14 +04:00
else if ( ( ! failed ) & & ( test - > type = = XML_SCHEMATRON_REPORT ) )
ctxt - > nberrors + + ;
2008-03-14 15:46:42 +03:00
xmlSchematronReportSuccess ( ctxt , test , cur , pattern , ! failed ) ;
2005-07-23 19:00:22 +04:00
return ( ! failed ) ;
}
/**
* xmlSchematronValidateDoc :
* @ ctxt : the schema validation context
2012-09-11 09:26:36 +04:00
* @ instance : the document instace tree
2005-07-23 19:00:22 +04:00
*
* Validate a tree instance against the schematron
*
* Returns 0 in case of success , - 1 in case of internal error
* and an error count otherwise .
*/
int
xmlSchematronValidateDoc ( xmlSchematronValidCtxtPtr ctxt , xmlDocPtr instance )
{
2005-07-31 01:09:12 +04:00
xmlNodePtr cur , root ;
xmlSchematronPatternPtr pattern ;
2005-07-23 19:00:22 +04:00
xmlSchematronRulePtr rule ;
xmlSchematronTestPtr test ;
if ( ( ctxt = = NULL ) | | ( ctxt - > schema = = NULL ) | |
( ctxt - > schema - > rules = = NULL ) | | ( instance = = NULL ) )
return ( - 1 ) ;
ctxt - > nberrors = 0 ;
2005-07-31 01:09:12 +04:00
root = xmlDocGetRootElement ( instance ) ;
if ( root = = NULL ) {
TODO
ctxt - > nberrors + + ;
return ( 1 ) ;
}
if ( ( ctxt - > flags & XML_SCHEMATRON_OUT_QUIET ) | |
( ctxt - > flags = = 0 ) ) {
/*
* we are just trying to assert the validity of the document ,
* speed primes over the output , run in a single pass
*/
cur = root ;
while ( cur ! = NULL ) {
rule = ctxt - > schema - > rules ;
while ( rule ! = NULL ) {
if ( xmlPatternMatch ( rule - > pattern , cur ) = = 1 ) {
test = rule - > tests ;
while ( test ! = NULL ) {
2008-09-01 17:38:22 +04:00
xmlSchematronRunTest ( ctxt , test , instance , cur , ( xmlSchematronPatternPtr ) rule - > pattern ) ;
2005-07-31 01:09:12 +04:00
test = test - > next ;
}
}
rule = rule - > next ;
}
2012-09-11 09:26:36 +04:00
2005-07-31 01:09:12 +04:00
cur = xmlSchematronNextNode ( cur ) ;
}
} else {
/*
* Process all contexts one at a time
*/
pattern = ctxt - > schema - > patterns ;
2012-09-11 09:26:36 +04:00
2005-07-31 01:09:12 +04:00
while ( pattern ! = NULL ) {
2005-07-31 16:17:24 +04:00
xmlSchematronReportPattern ( ctxt , pattern ) ;
2005-07-31 01:09:12 +04:00
/*
* TODO convert the pattern rule to a direct XPath and
* compute directly instead of using the pattern matching
2012-09-11 09:26:36 +04:00
* over the full document . . .
2005-07-31 01:09:12 +04:00
* Check the exact semantic
*/
cur = root ;
while ( cur ! = NULL ) {
rule = pattern - > rules ;
while ( rule ! = NULL ) {
if ( xmlPatternMatch ( rule - > pattern , cur ) = = 1 ) {
test = rule - > tests ;
while ( test ! = NULL ) {
2008-03-14 15:46:42 +03:00
xmlSchematronRunTest ( ctxt , test , instance , cur , pattern ) ;
2005-07-31 01:09:12 +04:00
test = test - > next ;
}
}
rule = rule - > patnext ;
2005-07-23 19:00:22 +04:00
}
2012-09-11 09:26:36 +04:00
2005-07-31 01:09:12 +04:00
cur = xmlSchematronNextNode ( cur ) ;
2005-07-23 19:00:22 +04:00
}
2005-07-31 16:17:24 +04:00
pattern = pattern - > next ;
2005-07-23 19:00:22 +04:00
}
}
return ( ctxt - > nberrors ) ;
}
# ifdef STANDALONE
int
main ( void )
{
int ret ;
xmlDocPtr instance ;
xmlSchematronParserCtxtPtr pctxt ;
xmlSchematronValidCtxtPtr vctxt ;
xmlSchematronPtr schema = NULL ;
pctxt = xmlSchematronNewParserCtxt ( " tst.sct " ) ;
if ( pctxt = = NULL ) {
fprintf ( stderr , " failed to build schematron parser \n " ) ;
} else {
schema = xmlSchematronParse ( pctxt ) ;
if ( schema = = NULL ) {
fprintf ( stderr , " failed to compile schematron \n " ) ;
}
xmlSchematronFreeParserCtxt ( pctxt ) ;
}
instance = xmlReadFile ( " tst.sct " , NULL ,
XML_PARSE_NOENT | XML_PARSE_NOCDATA ) ;
if ( instance = = NULL ) {
fprintf ( stderr , " failed to parse instance \n " ) ;
}
if ( ( schema ! = NULL ) & & ( instance ! = NULL ) ) {
vctxt = xmlSchematronNewValidCtxt ( schema ) ;
if ( vctxt = = NULL ) {
fprintf ( stderr , " failed to build schematron validator \n " ) ;
} else {
ret = xmlSchematronValidateDoc ( vctxt , instance ) ;
xmlSchematronFreeValidCtxt ( vctxt ) ;
}
}
xmlSchematronFree ( schema ) ;
xmlFreeDoc ( instance ) ;
xmlCleanupParser ( ) ;
xmlMemoryDump ( ) ;
return ( 0 ) ;
}
# endif
2005-08-23 17:41:17 +04:00
# define bottom_schematron
# include "elfgcchack.h"
2005-07-23 19:00:22 +04:00
# endif /* LIBXML_SCHEMATRON_ENABLED */