/* * schemas.c : implementation of the XML Schema handling and * schema validity checking * * See Copyright for the status of this software. * * Daniel Veillard */ /* * TODO: * - when types are redefined in includes, check that all * types in the redef list are equal * -> need a type equality operation. * - if we don't intend to use the schema for schemas, we * need to validate all schema attributes (ref, type, name) * against their types. * - Eliminate item creation for: ?? * * URGENT TODO: * - For xsi-driven schema acquisition, augment the IDCs after every * acquisition episode (xmlSchemaAugmentIDC). * * NOTES: * - Eliminated item creation for: , , * , , , * * PROBLEMS: * - http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005JulSep/0337.html * IDC XPath expression and chameleon includes: the targetNamespace is changed, so * XPath will have trouble to resolve to this namespace, since not known. * * * CONSTRAINTS: * * Schema Component Constraint: * All Group Limited (cos-all-limited) * Status: complete * (1.2) * In xmlSchemaGroupDefReferenceTermFixup() and * (2) * In xmlSchemaParseModelGroup() * TODO: Actually this should go to component-level checks, * but is done here due to performance. Move it to an other layer * is schema construction via an API is implemented. */ /* To avoid EBCDIC trouble when parsing on zOS */ #if defined(__MVS__) #pragma convert("ISO8859-1") #endif #define IN_LIBXML #include "libxml.h" #ifdef LIBXML_SCHEMAS_ENABLED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LIBXML_PATTERN_ENABLED #include #endif #ifdef LIBXML_READER_ENABLED #include #endif #include "private/error.h" #include "private/string.h" /* #define WXS_ELEM_DECL_CONS_ENABLED */ /* #define ENABLE_PARTICLE_RESTRICTION 1 */ #define ENABLE_REDEFINE /* #define ENABLE_NAMED_LOCALS */ /* #define ENABLE_IDC_NODE_TABLES_TEST */ #define DUMP_CONTENT_MODEL #ifdef LIBXML_READER_ENABLED /* #define XML_SCHEMA_READER_ENABLED */ #endif #define UNBOUNDED (1 << 30) #define XML_SCHEMAS_NO_NAMESPACE (const xmlChar *) "##" /* * The XML Schemas namespaces */ static const xmlChar *xmlSchemaNs = (const xmlChar *) "http://www.w3.org/2001/XMLSchema"; static const xmlChar *xmlSchemaInstanceNs = (const xmlChar *) "http://www.w3.org/2001/XMLSchema-instance"; static const xmlChar *xmlNamespaceNs = (const xmlChar *) "http://www.w3.org/2000/xmlns/"; /* * Come casting macros. */ #define ACTXT_CAST (xmlSchemaAbstractCtxtPtr) #define PCTXT_CAST (xmlSchemaParserCtxtPtr) #define VCTXT_CAST (xmlSchemaValidCtxtPtr) #define WXS_BASIC_CAST (xmlSchemaBasicItemPtr) #define WXS_TREE_CAST (xmlSchemaTreeItemPtr) #define WXS_PTC_CAST (xmlSchemaParticlePtr) #define WXS_TYPE_CAST (xmlSchemaTypePtr) #define WXS_ELEM_CAST (xmlSchemaElementPtr) #define WXS_ATTR_GROUP_CAST (xmlSchemaAttributeGroupPtr) #define WXS_ATTR_CAST (xmlSchemaAttributePtr) #define WXS_ATTR_USE_CAST (xmlSchemaAttributeUsePtr) #define WXS_ATTR_PROHIB_CAST (xmlSchemaAttributeUseProhibPtr) #define WXS_MODEL_GROUPDEF_CAST (xmlSchemaModelGroupDefPtr) #define WXS_MODEL_GROUP_CAST (xmlSchemaModelGroupPtr) #define WXS_IDC_CAST (xmlSchemaIDCPtr) #define WXS_QNAME_CAST (xmlSchemaQNameRefPtr) #define WXS_LIST_CAST (xmlSchemaItemListPtr) /* * Macros to query common properties of components. */ #define WXS_ITEM_NODE(i) xmlSchemaGetComponentNode(WXS_BASIC_CAST (i)) #define WXS_ITEM_TYPE_NAME(i) xmlSchemaGetComponentTypeStr(WXS_BASIC_CAST (i)) /* * Macros for element declarations. */ #define WXS_ELEM_TYPEDEF(e) (e)->subtypes #define WXS_SUBST_HEAD(item) (item)->refDecl /* * Macros for attribute declarations. */ #define WXS_ATTR_TYPEDEF(a) (a)->subtypes /* * Macros for attribute uses. */ #define WXS_ATTRUSE_DECL(au) (WXS_ATTR_USE_CAST (au))->attrDecl #define WXS_ATTRUSE_TYPEDEF(au) WXS_ATTR_TYPEDEF(WXS_ATTRUSE_DECL( WXS_ATTR_USE_CAST au)) #define WXS_ATTRUSE_DECL_NAME(au) (WXS_ATTRUSE_DECL(au))->name #define WXS_ATTRUSE_DECL_TNS(au) (WXS_ATTRUSE_DECL(au))->targetNamespace /* * Macros for attribute groups. */ #define WXS_ATTR_GROUP_HAS_REFS(ag) ((WXS_ATTR_GROUP_CAST (ag))->flags & XML_SCHEMAS_ATTRGROUP_HAS_REFS) #define WXS_ATTR_GROUP_EXPANDED(ag) ((WXS_ATTR_GROUP_CAST (ag))->flags & XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED) /* * Macros for particles. */ #define WXS_PARTICLE(p) WXS_PTC_CAST (p) #define WXS_PARTICLE_TERM(p) (WXS_PARTICLE(p))->children #define WXS_PARTICLE_TERM_AS_ELEM(p) (WXS_ELEM_CAST WXS_PARTICLE_TERM(p)) #define WXS_PARTICLE_MODEL(p) WXS_MODEL_GROUP_CAST WXS_PARTICLE(p)->children /* * Macros for model groups definitions. */ #define WXS_MODELGROUPDEF_MODEL(mgd) (WXS_MODEL_GROUP_CAST (mgd))->children /* * Macros for model groups. */ #define WXS_IS_MODEL_GROUP(i) \ (((i)->type == XML_SCHEMA_TYPE_SEQUENCE) || \ ((i)->type == XML_SCHEMA_TYPE_CHOICE) || \ ((i)->type == XML_SCHEMA_TYPE_ALL)) #define WXS_MODELGROUP_PARTICLE(mg) WXS_PTC_CAST (mg)->children /* * Macros for schema buckets. */ #define WXS_IS_BUCKET_INCREDEF(t) (((t) == XML_SCHEMA_SCHEMA_INCLUDE) || \ ((t) == XML_SCHEMA_SCHEMA_REDEFINE)) #define WXS_IS_BUCKET_IMPMAIN(t) (((t) == XML_SCHEMA_SCHEMA_MAIN) || \ ((t) == XML_SCHEMA_SCHEMA_IMPORT)) #define WXS_IMPBUCKET(b) ((xmlSchemaImportPtr) (b)) #define WXS_INCBUCKET(b) ((xmlSchemaIncludePtr) (b)) /* * Macros for complex/simple types. */ #define WXS_IS_ANYTYPE(i) \ (( (i)->type == XML_SCHEMA_TYPE_BASIC) && \ ( (WXS_TYPE_CAST (i))->builtInType == XML_SCHEMAS_ANYTYPE)) #define WXS_IS_COMPLEX(i) \ (((i)->type == XML_SCHEMA_TYPE_COMPLEX) || \ ((i)->builtInType == XML_SCHEMAS_ANYTYPE)) #define WXS_IS_SIMPLE(item) \ ((item->type == XML_SCHEMA_TYPE_SIMPLE) || \ ((item->type == XML_SCHEMA_TYPE_BASIC) && \ (item->builtInType != XML_SCHEMAS_ANYTYPE))) #define WXS_IS_ANY_SIMPLE_TYPE(i) \ (((i)->type == XML_SCHEMA_TYPE_BASIC) && \ ((i)->builtInType == XML_SCHEMAS_ANYSIMPLETYPE)) #define WXS_IS_RESTRICTION(t) \ ((t)->flags & XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION) #define WXS_IS_EXTENSION(t) \ ((t)->flags & XML_SCHEMAS_TYPE_DERIVATION_METHOD_EXTENSION) #define WXS_IS_TYPE_NOT_FIXED(i) \ (((i)->type != XML_SCHEMA_TYPE_BASIC) && \ (((i)->flags & XML_SCHEMAS_TYPE_INTERNAL_RESOLVED) == 0)) #define WXS_IS_TYPE_NOT_FIXED_1(item) \ (((item)->type != XML_SCHEMA_TYPE_BASIC) && \ (((item)->flags & XML_SCHEMAS_TYPE_FIXUP_1) == 0)) #define WXS_TYPE_IS_GLOBAL(t) ((t)->flags & XML_SCHEMAS_TYPE_GLOBAL) #define WXS_TYPE_IS_LOCAL(t) (((t)->flags & XML_SCHEMAS_TYPE_GLOBAL) == 0) /* * Macros for exclusively for complex types. */ #define WXS_HAS_COMPLEX_CONTENT(item) \ ((item->contentType == XML_SCHEMA_CONTENT_MIXED) || \ (item->contentType == XML_SCHEMA_CONTENT_EMPTY) || \ (item->contentType == XML_SCHEMA_CONTENT_ELEMENTS)) #define WXS_HAS_SIMPLE_CONTENT(item) \ ((item->contentType == XML_SCHEMA_CONTENT_SIMPLE) || \ (item->contentType == XML_SCHEMA_CONTENT_BASIC)) #define WXS_HAS_MIXED_CONTENT(item) \ (item->contentType == XML_SCHEMA_CONTENT_MIXED) #define WXS_EMPTIABLE(t) \ (xmlSchemaIsParticleEmptiable(WXS_PTC_CAST (t)->subtypes)) #define WXS_TYPE_CONTENTTYPE(t) (t)->subtypes #define WXS_TYPE_PARTICLE(t) WXS_PTC_CAST (t)->subtypes #define WXS_TYPE_PARTICLE_TERM(t) WXS_PARTICLE_TERM(WXS_TYPE_PARTICLE(t)) /* * Macros for exclusively for simple types. */ #define WXS_LIST_ITEMTYPE(t) (t)->subtypes #define WXS_IS_ATOMIC(t) (t->flags & XML_SCHEMAS_TYPE_VARIETY_ATOMIC) #define WXS_IS_LIST(t) (t->flags & XML_SCHEMAS_TYPE_VARIETY_LIST) #define WXS_IS_UNION(t) (t->flags & XML_SCHEMAS_TYPE_VARIETY_UNION) /* * Misc parser context macros. */ #define WXS_CONSTRUCTOR(ctx) (ctx)->constructor #define WXS_HAS_BUCKETS(ctx) \ ( (WXS_CONSTRUCTOR((ctx))->buckets != NULL) && \ (WXS_CONSTRUCTOR((ctx))->buckets->nbItems > 0) ) #define WXS_SUBST_GROUPS(ctx) WXS_CONSTRUCTOR((ctx))->substGroups #define WXS_BUCKET(ctx) WXS_CONSTRUCTOR((ctx))->bucket #define WXS_SCHEMA(ctx) (ctx)->schema #define WXS_ADD_LOCAL(ctx, item) \ do { \ if (xmlSchemaAddItemSize(&(WXS_BUCKET(ctx)->locals), 10, item) < 0) { \ xmlFree(item); \ item = NULL; \ } \ } while (0) #define WXS_ADD_GLOBAL(ctx, item) \ do { \ if (xmlSchemaAddItemSize(&(WXS_BUCKET(ctx)->globals), 5, item) < 0) { \ xmlFree(item); \ item = NULL; \ } \ } while (0) #define WXS_ADD_PENDING(ctx, item) \ xmlSchemaAddItemSize(&((ctx)->constructor->pending), 10, item) /* * xmlSchemaItemList macros. */ #define WXS_ILIST_IS_EMPTY(l) ((l == NULL) || ((l)->nbItems == 0)) /* * Misc macros. */ #define IS_SCHEMA(node, type) \ ((node != NULL) && (node->ns != NULL) && \ (xmlStrEqual(node->name, (const xmlChar *) type)) && \ (xmlStrEqual(node->ns->href, xmlSchemaNs))) #define FREE_AND_NULL(str) if ((str) != NULL) { xmlFree((xmlChar *) (str)); str = NULL; } /* * Since we put the default/fixed values into the dict, we can * use pointer comparison for those values. * REMOVED: (xmlStrEqual((v1), (v2))) */ #define WXS_ARE_DEFAULT_STR_EQUAL(v1, v2) ((v1) == (v2)) #define INODE_NILLED(item) (item->flags & XML_SCHEMA_ELEM_INFO_NILLED) #define CAN_PARSE_SCHEMA(b) (((b)->doc != NULL) && ((b)->parsed == 0)) #define HFAILURE if (res == -1) goto exit_failure; #define HERROR if (res != 0) goto exit_error; #define HSTOP(ctx) if ((ctx)->stop) goto exit; /* * Some flags used for various schema constraints. */ #define SUBSET_RESTRICTION 1<<0 #define SUBSET_EXTENSION 1<<1 #define SUBSET_SUBSTITUTION 1<<2 #define SUBSET_LIST 1<<3 #define SUBSET_UNION 1<<4 typedef struct _xmlSchemaNodeInfo xmlSchemaNodeInfo; typedef xmlSchemaNodeInfo *xmlSchemaNodeInfoPtr; typedef struct _xmlSchemaItemList xmlSchemaItemList; typedef xmlSchemaItemList *xmlSchemaItemListPtr; struct _xmlSchemaItemList { void **items; /* used for dynamic addition of schemata */ int nbItems; /* used for dynamic addition of schemata */ int sizeItems; /* used for dynamic addition of schemata */ }; #define XML_SCHEMA_CTXT_PARSER 1 #define XML_SCHEMA_CTXT_VALIDATOR 2 typedef struct _xmlSchemaAbstractCtxt xmlSchemaAbstractCtxt; typedef xmlSchemaAbstractCtxt *xmlSchemaAbstractCtxtPtr; struct _xmlSchemaAbstractCtxt { int type; /* E.g. XML_SCHEMA_CTXT_VALIDATOR */ void *dummy; /* Fix alignment issues */ }; typedef struct _xmlSchemaBucket xmlSchemaBucket; typedef xmlSchemaBucket *xmlSchemaBucketPtr; #define XML_SCHEMA_SCHEMA_MAIN 0 #define XML_SCHEMA_SCHEMA_IMPORT 1 #define XML_SCHEMA_SCHEMA_INCLUDE 2 #define XML_SCHEMA_SCHEMA_REDEFINE 3 /** * xmlSchemaSchemaRelation: * * Used to create a graph of schema relationships. */ typedef struct _xmlSchemaSchemaRelation xmlSchemaSchemaRelation; typedef xmlSchemaSchemaRelation *xmlSchemaSchemaRelationPtr; struct _xmlSchemaSchemaRelation { xmlSchemaSchemaRelationPtr next; int type; /* E.g. XML_SCHEMA_SCHEMA_IMPORT */ const xmlChar *importNamespace; xmlSchemaBucketPtr bucket; }; #define XML_SCHEMA_BUCKET_MARKED 1<<0 #define XML_SCHEMA_BUCKET_COMPS_ADDED 1<<1 struct _xmlSchemaBucket { int type; int flags; const xmlChar *schemaLocation; const xmlChar *origTargetNamespace; const xmlChar *targetNamespace; xmlDocPtr doc; xmlSchemaSchemaRelationPtr relations; int located; int parsed; int imported; int preserveDoc; xmlSchemaItemListPtr globals; /* Global components. */ xmlSchemaItemListPtr locals; /* Local components. */ }; /** * xmlSchemaImport: * (extends xmlSchemaBucket) * * Reflects a schema. Holds some information * about the schema and its toplevel components. Duplicate * toplevel components are not checked at this level. */ typedef struct _xmlSchemaImport xmlSchemaImport; typedef xmlSchemaImport *xmlSchemaImportPtr; struct _xmlSchemaImport { int type; /* Main OR import OR include. */ int flags; const xmlChar *schemaLocation; /* The URI of the schema document. */ /* For chameleon includes, @origTargetNamespace will be NULL */ const xmlChar *origTargetNamespace; /* * For chameleon includes, @targetNamespace will be the * targetNamespace of the including schema. */ const xmlChar *targetNamespace; xmlDocPtr doc; /* The schema node-tree. */ /* @relations will hold any included/imported/redefined schemas. */ xmlSchemaSchemaRelationPtr relations; int located; int parsed; int imported; int preserveDoc; xmlSchemaItemListPtr globals; xmlSchemaItemListPtr locals; /* The imported schema. */ xmlSchemaPtr schema; }; /* * (extends xmlSchemaBucket) */ typedef struct _xmlSchemaInclude xmlSchemaInclude; typedef xmlSchemaInclude *xmlSchemaIncludePtr; struct _xmlSchemaInclude { int type; int flags; const xmlChar *schemaLocation; const xmlChar *origTargetNamespace; const xmlChar *targetNamespace; xmlDocPtr doc; xmlSchemaSchemaRelationPtr relations; int located; int parsed; int imported; int preserveDoc; xmlSchemaItemListPtr globals; /* Global components. */ xmlSchemaItemListPtr locals; /* Local components. */ /* The owning main or import schema bucket. */ xmlSchemaImportPtr ownerImport; }; /** * xmlSchemaBasicItem: * * The abstract base type for schema components. */ typedef struct _xmlSchemaBasicItem xmlSchemaBasicItem; typedef xmlSchemaBasicItem *xmlSchemaBasicItemPtr; struct _xmlSchemaBasicItem { xmlSchemaTypeType type; void *dummy; /* Fix alignment issues */ }; /** * xmlSchemaAnnotItem: * * The abstract base type for annotated schema components. * (Extends xmlSchemaBasicItem) */ typedef struct _xmlSchemaAnnotItem xmlSchemaAnnotItem; typedef xmlSchemaAnnotItem *xmlSchemaAnnotItemPtr; struct _xmlSchemaAnnotItem { xmlSchemaTypeType type; xmlSchemaAnnotPtr annot; }; /** * xmlSchemaTreeItem: * * The abstract base type for tree-like structured schema components. * (Extends xmlSchemaAnnotItem) */ typedef struct _xmlSchemaTreeItem xmlSchemaTreeItem; typedef xmlSchemaTreeItem *xmlSchemaTreeItemPtr; struct _xmlSchemaTreeItem { xmlSchemaTypeType type; xmlSchemaAnnotPtr annot; xmlSchemaTreeItemPtr next; xmlSchemaTreeItemPtr children; }; #define XML_SCHEMA_ATTR_USE_FIXED 1<<0 /** * xmlSchemaAttributeUsePtr: * * The abstract base type for tree-like structured schema components. * (Extends xmlSchemaTreeItem) */ typedef struct _xmlSchemaAttributeUse xmlSchemaAttributeUse; typedef xmlSchemaAttributeUse *xmlSchemaAttributeUsePtr; struct _xmlSchemaAttributeUse { xmlSchemaTypeType type; xmlSchemaAnnotPtr annot; xmlSchemaAttributeUsePtr next; /* The next attr. use. */ /* * The attr. decl. OR a QName-ref. to an attr. decl. OR * a QName-ref. to an attribute group definition. */ xmlSchemaAttributePtr attrDecl; int flags; xmlNodePtr node; int occurs; /* required, optional */ const xmlChar * defValue; xmlSchemaValPtr defVal; }; /** * xmlSchemaAttributeUseProhibPtr: * * A helper component to reflect attribute prohibitions. * (Extends xmlSchemaBasicItem) */ typedef struct _xmlSchemaAttributeUseProhib xmlSchemaAttributeUseProhib; typedef xmlSchemaAttributeUseProhib *xmlSchemaAttributeUseProhibPtr; struct _xmlSchemaAttributeUseProhib { xmlSchemaTypeType type; /* == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB */ xmlNodePtr node; const xmlChar *name; const xmlChar *targetNamespace; int isRef; }; /** * xmlSchemaRedef: */ typedef struct _xmlSchemaRedef xmlSchemaRedef; typedef xmlSchemaRedef *xmlSchemaRedefPtr; struct _xmlSchemaRedef { xmlSchemaRedefPtr next; xmlSchemaBasicItemPtr item; /* The redefining component. */ xmlSchemaBasicItemPtr reference; /* The referencing component. */ xmlSchemaBasicItemPtr target; /* The to-be-redefined component. */ const xmlChar *refName; /* The name of the to-be-redefined component. */ const xmlChar *refTargetNs; /* The target namespace of the to-be-redefined comp. */ xmlSchemaBucketPtr targetBucket; /* The redefined schema. */ }; /** * xmlSchemaConstructionCtxt: */ typedef struct _xmlSchemaConstructionCtxt xmlSchemaConstructionCtxt; typedef xmlSchemaConstructionCtxt *xmlSchemaConstructionCtxtPtr; struct _xmlSchemaConstructionCtxt { xmlSchemaPtr mainSchema; /* The main schema. */ xmlSchemaBucketPtr mainBucket; /* The main schema bucket */ xmlDictPtr dict; xmlSchemaItemListPtr buckets; /* List of schema buckets. */ /* xmlSchemaItemListPtr relations; */ /* List of schema relations. */ xmlSchemaBucketPtr bucket; /* The current schema bucket */ xmlSchemaItemListPtr pending; /* All Components of all schemas that need to be fixed. */ xmlHashTablePtr substGroups; xmlSchemaRedefPtr redefs; xmlSchemaRedefPtr lastRedef; }; #define XML_SCHEMAS_PARSE_ERROR 1 #define SCHEMAS_PARSE_OPTIONS XML_PARSE_NOENT struct _xmlSchemaParserCtxt { int type; void *errCtxt; /* user specific error context */ xmlSchemaValidityErrorFunc error; /* the callback in case of errors */ xmlSchemaValidityWarningFunc warning; /* the callback in case of warning */ int err; int nberrors; xmlStructuredErrorFunc serror; xmlSchemaConstructionCtxtPtr constructor; int ownsConstructor; /* TODO: Move this to parser *flags*. */ /* xmlSchemaPtr topschema; */ /* xmlHashTablePtr namespaces; */ xmlSchemaPtr schema; /* The main schema in use */ int counter; const xmlChar *URL; xmlDocPtr doc; int preserve; /* Whether the doc should be freed */ const char *buffer; int size; /* * Used to build complex element content models */ xmlAutomataPtr am; xmlAutomataStatePtr start; xmlAutomataStatePtr end; xmlAutomataStatePtr state; xmlDictPtr dict; /* dictionary for interned string names */ xmlSchemaTypePtr ctxtType; /* The current context simple/complex type */ int options; xmlSchemaValidCtxtPtr vctxt; int isS4S; int isRedefine; int xsiAssemble; int stop; /* If the parser should stop; i.e. a critical error. */ const xmlChar *targetNamespace; xmlSchemaBucketPtr redefined; /* The schema to be redefined. */ xmlSchemaRedefPtr redef; /* Used for redefinitions. */ int redefCounter; /* Used for redefinitions. */ xmlSchemaItemListPtr attrProhibs; }; /** * xmlSchemaQNameRef: * * A component reference item (not a schema component) * (Extends xmlSchemaBasicItem) */ typedef struct _xmlSchemaQNameRef xmlSchemaQNameRef; typedef xmlSchemaQNameRef *xmlSchemaQNameRefPtr; struct _xmlSchemaQNameRef { xmlSchemaTypeType type; xmlSchemaBasicItemPtr item; /* The resolved referenced item. */ xmlSchemaTypeType itemType; const xmlChar *name; const xmlChar *targetNamespace; xmlNodePtr node; }; /** * xmlSchemaParticle: * * A particle component. * (Extends xmlSchemaTreeItem) */ typedef struct _xmlSchemaParticle xmlSchemaParticle; typedef xmlSchemaParticle *xmlSchemaParticlePtr; struct _xmlSchemaParticle { xmlSchemaTypeType type; xmlSchemaAnnotPtr annot; xmlSchemaTreeItemPtr next; /* next particle */ xmlSchemaTreeItemPtr children; /* the "term" (e.g. a model group, a group definition, a XML_SCHEMA_EXTRA_QNAMEREF (if a reference), etc.) */ int minOccurs; int maxOccurs; xmlNodePtr node; }; /** * xmlSchemaModelGroup: * * A model group component. * (Extends xmlSchemaTreeItem) */ typedef struct _xmlSchemaModelGroup xmlSchemaModelGroup; typedef xmlSchemaModelGroup *xmlSchemaModelGroupPtr; struct _xmlSchemaModelGroup { xmlSchemaTypeType type; /* XML_SCHEMA_TYPE_SEQUENCE, XML_SCHEMA_TYPE_CHOICE, XML_SCHEMA_TYPE_ALL */ xmlSchemaAnnotPtr annot; xmlSchemaTreeItemPtr next; /* not used */ xmlSchemaTreeItemPtr children; /* first particle (OR "element decl" OR "wildcard") */ xmlNodePtr node; }; #define XML_SCHEMA_MODEL_GROUP_DEF_MARKED 1<<0 #define XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED 1<<1 /** * xmlSchemaModelGroupDef: * * A model group definition component. * (Extends xmlSchemaTreeItem) */ typedef struct _xmlSchemaModelGroupDef xmlSchemaModelGroupDef; typedef xmlSchemaModelGroupDef *xmlSchemaModelGroupDefPtr; struct _xmlSchemaModelGroupDef { xmlSchemaTypeType type; /* XML_SCHEMA_TYPE_GROUP */ xmlSchemaAnnotPtr annot; xmlSchemaTreeItemPtr next; /* not used */ xmlSchemaTreeItemPtr children; /* the "model group" */ const xmlChar *name; const xmlChar *targetNamespace; xmlNodePtr node; int flags; }; typedef struct _xmlSchemaIDC xmlSchemaIDC; typedef xmlSchemaIDC *xmlSchemaIDCPtr; /** * xmlSchemaIDCSelect: * * The identity-constraint "field" and "selector" item, holding the * XPath expression. */ typedef struct _xmlSchemaIDCSelect xmlSchemaIDCSelect; typedef xmlSchemaIDCSelect *xmlSchemaIDCSelectPtr; struct _xmlSchemaIDCSelect { xmlSchemaIDCSelectPtr next; xmlSchemaIDCPtr idc; int index; /* an index position if significant for IDC key-sequences */ const xmlChar *xpath; /* the XPath expression */ void *xpathComp; /* the compiled XPath expression */ }; /** * xmlSchemaIDC: * * The identity-constraint definition component. * (Extends xmlSchemaAnnotItem) */ struct _xmlSchemaIDC { xmlSchemaTypeType type; xmlSchemaAnnotPtr annot; xmlSchemaIDCPtr next; xmlNodePtr node; const xmlChar *name; const xmlChar *targetNamespace; xmlSchemaIDCSelectPtr selector; xmlSchemaIDCSelectPtr fields; int nbFields; xmlSchemaQNameRefPtr ref; }; /** * xmlSchemaIDCAug: * * The augmented IDC information used for validation. */ typedef struct _xmlSchemaIDCAug xmlSchemaIDCAug; typedef xmlSchemaIDCAug *xmlSchemaIDCAugPtr; struct _xmlSchemaIDCAug { xmlSchemaIDCAugPtr next; /* next in a list */ xmlSchemaIDCPtr def; /* the IDC definition */ int keyrefDepth; /* the lowest tree level to which IDC tables need to be bubbled upwards */ }; /** * xmlSchemaPSVIIDCKeySequence: * * The key sequence of a node table item. */ typedef struct _xmlSchemaPSVIIDCKey xmlSchemaPSVIIDCKey; typedef xmlSchemaPSVIIDCKey *xmlSchemaPSVIIDCKeyPtr; struct _xmlSchemaPSVIIDCKey { xmlSchemaTypePtr type; xmlSchemaValPtr val; }; /** * xmlSchemaPSVIIDCNode: * * The node table item of a node table. */ typedef struct _xmlSchemaPSVIIDCNode xmlSchemaPSVIIDCNode; typedef xmlSchemaPSVIIDCNode *xmlSchemaPSVIIDCNodePtr; struct _xmlSchemaPSVIIDCNode { xmlNodePtr node; xmlSchemaPSVIIDCKeyPtr *keys; int nodeLine; int nodeQNameID; }; /** * xmlSchemaPSVIIDCBinding: * * The identity-constraint binding item of the [identity-constraint table]. */ typedef struct _xmlSchemaPSVIIDCBinding xmlSchemaPSVIIDCBinding; typedef xmlSchemaPSVIIDCBinding *xmlSchemaPSVIIDCBindingPtr; struct _xmlSchemaPSVIIDCBinding { xmlSchemaPSVIIDCBindingPtr next; /* next binding of a specific node */ xmlSchemaIDCPtr definition; /* the IDC definition */ xmlSchemaPSVIIDCNodePtr *nodeTable; /* array of key-sequences */ int nbNodes; /* number of entries in the node table */ int sizeNodes; /* size of the node table */ xmlSchemaItemListPtr dupls; }; #define XPATH_STATE_OBJ_TYPE_IDC_SELECTOR 1 #define XPATH_STATE_OBJ_TYPE_IDC_FIELD 2 #define XPATH_STATE_OBJ_MATCHES -2 #define XPATH_STATE_OBJ_BLOCKED -3 typedef struct _xmlSchemaIDCMatcher xmlSchemaIDCMatcher; typedef xmlSchemaIDCMatcher *xmlSchemaIDCMatcherPtr; /** * xmlSchemaIDCStateObj: * * The state object used to evaluate XPath expressions. */ typedef struct _xmlSchemaIDCStateObj xmlSchemaIDCStateObj; typedef xmlSchemaIDCStateObj *xmlSchemaIDCStateObjPtr; struct _xmlSchemaIDCStateObj { int type; xmlSchemaIDCStateObjPtr next; /* next if in a list */ int depth; /* depth of creation */ int *history; /* list of (depth, state-id) tuples */ int nbHistory; int sizeHistory; xmlSchemaIDCMatcherPtr matcher; /* the correspondent field/selector matcher */ xmlSchemaIDCSelectPtr sel; void *xpathCtxt; }; #define IDC_MATCHER 0 /** * xmlSchemaIDCMatcher: * * Used to evaluate IDC selectors (and fields). */ struct _xmlSchemaIDCMatcher { int type; int depth; /* the tree depth at creation time */ xmlSchemaIDCMatcherPtr next; /* next in the list */ xmlSchemaIDCMatcherPtr nextCached; /* next in the cache list */ xmlSchemaIDCAugPtr aidc; /* the augmented IDC item */ int idcType; xmlSchemaPSVIIDCKeyPtr **keySeqs; /* the key-sequences of the target elements */ int sizeKeySeqs; xmlSchemaItemListPtr targets; /* list of target-node (xmlSchemaPSVIIDCNodePtr) entries */ xmlHashTablePtr htab; }; /* * Element info flags. */ #define XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES 1<<0 #define XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES 1<<1 #define XML_SCHEMA_ELEM_INFO_NILLED 1<<2 #define XML_SCHEMA_ELEM_INFO_LOCAL_TYPE 1<<3 #define XML_SCHEMA_NODE_INFO_VALUE_NEEDED 1<<4 #define XML_SCHEMA_ELEM_INFO_EMPTY 1<<5 #define XML_SCHEMA_ELEM_INFO_HAS_CONTENT 1<<6 #define XML_SCHEMA_ELEM_INFO_HAS_ELEM_CONTENT 1<<7 #define XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT 1<<8 #define XML_SCHEMA_NODE_INFO_ERR_NOT_EXPECTED 1<<9 #define XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE 1<<10 /** * xmlSchemaNodeInfo: * * Holds information of an element node. */ struct _xmlSchemaNodeInfo { int nodeType; xmlNodePtr node; int nodeLine; const xmlChar *localName; const xmlChar *nsName; const xmlChar *value; xmlSchemaValPtr val; /* the pre-computed value if any */ xmlSchemaTypePtr typeDef; /* the complex/simple type definition if any */ int flags; /* combination of node info flags */ int valNeeded; int normVal; xmlSchemaElementPtr decl; /* the element/attribute declaration */ int depth; xmlSchemaPSVIIDCBindingPtr idcTable; /* the table of PSVI IDC bindings for the scope element*/ xmlSchemaIDCMatcherPtr idcMatchers; /* the IDC matchers for the scope element */ xmlRegExecCtxtPtr regexCtxt; const xmlChar **nsBindings; /* Namespace bindings on this element */ int nbNsBindings; int sizeNsBindings; int hasKeyrefs; int appliedXPath; /* Indicates that an XPath has been applied. */ }; #define XML_SCHEMAS_ATTR_UNKNOWN 1 #define XML_SCHEMAS_ATTR_ASSESSED 2 #define XML_SCHEMAS_ATTR_PROHIBITED 3 #define XML_SCHEMAS_ATTR_ERR_MISSING 4 #define XML_SCHEMAS_ATTR_INVALID_VALUE 5 #define XML_SCHEMAS_ATTR_ERR_NO_TYPE 6 #define XML_SCHEMAS_ATTR_ERR_FIXED_VALUE 7 #define XML_SCHEMAS_ATTR_DEFAULT 8 #define XML_SCHEMAS_ATTR_VALIDATE_VALUE 9 #define XML_SCHEMAS_ATTR_ERR_WILD_STRICT_NO_DECL 10 #define XML_SCHEMAS_ATTR_HAS_ATTR_USE 11 #define XML_SCHEMAS_ATTR_HAS_ATTR_DECL 12 #define XML_SCHEMAS_ATTR_WILD_SKIP 13 #define XML_SCHEMAS_ATTR_WILD_LAX_NO_DECL 14 #define XML_SCHEMAS_ATTR_ERR_WILD_DUPLICATE_ID 15 #define XML_SCHEMAS_ATTR_ERR_WILD_AND_USE_ID 16 #define XML_SCHEMAS_ATTR_META 17 /* * @metaType values of xmlSchemaAttrInfo. */ #define XML_SCHEMA_ATTR_INFO_META_XSI_TYPE 1 #define XML_SCHEMA_ATTR_INFO_META_XSI_NIL 2 #define XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC 3 #define XML_SCHEMA_ATTR_INFO_META_XSI_NO_NS_SCHEMA_LOC 4 #define XML_SCHEMA_ATTR_INFO_META_XMLNS 5 typedef struct _xmlSchemaAttrInfo xmlSchemaAttrInfo; typedef xmlSchemaAttrInfo *xmlSchemaAttrInfoPtr; struct _xmlSchemaAttrInfo { int nodeType; xmlNodePtr node; int nodeLine; const xmlChar *localName; const xmlChar *nsName; const xmlChar *value; xmlSchemaValPtr val; /* the pre-computed value if any */ xmlSchemaTypePtr typeDef; /* the complex/simple type definition if any */ int flags; /* combination of node info flags */ xmlSchemaAttributePtr decl; /* the attribute declaration */ xmlSchemaAttributeUsePtr use; /* the attribute use */ int state; int metaType; const xmlChar *vcValue; /* the value constraint value */ xmlSchemaNodeInfoPtr parent; }; #define XML_SCHEMA_VALID_CTXT_FLAG_STREAM 1 /** * xmlSchemaValidCtxt: * * A Schemas validation context */ struct _xmlSchemaValidCtxt { int type; void *errCtxt; /* user specific data block */ xmlSchemaValidityErrorFunc error; /* the callback in case of errors */ xmlSchemaValidityWarningFunc warning; /* the callback in case of warning */ xmlStructuredErrorFunc serror; xmlSchemaPtr schema; /* The schema in use */ xmlDocPtr doc; xmlParserInputBufferPtr input; xmlCharEncoding enc; xmlSAXHandlerPtr sax; xmlParserCtxtPtr parserCtxt; void *user_data; /* TODO: What is this for? */ char *filename; int err; int nberrors; xmlNodePtr node; xmlNodePtr cur; /* xmlSchemaTypePtr type; */ xmlRegExecCtxtPtr regexp; xmlSchemaValPtr value; int valueWS; int options; xmlNodePtr validationRoot; xmlSchemaParserCtxtPtr pctxt; int xsiAssemble; int depth; xmlSchemaNodeInfoPtr *elemInfos; /* array of element information */ int sizeElemInfos; xmlSchemaNodeInfoPtr inode; /* the current element information */ xmlSchemaIDCAugPtr aidcs; /* a list of augmented IDC information */ xmlSchemaIDCStateObjPtr xpathStates; /* first active state object. */ xmlSchemaIDCStateObjPtr xpathStatePool; /* first stored state object. */ xmlSchemaIDCMatcherPtr idcMatcherCache; /* Cache for IDC matcher objects. */ xmlSchemaPSVIIDCNodePtr *idcNodes; /* list of all IDC node-table entries*/ int nbIdcNodes; int sizeIdcNodes; xmlSchemaPSVIIDCKeyPtr *idcKeys; /* list of all IDC node-table entries */ int nbIdcKeys; int sizeIdcKeys; int flags; xmlDictPtr dict; #ifdef LIBXML_READER_ENABLED xmlTextReaderPtr reader; #endif xmlSchemaAttrInfoPtr *attrInfos; int nbAttrInfos; int sizeAttrInfos; int skipDepth; xmlSchemaItemListPtr nodeQNames; int hasKeyrefs; int createIDCNodeTables; int psviExposeIDCNodeTables; /* Locator for error reporting in streaming mode */ xmlSchemaValidityLocatorFunc locFunc; void *locCtxt; }; /** * xmlSchemaSubstGroup: * * */ typedef struct _xmlSchemaSubstGroup xmlSchemaSubstGroup; typedef xmlSchemaSubstGroup *xmlSchemaSubstGroupPtr; struct _xmlSchemaSubstGroup { xmlSchemaElementPtr head; xmlSchemaItemListPtr members; }; /** * xmlIDCHashEntry: * * an entry in hash tables to quickly look up keys/uniques */ typedef struct _xmlIDCHashEntry xmlIDCHashEntry; typedef xmlIDCHashEntry *xmlIDCHashEntryPtr; struct _xmlIDCHashEntry { xmlIDCHashEntryPtr next; /* next item with same hash */ int index; /* index into associated item list */ }; /************************************************************************ * * * Some predeclarations * * * ************************************************************************/ static int xmlSchemaParseInclude(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node); static int xmlSchemaParseRedefine(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node); static int xmlSchemaTypeFixup(xmlSchemaTypePtr type, xmlSchemaAbstractCtxtPtr ctxt); static const xmlChar * xmlSchemaFacetTypeToString(xmlSchemaTypeType type); static int xmlSchemaParseImport(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node); static int xmlSchemaCheckFacetValues(xmlSchemaTypePtr typeDecl, xmlSchemaParserCtxtPtr ctxt); static void xmlSchemaClearValidCtxt(xmlSchemaValidCtxtPtr vctxt); static xmlSchemaWhitespaceValueType xmlSchemaGetWhiteSpaceFacetValue(xmlSchemaTypePtr type); static xmlSchemaTreeItemPtr xmlSchemaParseModelGroup(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaTypeType type, int withParticle); static const xmlChar * xmlSchemaGetComponentTypeStr(xmlSchemaBasicItemPtr item); static xmlSchemaTypeLinkPtr xmlSchemaGetUnionSimpleTypeMemberTypes(xmlSchemaTypePtr type); static void xmlSchemaInternalErr(xmlSchemaAbstractCtxtPtr actxt, const char *funcName, const char *message) LIBXML_ATTR_FORMAT(3,0); static int xmlSchemaCheckCOSSTDerivedOK(xmlSchemaAbstractCtxtPtr ctxt, xmlSchemaTypePtr type, xmlSchemaTypePtr baseType, int subset); static void xmlSchemaCheckElementDeclComponent(xmlSchemaElementPtr elemDecl, xmlSchemaParserCtxtPtr ctxt); static void xmlSchemaComponentListFree(xmlSchemaItemListPtr list); static xmlSchemaQNameRefPtr xmlSchemaParseAttributeGroupRef(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node); /************************************************************************ * * * Helper functions * * * ************************************************************************/ /** * xmlSchemaItemTypeToStr: * @type: the type of the schema item * * Returns the component name of a schema item. */ static const xmlChar * xmlSchemaItemTypeToStr(xmlSchemaTypeType type) { switch (type) { case XML_SCHEMA_TYPE_BASIC: return(BAD_CAST "simple type definition"); case XML_SCHEMA_TYPE_SIMPLE: return(BAD_CAST "simple type definition"); case XML_SCHEMA_TYPE_COMPLEX: return(BAD_CAST "complex type definition"); case XML_SCHEMA_TYPE_ELEMENT: return(BAD_CAST "element declaration"); case XML_SCHEMA_TYPE_ATTRIBUTE_USE: return(BAD_CAST "attribute use"); case XML_SCHEMA_TYPE_ATTRIBUTE: return(BAD_CAST "attribute declaration"); case XML_SCHEMA_TYPE_GROUP: return(BAD_CAST "model group definition"); case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: return(BAD_CAST "attribute group definition"); case XML_SCHEMA_TYPE_NOTATION: return(BAD_CAST "notation declaration"); case XML_SCHEMA_TYPE_SEQUENCE: return(BAD_CAST "model group (sequence)"); case XML_SCHEMA_TYPE_CHOICE: return(BAD_CAST "model group (choice)"); case XML_SCHEMA_TYPE_ALL: return(BAD_CAST "model group (all)"); case XML_SCHEMA_TYPE_PARTICLE: return(BAD_CAST "particle"); case XML_SCHEMA_TYPE_IDC_UNIQUE: return(BAD_CAST "unique identity-constraint"); /* return(BAD_CAST "IDC (unique)"); */ case XML_SCHEMA_TYPE_IDC_KEY: return(BAD_CAST "key identity-constraint"); /* return(BAD_CAST "IDC (key)"); */ case XML_SCHEMA_TYPE_IDC_KEYREF: return(BAD_CAST "keyref identity-constraint"); /* return(BAD_CAST "IDC (keyref)"); */ case XML_SCHEMA_TYPE_ANY: return(BAD_CAST "wildcard (any)"); case XML_SCHEMA_EXTRA_QNAMEREF: return(BAD_CAST "[helper component] QName reference"); case XML_SCHEMA_EXTRA_ATTR_USE_PROHIB: return(BAD_CAST "[helper component] attribute use prohibition"); default: return(BAD_CAST "Not a schema component"); } } /** * xmlSchemaGetComponentTypeStr: * @type: the type of the schema item * * Returns the component name of a schema item. */ static const xmlChar * xmlSchemaGetComponentTypeStr(xmlSchemaBasicItemPtr item) { switch (item->type) { case XML_SCHEMA_TYPE_BASIC: if (WXS_IS_COMPLEX(WXS_TYPE_CAST item)) return(BAD_CAST "complex type definition"); else return(BAD_CAST "simple type definition"); default: return(xmlSchemaItemTypeToStr(item->type)); } } /** * xmlSchemaGetComponentNode: * @item: a schema component * * Returns node associated with the schema component. * NOTE that such a node need not be available; plus, a component's * node need not to reflect the component directly, since there is no * one-to-one relationship between the XML Schema representation and * the component representation. */ static xmlNodePtr xmlSchemaGetComponentNode(xmlSchemaBasicItemPtr item) { switch (item->type) { case XML_SCHEMA_TYPE_ELEMENT: return (((xmlSchemaElementPtr) item)->node); case XML_SCHEMA_TYPE_ATTRIBUTE: return (((xmlSchemaAttributePtr) item)->node); case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: return (((xmlSchemaTypePtr) item)->node); case XML_SCHEMA_TYPE_ANY: case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: return (((xmlSchemaWildcardPtr) item)->node); case XML_SCHEMA_TYPE_PARTICLE: return (((xmlSchemaParticlePtr) item)->node); case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: case XML_SCHEMA_TYPE_ALL: return (((xmlSchemaModelGroupPtr) item)->node); case XML_SCHEMA_TYPE_GROUP: return (((xmlSchemaModelGroupDefPtr) item)->node); case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: return (((xmlSchemaAttributeGroupPtr) item)->node); case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_KEYREF: return (((xmlSchemaIDCPtr) item)->node); case XML_SCHEMA_EXTRA_QNAMEREF: return(((xmlSchemaQNameRefPtr) item)->node); /* TODO: What to do with NOTATIONs? case XML_SCHEMA_TYPE_NOTATION: return (((xmlSchemaNotationPtr) item)->node); */ case XML_SCHEMA_TYPE_ATTRIBUTE_USE: return (((xmlSchemaAttributeUsePtr) item)->node); default: return (NULL); } } #if 0 /** * xmlSchemaGetNextComponent: * @item: a schema component * * Returns the next sibling of the schema component. */ static xmlSchemaBasicItemPtr xmlSchemaGetNextComponent(xmlSchemaBasicItemPtr item) { switch (item->type) { case XML_SCHEMA_TYPE_ELEMENT: return ((xmlSchemaBasicItemPtr) ((xmlSchemaElementPtr) item)->next); case XML_SCHEMA_TYPE_ATTRIBUTE: return ((xmlSchemaBasicItemPtr) ((xmlSchemaAttributePtr) item)->next); case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: return ((xmlSchemaBasicItemPtr) ((xmlSchemaTypePtr) item)->next); case XML_SCHEMA_TYPE_ANY: case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: return (NULL); case XML_SCHEMA_TYPE_PARTICLE: return ((xmlSchemaBasicItemPtr) ((xmlSchemaParticlePtr) item)->next); case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: case XML_SCHEMA_TYPE_ALL: return (NULL); case XML_SCHEMA_TYPE_GROUP: return (NULL); case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: return ((xmlSchemaBasicItemPtr) ((xmlSchemaAttributeGroupPtr) item)->next); case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_KEYREF: return ((xmlSchemaBasicItemPtr) ((xmlSchemaIDCPtr) item)->next); default: return (NULL); } } #endif /** * xmlSchemaFormatQName: * @buf: the string buffer * @namespaceName: the namespace name * @localName: the local name * * Returns the given QName in the format "{namespaceName}localName" or * just "localName" if @namespaceName is NULL. * * Returns the localName if @namespaceName is NULL, a formatted * string otherwise. */ static const xmlChar* xmlSchemaFormatQName(xmlChar **buf, const xmlChar *namespaceName, const xmlChar *localName) { FREE_AND_NULL(*buf) if (namespaceName != NULL) { *buf = xmlStrdup(BAD_CAST "{"); *buf = xmlStrcat(*buf, namespaceName); *buf = xmlStrcat(*buf, BAD_CAST "}"); } if (localName != NULL) { if (namespaceName == NULL) return(localName); *buf = xmlStrcat(*buf, localName); } else { *buf = xmlStrcat(*buf, BAD_CAST "(NULL)"); } return ((const xmlChar *) *buf); } static const xmlChar* xmlSchemaFormatQNameNs(xmlChar **buf, xmlNsPtr ns, const xmlChar *localName) { if (ns != NULL) return (xmlSchemaFormatQName(buf, ns->href, localName)); else return (xmlSchemaFormatQName(buf, NULL, localName)); } static const xmlChar * xmlSchemaGetComponentName(xmlSchemaBasicItemPtr item) { if (item == NULL) { return (NULL); } switch (item->type) { case XML_SCHEMA_TYPE_ELEMENT: return (((xmlSchemaElementPtr) item)->name); case XML_SCHEMA_TYPE_ATTRIBUTE: return (((xmlSchemaAttributePtr) item)->name); case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: return (((xmlSchemaAttributeGroupPtr) item)->name); case XML_SCHEMA_TYPE_BASIC: case XML_SCHEMA_TYPE_SIMPLE: case XML_SCHEMA_TYPE_COMPLEX: return (((xmlSchemaTypePtr) item)->name); case XML_SCHEMA_TYPE_GROUP: return (((xmlSchemaModelGroupDefPtr) item)->name); case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEYREF: return (((xmlSchemaIDCPtr) item)->name); case XML_SCHEMA_TYPE_ATTRIBUTE_USE: if (WXS_ATTRUSE_DECL(item) != NULL) { return(xmlSchemaGetComponentName( WXS_BASIC_CAST WXS_ATTRUSE_DECL(item))); } else return(NULL); case XML_SCHEMA_EXTRA_QNAMEREF: return (((xmlSchemaQNameRefPtr) item)->name); case XML_SCHEMA_TYPE_NOTATION: return (((xmlSchemaNotationPtr) item)->name); default: /* * Other components cannot have names. */ break; } return (NULL); } #define xmlSchemaGetQNameRefName(r) (WXS_QNAME_CAST (r))->name #define xmlSchemaGetQNameRefTargetNs(r) (WXS_QNAME_CAST (r))->targetNamespace /* static const xmlChar * xmlSchemaGetQNameRefName(void *ref) { return(((xmlSchemaQNameRefPtr) ref)->name); } static const xmlChar * xmlSchemaGetQNameRefTargetNs(void *ref) { return(((xmlSchemaQNameRefPtr) ref)->targetNamespace); } */ static const xmlChar * xmlSchemaGetComponentTargetNs(xmlSchemaBasicItemPtr item) { if (item == NULL) { return (NULL); } switch (item->type) { case XML_SCHEMA_TYPE_ELEMENT: return (((xmlSchemaElementPtr) item)->targetNamespace); case XML_SCHEMA_TYPE_ATTRIBUTE: return (((xmlSchemaAttributePtr) item)->targetNamespace); case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: return (((xmlSchemaAttributeGroupPtr) item)->targetNamespace); case XML_SCHEMA_TYPE_BASIC: return (BAD_CAST "http://www.w3.org/2001/XMLSchema"); case XML_SCHEMA_TYPE_SIMPLE: case XML_SCHEMA_TYPE_COMPLEX: return (((xmlSchemaTypePtr) item)->targetNamespace); case XML_SCHEMA_TYPE_GROUP: return (((xmlSchemaModelGroupDefPtr) item)->targetNamespace); case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEYREF: return (((xmlSchemaIDCPtr) item)->targetNamespace); case XML_SCHEMA_TYPE_ATTRIBUTE_USE: if (WXS_ATTRUSE_DECL(item) != NULL) { return(xmlSchemaGetComponentTargetNs( WXS_BASIC_CAST WXS_ATTRUSE_DECL(item))); } /* TODO: Will returning NULL break something? */ break; case XML_SCHEMA_EXTRA_QNAMEREF: return (((xmlSchemaQNameRefPtr) item)->targetNamespace); case XML_SCHEMA_TYPE_NOTATION: return (((xmlSchemaNotationPtr) item)->targetNamespace); default: /* * Other components cannot have names. */ break; } return (NULL); } static const xmlChar* xmlSchemaGetComponentQName(xmlChar **buf, void *item) { return (xmlSchemaFormatQName(buf, xmlSchemaGetComponentTargetNs((xmlSchemaBasicItemPtr) item), xmlSchemaGetComponentName((xmlSchemaBasicItemPtr) item))); } static const xmlChar* xmlSchemaGetComponentDesignation(xmlChar **buf, void *item) { xmlChar *str = NULL; *buf = xmlStrcat(*buf, WXS_ITEM_TYPE_NAME(item)); *buf = xmlStrcat(*buf, BAD_CAST " '"); *buf = xmlStrcat(*buf, xmlSchemaGetComponentQName(&str, (xmlSchemaBasicItemPtr) item)); *buf = xmlStrcat(*buf, BAD_CAST "'"); FREE_AND_NULL(str); return(*buf); } static const xmlChar* xmlSchemaGetIDCDesignation(xmlChar **buf, xmlSchemaIDCPtr idc) { return(xmlSchemaGetComponentDesignation(buf, idc)); } /** * xmlSchemaWildcardPCToString: * @pc: the type of processContents * * Returns a string representation of the type of * processContents. */ static const xmlChar * xmlSchemaWildcardPCToString(int pc) { switch (pc) { case XML_SCHEMAS_ANY_SKIP: return (BAD_CAST "skip"); case XML_SCHEMAS_ANY_LAX: return (BAD_CAST "lax"); case XML_SCHEMAS_ANY_STRICT: return (BAD_CAST "strict"); default: return (BAD_CAST "invalid process contents"); } } /** * xmlSchemaGetCanonValueWhtspExt: * @val: the precomputed value * @retValue: the returned value * @ws: the whitespace type of the value * @for_hash: non-zero if this is supposed to generate a string for hashing * * Get a the canonical representation of the value. * The caller has to free the returned retValue. * * Returns 0 if the value could be built and -1 in case of * API errors or if the value type is not supported yet. */ static int xmlSchemaGetCanonValueWhtspExt_1(xmlSchemaValPtr val, xmlSchemaWhitespaceValueType ws, xmlChar **retValue, int for_hash) { int list; xmlSchemaValType valType; const xmlChar *value, *value2 = NULL; if ((retValue == NULL) || (val == NULL)) return (-1); list = xmlSchemaValueGetNext(val) ? 1 : 0; *retValue = NULL; do { value = NULL; valType = xmlSchemaGetValType(val); switch (valType) { case XML_SCHEMAS_STRING: case XML_SCHEMAS_NORMSTRING: case XML_SCHEMAS_ANYSIMPLETYPE: value = xmlSchemaValueGetAsString(val); if (value != NULL) { if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) value2 = xmlSchemaCollapseString(value); else if (ws == XML_SCHEMA_WHITESPACE_REPLACE) value2 = xmlSchemaWhiteSpaceReplace(value); if (value2 != NULL) value = value2; } break; default: if (xmlSchemaGetCanonValue(val, &value2) == -1) { if (value2 != NULL) xmlFree((xmlChar *) value2); goto internal_error; } if (for_hash && valType == XML_SCHEMAS_DECIMAL) { /* We can mostly use the canonical value for hashing, except in the case of decimal. There the canonical representation requires a trailing '.0' even for non-fractional numbers, but for the derived integer types it forbids any decimal point. Nevertheless they compare equal if the value is equal. We need to generate the same hash value for this to work, and it's easiest to just cut off the useless '.0' suffix for the decimal type. */ int len = xmlStrlen(value2); if (len > 2 && value2[len-1] == '0' && value2[len-2] == '.') ((xmlChar*)value2)[len-2] = 0; } value = value2; } if (*retValue == NULL) if (value == NULL) { if (! list) *retValue = xmlStrdup(BAD_CAST ""); } else *retValue = xmlStrdup(value); else if (value != NULL) { /* List. */ *retValue = xmlStrcat((xmlChar *) *retValue, BAD_CAST " "); *retValue = xmlStrcat((xmlChar *) *retValue, value); } FREE_AND_NULL(value2) val = xmlSchemaValueGetNext(val); } while (val != NULL); return (0); internal_error: if (*retValue != NULL) xmlFree((xmlChar *) (*retValue)); if (value2 != NULL) xmlFree((xmlChar *) value2); return (-1); } static int xmlSchemaGetCanonValueWhtspExt(xmlSchemaValPtr val, xmlSchemaWhitespaceValueType ws, xmlChar **retValue) { return xmlSchemaGetCanonValueWhtspExt_1(val, ws, retValue, 0); } static int xmlSchemaGetCanonValueHash(xmlSchemaValPtr val, xmlChar **retValue) { return xmlSchemaGetCanonValueWhtspExt_1(val, XML_SCHEMA_WHITESPACE_COLLAPSE, retValue, 1); } /** * xmlSchemaFormatItemForReport: * @buf: the string buffer * @itemDes: the designation of the item * @itemName: the name of the item * @item: the item as an object * @itemNode: the node of the item * @local: the local name * @parsing: if the function is used during the parse * * Returns a representation of the given item used * for error reports. * * The following order is used to build the resulting * designation if the arguments are not NULL: * 1a. If itemDes not NULL -> itemDes * 1b. If (itemDes not NULL) and (itemName not NULL) * -> itemDes + itemName * 2. If the preceding was NULL and (item not NULL) -> item * 3. If the preceding was NULL and (itemNode not NULL) -> itemNode * * If the itemNode is an attribute node, the name of the attribute * will be appended to the result. * * Returns the formatted string and sets @buf to the resulting value. */ static xmlChar* xmlSchemaFormatItemForReport(xmlChar **buf, const xmlChar *itemDes, xmlSchemaBasicItemPtr item, xmlNodePtr itemNode) { xmlChar *str = NULL; int named = 1; if (*buf != NULL) { xmlFree(*buf); *buf = NULL; } if (itemDes != NULL) { *buf = xmlStrdup(itemDes); } else if (item != NULL) { switch (item->type) { case XML_SCHEMA_TYPE_BASIC: { xmlSchemaTypePtr type = WXS_TYPE_CAST item; if (WXS_IS_ATOMIC(type)) *buf = xmlStrdup(BAD_CAST "atomic type 'xs:"); else if (WXS_IS_LIST(type)) *buf = xmlStrdup(BAD_CAST "list type 'xs:"); else if (WXS_IS_UNION(type)) *buf = xmlStrdup(BAD_CAST "union type 'xs:"); else *buf = xmlStrdup(BAD_CAST "simple type 'xs:"); *buf = xmlStrcat(*buf, type->name); *buf = xmlStrcat(*buf, BAD_CAST "'"); } break; case XML_SCHEMA_TYPE_SIMPLE: { xmlSchemaTypePtr type = WXS_TYPE_CAST item; if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) { *buf = xmlStrdup(BAD_CAST""); } else { *buf = xmlStrdup(BAD_CAST "local "); } if (WXS_IS_ATOMIC(type)) *buf = xmlStrcat(*buf, BAD_CAST "atomic type"); else if (WXS_IS_LIST(type)) *buf = xmlStrcat(*buf, BAD_CAST "list type"); else if (WXS_IS_UNION(type)) *buf = xmlStrcat(*buf, BAD_CAST "union type"); else *buf = xmlStrcat(*buf, BAD_CAST "simple type"); if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) { *buf = xmlStrcat(*buf, BAD_CAST " '"); *buf = xmlStrcat(*buf, type->name); *buf = xmlStrcat(*buf, BAD_CAST "'"); } } break; case XML_SCHEMA_TYPE_COMPLEX: { xmlSchemaTypePtr type = WXS_TYPE_CAST item; if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) *buf = xmlStrdup(BAD_CAST ""); else *buf = xmlStrdup(BAD_CAST "local "); *buf = xmlStrcat(*buf, BAD_CAST "complex type"); if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) { *buf = xmlStrcat(*buf, BAD_CAST " '"); *buf = xmlStrcat(*buf, type->name); *buf = xmlStrcat(*buf, BAD_CAST "'"); } } break; case XML_SCHEMA_TYPE_ATTRIBUTE_USE: { xmlSchemaAttributeUsePtr ause; ause = WXS_ATTR_USE_CAST item; *buf = xmlStrdup(BAD_CAST "attribute use "); if (WXS_ATTRUSE_DECL(ause) != NULL) { *buf = xmlStrcat(*buf, BAD_CAST "'"); *buf = xmlStrcat(*buf, xmlSchemaGetComponentQName(&str, WXS_ATTRUSE_DECL(ause))); FREE_AND_NULL(str) *buf = xmlStrcat(*buf, BAD_CAST "'"); } else { *buf = xmlStrcat(*buf, BAD_CAST "(unknown)"); } } break; case XML_SCHEMA_TYPE_ATTRIBUTE: { xmlSchemaAttributePtr attr; attr = (xmlSchemaAttributePtr) item; *buf = xmlStrdup(BAD_CAST "attribute decl."); *buf = xmlStrcat(*buf, BAD_CAST " '"); *buf = xmlStrcat(*buf, xmlSchemaFormatQName(&str, attr->targetNamespace, attr->name)); FREE_AND_NULL(str) *buf = xmlStrcat(*buf, BAD_CAST "'"); } break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: xmlSchemaGetComponentDesignation(buf, item); break; case XML_SCHEMA_TYPE_ELEMENT: { xmlSchemaElementPtr elem; elem = (xmlSchemaElementPtr) item; *buf = xmlStrdup(BAD_CAST "element decl."); *buf = xmlStrcat(*buf, BAD_CAST " '"); *buf = xmlStrcat(*buf, xmlSchemaFormatQName(&str, elem->targetNamespace, elem->name)); *buf = xmlStrcat(*buf, BAD_CAST "'"); } break; case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_KEYREF: if (item->type == XML_SCHEMA_TYPE_IDC_UNIQUE) *buf = xmlStrdup(BAD_CAST "unique '"); else if (item->type == XML_SCHEMA_TYPE_IDC_KEY) *buf = xmlStrdup(BAD_CAST "key '"); else *buf = xmlStrdup(BAD_CAST "keyRef '"); *buf = xmlStrcat(*buf, ((xmlSchemaIDCPtr) item)->name); *buf = xmlStrcat(*buf, BAD_CAST "'"); break; case XML_SCHEMA_TYPE_ANY: case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: *buf = xmlStrdup(xmlSchemaWildcardPCToString( ((xmlSchemaWildcardPtr) item)->processContents)); *buf = xmlStrcat(*buf, BAD_CAST " wildcard"); break; case XML_SCHEMA_FACET_MININCLUSIVE: case XML_SCHEMA_FACET_MINEXCLUSIVE: case XML_SCHEMA_FACET_MAXINCLUSIVE: case XML_SCHEMA_FACET_MAXEXCLUSIVE: case XML_SCHEMA_FACET_TOTALDIGITS: case XML_SCHEMA_FACET_FRACTIONDIGITS: case XML_SCHEMA_FACET_PATTERN: case XML_SCHEMA_FACET_ENUMERATION: case XML_SCHEMA_FACET_WHITESPACE: case XML_SCHEMA_FACET_LENGTH: case XML_SCHEMA_FACET_MAXLENGTH: case XML_SCHEMA_FACET_MINLENGTH: *buf = xmlStrdup(BAD_CAST "facet '"); *buf = xmlStrcat(*buf, xmlSchemaFacetTypeToString(item->type)); *buf = xmlStrcat(*buf, BAD_CAST "'"); break; case XML_SCHEMA_TYPE_GROUP: { *buf = xmlStrdup(BAD_CAST "model group def."); *buf = xmlStrcat(*buf, BAD_CAST " '"); *buf = xmlStrcat(*buf, xmlSchemaGetComponentQName(&str, item)); *buf = xmlStrcat(*buf, BAD_CAST "'"); FREE_AND_NULL(str) } break; case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: case XML_SCHEMA_TYPE_ALL: case XML_SCHEMA_TYPE_PARTICLE: *buf = xmlStrdup(WXS_ITEM_TYPE_NAME(item)); break; case XML_SCHEMA_TYPE_NOTATION: { *buf = xmlStrdup(WXS_ITEM_TYPE_NAME(item)); *buf = xmlStrcat(*buf, BAD_CAST " '"); *buf = xmlStrcat(*buf, xmlSchemaGetComponentQName(&str, item)); *buf = xmlStrcat(*buf, BAD_CAST "'"); FREE_AND_NULL(str); } /* Falls through. */ default: named = 0; } } else named = 0; if ((named == 0) && (itemNode != NULL)) { xmlNodePtr elem; if (itemNode->type == XML_ATTRIBUTE_NODE) elem = itemNode->parent; else elem = itemNode; *buf = xmlStrdup(BAD_CAST "Element '"); if (elem->ns != NULL) { *buf = xmlStrcat(*buf, xmlSchemaFormatQName(&str, elem->ns->href, elem->name)); FREE_AND_NULL(str) } else *buf = xmlStrcat(*buf, elem->name); *buf = xmlStrcat(*buf, BAD_CAST "'"); } if ((itemNode != NULL) && (itemNode->type == XML_ATTRIBUTE_NODE)) { *buf = xmlStrcat(*buf, BAD_CAST ", attribute '"); if (itemNode->ns != NULL) { *buf = xmlStrcat(*buf, xmlSchemaFormatQName(&str, itemNode->ns->href, itemNode->name)); FREE_AND_NULL(str) } else *buf = xmlStrcat(*buf, itemNode->name); *buf = xmlStrcat(*buf, BAD_CAST "'"); } FREE_AND_NULL(str) return (xmlEscapeFormatString(buf)); } /** * xmlSchemaFormatFacetEnumSet: * @buf: the string buffer * @type: the type holding the enumeration facets * * Builds a string consisting of all enumeration elements. * * Returns a string of all enumeration elements. */ static const xmlChar * xmlSchemaFormatFacetEnumSet(xmlSchemaAbstractCtxtPtr actxt, xmlChar **buf, xmlSchemaTypePtr type) { xmlSchemaFacetPtr facet; xmlSchemaWhitespaceValueType ws; xmlChar *value = NULL; int res, found = 0; if (*buf != NULL) xmlFree(*buf); *buf = NULL; do { /* * Use the whitespace type of the base type. */ ws = xmlSchemaGetWhiteSpaceFacetValue(type->baseType); for (facet = type->facets; facet != NULL; facet = facet->next) { if (facet->type != XML_SCHEMA_FACET_ENUMERATION) continue; found = 1; res = xmlSchemaGetCanonValueWhtspExt(facet->val, ws, &value); if (res == -1) { xmlSchemaInternalErr(actxt, "xmlSchemaFormatFacetEnumSet", "compute the canonical lexical representation"); if (*buf != NULL) xmlFree(*buf); *buf = NULL; return (NULL); } if (*buf == NULL) *buf = xmlStrdup(BAD_CAST "'"); else *buf = xmlStrcat(*buf, BAD_CAST ", '"); *buf = xmlStrcat(*buf, BAD_CAST value); *buf = xmlStrcat(*buf, BAD_CAST "'"); if (value != NULL) { xmlFree((xmlChar *)value); value = NULL; } } /* * The enumeration facet of a type restricts the enumeration * facet of the ancestor type; i.e., such restricted enumerations * do not belong to the set of the given type. Thus we break * on the first found enumeration. */ if (found) break; type = type->baseType; } while ((type != NULL) && (type->type != XML_SCHEMA_TYPE_BASIC)); return ((const xmlChar *) *buf); } /************************************************************************ * * * Error functions * * * ************************************************************************/ /** * xmlSchemaPErrMemory: * @node: a context node * @extra: extra information * * Handle an out of memory condition */ static void xmlSchemaPErrMemory(xmlSchemaParserCtxtPtr ctxt) { xmlGenericErrorFunc channel = NULL; xmlStructuredErrorFunc schannel = NULL; void *data = NULL; if (ctxt != NULL) { ctxt->nberrors++; ctxt->err = XML_ERR_NO_MEMORY; channel = ctxt->error; schannel = ctxt->serror; data = ctxt->errCtxt; } xmlRaiseMemoryError(schannel, channel, data, XML_FROM_SCHEMASP, NULL); } static void LIBXML_ATTR_FORMAT(11,12) xmlSchemaPErrFull(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int code, xmlErrorLevel level, const char *file, int line, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, int col, const char *msg, ...) { xmlGenericErrorFunc channel = NULL; xmlStructuredErrorFunc schannel = NULL; void *data = NULL; int res; va_list ap; if (ctxt != NULL) { /* Don't overwrite memory errors */ if (ctxt->err == XML_ERR_NO_MEMORY) return; if (level == XML_ERR_WARNING) { channel = ctxt->warning; } else { ctxt->nberrors++; ctxt->err = code; channel = ctxt->error; } data = ctxt->errCtxt; schannel = ctxt->serror; } if ((channel == NULL) && (schannel == NULL)) { channel = xmlGenericError; data = xmlGenericErrorContext; } va_start(ap, msg); res = xmlVRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP, code, level, file, line, (const char *) str1, (const char *) str2, (const char *) str3, 0, col, msg, ap); va_end(ap); if (res < 0) xmlSchemaPErrMemory(ctxt); } /** * xmlSchemaPErr: * @ctxt: the parsing context * @node: the context node * @error: the error code * @msg: the error message * @str1: extra data * @str2: extra data * * Handle a parser error */ static void LIBXML_ATTR_FORMAT(4,0) xmlSchemaPErr(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int code, const char *msg, const xmlChar * str1, const xmlChar * str2) { xmlSchemaPErrFull(ctxt, node, code, XML_ERR_ERROR, NULL, 0, str1, str2, NULL, 0, msg, str1, str2); } /** * xmlSchemaPErr2: * @ctxt: the parsing context * @node: the context node * @node: the current child * @error: the error code * @msg: the error message * @str1: extra data * @str2: extra data * * Handle a parser error */ static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaPErr2(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, xmlNodePtr child, int error, const char *msg, const xmlChar * str1, const xmlChar * str2) { if (child != NULL) xmlSchemaPErr(ctxt, child, error, msg, str1, str2); else xmlSchemaPErr(ctxt, node, error, msg, str1, str2); } /** * xmlSchemaPErrExt: * @ctxt: the parsing context * @node: the context node * @error: the error code * @strData1: extra data * @strData2: extra data * @strData3: extra data * @msg: the message * @str1: extra parameter for the message display * @str2: extra parameter for the message display * @str3: extra parameter for the message display * @str4: extra parameter for the message display * @str5: extra parameter for the message display * * Handle a parser error */ static void LIBXML_ATTR_FORMAT(7,0) xmlSchemaPErrExt(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int code, const xmlChar * strData1, const xmlChar * strData2, const xmlChar * strData3, const char *msg, const xmlChar * str1, const xmlChar * str2, const xmlChar * str3, const xmlChar * str4, const xmlChar * str5) { xmlSchemaPErrFull(ctxt, node, code, XML_ERR_ERROR, NULL, 0, strData1, strData2, strData3, 0, msg, str1, str2, str3, str4, str5); } /************************************************************************ * * * Allround error functions * * * ************************************************************************/ /** * xmlSchemaVTypeErrMemory: * @node: a context node * @extra: extra information * * Handle an out of memory condition */ static void xmlSchemaVErrMemory(xmlSchemaValidCtxtPtr ctxt) { xmlGenericErrorFunc channel = NULL; xmlStructuredErrorFunc schannel = NULL; void *data = NULL; if (ctxt != NULL) { ctxt->nberrors++; ctxt->err = XML_ERR_NO_MEMORY; channel = ctxt->error; schannel = ctxt->serror; data = ctxt->errCtxt; } xmlRaiseMemoryError(schannel, channel, data, XML_FROM_SCHEMASV, NULL); } static void LIBXML_ATTR_FORMAT(11,12) xmlSchemaVErrFull(xmlSchemaValidCtxtPtr ctxt, xmlNodePtr node, int code, xmlErrorLevel level, const char *file, int line, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, int col, const char *msg, ...) { xmlGenericErrorFunc channel = NULL; xmlStructuredErrorFunc schannel = NULL; void *data = NULL; int res; va_list ap; if (ctxt != NULL) { /* Don't overwrite memory errors */ if (ctxt->err == XML_ERR_NO_MEMORY) return; if (level == XML_ERR_WARNING) { channel = ctxt->warning; } else { ctxt->nberrors++; ctxt->err = code; channel = ctxt->error; } data = ctxt->errCtxt; schannel = ctxt->serror; } if ((channel == NULL) && (schannel == NULL)) { channel = xmlGenericError; data = xmlGenericErrorContext; } va_start(ap, msg); res = xmlVRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASV, code, level, file, line, (const char *) str1, (const char *) str2, (const char *) str3, 0, col, msg, ap); va_end(ap); if (res < 0) xmlSchemaVErrMemory(ctxt); } #define WXS_ERROR_TYPE_ERROR 1 #define WXS_ERROR_TYPE_WARNING 2 /** * xmlSchemaErr4Line: * @ctxt: the validation context * @errorLevel: the error level * @error: the error code * @node: the context node * @line: the line number * @msg: the error message * @str1: extra data * @str2: extra data * @str3: extra data * @str4: extra data * * Handle a validation error */ static void LIBXML_ATTR_FORMAT(6,0) xmlSchemaErr4Line(xmlSchemaAbstractCtxtPtr ctxt, xmlErrorLevel errorLevel, int code, xmlNodePtr node, int line, const char *msg, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, const xmlChar *str4) { if (ctxt != NULL) { if (ctxt->type == XML_SCHEMA_CTXT_VALIDATOR) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctxt; const char *file = NULL; int col = 0; /* * Error node. If we specify a line number, then * do not channel any node to the error function. */ if (line == 0) { if ((node == NULL) && (vctxt->depth >= 0) && (vctxt->inode != NULL)) { node = vctxt->inode->node; } /* * Get filename and line if no node-tree. */ if ((node == NULL) && (vctxt->parserCtxt != NULL) && (vctxt->parserCtxt->input != NULL)) { file = vctxt->parserCtxt->input->filename; if (vctxt->inode != NULL) { line = vctxt->inode->nodeLine; col = 0; } else { /* This is inaccurate. */ line = vctxt->parserCtxt->input->line; col = vctxt->parserCtxt->input->col; } } } else { /* * Override the given node's (if any) position * and channel only the given line number. */ node = NULL; /* * Get filename. */ if (vctxt->doc != NULL) file = (const char *) vctxt->doc->URL; else if ((vctxt->parserCtxt != NULL) && (vctxt->parserCtxt->input != NULL)) file = vctxt->parserCtxt->input->filename; } if (vctxt->locFunc != NULL) { if ((file == NULL) || (line == 0)) { unsigned long l; const char *f; vctxt->locFunc(vctxt->locCtxt, &f, &l); if (file == NULL) file = f; if (line == 0) line = (int) l; } } if ((file == NULL) && (vctxt->filename != NULL)) file = vctxt->filename; xmlSchemaVErrFull(vctxt, node, code, errorLevel, file, line, str1, str2, str3, col, msg, str1, str2, str3, str4); } else if (ctxt->type == XML_SCHEMA_CTXT_PARSER) { xmlSchemaParserCtxtPtr pctxt = (xmlSchemaParserCtxtPtr) ctxt; xmlSchemaPErrFull(pctxt, node, code, errorLevel, NULL, 0, str1, str2, str3, 0, msg, str1, str2, str3, str4); } } } /** * xmlSchemaErr3: * @ctxt: the validation context * @node: the context node * @error: the error code * @msg: the error message * @str1: extra data * @str2: extra data * @str3: extra data * * Handle a validation error */ static void LIBXML_ATTR_FORMAT(4,0) xmlSchemaErr3(xmlSchemaAbstractCtxtPtr actxt, int error, xmlNodePtr node, const char *msg, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3) { xmlSchemaErr4Line(actxt, XML_ERR_ERROR, error, node, 0, msg, str1, str2, str3, NULL); } static void LIBXML_ATTR_FORMAT(4,0) xmlSchemaErr4(xmlSchemaAbstractCtxtPtr actxt, int error, xmlNodePtr node, const char *msg, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, const xmlChar *str4) { xmlSchemaErr4Line(actxt, XML_ERR_ERROR, error, node, 0, msg, str1, str2, str3, str4); } static void LIBXML_ATTR_FORMAT(4,0) xmlSchemaErr(xmlSchemaAbstractCtxtPtr actxt, int error, xmlNodePtr node, const char *msg, const xmlChar *str1, const xmlChar *str2) { xmlSchemaErr4(actxt, error, node, msg, str1, str2, NULL, NULL); } static xmlChar * xmlSchemaFormatNodeForError(xmlChar ** msg, xmlSchemaAbstractCtxtPtr actxt, xmlNodePtr node) { xmlChar *str = NULL; *msg = NULL; if ((node != NULL) && (node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) { /* * Don't try to format other nodes than element and * attribute nodes. * Play safe and return an empty string. */ *msg = xmlStrdup(BAD_CAST ""); return(*msg); } if (node != NULL) { /* * Work on tree nodes. */ if (node->type == XML_ATTRIBUTE_NODE) { xmlNodePtr elem = node->parent; *msg = xmlStrdup(BAD_CAST "Element '"); if (elem->ns != NULL) *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, elem->ns->href, elem->name)); else *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, NULL, elem->name)); FREE_AND_NULL(str); *msg = xmlStrcat(*msg, BAD_CAST "', "); *msg = xmlStrcat(*msg, BAD_CAST "attribute '"); } else { *msg = xmlStrdup(BAD_CAST "Element '"); } if (node->ns != NULL) *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, node->ns->href, node->name)); else *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, NULL, node->name)); FREE_AND_NULL(str); *msg = xmlStrcat(*msg, BAD_CAST "': "); } else if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) actxt; /* * Work on node infos. */ if (vctxt->inode->nodeType == XML_ATTRIBUTE_NODE) { xmlSchemaNodeInfoPtr ielem = vctxt->elemInfos[vctxt->depth]; *msg = xmlStrdup(BAD_CAST "Element '"); *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, ielem->nsName, ielem->localName)); FREE_AND_NULL(str); *msg = xmlStrcat(*msg, BAD_CAST "', "); *msg = xmlStrcat(*msg, BAD_CAST "attribute '"); } else { *msg = xmlStrdup(BAD_CAST "Element '"); } *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, vctxt->inode->nsName, vctxt->inode->localName)); FREE_AND_NULL(str); *msg = xmlStrcat(*msg, BAD_CAST "': "); } else if (actxt->type == XML_SCHEMA_CTXT_PARSER) { /* * Hmm, no node while parsing? * Return an empty string, in case NULL will break something. */ *msg = xmlStrdup(BAD_CAST ""); } else { /* TODO */ return (NULL); } /* * xmlSchemaFormatItemForReport() also returns an escaped format * string, so do this before calling it below (in the future). */ xmlEscapeFormatString(msg); /* * VAL TODO: The output of the given schema component is currently * disabled. */ #if 0 if ((type != NULL) && (xmlSchemaIsGlobalItem(type))) { *msg = xmlStrcat(*msg, BAD_CAST " ["); *msg = xmlStrcat(*msg, xmlSchemaFormatItemForReport(&str, NULL, type, NULL, 0)); FREE_AND_NULL(str) *msg = xmlStrcat(*msg, BAD_CAST "]"); } #endif return (*msg); } static void LIBXML_ATTR_FORMAT(3,0) xmlSchemaInternalErr2(xmlSchemaAbstractCtxtPtr actxt, const char *funcName, const char *message, const xmlChar *str1, const xmlChar *str2) { xmlChar *msg = NULL; if (actxt == NULL) return; msg = xmlStrdup(BAD_CAST "Internal error: %s, "); msg = xmlStrcat(msg, BAD_CAST message); msg = xmlStrcat(msg, BAD_CAST ".\n"); if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR) xmlSchemaErr3(actxt, XML_SCHEMAV_INTERNAL, NULL, (const char *) msg, (const xmlChar *) funcName, str1, str2); else if (actxt->type == XML_SCHEMA_CTXT_PARSER) xmlSchemaErr3(actxt, XML_SCHEMAP_INTERNAL, NULL, (const char *) msg, (const xmlChar *) funcName, str1, str2); FREE_AND_NULL(msg) } static void LIBXML_ATTR_FORMAT(3,0) xmlSchemaInternalErr(xmlSchemaAbstractCtxtPtr actxt, const char *funcName, const char *message) { xmlSchemaInternalErr2(actxt, funcName, message, NULL, NULL); } #if 0 static void LIBXML_ATTR_FORMAT(3,0) xmlSchemaPInternalErr(xmlSchemaParserCtxtPtr pctxt, const char *funcName, const char *message, const xmlChar *str1, const xmlChar *str2) { xmlSchemaInternalErr2(ACTXT_CAST pctxt, funcName, message, str1, str2); } #endif static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaCustomErr4(xmlSchemaAbstractCtxtPtr actxt, xmlParserErrors error, xmlNodePtr node, xmlSchemaBasicItemPtr item, const char *message, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, const xmlChar *str4) { xmlChar *msg = NULL; if ((node == NULL) && (item != NULL) && (actxt->type == XML_SCHEMA_CTXT_PARSER)) { node = WXS_ITEM_NODE(item); xmlSchemaFormatItemForReport(&msg, NULL, item, NULL); msg = xmlStrcat(msg, BAD_CAST ": "); } else xmlSchemaFormatNodeForError(&msg, actxt, node); msg = xmlStrcat(msg, (const xmlChar *) message); msg = xmlStrcat(msg, BAD_CAST ".\n"); xmlSchemaErr4(actxt, error, node, (const char *) msg, str1, str2, str3, str4); FREE_AND_NULL(msg) } static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaCustomErr(xmlSchemaAbstractCtxtPtr actxt, xmlParserErrors error, xmlNodePtr node, xmlSchemaBasicItemPtr item, const char *message, const xmlChar *str1, const xmlChar *str2) { xmlSchemaCustomErr4(actxt, error, node, item, message, str1, str2, NULL, NULL); } static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaCustomWarning(xmlSchemaAbstractCtxtPtr actxt, xmlParserErrors error, xmlNodePtr node, xmlSchemaTypePtr type ATTRIBUTE_UNUSED, const char *message, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3) { xmlChar *msg = NULL; xmlSchemaFormatNodeForError(&msg, actxt, node); msg = xmlStrcat(msg, (const xmlChar *) message); msg = xmlStrcat(msg, BAD_CAST ".\n"); /* URGENT TODO: Set the error code to something sane. */ xmlSchemaErr4Line(actxt, XML_ERR_WARNING, error, node, 0, (const char *) msg, str1, str2, str3, NULL); FREE_AND_NULL(msg) } static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaKeyrefErr(xmlSchemaValidCtxtPtr vctxt, xmlParserErrors error, xmlSchemaPSVIIDCNodePtr idcNode, xmlSchemaTypePtr type ATTRIBUTE_UNUSED, const char *message, const xmlChar *str1, const xmlChar *str2) { xmlChar *msg = NULL, *qname = NULL; msg = xmlStrdup(BAD_CAST "Element '%s': "); msg = xmlStrcat(msg, (const xmlChar *) message); msg = xmlStrcat(msg, BAD_CAST ".\n"); xmlSchemaErr4Line(ACTXT_CAST vctxt, XML_ERR_ERROR, error, NULL, idcNode->nodeLine, (const char *) msg, xmlSchemaFormatQName(&qname, vctxt->nodeQNames->items[idcNode->nodeQNameID +1], vctxt->nodeQNames->items[idcNode->nodeQNameID]), str1, str2, NULL); FREE_AND_NULL(qname); FREE_AND_NULL(msg); } static int xmlSchemaEvalErrorNodeType(xmlSchemaAbstractCtxtPtr actxt, xmlNodePtr node) { if (node != NULL) return (node->type); if ((actxt->type == XML_SCHEMA_CTXT_VALIDATOR) && (((xmlSchemaValidCtxtPtr) actxt)->inode != NULL)) return ( ((xmlSchemaValidCtxtPtr) actxt)->inode->nodeType); return (-1); } static int xmlSchemaIsGlobalItem(xmlSchemaTypePtr item) { switch (item->type) { case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: if (item->flags & XML_SCHEMAS_TYPE_GLOBAL) return(1); break; case XML_SCHEMA_TYPE_GROUP: return (1); case XML_SCHEMA_TYPE_ELEMENT: if ( ((xmlSchemaElementPtr) item)->flags & XML_SCHEMAS_ELEM_GLOBAL) return(1); break; case XML_SCHEMA_TYPE_ATTRIBUTE: if ( ((xmlSchemaAttributePtr) item)->flags & XML_SCHEMAS_ATTR_GLOBAL) return(1); break; /* Note that attribute groups are always global. */ default: return(1); } return (0); } static void xmlSchemaSimpleTypeErr(xmlSchemaAbstractCtxtPtr actxt, xmlParserErrors error, xmlNodePtr node, const xmlChar *value, xmlSchemaTypePtr type, int displayValue) { xmlChar *msg = NULL; xmlSchemaFormatNodeForError(&msg, actxt, node); if (displayValue || (xmlSchemaEvalErrorNodeType(actxt, node) == XML_ATTRIBUTE_NODE)) msg = xmlStrcat(msg, BAD_CAST "'%s' is not a valid value of "); else msg = xmlStrcat(msg, BAD_CAST "The character content is not a valid " "value of "); if (! xmlSchemaIsGlobalItem(type)) msg = xmlStrcat(msg, BAD_CAST "the local "); else msg = xmlStrcat(msg, BAD_CAST "the "); if (WXS_IS_ATOMIC(type)) msg = xmlStrcat(msg, BAD_CAST "atomic type"); else if (WXS_IS_LIST(type)) msg = xmlStrcat(msg, BAD_CAST "list type"); else if (WXS_IS_UNION(type)) msg = xmlStrcat(msg, BAD_CAST "union type"); if (xmlSchemaIsGlobalItem(type)) { xmlChar *str = NULL; msg = xmlStrcat(msg, BAD_CAST " '"); if (type->builtInType != 0) { msg = xmlStrcat(msg, BAD_CAST "xs:"); str = xmlStrdup(type->name); } else { const xmlChar *qName = xmlSchemaFormatQName(&str, type->targetNamespace, type->name); if (!str) str = xmlStrdup(qName); } msg = xmlStrcat(msg, xmlEscapeFormatString(&str)); msg = xmlStrcat(msg, BAD_CAST "'"); FREE_AND_NULL(str); } msg = xmlStrcat(msg, BAD_CAST ".\n"); if (displayValue || (xmlSchemaEvalErrorNodeType(actxt, node) == XML_ATTRIBUTE_NODE)) xmlSchemaErr(actxt, error, node, (const char *) msg, value, NULL); else xmlSchemaErr(actxt, error, node, (const char *) msg, NULL, NULL); FREE_AND_NULL(msg) } static const xmlChar * xmlSchemaFormatErrorNodeQName(xmlChar ** str, xmlSchemaNodeInfoPtr ni, xmlNodePtr node) { if (node != NULL) { if (node->ns != NULL) return (xmlSchemaFormatQName(str, node->ns->href, node->name)); else return (xmlSchemaFormatQName(str, NULL, node->name)); } else if (ni != NULL) return (xmlSchemaFormatQName(str, ni->nsName, ni->localName)); return (NULL); } static void xmlSchemaIllegalAttrErr(xmlSchemaAbstractCtxtPtr actxt, xmlParserErrors error, xmlSchemaAttrInfoPtr ni, xmlNodePtr node) { xmlChar *msg = NULL, *str = NULL; xmlSchemaFormatNodeForError(&msg, actxt, node); msg = xmlStrcat(msg, BAD_CAST "The attribute '%s' is not allowed.\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, xmlSchemaFormatErrorNodeQName(&str, (xmlSchemaNodeInfoPtr) ni, node), NULL); FREE_AND_NULL(str) FREE_AND_NULL(msg) } static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaComplexTypeErr(xmlSchemaAbstractCtxtPtr actxt, xmlParserErrors error, xmlNodePtr node, xmlSchemaTypePtr type ATTRIBUTE_UNUSED, const char *message, int nbval, int nbneg, xmlChar **values) { xmlChar *str = NULL, *msg = NULL; xmlChar *localName, *nsName; const xmlChar *cur, *end; int i; xmlSchemaFormatNodeForError(&msg, actxt, node); msg = xmlStrcat(msg, (const xmlChar *) message); msg = xmlStrcat(msg, BAD_CAST "."); /* * Note that is does not make sense to report that we have a * wildcard here, since the wildcard might be unfolded into * multiple transitions. */ if (nbval + nbneg > 0) { if (nbval + nbneg > 1) { str = xmlStrdup(BAD_CAST " Expected is one of ( "); } else str = xmlStrdup(BAD_CAST " Expected is ( "); nsName = NULL; for (i = 0; i < nbval + nbneg; i++) { cur = values[i]; if (cur == NULL) continue; if ((cur[0] == 'n') && (cur[1] == 'o') && (cur[2] == 't') && (cur[3] == ' ')) { cur += 4; str = xmlStrcat(str, BAD_CAST "##other"); } /* * Get the local name. */ localName = NULL; end = cur; if (*end == '*') { localName = xmlStrdup(BAD_CAST "*"); end++; } else { while ((*end != 0) && (*end != '|')) end++; localName = xmlStrncat(localName, BAD_CAST cur, end - cur); } if (*end != 0) { end++; /* * Skip "*|*" if they come with negated expressions, since * they represent the same negated wildcard. */ if ((nbneg == 0) || (*end != '*') || (*localName != '*')) { /* * Get the namespace name. */ cur = end; if (*end == '*') { nsName = xmlStrdup(BAD_CAST "{*}"); } else { while (*end != 0) end++; if (i >= nbval) nsName = xmlStrdup(BAD_CAST "{##other:"); else nsName = xmlStrdup(BAD_CAST "{"); nsName = xmlStrncat(nsName, BAD_CAST cur, end - cur); nsName = xmlStrcat(nsName, BAD_CAST "}"); } str = xmlStrcat(str, BAD_CAST nsName); FREE_AND_NULL(nsName) } else { FREE_AND_NULL(localName); continue; } } str = xmlStrcat(str, BAD_CAST localName); FREE_AND_NULL(localName); if (i < nbval + nbneg -1) str = xmlStrcat(str, BAD_CAST ", "); } str = xmlStrcat(str, BAD_CAST " ).\n"); msg = xmlStrcat(msg, xmlEscapeFormatString(&str)); FREE_AND_NULL(str) } else msg = xmlStrcat(msg, BAD_CAST "\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, NULL, NULL); xmlFree(msg); } static void LIBXML_ATTR_FORMAT(8,0) xmlSchemaFacetErr(xmlSchemaAbstractCtxtPtr actxt, xmlParserErrors error, xmlNodePtr node, const xmlChar *value, unsigned long length, xmlSchemaTypePtr type, xmlSchemaFacetPtr facet, const char *message, const xmlChar *str1, const xmlChar *str2) { xmlChar *str = NULL, *msg = NULL; xmlSchemaTypeType facetType; int nodeType = xmlSchemaEvalErrorNodeType(actxt, node); xmlSchemaFormatNodeForError(&msg, actxt, node); if (error == XML_SCHEMAV_CVC_ENUMERATION_VALID) { facetType = XML_SCHEMA_FACET_ENUMERATION; /* * If enumerations are validated, one must not expect the * facet to be given. */ } else facetType = facet->type; msg = xmlStrcat(msg, BAD_CAST "["); msg = xmlStrcat(msg, BAD_CAST "facet '"); msg = xmlStrcat(msg, xmlSchemaFacetTypeToString(facetType)); msg = xmlStrcat(msg, BAD_CAST "'] "); if (message == NULL) { /* * Use a default message. */ if ((facetType == XML_SCHEMA_FACET_LENGTH) || (facetType == XML_SCHEMA_FACET_MINLENGTH) || (facetType == XML_SCHEMA_FACET_MAXLENGTH)) { char len[25], actLen[25]; /* FIXME, TODO: What is the max expected string length of the * this value? */ if (nodeType == XML_ATTRIBUTE_NODE) msg = xmlStrcat(msg, BAD_CAST "The value '%s' has a length of '%s'; "); else msg = xmlStrcat(msg, BAD_CAST "The value has a length of '%s'; "); snprintf(len, 24, "%lu", xmlSchemaGetFacetValueAsULong(facet)); snprintf(actLen, 24, "%lu", length); if (facetType == XML_SCHEMA_FACET_LENGTH) msg = xmlStrcat(msg, BAD_CAST "this differs from the allowed length of '%s'.\n"); else if (facetType == XML_SCHEMA_FACET_MAXLENGTH) msg = xmlStrcat(msg, BAD_CAST "this exceeds the allowed maximum length of '%s'.\n"); else if (facetType == XML_SCHEMA_FACET_MINLENGTH) msg = xmlStrcat(msg, BAD_CAST "this underruns the allowed minimum length of '%s'.\n"); if (nodeType == XML_ATTRIBUTE_NODE) xmlSchemaErr3(actxt, error, node, (const char *) msg, value, (const xmlChar *) actLen, (const xmlChar *) len); else xmlSchemaErr(actxt, error, node, (const char *) msg, (const xmlChar *) actLen, (const xmlChar *) len); } else if (facetType == XML_SCHEMA_FACET_ENUMERATION) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not an element " "of the set {%s}.\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, value, xmlSchemaFormatFacetEnumSet(actxt, &str, type)); } else if (facetType == XML_SCHEMA_FACET_PATTERN) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not accepted " "by the pattern '%s'.\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, value, facet->value); } else if (facetType == XML_SCHEMA_FACET_MININCLUSIVE) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' is less than the " "minimum value allowed ('%s').\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, value, facet->value); } else if (facetType == XML_SCHEMA_FACET_MAXINCLUSIVE) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' is greater than the " "maximum value allowed ('%s').\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, value, facet->value); } else if (facetType == XML_SCHEMA_FACET_MINEXCLUSIVE) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' must be greater than " "'%s'.\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, value, facet->value); } else if (facetType == XML_SCHEMA_FACET_MAXEXCLUSIVE) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' must be less than " "'%s'.\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, value, facet->value); } else if (facetType == XML_SCHEMA_FACET_TOTALDIGITS) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' has more " "digits than are allowed ('%s').\n"); xmlSchemaErr(actxt, error, node, (const char*) msg, value, facet->value); } else if (facetType == XML_SCHEMA_FACET_FRACTIONDIGITS) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' has more fractional " "digits than are allowed ('%s').\n"); xmlSchemaErr(actxt, error, node, (const char*) msg, value, facet->value); } else if (nodeType == XML_ATTRIBUTE_NODE) { msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not facet-valid.\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, value, NULL); } else { msg = xmlStrcat(msg, BAD_CAST "The value is not facet-valid.\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, NULL, NULL); } } else { msg = xmlStrcat(msg, (const xmlChar *) message); msg = xmlStrcat(msg, BAD_CAST ".\n"); xmlSchemaErr(actxt, error, node, (const char *) msg, str1, str2); } FREE_AND_NULL(str) xmlFree(msg); } #define VERROR(err, type, msg) \ xmlSchemaCustomErr(ACTXT_CAST vctxt, err, NULL, type, msg, NULL, NULL); #define VERROR_INT(func, msg) xmlSchemaInternalErr(ACTXT_CAST vctxt, func, msg); #define PERROR_INT(func, msg) xmlSchemaInternalErr(ACTXT_CAST pctxt, func, msg); #define PERROR_INT2(func, msg) xmlSchemaInternalErr(ACTXT_CAST ctxt, func, msg); #define AERROR_INT(func, msg) xmlSchemaInternalErr(actxt, func, msg); /** * xmlSchemaPMissingAttrErr: * @ctxt: the schema validation context * @ownerItem: the owner as a schema object * @ownerElem: the owner as an element node * @node: the parent element node of the missing attribute node * @type: the corresponding type of the attribute node * * Reports an illegal attribute. */ static void xmlSchemaPMissingAttrErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr ownerItem, xmlNodePtr ownerElem, const char *name, const char *message) { xmlChar *des = NULL; xmlSchemaFormatItemForReport(&des, NULL, ownerItem, ownerElem); if (message != NULL) xmlSchemaPErr(ctxt, ownerElem, error, "%s: %s.\n", BAD_CAST des, BAD_CAST message); else xmlSchemaPErr(ctxt, ownerElem, error, "%s: The attribute '%s' is required but missing.\n", BAD_CAST des, BAD_CAST name); FREE_AND_NULL(des); } /** * xmlSchemaPResCompAttrErr: * @ctxt: the schema validation context * @error: the error code * @ownerItem: the owner as a schema object * @ownerElem: the owner as an element node * @name: the name of the attribute holding the QName * @refName: the referenced local name * @refURI: the referenced namespace URI * @message: optional message * * Used to report QName attribute values that failed to resolve * to schema components. */ static void xmlSchemaPResCompAttrErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr ownerItem, xmlNodePtr ownerElem, const char *name, const xmlChar *refName, const xmlChar *refURI, xmlSchemaTypeType refType, const char *refTypeStr) { xmlChar *des = NULL, *strA = NULL; xmlSchemaFormatItemForReport(&des, NULL, ownerItem, ownerElem); if (refTypeStr == NULL) refTypeStr = (const char *) xmlSchemaItemTypeToStr(refType); xmlSchemaPErrExt(ctxt, ownerElem, error, NULL, NULL, NULL, "%s, attribute '%s': The QName value '%s' does not resolve to a(n) " "%s.\n", BAD_CAST des, BAD_CAST name, xmlSchemaFormatQName(&strA, refURI, refName), BAD_CAST refTypeStr, NULL); FREE_AND_NULL(des) FREE_AND_NULL(strA) } /** * xmlSchemaPCustomAttrErr: * @ctxt: the schema parser context * @error: the error code * @ownerDes: the designation of the owner * @ownerItem: the owner as a schema object * @attr: the illegal attribute node * * Reports an illegal attribute during the parse. */ static void xmlSchemaPCustomAttrErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlChar **ownerDes, xmlSchemaBasicItemPtr ownerItem, xmlAttrPtr attr, const char *msg) { xmlChar *des = NULL; if (ownerDes == NULL) xmlSchemaFormatItemForReport(&des, NULL, ownerItem, attr->parent); else if (*ownerDes == NULL) { xmlSchemaFormatItemForReport(ownerDes, NULL, ownerItem, attr->parent); des = *ownerDes; } else des = *ownerDes; if (attr == NULL) { xmlSchemaPErrExt(ctxt, NULL, error, NULL, NULL, NULL, "%s, attribute '%s': %s.\n", BAD_CAST des, (const xmlChar *) "Unknown", (const xmlChar *) msg, NULL, NULL); } else { xmlSchemaPErrExt(ctxt, (xmlNodePtr) attr, error, NULL, NULL, NULL, "%s, attribute '%s': %s.\n", BAD_CAST des, attr->name, (const xmlChar *) msg, NULL, NULL); } if (ownerDes == NULL) FREE_AND_NULL(des); } /** * xmlSchemaPIllegalAttrErr: * @ctxt: the schema parser context * @error: the error code * @ownerItem: the attribute's owner item * @attr: the illegal attribute node * * Reports an illegal attribute during the parse. */ static void xmlSchemaPIllegalAttrErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr ownerComp ATTRIBUTE_UNUSED, xmlAttrPtr attr) { xmlChar *strA = NULL, *strB = NULL; xmlSchemaFormatNodeForError(&strA, ACTXT_CAST ctxt, attr->parent); xmlSchemaErr4(ACTXT_CAST ctxt, error, (xmlNodePtr) attr, "%sThe attribute '%s' is not allowed.\n", BAD_CAST strA, xmlSchemaFormatQNameNs(&strB, attr->ns, attr->name), NULL, NULL); FREE_AND_NULL(strA); FREE_AND_NULL(strB); } /** * xmlSchemaPCustomErr: * @ctxt: the schema parser context * @error: the error code * @itemDes: the designation of the schema item * @item: the schema item * @itemElem: the node of the schema item * @message: the error message * @str1: an optional param for the error message * @str2: an optional param for the error message * @str3: an optional param for the error message * * Reports an error during parsing. */ static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaPCustomErrExt(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr item, xmlNodePtr itemElem, const char *message, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3) { xmlChar *des = NULL, *msg = NULL; xmlSchemaFormatItemForReport(&des, NULL, item, itemElem); msg = xmlStrdup(BAD_CAST "%s: "); msg = xmlStrcat(msg, (const xmlChar *) message); msg = xmlStrcat(msg, BAD_CAST ".\n"); if ((itemElem == NULL) && (item != NULL)) itemElem = WXS_ITEM_NODE(item); xmlSchemaPErrExt(ctxt, itemElem, error, NULL, NULL, NULL, (const char *) msg, BAD_CAST des, str1, str2, str3, NULL); FREE_AND_NULL(des); FREE_AND_NULL(msg); } /** * xmlSchemaPCustomErr: * @ctxt: the schema parser context * @error: the error code * @itemDes: the designation of the schema item * @item: the schema item * @itemElem: the node of the schema item * @message: the error message * @str1: the optional param for the error message * * Reports an error during parsing. */ static void LIBXML_ATTR_FORMAT(5,0) xmlSchemaPCustomErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr item, xmlNodePtr itemElem, const char *message, const xmlChar *str1) { xmlSchemaPCustomErrExt(ctxt, error, item, itemElem, message, str1, NULL, NULL); } /** * xmlSchemaPAttrUseErr: * @ctxt: the schema parser context * @error: the error code * @itemDes: the designation of the schema type * @item: the schema type * @itemElem: the node of the schema type * @attr: the invalid schema attribute * @message: the error message * @str1: the optional param for the error message * * Reports an attribute use error during parsing. */ static void LIBXML_ATTR_FORMAT(6,0) xmlSchemaPAttrUseErr4(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlNodePtr node, xmlSchemaBasicItemPtr ownerItem, const xmlSchemaAttributeUsePtr attruse, const char *message, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3,const xmlChar *str4) { xmlChar *str = NULL, *msg = NULL; xmlSchemaFormatItemForReport(&msg, NULL, ownerItem, NULL); msg = xmlStrcat(msg, BAD_CAST ", "); msg = xmlStrcat(msg, BAD_CAST xmlSchemaFormatItemForReport(&str, NULL, WXS_BASIC_CAST attruse, NULL)); FREE_AND_NULL(str); msg = xmlStrcat(msg, BAD_CAST ": "); msg = xmlStrcat(msg, (const xmlChar *) message); msg = xmlStrcat(msg, BAD_CAST ".\n"); xmlSchemaErr4(ACTXT_CAST ctxt, error, node, (const char *) msg, str1, str2, str3, str4); xmlFree(msg); } /** * xmlSchemaPIllegalFacetAtomicErr: * @ctxt: the schema parser context * @error: the error code * @type: the schema type * @baseType: the base type of type * @facet: the illegal facet * * Reports an illegal facet for atomic simple types. */ static void xmlSchemaPIllegalFacetAtomicErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaTypePtr type, xmlSchemaTypePtr baseType, xmlSchemaFacetPtr facet) { xmlChar *des = NULL, *strT = NULL; xmlSchemaFormatItemForReport(&des, NULL, WXS_BASIC_CAST type, type->node); xmlSchemaPErrExt(ctxt, type->node, error, NULL, NULL, NULL, "%s: The facet '%s' is not allowed on types derived from the " "type %s.\n", BAD_CAST des, xmlSchemaFacetTypeToString(facet->type), xmlSchemaFormatItemForReport(&strT, NULL, WXS_BASIC_CAST baseType, NULL), NULL, NULL); FREE_AND_NULL(des); FREE_AND_NULL(strT); } /** * xmlSchemaPIllegalFacetListUnionErr: * @ctxt: the schema parser context * @error: the error code * @itemDes: the designation of the schema item involved * @item: the schema item involved * @facet: the illegal facet * * Reports an illegal facet for and . */ static void xmlSchemaPIllegalFacetListUnionErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaTypePtr type, xmlSchemaFacetPtr facet) { xmlChar *des = NULL; xmlSchemaFormatItemForReport(&des, NULL, WXS_BASIC_CAST type, type->node); xmlSchemaPErr(ctxt, type->node, error, "%s: The facet '%s' is not allowed.\n", BAD_CAST des, xmlSchemaFacetTypeToString(facet->type)); FREE_AND_NULL(des); } /** * xmlSchemaPMutualExclAttrErr: * @ctxt: the schema validation context * @error: the error code * @elemDes: the designation of the parent element node * @attr: the bad attribute node * @type: the corresponding type of the attribute node * * Reports an illegal attribute. */ static void xmlSchemaPMutualExclAttrErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr ownerItem, xmlAttrPtr attr, const char *name1, const char *name2) { xmlChar *des = NULL; xmlSchemaFormatItemForReport(&des, NULL, WXS_BASIC_CAST ownerItem, attr->parent); xmlSchemaPErrExt(ctxt, (xmlNodePtr) attr, error, NULL, NULL, NULL, "%s: The attributes '%s' and '%s' are mutually exclusive.\n", BAD_CAST des, BAD_CAST name1, BAD_CAST name2, NULL, NULL); FREE_AND_NULL(des); } /** * xmlSchemaPSimpleTypeErr: * @ctxt: the schema validation context * @error: the error code * @type: the type specifier * @ownerItem: the schema object if existent * @node: the validated node * @value: the validated value * * Reports a simple type validation error. * TODO: Should this report the value of an element as well? */ static void LIBXML_ATTR_FORMAT(8,0) xmlSchemaPSimpleTypeErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr ownerItem ATTRIBUTE_UNUSED, xmlNodePtr node, xmlSchemaTypePtr type, const char *expected, const xmlChar *value, const char *message, const xmlChar *str1, const xmlChar *str2) { xmlChar *msg = NULL; xmlSchemaFormatNodeForError(&msg, ACTXT_CAST ctxt, node); if (message == NULL) { /* * Use default messages. */ if (type != NULL) { if (node->type == XML_ATTRIBUTE_NODE) msg = xmlStrcat(msg, BAD_CAST "'%s' is not a valid value of "); else msg = xmlStrcat(msg, BAD_CAST "The character content is not a " "valid value of "); if (! xmlSchemaIsGlobalItem(type)) msg = xmlStrcat(msg, BAD_CAST "the local "); else msg = xmlStrcat(msg, BAD_CAST "the "); if (WXS_IS_ATOMIC(type)) msg = xmlStrcat(msg, BAD_CAST "atomic type"); else if (WXS_IS_LIST(type)) msg = xmlStrcat(msg, BAD_CAST "list type"); else if (WXS_IS_UNION(type)) msg = xmlStrcat(msg, BAD_CAST "union type"); if (xmlSchemaIsGlobalItem(type)) { xmlChar *str = NULL; msg = xmlStrcat(msg, BAD_CAST " '"); if (type->builtInType != 0) { msg = xmlStrcat(msg, BAD_CAST "xs:"); str = xmlStrdup(type->name); } else { const xmlChar *qName = xmlSchemaFormatQName(&str, type->targetNamespace, type->name); if (!str) str = xmlStrdup(qName); } msg = xmlStrcat(msg, xmlEscapeFormatString(&str)); msg = xmlStrcat(msg, BAD_CAST "'."); FREE_AND_NULL(str); } } else { if (node->type == XML_ATTRIBUTE_NODE) msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not valid."); else msg = xmlStrcat(msg, BAD_CAST "The character content is not " "valid."); } if (expected) { xmlChar *expectedEscaped = xmlCharStrdup(expected); msg = xmlStrcat(msg, BAD_CAST " Expected is '"); msg = xmlStrcat(msg, xmlEscapeFormatString(&expectedEscaped)); FREE_AND_NULL(expectedEscaped); msg = xmlStrcat(msg, BAD_CAST "'.\n"); } else msg = xmlStrcat(msg, BAD_CAST "\n"); if (node->type == XML_ATTRIBUTE_NODE) xmlSchemaPErr(ctxt, node, error, (const char *) msg, value, NULL); else xmlSchemaPErr(ctxt, node, error, (const char *) msg, NULL, NULL); } else { msg = xmlStrcat(msg, BAD_CAST message); msg = xmlStrcat(msg, BAD_CAST ".\n"); xmlSchemaPErrExt(ctxt, node, error, NULL, NULL, NULL, (const char*) msg, str1, str2, NULL, NULL, NULL); } /* Cleanup. */ FREE_AND_NULL(msg) } /** * xmlSchemaPContentErr: * @ctxt: the schema parser context * @error: the error code * @ownerItem: the owner item of the holder of the content * @ownerElem: the node of the holder of the content * @child: the invalid child node * @message: the optional error message * @content: the optional string describing the correct content * * Reports an error concerning the content of a schema element. */ static void xmlSchemaPContentErr(xmlSchemaParserCtxtPtr ctxt, xmlParserErrors error, xmlSchemaBasicItemPtr ownerItem, xmlNodePtr ownerElem, xmlNodePtr child, const char *message, const char *content) { xmlChar *des = NULL; xmlSchemaFormatItemForReport(&des, NULL, ownerItem, ownerElem); if (message != NULL) xmlSchemaPErr2(ctxt, ownerElem, child, error, "%s: %s.\n", BAD_CAST des, BAD_CAST message); else { if (content != NULL) { xmlSchemaPErr2(ctxt, ownerElem, child, error, "%s: The content is not valid. Expected is %s.\n", BAD_CAST des, BAD_CAST content); } else { xmlSchemaPErr2(ctxt, ownerElem, child, error, "%s: The content is not valid.\n", BAD_CAST des, NULL); } } FREE_AND_NULL(des) } /************************************************************************ * * * Streamable error functions * * * ************************************************************************/ /************************************************************************ * * * Validation helper functions * * * ************************************************************************/ /************************************************************************ * * * Allocation functions * * * ************************************************************************/ /** * xmlSchemaNewSchemaForParserCtxt: * @ctxt: a schema validation context * * Allocate a new Schema structure. * * Returns the newly allocated structure or NULL in case or error */ static xmlSchemaPtr xmlSchemaNewSchema(xmlSchemaParserCtxtPtr ctxt) { xmlSchemaPtr ret; ret = (xmlSchemaPtr) xmlMalloc(sizeof(xmlSchema)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchema)); ret->dict = ctxt->dict; xmlDictReference(ret->dict); return (ret); } /** * xmlSchemaNewFacet: * * Allocate a new Facet structure. * * Returns the newly allocated structure or NULL in case or error */ xmlSchemaFacetPtr xmlSchemaNewFacet(void) { xmlSchemaFacetPtr ret; ret = (xmlSchemaFacetPtr) xmlMalloc(sizeof(xmlSchemaFacet)); if (ret == NULL) { return (NULL); } memset(ret, 0, sizeof(xmlSchemaFacet)); return (ret); } /** * xmlSchemaNewAnnot: * @ctxt: a schema validation context * @node: a node * * Allocate a new annotation structure. * * Returns the newly allocated structure or NULL in case or error */ static xmlSchemaAnnotPtr xmlSchemaNewAnnot(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node) { xmlSchemaAnnotPtr ret; ret = (xmlSchemaAnnotPtr) xmlMalloc(sizeof(xmlSchemaAnnot)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaAnnot)); ret->content = node; return (ret); } static xmlSchemaItemListPtr xmlSchemaItemListCreate(void) { xmlSchemaItemListPtr ret; ret = xmlMalloc(sizeof(xmlSchemaItemList)); if (ret == NULL) { xmlSchemaPErrMemory(NULL); return (NULL); } memset(ret, 0, sizeof(xmlSchemaItemList)); return (ret); } static void xmlSchemaItemListClear(xmlSchemaItemListPtr list) { if (list->items != NULL) { xmlFree(list->items); list->items = NULL; } list->nbItems = 0; list->sizeItems = 0; } static int xmlSchemaItemListAdd(xmlSchemaItemListPtr list, void *item) { if (list->sizeItems <= list->nbItems) { void **tmp; size_t newSize = list->sizeItems == 0 ? 20 : list->sizeItems * 2; tmp = (void **) xmlRealloc(list->items, newSize * sizeof(void *)); if (tmp == NULL) { xmlSchemaPErrMemory(NULL); return(-1); } list->items = tmp; list->sizeItems = newSize; } list->items[list->nbItems++] = item; return(0); } static int xmlSchemaItemListAddSize(xmlSchemaItemListPtr list, int initialSize, void *item) { if (list->items == NULL) { if (initialSize <= 0) initialSize = 1; list->items = (void **) xmlMalloc( initialSize * sizeof(void *)); if (list->items == NULL) { xmlSchemaPErrMemory(NULL); return(-1); } list->sizeItems = initialSize; } else if (list->sizeItems <= list->nbItems) { void **tmp; list->sizeItems *= 2; tmp = (void **) xmlRealloc(list->items, list->sizeItems * sizeof(void *)); if (tmp == NULL) { xmlSchemaPErrMemory(NULL); list->sizeItems /= 2; return(-1); } list->items = tmp; } list->items[list->nbItems++] = item; return(0); } static int xmlSchemaItemListInsert(xmlSchemaItemListPtr list, void *item, int idx) { if (list->sizeItems <= list->nbItems) { void **tmp; size_t newSize = list->sizeItems == 0 ? 20 : list->sizeItems * 2; tmp = (void **) xmlRealloc(list->items, newSize * sizeof(void *)); if (tmp == NULL) { xmlSchemaPErrMemory(NULL); return(-1); } list->items = tmp; list->sizeItems = newSize; } /* * Just append if the index is greater/equal than the item count. */ if (idx >= list->nbItems) { list->items[list->nbItems++] = item; } else { int i; for (i = list->nbItems; i > idx; i--) list->items[i] = list->items[i-1]; list->items[idx] = item; list->nbItems++; } return(0); } #if 0 /* enable if ever needed */ static int xmlSchemaItemListInsertSize(xmlSchemaItemListPtr list, int initialSize, void *item, int idx) { if (list->items == NULL) { if (initialSize <= 0) initialSize = 1; list->items = (void **) xmlMalloc( initialSize * sizeof(void *)); if (list->items == NULL) { xmlSchemaPErrMemory(NULL); return(-1); } list->sizeItems = initialSize; } else if (list->sizeItems <= list->nbItems) { list->sizeItems *= 2; list->items = (void **) xmlRealloc(list->items, list->sizeItems * sizeof(void *)); if (list->items == NULL) { xmlSchemaPErrMemory(NULL); list->sizeItems = 0; return(-1); } } /* * Just append if the index is greater/equal than the item count. */ if (idx >= list->nbItems) { list->items[list->nbItems++] = item; } else { int i; for (i = list->nbItems; i > idx; i--) list->items[i] = list->items[i-1]; list->items[idx] = item; list->nbItems++; } return(0); } #endif static int xmlSchemaItemListRemove(xmlSchemaItemListPtr list, int idx) { int i; if ((list->items == NULL) || (idx >= list->nbItems)) return(-1); if (list->nbItems == 1) { /* TODO: Really free the list? */ xmlFree(list->items); list->items = NULL; list->nbItems = 0; list->sizeItems = 0; } else if (list->nbItems -1 == idx) { list->nbItems--; } else { for (i = idx; i < list->nbItems -1; i++) list->items[i] = list->items[i+1]; list->nbItems--; } return(0); } /** * xmlSchemaItemListFree: * @annot: a schema type structure * * Deallocate a annotation structure */ static void xmlSchemaItemListFree(xmlSchemaItemListPtr list) { if (list == NULL) return; if (list->items != NULL) xmlFree(list->items); xmlFree(list); } static void xmlSchemaBucketFree(xmlSchemaBucketPtr bucket) { if (bucket == NULL) return; if (bucket->globals != NULL) { xmlSchemaComponentListFree(bucket->globals); xmlSchemaItemListFree(bucket->globals); } if (bucket->locals != NULL) { xmlSchemaComponentListFree(bucket->locals); xmlSchemaItemListFree(bucket->locals); } if (bucket->relations != NULL) { xmlSchemaSchemaRelationPtr prev, cur = bucket->relations; do { prev = cur; cur = cur->next; xmlFree(prev); } while (cur != NULL); } if ((! bucket->preserveDoc) && (bucket->doc != NULL)) { xmlFreeDoc(bucket->doc); } if (bucket->type == XML_SCHEMA_SCHEMA_IMPORT) { if (WXS_IMPBUCKET(bucket)->schema != NULL) xmlSchemaFree(WXS_IMPBUCKET(bucket)->schema); } xmlFree(bucket); } static void xmlSchemaBucketFreeEntry(void *bucket, const xmlChar *name ATTRIBUTE_UNUSED) { xmlSchemaBucketFree((xmlSchemaBucketPtr) bucket); } static xmlSchemaBucketPtr xmlSchemaBucketCreate(xmlSchemaParserCtxtPtr pctxt, int type, const xmlChar *targetNamespace) { xmlSchemaBucketPtr ret; int size; xmlSchemaPtr mainSchema; if (WXS_CONSTRUCTOR(pctxt)->mainSchema == NULL) { PERROR_INT("xmlSchemaBucketCreate", "no main schema on constructor"); return(NULL); } mainSchema = WXS_CONSTRUCTOR(pctxt)->mainSchema; /* Create the schema bucket. */ if (WXS_IS_BUCKET_INCREDEF(type)) size = sizeof(xmlSchemaInclude); else size = sizeof(xmlSchemaImport); ret = (xmlSchemaBucketPtr) xmlMalloc(size); if (ret == NULL) { xmlSchemaPErrMemory(NULL); return(NULL); } memset(ret, 0, size); ret->targetNamespace = targetNamespace; ret->type = type; ret->globals = xmlSchemaItemListCreate(); if (ret->globals == NULL) { xmlSchemaBucketFree(ret); return(NULL); } ret->locals = xmlSchemaItemListCreate(); if (ret->locals == NULL) { xmlSchemaBucketFree(ret); return(NULL); } /* * The following will assure that only the first bucket is marked as * XML_SCHEMA_SCHEMA_MAIN and it points to the *main* schema. * For each following import buckets an xmlSchema will be created. * An xmlSchema will be created for every distinct targetNamespace. * We assign the targetNamespace to the schemata here. */ if (! WXS_HAS_BUCKETS(pctxt)) { if (WXS_IS_BUCKET_INCREDEF(type)) { PERROR_INT("xmlSchemaBucketCreate", "first bucket but it's an include or redefine"); xmlSchemaBucketFree(ret); return(NULL); } /* Force the type to be XML_SCHEMA_SCHEMA_MAIN. */ ret->type = XML_SCHEMA_SCHEMA_MAIN; /* Point to the *main* schema. */ WXS_CONSTRUCTOR(pctxt)->mainBucket = ret; WXS_IMPBUCKET(ret)->schema = mainSchema; /* * Ensure that the main schema gets a targetNamespace. */ mainSchema->targetNamespace = targetNamespace; } else { if (type == XML_SCHEMA_SCHEMA_MAIN) { PERROR_INT("xmlSchemaBucketCreate", "main bucket but it's not the first one"); xmlSchemaBucketFree(ret); return(NULL); } else if (type == XML_SCHEMA_SCHEMA_IMPORT) { /* * Create a schema for imports and assign the * targetNamespace. */ WXS_IMPBUCKET(ret)->schema = xmlSchemaNewSchema(pctxt); if (WXS_IMPBUCKET(ret)->schema == NULL) { xmlSchemaBucketFree(ret); return(NULL); } WXS_IMPBUCKET(ret)->schema->targetNamespace = targetNamespace; } } if (WXS_IS_BUCKET_IMPMAIN(type)) { int res; /* * Imports go into the "schemasImports" slot of the main *schema*. * Note that we create an import entry for the main schema as well; i.e., * even if there's only one schema, we'll get an import. */ if (mainSchema->schemasImports == NULL) { mainSchema->schemasImports = xmlHashCreateDict(5, WXS_CONSTRUCTOR(pctxt)->dict); if (mainSchema->schemasImports == NULL) { xmlSchemaBucketFree(ret); return(NULL); } } if (targetNamespace == NULL) res = xmlHashAddEntry(mainSchema->schemasImports, XML_SCHEMAS_NO_NAMESPACE, ret); else res = xmlHashAddEntry(mainSchema->schemasImports, targetNamespace, ret); if (res != 0) { PERROR_INT("xmlSchemaBucketCreate", "failed to add the schema bucket to the hash"); xmlSchemaBucketFree(ret); return(NULL); } } else { /* Set the @ownerImport of an include bucket. */ if (WXS_IS_BUCKET_IMPMAIN(WXS_CONSTRUCTOR(pctxt)->bucket->type)) WXS_INCBUCKET(ret)->ownerImport = WXS_IMPBUCKET(WXS_CONSTRUCTOR(pctxt)->bucket); else WXS_INCBUCKET(ret)->ownerImport = WXS_INCBUCKET(WXS_CONSTRUCTOR(pctxt)->bucket)->ownerImport; /* Includes got into the "includes" slot of the *main* schema. */ if (mainSchema->includes == NULL) { mainSchema->includes = xmlSchemaItemListCreate(); if (mainSchema->includes == NULL) { xmlSchemaBucketFree(ret); return(NULL); } } if (xmlSchemaItemListAdd(mainSchema->includes, ret) < 0) { xmlSchemaBucketFree(ret); return(NULL); } } /* * Add to list of all buckets; this is used for lookup * during schema construction time only. */ if (xmlSchemaItemListAdd(WXS_CONSTRUCTOR(pctxt)->buckets, ret) == -1) return(NULL); return(ret); } static int xmlSchemaAddItemSize(xmlSchemaItemListPtr *list, int initialSize, void *item) { if (*list == NULL) { *list = xmlSchemaItemListCreate(); if (*list == NULL) return(-1); } return(xmlSchemaItemListAddSize(*list, initialSize, item)); } /** * xmlSchemaFreeAnnot: * @annot: a schema type structure * * Deallocate a annotation structure */ static void xmlSchemaFreeAnnot(xmlSchemaAnnotPtr annot) { if (annot == NULL) return; if (annot->next == NULL) { xmlFree(annot); } else { xmlSchemaAnnotPtr prev; do { prev = annot; annot = annot->next; xmlFree(prev); } while (annot != NULL); } } /** * xmlSchemaFreeNotation: * @schema: a schema notation structure * * Deallocate a Schema Notation structure. */ static void xmlSchemaFreeNotation(xmlSchemaNotationPtr nota) { if (nota == NULL) return; if (nota->annot != NULL) xmlSchemaFreeAnnot(nota->annot); xmlFree(nota); } /** * xmlSchemaFreeAttribute: * @attr: an attribute declaration * * Deallocates an attribute declaration structure. */ static void xmlSchemaFreeAttribute(xmlSchemaAttributePtr attr) { if (attr == NULL) return; if (attr->annot != NULL) xmlSchemaFreeAnnot(attr->annot); if (attr->defVal != NULL) xmlSchemaFreeValue(attr->defVal); xmlFree(attr); } /** * xmlSchemaFreeAttributeUse: * @use: an attribute use * * Deallocates an attribute use structure. */ static void xmlSchemaFreeAttributeUse(xmlSchemaAttributeUsePtr use) { if (use == NULL) return; if (use->annot != NULL) xmlSchemaFreeAnnot(use->annot); if (use->defVal != NULL) xmlSchemaFreeValue(use->defVal); xmlFree(use); } /** * xmlSchemaFreeAttributeUseProhib: * @prohib: an attribute use prohibition * * Deallocates an attribute use structure. */ static void xmlSchemaFreeAttributeUseProhib(xmlSchemaAttributeUseProhibPtr prohib) { if (prohib == NULL) return; xmlFree(prohib); } /** * xmlSchemaFreeWildcardNsSet: * set: a schema wildcard namespace * * Deallocates a list of wildcard constraint structures. */ static void xmlSchemaFreeWildcardNsSet(xmlSchemaWildcardNsPtr set) { xmlSchemaWildcardNsPtr next; while (set != NULL) { next = set->next; xmlFree(set); set = next; } } /** * xmlSchemaFreeWildcard: * @wildcard: a wildcard structure * * Deallocates a wildcard structure. */ void xmlSchemaFreeWildcard(xmlSchemaWildcardPtr wildcard) { if (wildcard == NULL) return; if (wildcard->annot != NULL) xmlSchemaFreeAnnot(wildcard->annot); if (wildcard->nsSet != NULL) xmlSchemaFreeWildcardNsSet(wildcard->nsSet); if (wildcard->negNsSet != NULL) xmlFree(wildcard->negNsSet); xmlFree(wildcard); } /** * xmlSchemaFreeAttributeGroup: * @schema: a schema attribute group structure * * Deallocate a Schema Attribute Group structure. */ static void xmlSchemaFreeAttributeGroup(xmlSchemaAttributeGroupPtr attrGr) { if (attrGr == NULL) return; if (attrGr->annot != NULL) xmlSchemaFreeAnnot(attrGr->annot); if (attrGr->attrUses != NULL) xmlSchemaItemListFree(WXS_LIST_CAST attrGr->attrUses); xmlFree(attrGr); } /** * xmlSchemaFreeQNameRef: * @item: a QName reference structure * * Deallocatea a QName reference structure. */ static void xmlSchemaFreeQNameRef(xmlSchemaQNameRefPtr item) { xmlFree(item); } /** * xmlSchemaFreeTypeLinkList: * @alink: a type link * * Deallocate a list of types. */ static void xmlSchemaFreeTypeLinkList(xmlSchemaTypeLinkPtr link) { xmlSchemaTypeLinkPtr next; while (link != NULL) { next = link->next; xmlFree(link); link = next; } } static void xmlSchemaFreeIDCStateObjList(xmlSchemaIDCStateObjPtr sto) { xmlSchemaIDCStateObjPtr next; while (sto != NULL) { next = sto->next; if (sto->history != NULL) xmlFree(sto->history); if (sto->xpathCtxt != NULL) xmlFreeStreamCtxt((xmlStreamCtxtPtr) sto->xpathCtxt); xmlFree(sto); sto = next; } } /** * xmlSchemaFreeIDC: * @idc: a identity-constraint definition * * Deallocates an identity-constraint definition. */ static void xmlSchemaFreeIDC(xmlSchemaIDCPtr idcDef) { xmlSchemaIDCSelectPtr cur, prev; if (idcDef == NULL) return; if (idcDef->annot != NULL) xmlSchemaFreeAnnot(idcDef->annot); /* Selector */ if (idcDef->selector != NULL) { if (idcDef->selector->xpathComp != NULL) xmlFreePattern((xmlPatternPtr) idcDef->selector->xpathComp); xmlFree(idcDef->selector); } /* Fields */ if (idcDef->fields != NULL) { cur = idcDef->fields; do { prev = cur; cur = cur->next; if (prev->xpathComp != NULL) xmlFreePattern((xmlPatternPtr) prev->xpathComp); xmlFree(prev); } while (cur != NULL); } xmlFree(idcDef); } /** * xmlSchemaFreeElement: * @schema: a schema element structure * * Deallocate a Schema Element structure. */ static void xmlSchemaFreeElement(xmlSchemaElementPtr elem) { if (elem == NULL) return; if (elem->annot != NULL) xmlSchemaFreeAnnot(elem->annot); if (elem->contModel != NULL) xmlRegFreeRegexp(elem->contModel); if (elem->defVal != NULL) xmlSchemaFreeValue(elem->defVal); xmlFree(elem); } /** * xmlSchemaFreeFacet: * @facet: a schema facet structure * * Deallocate a Schema Facet structure. */ void xmlSchemaFreeFacet(xmlSchemaFacetPtr facet) { if (facet == NULL) return; if (facet->val != NULL) xmlSchemaFreeValue(facet->val); if (facet->regexp != NULL) xmlRegFreeRegexp(facet->regexp); if (facet->annot != NULL) xmlSchemaFreeAnnot(facet->annot); xmlFree(facet); } /** * xmlSchemaFreeType: * @type: a schema type structure * * Deallocate a Schema Type structure. */ void xmlSchemaFreeType(xmlSchemaTypePtr type) { if (type == NULL) return; if (type->annot != NULL) xmlSchemaFreeAnnot(type->annot); if (type->facets != NULL) { xmlSchemaFacetPtr facet, next; facet = type->facets; while (facet != NULL) { next = facet->next; xmlSchemaFreeFacet(facet); facet = next; } } if (type->attrUses != NULL) xmlSchemaItemListFree((xmlSchemaItemListPtr) type->attrUses); if (type->memberTypes != NULL) xmlSchemaFreeTypeLinkList(type->memberTypes); if (type->facetSet != NULL) { xmlSchemaFacetLinkPtr next, link; link = type->facetSet; do { next = link->next; xmlFree(link); link = next; } while (link != NULL); } if (type->contModel != NULL) xmlRegFreeRegexp(type->contModel); xmlFree(type); } /** * xmlSchemaFreeModelGroupDef: * @item: a schema model group definition * * Deallocates a schema model group definition. */ static void xmlSchemaFreeModelGroupDef(xmlSchemaModelGroupDefPtr item) { if (item->annot != NULL) xmlSchemaFreeAnnot(item->annot); xmlFree(item); } /** * xmlSchemaFreeModelGroup: * @item: a schema model group * * Deallocates a schema model group structure. */ static void xmlSchemaFreeModelGroup(xmlSchemaModelGroupPtr item) { if (item->annot != NULL) xmlSchemaFreeAnnot(item->annot); xmlFree(item); } static void xmlSchemaComponentListFree(xmlSchemaItemListPtr list) { if ((list == NULL) || (list->nbItems == 0)) return; { xmlSchemaTreeItemPtr item; xmlSchemaTreeItemPtr *items = (xmlSchemaTreeItemPtr *) list->items; int i; for (i = 0; i < list->nbItems; i++) { item = items[i]; if (item == NULL) continue; switch (item->type) { case XML_SCHEMA_TYPE_SIMPLE: case XML_SCHEMA_TYPE_COMPLEX: xmlSchemaFreeType((xmlSchemaTypePtr) item); break; case XML_SCHEMA_TYPE_ATTRIBUTE: xmlSchemaFreeAttribute((xmlSchemaAttributePtr) item); break; case XML_SCHEMA_TYPE_ATTRIBUTE_USE: xmlSchemaFreeAttributeUse((xmlSchemaAttributeUsePtr) item); break; case XML_SCHEMA_EXTRA_ATTR_USE_PROHIB: xmlSchemaFreeAttributeUseProhib( (xmlSchemaAttributeUseProhibPtr) item); break; case XML_SCHEMA_TYPE_ELEMENT: xmlSchemaFreeElement((xmlSchemaElementPtr) item); break; case XML_SCHEMA_TYPE_PARTICLE: if (item->annot != NULL) xmlSchemaFreeAnnot(item->annot); xmlFree(item); break; case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: case XML_SCHEMA_TYPE_ALL: xmlSchemaFreeModelGroup((xmlSchemaModelGroupPtr) item); break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: xmlSchemaFreeAttributeGroup( (xmlSchemaAttributeGroupPtr) item); break; case XML_SCHEMA_TYPE_GROUP: xmlSchemaFreeModelGroupDef( (xmlSchemaModelGroupDefPtr) item); break; case XML_SCHEMA_TYPE_ANY: case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: xmlSchemaFreeWildcard((xmlSchemaWildcardPtr) item); break; case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEYREF: xmlSchemaFreeIDC((xmlSchemaIDCPtr) item); break; case XML_SCHEMA_TYPE_NOTATION: xmlSchemaFreeNotation((xmlSchemaNotationPtr) item); break; case XML_SCHEMA_EXTRA_QNAMEREF: xmlSchemaFreeQNameRef((xmlSchemaQNameRefPtr) item); break; default: /* TODO: This should never be hit. */ break; } } list->nbItems = 0; } } /** * xmlSchemaFree: * @schema: a schema structure * * Deallocate a Schema structure. */ void xmlSchemaFree(xmlSchemaPtr schema) { if (schema == NULL) return; /* * Note that those slots are not responsible for freeing * schema components anymore; this will now be done by * the schema buckets. */ if (schema->notaDecl != NULL) xmlHashFree(schema->notaDecl, NULL); if (schema->attrDecl != NULL) xmlHashFree(schema->attrDecl, NULL); if (schema->attrgrpDecl != NULL) xmlHashFree(schema->attrgrpDecl, NULL); if (schema->elemDecl != NULL) xmlHashFree(schema->elemDecl, NULL); if (schema->typeDecl != NULL) xmlHashFree(schema->typeDecl, NULL); if (schema->groupDecl != NULL) xmlHashFree(schema->groupDecl, NULL); if (schema->idcDef != NULL) xmlHashFree(schema->idcDef, NULL); if (schema->schemasImports != NULL) xmlHashFree(schema->schemasImports, xmlSchemaBucketFreeEntry); if (schema->includes != NULL) { xmlSchemaItemListPtr list = (xmlSchemaItemListPtr) schema->includes; int i; for (i = 0; i < list->nbItems; i++) { xmlSchemaBucketFree((xmlSchemaBucketPtr) list->items[i]); } xmlSchemaItemListFree(list); } if (schema->annot != NULL) xmlSchemaFreeAnnot(schema->annot); /* Never free the doc here, since this will be done by the buckets. */ xmlDictFree(schema->dict); xmlFree(schema); } /************************************************************************ * * * Debug functions * * * ************************************************************************/ #ifdef LIBXML_OUTPUT_ENABLED static void xmlSchemaTypeDump(xmlSchemaTypePtr type, FILE * output); /* forward */ /** * xmlSchemaElementDump: * @elem: an element * @output: the file output * * Dump the element */ static void xmlSchemaElementDump(void *payload, void *data, const xmlChar * name ATTRIBUTE_UNUSED, const xmlChar * namespace ATTRIBUTE_UNUSED, const xmlChar * context ATTRIBUTE_UNUSED) { xmlSchemaElementPtr elem = (xmlSchemaElementPtr) payload; FILE *output = (FILE *) data; if (elem == NULL) return; fprintf(output, "Element"); if (elem->flags & XML_SCHEMAS_ELEM_GLOBAL) fprintf(output, " (global)"); fprintf(output, ": '%s' ", elem->name); if (namespace != NULL) fprintf(output, "ns '%s'", namespace); fprintf(output, "\n"); #if 0 if ((elem->minOccurs != 1) || (elem->maxOccurs != 1)) { fprintf(output, " min %d ", elem->minOccurs); if (elem->maxOccurs >= UNBOUNDED) fprintf(output, "max: unbounded\n"); else if (elem->maxOccurs != 1) fprintf(output, "max: %d\n", elem->maxOccurs); else fprintf(output, "\n"); } #endif /* * Misc other properties. */ if ((elem->flags & XML_SCHEMAS_ELEM_NILLABLE) || (elem->flags & XML_SCHEMAS_ELEM_ABSTRACT) || (elem->flags & XML_SCHEMAS_ELEM_FIXED) || (elem->flags & XML_SCHEMAS_ELEM_DEFAULT)) { fprintf(output, " props: "); if (elem->flags & XML_SCHEMAS_ELEM_FIXED) fprintf(output, "[fixed] "); if (elem->flags & XML_SCHEMAS_ELEM_DEFAULT) fprintf(output, "[default] "); if (elem->flags & XML_SCHEMAS_ELEM_ABSTRACT) fprintf(output, "[abstract] "); if (elem->flags & XML_SCHEMAS_ELEM_NILLABLE) fprintf(output, "[nillable] "); fprintf(output, "\n"); } /* * Default/fixed value. */ if (elem->value != NULL) fprintf(output, " value: '%s'\n", elem->value); /* * Type. */ if (elem->namedType != NULL) { fprintf(output, " type: '%s' ", elem->namedType); if (elem->namedTypeNs != NULL) fprintf(output, "ns '%s'\n", elem->namedTypeNs); else fprintf(output, "\n"); } else if (elem->subtypes != NULL) { /* * Dump local types. */ xmlSchemaTypeDump(elem->subtypes, output); } /* * Substitution group. */ if (elem->substGroup != NULL) { fprintf(output, " substitutionGroup: '%s' ", elem->substGroup); if (elem->substGroupNs != NULL) fprintf(output, "ns '%s'\n", elem->substGroupNs); else fprintf(output, "\n"); } } /** * xmlSchemaAnnotDump: * @output: the file output * @annot: a annotation * * Dump the annotation */ static void xmlSchemaAnnotDump(FILE * output, xmlSchemaAnnotPtr annot) { xmlChar *content; if (annot == NULL) return; content = xmlNodeGetContent(annot->content); if (content != NULL) { fprintf(output, " Annot: %s\n", content); xmlFree(content); } else fprintf(output, " Annot: empty\n"); } /** * xmlSchemaContentModelDump: * @particle: the schema particle * @output: the file output * @depth: the depth used for indentation * * Dump a SchemaType structure */ static void xmlSchemaContentModelDump(xmlSchemaParticlePtr particle, FILE * output, int depth) { xmlChar *str = NULL; xmlSchemaTreeItemPtr term; char shift[100]; int i; if (particle == NULL) return; for (i = 0;((i < depth) && (i < 25));i++) shift[2 * i] = shift[2 * i + 1] = ' '; shift[2 * i] = shift[2 * i + 1] = 0; fprintf(output, "%s", shift); if (particle->children == NULL) { fprintf(output, "MISSING particle term\n"); return; } term = particle->children; if (term == NULL) { fprintf(output, "(NULL)"); } else { switch (term->type) { case XML_SCHEMA_TYPE_ELEMENT: fprintf(output, "ELEM '%s'", xmlSchemaFormatQName(&str, ((xmlSchemaElementPtr)term)->targetNamespace, ((xmlSchemaElementPtr)term)->name)); FREE_AND_NULL(str); break; case XML_SCHEMA_TYPE_SEQUENCE: fprintf(output, "SEQUENCE"); break; case XML_SCHEMA_TYPE_CHOICE: fprintf(output, "CHOICE"); break; case XML_SCHEMA_TYPE_ALL: fprintf(output, "ALL"); break; case XML_SCHEMA_TYPE_ANY: fprintf(output, "ANY"); break; default: fprintf(output, "UNKNOWN\n"); return; } } if (particle->minOccurs != 1) fprintf(output, " min: %d", particle->minOccurs); if (particle->maxOccurs >= UNBOUNDED) fprintf(output, " max: unbounded"); else if (particle->maxOccurs != 1) fprintf(output, " max: %d", particle->maxOccurs); fprintf(output, "\n"); if (term && ((term->type == XML_SCHEMA_TYPE_SEQUENCE) || (term->type == XML_SCHEMA_TYPE_CHOICE) || (term->type == XML_SCHEMA_TYPE_ALL)) && (term->children != NULL)) { xmlSchemaContentModelDump((xmlSchemaParticlePtr) term->children, output, depth +1); } if (particle->next != NULL) xmlSchemaContentModelDump((xmlSchemaParticlePtr) particle->next, output, depth); } /** * xmlSchemaAttrUsesDump: * @uses: attribute uses list * @output: the file output * * Dumps a list of attribute use components. */ static void xmlSchemaAttrUsesDump(xmlSchemaItemListPtr uses, FILE * output) { xmlSchemaAttributeUsePtr use; xmlSchemaAttributeUseProhibPtr prohib; xmlSchemaQNameRefPtr ref; const xmlChar *name, *tns; xmlChar *str = NULL; int i; if ((uses == NULL) || (uses->nbItems == 0)) return; fprintf(output, " attributes:\n"); for (i = 0; i < uses->nbItems; i++) { use = uses->items[i]; if (use->type == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB) { fprintf(output, " [prohibition] "); prohib = (xmlSchemaAttributeUseProhibPtr) use; name = prohib->name; tns = prohib->targetNamespace; } else if (use->type == XML_SCHEMA_EXTRA_QNAMEREF) { fprintf(output, " [reference] "); ref = (xmlSchemaQNameRefPtr) use; name = ref->name; tns = ref->targetNamespace; } else { fprintf(output, " [use] "); name = WXS_ATTRUSE_DECL_NAME(use); tns = WXS_ATTRUSE_DECL_TNS(use); } fprintf(output, "'%s'\n", (const char *) xmlSchemaFormatQName(&str, tns, name)); FREE_AND_NULL(str); } } /** * xmlSchemaTypeDump: * @output: the file output * @type: a type structure * * Dump a SchemaType structure */ static void xmlSchemaTypeDump(xmlSchemaTypePtr type, FILE * output) { if (type == NULL) { fprintf(output, "Type: NULL\n"); return; } fprintf(output, "Type: "); if (type->name != NULL) fprintf(output, "'%s' ", type->name); else fprintf(output, "(no name) "); if (type->targetNamespace != NULL) fprintf(output, "ns '%s' ", type->targetNamespace); switch (type->type) { case XML_SCHEMA_TYPE_BASIC: fprintf(output, "[basic] "); break; case XML_SCHEMA_TYPE_SIMPLE: fprintf(output, "[simple] "); break; case XML_SCHEMA_TYPE_COMPLEX: fprintf(output, "[complex] "); break; case XML_SCHEMA_TYPE_SEQUENCE: fprintf(output, "[sequence] "); break; case XML_SCHEMA_TYPE_CHOICE: fprintf(output, "[choice] "); break; case XML_SCHEMA_TYPE_ALL: fprintf(output, "[all] "); break; case XML_SCHEMA_TYPE_UR: fprintf(output, "[ur] "); break; case XML_SCHEMA_TYPE_RESTRICTION: fprintf(output, "[restriction] "); break; case XML_SCHEMA_TYPE_EXTENSION: fprintf(output, "[extension] "); break; default: fprintf(output, "[unknown type %d] ", type->type); break; } fprintf(output, "content: "); switch (type->contentType) { case XML_SCHEMA_CONTENT_UNKNOWN: fprintf(output, "[unknown] "); break; case XML_SCHEMA_CONTENT_EMPTY: fprintf(output, "[empty] "); break; case XML_SCHEMA_CONTENT_ELEMENTS: fprintf(output, "[element] "); break; case XML_SCHEMA_CONTENT_MIXED: fprintf(output, "[mixed] "); break; case XML_SCHEMA_CONTENT_MIXED_OR_ELEMENTS: /* not used. */ break; case XML_SCHEMA_CONTENT_BASIC: fprintf(output, "[basic] "); break; case XML_SCHEMA_CONTENT_SIMPLE: fprintf(output, "[simple] "); break; case XML_SCHEMA_CONTENT_ANY: fprintf(output, "[any] "); break; } fprintf(output, "\n"); if (type->base != NULL) { fprintf(output, " base type: '%s'", type->base); if (type->baseNs != NULL) fprintf(output, " ns '%s'\n", type->baseNs); else fprintf(output, "\n"); } if (type->attrUses != NULL) xmlSchemaAttrUsesDump(type->attrUses, output); if (type->annot != NULL) xmlSchemaAnnotDump(output, type->annot); #ifdef DUMP_CONTENT_MODEL if ((type->type == XML_SCHEMA_TYPE_COMPLEX) && (type->subtypes != NULL)) { xmlSchemaContentModelDump((xmlSchemaParticlePtr) type->subtypes, output, 1); } #endif } static void xmlSchemaTypeDumpEntry(void *type, void *output, const xmlChar *name ATTRIBUTE_UNUSED) { xmlSchemaTypeDump((xmlSchemaTypePtr) type, (FILE *) output); } /** * xmlSchemaDump: * @output: the file output * @schema: a schema structure * * Dump a Schema structure. */ void xmlSchemaDump(FILE * output, xmlSchemaPtr schema) { if (output == NULL) return; if (schema == NULL) { fprintf(output, "Schemas: NULL\n"); return; } fprintf(output, "Schemas: "); if (schema->name != NULL) fprintf(output, "%s, ", schema->name); else fprintf(output, "no name, "); if (schema->targetNamespace != NULL) fprintf(output, "%s", (const char *) schema->targetNamespace); else fprintf(output, "no target namespace"); fprintf(output, "\n"); if (schema->annot != NULL) xmlSchemaAnnotDump(output, schema->annot); xmlHashScan(schema->typeDecl, xmlSchemaTypeDumpEntry, output); xmlHashScanFull(schema->elemDecl, xmlSchemaElementDump, output); } #endif /* LIBXML_OUTPUT_ENABLED */ /************************************************************************ * * * Utilities * * * ************************************************************************/ /** * xmlSchemaGetPropNode: * @node: the element node * @name: the name of the attribute * * Seeks an attribute with a name of @name in * no namespace. * * Returns the attribute or NULL if not present. */ static xmlAttrPtr xmlSchemaGetPropNode(xmlNodePtr node, const char *name) { xmlAttrPtr prop; if ((node == NULL) || (name == NULL)) return(NULL); prop = node->properties; while (prop != NULL) { if ((prop->ns == NULL) && xmlStrEqual(prop->name, BAD_CAST name)) return(prop); prop = prop->next; } return (NULL); } /** * xmlSchemaGetPropNodeNs: * @node: the element node * @uri: the uri * @name: the name of the attribute * * Seeks an attribute with a local name of @name and * a namespace URI of @uri. * * Returns the attribute or NULL if not present. */ static xmlAttrPtr xmlSchemaGetPropNodeNs(xmlNodePtr node, const char *uri, const char *name) { xmlAttrPtr prop; if ((node == NULL) || (name == NULL)) return(NULL); prop = node->properties; while (prop != NULL) { if ((prop->ns != NULL) && xmlStrEqual(prop->name, BAD_CAST name) && xmlStrEqual(prop->ns->href, BAD_CAST uri)) return(prop); prop = prop->next; } return (NULL); } static const xmlChar * xmlSchemaGetNodeContent(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node) { xmlChar *val; const xmlChar *ret; val = xmlNodeGetContent(node); if (val == NULL) val = xmlStrdup((xmlChar *)""); ret = xmlDictLookup(ctxt->dict, val, -1); xmlFree(val); if (ret == NULL) xmlSchemaPErrMemory(ctxt); return(ret); } static const xmlChar * xmlSchemaGetNodeContentNoDict(xmlNodePtr node) { return((const xmlChar*) xmlNodeGetContent(node)); } /** * xmlSchemaGetProp: * @ctxt: the parser context * @node: the node * @name: the property name * * Read a attribute value and internalize the string * * Returns the string or NULL if not present. */ static const xmlChar * xmlSchemaGetProp(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, const char *name) { xmlChar *val; const xmlChar *ret; val = xmlGetNoNsProp(node, BAD_CAST name); if (val == NULL) return(NULL); ret = xmlDictLookup(ctxt->dict, val, -1); xmlFree(val); return(ret); } /************************************************************************ * * * Parsing functions * * * ************************************************************************/ #define WXS_FIND_GLOBAL_ITEM(slot) \ if (xmlStrEqual(nsName, schema->targetNamespace)) { \ ret = xmlHashLookup(schema->slot, name); \ if (ret != NULL) goto exit; \ } \ if (xmlHashSize(schema->schemasImports) > 1) { \ xmlSchemaImportPtr import; \ if (nsName == NULL) \ import = xmlHashLookup(schema->schemasImports, \ XML_SCHEMAS_NO_NAMESPACE); \ else \ import = xmlHashLookup(schema->schemasImports, nsName); \ if (import == NULL) \ goto exit; \ ret = xmlHashLookup(import->schema->slot, name); \ } /** * xmlSchemaGetElem: * @schema: the schema context * @name: the element name * @ns: the element namespace * * Lookup a global element declaration in the schema. * * Returns the element declaration or NULL if not found. */ static xmlSchemaElementPtr xmlSchemaGetElem(xmlSchemaPtr schema, const xmlChar * name, const xmlChar * nsName) { xmlSchemaElementPtr ret = NULL; if ((name == NULL) || (schema == NULL)) return(NULL); if (schema != NULL) { WXS_FIND_GLOBAL_ITEM(elemDecl) } exit: return (ret); } /** * xmlSchemaGetType: * @schema: the main schema * @name: the type's name * nsName: the type's namespace * * Lookup a type in the schemas or the predefined types * * Returns the group definition or NULL if not found. */ static xmlSchemaTypePtr xmlSchemaGetType(xmlSchemaPtr schema, const xmlChar * name, const xmlChar * nsName) { xmlSchemaTypePtr ret = NULL; if (name == NULL) return (NULL); /* First try the built-in types. */ if ((nsName != NULL) && xmlStrEqual(nsName, xmlSchemaNs)) { ret = xmlSchemaGetPredefinedType(name, nsName); if (ret != NULL) goto exit; /* * Note that we try the parsed schemas as well here * since one might have parsed the S4S, which contain more * than the built-in types. * TODO: Can we optimize this? */ } if (schema != NULL) { WXS_FIND_GLOBAL_ITEM(typeDecl) } exit: return (ret); } /** * xmlSchemaGetAttributeDecl: * @schema: the context of the schema * @name: the name of the attribute * @ns: the target namespace of the attribute * * Lookup a an attribute in the schema or imported schemas * * Returns the attribute declaration or NULL if not found. */ static xmlSchemaAttributePtr xmlSchemaGetAttributeDecl(xmlSchemaPtr schema, const xmlChar * name, const xmlChar * nsName) { xmlSchemaAttributePtr ret = NULL; if ((name == NULL) || (schema == NULL)) return (NULL); if (schema != NULL) { WXS_FIND_GLOBAL_ITEM(attrDecl) } exit: return (ret); } /** * xmlSchemaGetAttributeGroup: * @schema: the context of the schema * @name: the name of the attribute group * @ns: the target namespace of the attribute group * * Lookup a an attribute group in the schema or imported schemas * * Returns the attribute group definition or NULL if not found. */ static xmlSchemaAttributeGroupPtr xmlSchemaGetAttributeGroup(xmlSchemaPtr schema, const xmlChar * name, const xmlChar * nsName) { xmlSchemaAttributeGroupPtr ret = NULL; if ((name == NULL) || (schema == NULL)) return (NULL); if (schema != NULL) { WXS_FIND_GLOBAL_ITEM(attrgrpDecl) } exit: /* TODO: if ((ret != NULL) && (ret->redef != NULL)) { * Return the last redefinition. * ret = ret->redef; } */ return (ret); } /** * xmlSchemaGetGroup: * @schema: the context of the schema * @name: the name of the group * @ns: the target namespace of the group * * Lookup a group in the schema or imported schemas * * Returns the group definition or NULL if not found. */ static xmlSchemaModelGroupDefPtr xmlSchemaGetGroup(xmlSchemaPtr schema, const xmlChar * name, const xmlChar * nsName) { xmlSchemaModelGroupDefPtr ret = NULL; if ((name == NULL) || (schema == NULL)) return (NULL); if (schema != NULL) { WXS_FIND_GLOBAL_ITEM(groupDecl) } exit: return (ret); } static xmlSchemaNotationPtr xmlSchemaGetNotation(xmlSchemaPtr schema, const xmlChar *name, const xmlChar *nsName) { xmlSchemaNotationPtr ret = NULL; if ((name == NULL) || (schema == NULL)) return (NULL); if (schema != NULL) { WXS_FIND_GLOBAL_ITEM(notaDecl) } exit: return (ret); } static xmlSchemaIDCPtr xmlSchemaGetIDC(xmlSchemaPtr schema, const xmlChar *name, const xmlChar *nsName) { xmlSchemaIDCPtr ret = NULL; if ((name == NULL) || (schema == NULL)) return (NULL); if (schema != NULL) { WXS_FIND_GLOBAL_ITEM(idcDef) } exit: return (ret); } /** * xmlSchemaGetNamedComponent: * @schema: the schema * @name: the name of the group * @ns: the target namespace of the group * * Lookup a group in the schema or imported schemas * * Returns the group definition or NULL if not found. */ static xmlSchemaBasicItemPtr xmlSchemaGetNamedComponent(xmlSchemaPtr schema, xmlSchemaTypeType itemType, const xmlChar *name, const xmlChar *targetNs) { switch (itemType) { case XML_SCHEMA_TYPE_GROUP: return ((xmlSchemaBasicItemPtr) xmlSchemaGetGroup(schema, name, targetNs)); case XML_SCHEMA_TYPE_ELEMENT: return ((xmlSchemaBasicItemPtr) xmlSchemaGetElem(schema, name, targetNs)); default: /* TODO */ return (NULL); } } /************************************************************************ * * * Parsing functions * * * ************************************************************************/ #define IS_BLANK_NODE(n) \ (((n)->type == XML_TEXT_NODE) && (xmlSchemaIsBlank((n)->content, -1))) /** * xmlSchemaIsBlank: * @str: a string * @len: the length of the string or -1 * * Check if a string is ignorable * * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise */ static int xmlSchemaIsBlank(xmlChar * str, int len) { if (str == NULL) return (1); if (len < 0) { while (*str != 0) { if (!(IS_BLANK_CH(*str))) return (0); str++; } } else while ((*str != 0) && (len != 0)) { if (!(IS_BLANK_CH(*str))) return (0); str++; len--; } return (1); } #define WXS_COMP_NAME(c, t) ((t) (c))->name #define WXS_COMP_TNS(c, t) ((t) (c))->targetNamespace /* * xmlSchemaFindRedefCompInGraph: * ATTENTION TODO: This uses pointer comp. for strings. */ static xmlSchemaBasicItemPtr xmlSchemaFindRedefCompInGraph(xmlSchemaBucketPtr bucket, xmlSchemaTypeType type, const xmlChar *name, const xmlChar *nsName) { xmlSchemaBasicItemPtr ret; int i; if ((bucket == NULL) || (name == NULL)) return(NULL); if ((bucket->globals == NULL) || (bucket->globals->nbItems == 0)) goto subschemas; /* * Search in global components. */ for (i = 0; i < bucket->globals->nbItems; i++) { ret = bucket->globals->items[i]; if (ret->type == type) { switch (type) { case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: if ((WXS_COMP_NAME(ret, xmlSchemaTypePtr) == name) && (WXS_COMP_TNS(ret, xmlSchemaTypePtr) == nsName)) { return(ret); } break; case XML_SCHEMA_TYPE_GROUP: if ((WXS_COMP_NAME(ret, xmlSchemaModelGroupDefPtr) == name) && (WXS_COMP_TNS(ret, xmlSchemaModelGroupDefPtr) == nsName)) { return(ret); } break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: if ((WXS_COMP_NAME(ret, xmlSchemaAttributeGroupPtr) == name) && (WXS_COMP_TNS(ret, xmlSchemaAttributeGroupPtr) == nsName)) { return(ret); } break; default: /* Should not be hit. */ return(NULL); } } } subschemas: /* * Process imported/included schemas. */ if (bucket->relations != NULL) { xmlSchemaSchemaRelationPtr rel = bucket->relations; /* * TODO: Marking the bucket will not avoid multiple searches * in the same schema, but avoids at least circularity. */ bucket->flags |= XML_SCHEMA_BUCKET_MARKED; do { if ((rel->bucket != NULL) && ((rel->bucket->flags & XML_SCHEMA_BUCKET_MARKED) == 0)) { ret = xmlSchemaFindRedefCompInGraph(rel->bucket, type, name, nsName); if (ret != NULL) return(ret); } rel = rel->next; } while (rel != NULL); bucket->flags ^= XML_SCHEMA_BUCKET_MARKED; } return(NULL); } /** * xmlSchemaAddNotation: * @ctxt: a schema parser context * @schema: the schema being built * @name: the item name * * Add an XML schema annotation declaration * *WARNING* this interface is highly subject to change * * Returns the new structure or NULL in case of error */ static xmlSchemaNotationPtr xmlSchemaAddNotation(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, const xmlChar *name, const xmlChar *nsName, xmlNodePtr node ATTRIBUTE_UNUSED) { xmlSchemaNotationPtr ret = NULL; if ((ctxt == NULL) || (schema == NULL) || (name == NULL)) return (NULL); ret = (xmlSchemaNotationPtr) xmlMalloc(sizeof(xmlSchemaNotation)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaNotation)); ret->type = XML_SCHEMA_TYPE_NOTATION; ret->name = name; ret->targetNamespace = nsName; /* TODO: do we need the node to be set? * ret->node = node;*/ WXS_ADD_GLOBAL(ctxt, ret); return (ret); } /** * xmlSchemaAddAttribute: * @ctxt: a schema parser context * @schema: the schema being built * @name: the item name * @namespace: the namespace * * Add an XML schema Attribute declaration * *WARNING* this interface is highly subject to change * * Returns the new structure or NULL in case of error */ static xmlSchemaAttributePtr xmlSchemaAddAttribute(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, const xmlChar * name, const xmlChar * nsName, xmlNodePtr node, int topLevel) { xmlSchemaAttributePtr ret = NULL; if ((ctxt == NULL) || (schema == NULL)) return (NULL); ret = (xmlSchemaAttributePtr) xmlMalloc(sizeof(xmlSchemaAttribute)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaAttribute)); ret->type = XML_SCHEMA_TYPE_ATTRIBUTE; ret->node = node; ret->name = name; ret->targetNamespace = nsName; if (topLevel) WXS_ADD_GLOBAL(ctxt, ret); else WXS_ADD_LOCAL(ctxt, ret); WXS_ADD_PENDING(ctxt, ret); return (ret); } /** * xmlSchemaAddAttributeUse: * @ctxt: a schema parser context * @schema: the schema being built * @name: the item name * @namespace: the namespace * * Add an XML schema Attribute declaration * *WARNING* this interface is highly subject to change * * Returns the new structure or NULL in case of error */ static xmlSchemaAttributeUsePtr xmlSchemaAddAttributeUse(xmlSchemaParserCtxtPtr pctxt, xmlNodePtr node) { xmlSchemaAttributeUsePtr ret = NULL; if (pctxt == NULL) return (NULL); ret = (xmlSchemaAttributeUsePtr) xmlMalloc(sizeof(xmlSchemaAttributeUse)); if (ret == NULL) { xmlSchemaPErrMemory(pctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaAttributeUse)); ret->type = XML_SCHEMA_TYPE_ATTRIBUTE_USE; ret->node = node; WXS_ADD_LOCAL(pctxt, ret); return (ret); } /* * xmlSchemaAddRedef: * * Adds a redefinition information. This is used at a later stage to: * resolve references to the redefined components and to check constraints. */ static xmlSchemaRedefPtr xmlSchemaAddRedef(xmlSchemaParserCtxtPtr pctxt, xmlSchemaBucketPtr targetBucket, void *item, const xmlChar *refName, const xmlChar *refTargetNs) { xmlSchemaRedefPtr ret; ret = (xmlSchemaRedefPtr) xmlMalloc(sizeof(xmlSchemaRedef)); if (ret == NULL) { xmlSchemaPErrMemory(pctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaRedef)); ret->item = item; ret->targetBucket = targetBucket; ret->refName = refName; ret->refTargetNs = refTargetNs; if (WXS_CONSTRUCTOR(pctxt)->redefs == NULL) WXS_CONSTRUCTOR(pctxt)->redefs = ret; else WXS_CONSTRUCTOR(pctxt)->lastRedef->next = ret; WXS_CONSTRUCTOR(pctxt)->lastRedef = ret; return (ret); } /** * xmlSchemaAddAttributeGroupDefinition: * @ctxt: a schema parser context * @schema: the schema being built * @name: the item name * @nsName: the target namespace * @node: the corresponding node * * Add an XML schema Attribute Group definition. * * Returns the new structure or NULL in case of error */ static xmlSchemaAttributeGroupPtr xmlSchemaAddAttributeGroupDefinition(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar *nsName, xmlNodePtr node) { xmlSchemaAttributeGroupPtr ret = NULL; if ((pctxt == NULL) || (name == NULL)) return (NULL); ret = (xmlSchemaAttributeGroupPtr) xmlMalloc(sizeof(xmlSchemaAttributeGroup)); if (ret == NULL) { xmlSchemaPErrMemory(pctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaAttributeGroup)); ret->type = XML_SCHEMA_TYPE_ATTRIBUTEGROUP; ret->name = name; ret->targetNamespace = nsName; ret->node = node; /* TODO: Remove the flag. */ ret->flags |= XML_SCHEMAS_ATTRGROUP_GLOBAL; if (pctxt->isRedefine) { pctxt->redef = xmlSchemaAddRedef(pctxt, pctxt->redefined, ret, name, nsName); if (pctxt->redef == NULL) { xmlFree(ret); return(NULL); } pctxt->redefCounter = 0; } WXS_ADD_GLOBAL(pctxt, ret); WXS_ADD_PENDING(pctxt, ret); return (ret); } /** * xmlSchemaAddElement: * @ctxt: a schema parser context * @schema: the schema being built * @name: the type name * @namespace: the type namespace * * Add an XML schema Element declaration * *WARNING* this interface is highly subject to change * * Returns the new structure or NULL in case of error */ static xmlSchemaElementPtr xmlSchemaAddElement(xmlSchemaParserCtxtPtr ctxt, const xmlChar * name, const xmlChar * nsName, xmlNodePtr node, int topLevel) { xmlSchemaElementPtr ret = NULL; if ((ctxt == NULL) || (name == NULL)) return (NULL); ret = (xmlSchemaElementPtr) xmlMalloc(sizeof(xmlSchemaElement)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaElement)); ret->type = XML_SCHEMA_TYPE_ELEMENT; ret->name = name; ret->targetNamespace = nsName; ret->node = node; if (topLevel) WXS_ADD_GLOBAL(ctxt, ret); else WXS_ADD_LOCAL(ctxt, ret); WXS_ADD_PENDING(ctxt, ret); return (ret); } /** * xmlSchemaAddType: * @ctxt: a schema parser context * @schema: the schema being built * @name: the item name * @namespace: the namespace * * Add an XML schema item * *WARNING* this interface is highly subject to change * * Returns the new structure or NULL in case of error */ static xmlSchemaTypePtr xmlSchemaAddType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlSchemaTypeType type, const xmlChar * name, const xmlChar * nsName, xmlNodePtr node, int topLevel) { xmlSchemaTypePtr ret = NULL; if ((ctxt == NULL) || (schema == NULL)) return (NULL); ret = (xmlSchemaTypePtr) xmlMalloc(sizeof(xmlSchemaType)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaType)); ret->type = type; ret->name = name; ret->targetNamespace = nsName; ret->node = node; if (topLevel) { if (ctxt->isRedefine) { ctxt->redef = xmlSchemaAddRedef(ctxt, ctxt->redefined, ret, name, nsName); if (ctxt->redef == NULL) { xmlFree(ret); return(NULL); } ctxt->redefCounter = 0; } WXS_ADD_GLOBAL(ctxt, ret); } else WXS_ADD_LOCAL(ctxt, ret); WXS_ADD_PENDING(ctxt, ret); return (ret); } static xmlSchemaQNameRefPtr xmlSchemaNewQNameRef(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypeType refType, const xmlChar *refName, const xmlChar *refNs) { xmlSchemaQNameRefPtr ret; ret = (xmlSchemaQNameRefPtr) xmlMalloc(sizeof(xmlSchemaQNameRef)); if (ret == NULL) { xmlSchemaPErrMemory(pctxt); return (NULL); } ret->node = NULL; ret->type = XML_SCHEMA_EXTRA_QNAMEREF; ret->name = refName; ret->targetNamespace = refNs; ret->item = NULL; ret->itemType = refType; /* * Store the reference item in the schema. */ WXS_ADD_LOCAL(pctxt, ret); return (ret); } static xmlSchemaAttributeUseProhibPtr xmlSchemaAddAttributeUseProhib(xmlSchemaParserCtxtPtr pctxt) { xmlSchemaAttributeUseProhibPtr ret; ret = (xmlSchemaAttributeUseProhibPtr) xmlMalloc(sizeof(xmlSchemaAttributeUseProhib)); if (ret == NULL) { xmlSchemaPErrMemory(pctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaAttributeUseProhib)); ret->type = XML_SCHEMA_EXTRA_ATTR_USE_PROHIB; WXS_ADD_LOCAL(pctxt, ret); return (ret); } /** * xmlSchemaAddModelGroup: * @ctxt: a schema parser context * @schema: the schema being built * @type: the "compositor" type of the model group * @node: the node in the schema doc * * Adds a schema model group * *WARNING* this interface is highly subject to change * * Returns the new structure or NULL in case of error */ static xmlSchemaModelGroupPtr xmlSchemaAddModelGroup(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlSchemaTypeType type, xmlNodePtr node) { xmlSchemaModelGroupPtr ret = NULL; if ((ctxt == NULL) || (schema == NULL)) return (NULL); ret = (xmlSchemaModelGroupPtr) xmlMalloc(sizeof(xmlSchemaModelGroup)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaModelGroup)); ret->type = type; ret->node = node; WXS_ADD_LOCAL(ctxt, ret); if ((type == XML_SCHEMA_TYPE_SEQUENCE) || (type == XML_SCHEMA_TYPE_CHOICE)) WXS_ADD_PENDING(ctxt, ret); return (ret); } /** * xmlSchemaAddParticle: * @ctxt: a schema parser context * @schema: the schema being built * @node: the corresponding node in the schema doc * @min: the minOccurs * @max: the maxOccurs * * Adds an XML schema particle component. * *WARNING* this interface is highly subject to change * * Returns the new structure or NULL in case of error */ static xmlSchemaParticlePtr xmlSchemaAddParticle(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int min, int max) { xmlSchemaParticlePtr ret = NULL; if (ctxt == NULL) return (NULL); ret = (xmlSchemaParticlePtr) xmlMalloc(sizeof(xmlSchemaParticle)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } ret->type = XML_SCHEMA_TYPE_PARTICLE; ret->annot = NULL; ret->node = node; ret->minOccurs = min; ret->maxOccurs = max; ret->next = NULL; ret->children = NULL; WXS_ADD_LOCAL(ctxt, ret); /* * Note that addition to pending components will be done locally * to the specific parsing function, since the most particles * need not to be fixed up (i.e. the reference to be resolved). * REMOVED: WXS_ADD_PENDING(ctxt, ret); */ return (ret); } /** * xmlSchemaAddModelGroupDefinition: * @ctxt: a schema validation context * @schema: the schema being built * @name: the group name * * Add an XML schema Group definition * * Returns the new structure or NULL in case of error */ static xmlSchemaModelGroupDefPtr xmlSchemaAddModelGroupDefinition(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, const xmlChar *name, const xmlChar *nsName, xmlNodePtr node) { xmlSchemaModelGroupDefPtr ret = NULL; if ((ctxt == NULL) || (schema == NULL) || (name == NULL)) return (NULL); ret = (xmlSchemaModelGroupDefPtr) xmlMalloc(sizeof(xmlSchemaModelGroupDef)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaModelGroupDef)); ret->name = name; ret->type = XML_SCHEMA_TYPE_GROUP; ret->node = node; ret->targetNamespace = nsName; if (ctxt->isRedefine) { ctxt->redef = xmlSchemaAddRedef(ctxt, ctxt->redefined, ret, name, nsName); if (ctxt->redef == NULL) { xmlFree(ret); return(NULL); } ctxt->redefCounter = 0; } WXS_ADD_GLOBAL(ctxt, ret); WXS_ADD_PENDING(ctxt, ret); return (ret); } /** * xmlSchemaNewWildcardNs: * @ctxt: a schema validation context * * Creates a new wildcard namespace constraint. * * Returns the new structure or NULL in case of error */ static xmlSchemaWildcardNsPtr xmlSchemaNewWildcardNsConstraint(xmlSchemaParserCtxtPtr ctxt) { xmlSchemaWildcardNsPtr ret; ret = (xmlSchemaWildcardNsPtr) xmlMalloc(sizeof(xmlSchemaWildcardNs)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } ret->value = NULL; ret->next = NULL; return (ret); } static xmlSchemaIDCPtr xmlSchemaAddIDC(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, const xmlChar *name, const xmlChar *nsName, int category, xmlNodePtr node) { xmlSchemaIDCPtr ret = NULL; if ((ctxt == NULL) || (schema == NULL) || (name == NULL)) return (NULL); ret = (xmlSchemaIDCPtr) xmlMalloc(sizeof(xmlSchemaIDC)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaIDC)); /* The target namespace of the parent element declaration. */ ret->targetNamespace = nsName; ret->name = name; ret->type = category; ret->node = node; WXS_ADD_GLOBAL(ctxt, ret); /* * Only keyrefs need to be fixup up. */ if (category == XML_SCHEMA_TYPE_IDC_KEYREF) WXS_ADD_PENDING(ctxt, ret); return (ret); } /** * xmlSchemaAddWildcard: * @ctxt: a schema validation context * @schema: a schema * * Adds a wildcard. * It corresponds to a xsd:anyAttribute and xsd:any. * * Returns the new structure or NULL in case of error */ static xmlSchemaWildcardPtr xmlSchemaAddWildcard(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlSchemaTypeType type, xmlNodePtr node) { xmlSchemaWildcardPtr ret = NULL; if ((ctxt == NULL) || (schema == NULL)) return (NULL); ret = (xmlSchemaWildcardPtr) xmlMalloc(sizeof(xmlSchemaWildcard)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(ret, 0, sizeof(xmlSchemaWildcard)); ret->type = type; ret->node = node; WXS_ADD_LOCAL(ctxt, ret); return (ret); } static void xmlSchemaSubstGroupFree(xmlSchemaSubstGroupPtr group) { if (group == NULL) return; if (group->members != NULL) xmlSchemaItemListFree(group->members); xmlFree(group); } static void xmlSchemaSubstGroupFreeEntry(void *group, const xmlChar *name ATTRIBUTE_UNUSED) { xmlSchemaSubstGroupFree((xmlSchemaSubstGroupPtr) group); } static xmlSchemaSubstGroupPtr xmlSchemaSubstGroupAdd(xmlSchemaParserCtxtPtr pctxt, xmlSchemaElementPtr head) { xmlSchemaSubstGroupPtr ret; /* Init subst group hash. */ if (WXS_SUBST_GROUPS(pctxt) == NULL) { WXS_SUBST_GROUPS(pctxt) = xmlHashCreateDict(10, pctxt->dict); if (WXS_SUBST_GROUPS(pctxt) == NULL) return(NULL); } /* Create a new substitution group. */ ret = (xmlSchemaSubstGroupPtr) xmlMalloc(sizeof(xmlSchemaSubstGroup)); if (ret == NULL) { xmlSchemaPErrMemory(NULL); return(NULL); } memset(ret, 0, sizeof(xmlSchemaSubstGroup)); ret->head = head; /* Create list of members. */ ret->members = xmlSchemaItemListCreate(); if (ret->members == NULL) { xmlSchemaSubstGroupFree(ret); return(NULL); } /* Add subst group to hash. */ if (xmlHashAddEntry2(WXS_SUBST_GROUPS(pctxt), head->name, head->targetNamespace, ret) != 0) { PERROR_INT("xmlSchemaSubstGroupAdd", "failed to add a new substitution container"); xmlSchemaSubstGroupFree(ret); return(NULL); } return(ret); } static xmlSchemaSubstGroupPtr xmlSchemaSubstGroupGet(xmlSchemaParserCtxtPtr pctxt, xmlSchemaElementPtr head) { if (WXS_SUBST_GROUPS(pctxt) == NULL) return(NULL); return(xmlHashLookup2(WXS_SUBST_GROUPS(pctxt), head->name, head->targetNamespace)); } /** * xmlSchemaAddElementSubstitutionMember: * @pctxt: a schema parser context * @head: the head of the substitution group * @member: the new member of the substitution group * * Allocate a new annotation structure. * * Returns the newly allocated structure or NULL in case or error */ static int xmlSchemaAddElementSubstitutionMember(xmlSchemaParserCtxtPtr pctxt, xmlSchemaElementPtr head, xmlSchemaElementPtr member) { xmlSchemaSubstGroupPtr substGroup = NULL; if ((pctxt == NULL) || (head == NULL) || (member == NULL)) return (-1); substGroup = xmlSchemaSubstGroupGet(pctxt, head); if (substGroup == NULL) substGroup = xmlSchemaSubstGroupAdd(pctxt, head); if (substGroup == NULL) return(-1); if (xmlSchemaItemListAdd(substGroup->members, member) == -1) return(-1); return(0); } /************************************************************************ * * * Utilities for parsing * * * ************************************************************************/ /** * xmlSchemaPValAttrNodeQNameValue: * @ctxt: a schema parser context * @schema: the schema context * @ownerItem: the parent as a schema object * @value: the QName value * @uri: the resulting namespace URI if found * @local: the resulting local part if found, the attribute value otherwise * * Extracts the local name and the URI of a QName value and validates it. * This one is intended to be used on attribute values that * should resolve to schema components. * * Returns 0, in case the QName is valid, a positive error code * if not valid and -1 if an internal error occurs. */ static int xmlSchemaPValAttrNodeQNameValue(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlSchemaBasicItemPtr ownerItem, xmlAttrPtr attr, const xmlChar *value, const xmlChar **uri, const xmlChar **local) { const xmlChar *pref; xmlNsPtr ns; int len, ret; *uri = NULL; *local = NULL; ret = xmlValidateQName(value, 1); if (ret > 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, ownerItem, (xmlNodePtr) attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), NULL, value, NULL, NULL, NULL); *local = value; return (ctxt->err); } else if (ret < 0) return (-1); if (!strchr((char *) value, ':')) { ns = xmlSearchNs(attr->doc, attr->parent, NULL); if (ns && ns->href && ns->href[0]) *uri = xmlDictLookup(ctxt->dict, ns->href, -1); else if (schema->flags & XML_SCHEMAS_INCLUDING_CONVERT_NS) { /* TODO: move XML_SCHEMAS_INCLUDING_CONVERT_NS to the * parser context. */ /* * This one takes care of included schemas with no * target namespace. */ *uri = ctxt->targetNamespace; } *local = xmlDictLookup(ctxt->dict, value, -1); return (0); } /* * At this point xmlSplitQName3 has to return a local name. */ *local = xmlSplitQName3(value, &len); *local = xmlDictLookup(ctxt->dict, *local, -1); pref = xmlDictLookup(ctxt->dict, value, len); ns = xmlSearchNs(attr->doc, attr->parent, pref); if (ns == NULL) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, ownerItem, (xmlNodePtr) attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), NULL, value, "The value '%s' of simple type 'xs:QName' has no " "corresponding namespace declaration in scope", value, NULL); return (ctxt->err); } else { *uri = xmlDictLookup(ctxt->dict, ns->href, -1); } return (0); } /** * xmlSchemaPValAttrNodeQName: * @ctxt: a schema parser context * @schema: the schema context * @ownerItem: the owner as a schema object * @attr: the attribute node * @uri: the resulting namespace URI if found * @local: the resulting local part if found, the attribute value otherwise * * Extracts and validates the QName of an attribute value. * This one is intended to be used on attribute values that * should resolve to schema components. * * Returns 0, in case the QName is valid, a positive error code * if not valid and -1 if an internal error occurs. */ static int xmlSchemaPValAttrNodeQName(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlSchemaBasicItemPtr ownerItem, xmlAttrPtr attr, const xmlChar **uri, const xmlChar **local) { const xmlChar *value; value = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); return (xmlSchemaPValAttrNodeQNameValue(ctxt, schema, ownerItem, attr, value, uri, local)); } /** * xmlSchemaPValAttrQName: * @ctxt: a schema parser context * @schema: the schema context * @ownerItem: the owner as a schema object * @ownerElem: the parent node of the attribute * @name: the name of the attribute * @uri: the resulting namespace URI if found * @local: the resulting local part if found, the attribute value otherwise * * Extracts and validates the QName of an attribute value. * * Returns 0, in case the QName is valid, a positive error code * if not valid and -1 if an internal error occurs. */ static int xmlSchemaPValAttrQName(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlSchemaBasicItemPtr ownerItem, xmlNodePtr ownerElem, const char *name, const xmlChar **uri, const xmlChar **local) { xmlAttrPtr attr; attr = xmlSchemaGetPropNode(ownerElem, name); if (attr == NULL) { *local = NULL; *uri = NULL; return (0); } return (xmlSchemaPValAttrNodeQName(ctxt, schema, ownerItem, attr, uri, local)); } /** * xmlSchemaPValAttrID: * @ctxt: a schema parser context * * Extracts and validates the ID of an attribute value. * * Returns 0, in case the ID is valid, a positive error code * if not valid and -1 if an internal error occurs. */ static int xmlSchemaPValAttrNodeID(xmlSchemaParserCtxtPtr ctxt, xmlAttrPtr attr) { int ret; const xmlChar *value; if (attr == NULL) return(0); value = xmlSchemaGetNodeContentNoDict((xmlNodePtr) attr); ret = xmlValidateNCName(value, 1); if (ret == 0) { /* * NOTE: the IDness might have already be declared in the DTD */ if (attr->atype != XML_ATTRIBUTE_ID) { xmlChar *strip; int res; /* * TODO: Use xmlSchemaStrip here; it's not exported at this * moment. */ strip = xmlSchemaCollapseString(value); if (strip != NULL) { xmlFree((xmlChar *) value); value = strip; } res = xmlAddIDSafe(attr, value); if (res < 0) { xmlSchemaPErrMemory(ctxt); } else if (res == 0) { ret = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_ID), NULL, NULL, "Duplicate value '%s' of simple " "type 'xs:ID'", value, NULL); } } } else if (ret > 0) { ret = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_ID), NULL, NULL, "The value '%s' of simple type 'xs:ID' is " "not a valid 'xs:NCName'", value, NULL); } if (value != NULL) xmlFree((xmlChar *)value); return (ret); } static int xmlSchemaPValAttrID(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr ownerElem, const xmlChar *name) { xmlAttrPtr attr; attr = xmlSchemaGetPropNode(ownerElem, (const char *) name); if (attr == NULL) return(0); return(xmlSchemaPValAttrNodeID(ctxt, attr)); } /** * xmlGetMaxOccurs: * @ctxt: a schema validation context * @node: a subtree containing XML Schema information * * Get the maxOccurs property * * Returns the default if not found, or the value */ static int xmlGetMaxOccurs(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int min, int max, int def, const char *expected) { const xmlChar *val, *cur; int ret = 0; xmlAttrPtr attr; attr = xmlSchemaGetPropNode(node, "maxOccurs"); if (attr == NULL) return (def); val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (val == NULL) return (def); if (xmlStrEqual(val, (const xmlChar *) "unbounded")) { if (max != UNBOUNDED) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, /* XML_SCHEMAP_INVALID_MINOCCURS, */ NULL, (xmlNodePtr) attr, NULL, expected, val, NULL, NULL, NULL); return (def); } else return (UNBOUNDED); /* encoding it with -1 might be another option */ } cur = val; while (IS_BLANK_CH(*cur)) cur++; if (*cur == 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, /* XML_SCHEMAP_INVALID_MINOCCURS, */ NULL, (xmlNodePtr) attr, NULL, expected, val, NULL, NULL, NULL); return (def); } while ((*cur >= '0') && (*cur <= '9')) { if (ret > INT_MAX / 10) { ret = INT_MAX; } else { int digit = *cur - '0'; ret *= 10; if (ret > INT_MAX - digit) ret = INT_MAX; else ret += digit; } cur++; } while (IS_BLANK_CH(*cur)) cur++; /* * TODO: Restrict the maximal value to Integer. */ if ((*cur != 0) || (ret < min) || ((max != -1) && (ret > max))) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, /* XML_SCHEMAP_INVALID_MINOCCURS, */ NULL, (xmlNodePtr) attr, NULL, expected, val, NULL, NULL, NULL); return (def); } return (ret); } /** * xmlGetMinOccurs: * @ctxt: a schema validation context * @node: a subtree containing XML Schema information * * Get the minOccurs property * * Returns the default if not found, or the value */ static int xmlGetMinOccurs(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int min, int max, int def, const char *expected) { const xmlChar *val, *cur; int ret = 0; xmlAttrPtr attr; attr = xmlSchemaGetPropNode(node, "minOccurs"); if (attr == NULL) return (def); val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (val == NULL) return (def); cur = val; while (IS_BLANK_CH(*cur)) cur++; if (*cur == 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, /* XML_SCHEMAP_INVALID_MINOCCURS, */ NULL, (xmlNodePtr) attr, NULL, expected, val, NULL, NULL, NULL); return (def); } while ((*cur >= '0') && (*cur <= '9')) { if (ret > INT_MAX / 10) { ret = INT_MAX; } else { int digit = *cur - '0'; ret *= 10; if (ret > INT_MAX - digit) ret = INT_MAX; else ret += digit; } cur++; } while (IS_BLANK_CH(*cur)) cur++; /* * TODO: Restrict the maximal value to Integer. */ if ((*cur != 0) || (ret < min) || ((max != -1) && (ret > max))) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, /* XML_SCHEMAP_INVALID_MINOCCURS, */ NULL, (xmlNodePtr) attr, NULL, expected, val, NULL, NULL, NULL); return (def); } return (ret); } /** * xmlSchemaPGetBoolNodeValue: * @ctxt: a schema validation context * @ownerItem: the owner as a schema item * @node: the node holding the value * * Converts a boolean string value into 1 or 0. * * Returns 0 or 1. */ static int xmlSchemaPGetBoolNodeValue(xmlSchemaParserCtxtPtr ctxt, xmlSchemaBasicItemPtr ownerItem, xmlNodePtr node) { xmlChar *value = NULL; int res = 0; value = xmlNodeGetContent(node); /* * 3.2.2.1 Lexical representation * An instance of a datatype that is defined as `boolean` * can have the following legal literals {true, false, 1, 0}. */ if (xmlStrEqual(BAD_CAST value, BAD_CAST "true")) res = 1; else if (xmlStrEqual(BAD_CAST value, BAD_CAST "false")) res = 0; else if (xmlStrEqual(BAD_CAST value, BAD_CAST "1")) res = 1; else if (xmlStrEqual(BAD_CAST value, BAD_CAST "0")) res = 0; else { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_INVALID_BOOLEAN, ownerItem, node, xmlSchemaGetBuiltInType(XML_SCHEMAS_BOOLEAN), NULL, BAD_CAST value, NULL, NULL, NULL); } if (value != NULL) xmlFree(value); return (res); } /** * xmlGetBooleanProp: * @ctxt: a schema validation context * @node: a subtree containing XML Schema information * @name: the attribute name * @def: the default value * * Evaluate if a boolean property is set * * Returns the default if not found, 0 if found to be false, * 1 if found to be true */ static int xmlGetBooleanProp(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, const char *name, int def) { const xmlChar *val; val = xmlSchemaGetProp(ctxt, node, name); if (val == NULL) return (def); /* * 3.2.2.1 Lexical representation * An instance of a datatype that is defined as `boolean` * can have the following legal literals {true, false, 1, 0}. */ if (xmlStrEqual(val, BAD_CAST "true")) def = 1; else if (xmlStrEqual(val, BAD_CAST "false")) def = 0; else if (xmlStrEqual(val, BAD_CAST "1")) def = 1; else if (xmlStrEqual(val, BAD_CAST "0")) def = 0; else { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_INVALID_BOOLEAN, NULL, (xmlNodePtr) xmlSchemaGetPropNode(node, name), xmlSchemaGetBuiltInType(XML_SCHEMAS_BOOLEAN), NULL, val, NULL, NULL, NULL); } return (def); } /************************************************************************ * * * Schema extraction from an Infoset * * * ************************************************************************/ static xmlSchemaTypePtr xmlSchemaParseSimpleType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, int topLevel); static xmlSchemaTypePtr xmlSchemaParseComplexType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, int topLevel); static xmlSchemaTypePtr xmlSchemaParseRestriction(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaTypeType parentType); static xmlSchemaBasicItemPtr xmlSchemaParseLocalAttribute(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaItemListPtr uses, int parentType); static xmlSchemaTypePtr xmlSchemaParseList(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node); static xmlSchemaWildcardPtr xmlSchemaParseAnyAttribute(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node); /** * xmlSchemaPValAttrNodeValue: * * @pctxt: a schema parser context * @ownerItem: the schema object owner if existent * @attr: the schema attribute node being validated * @value: the value * @type: the built-in type to be validated against * * Validates a value against the given built-in type. * This one is intended to be used internally for validation * of schema attribute values during parsing of the schema. * * Returns 0 if the value is valid, a positive error code * number otherwise and -1 in case of an internal or API error. */ static int xmlSchemaPValAttrNodeValue(xmlSchemaParserCtxtPtr pctxt, xmlSchemaBasicItemPtr ownerItem, xmlAttrPtr attr, const xmlChar *value, xmlSchemaTypePtr type) { int ret = 0; /* * NOTE: Should we move this to xmlschematypes.c? Hmm, but this * one is really meant to be used internally, so better not. */ if ((pctxt == NULL) || (type == NULL) || (attr == NULL)) return (-1); if (type->type != XML_SCHEMA_TYPE_BASIC) { PERROR_INT("xmlSchemaPValAttrNodeValue", "the given type is not a built-in type"); return (-1); } switch (type->builtInType) { case XML_SCHEMAS_NCNAME: case XML_SCHEMAS_QNAME: case XML_SCHEMAS_ANYURI: case XML_SCHEMAS_TOKEN: case XML_SCHEMAS_LANGUAGE: ret = xmlSchemaValPredefTypeNode(type, value, NULL, (xmlNodePtr) attr); break; default: { PERROR_INT("xmlSchemaPValAttrNodeValue", "validation using the given type is not supported while " "parsing a schema"); return (-1); } } /* * TODO: Should we use the S4S error codes instead? */ if (ret < 0) { PERROR_INT("xmlSchemaPValAttrNodeValue", "failed to validate a schema attribute value"); return (-1); } else if (ret > 0) { if (WXS_IS_LIST(type)) ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; else ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; xmlSchemaPSimpleTypeErr(pctxt, ret, ownerItem, (xmlNodePtr) attr, type, NULL, value, NULL, NULL, NULL); } return (ret); } /** * xmlSchemaPValAttrNode: * * @ctxt: a schema parser context * @ownerItem: the schema object owner if existent * @attr: the schema attribute node being validated * @type: the built-in type to be validated against * @value: the resulting value if any * * Extracts and validates a value against the given built-in type. * This one is intended to be used internally for validation * of schema attribute values during parsing of the schema. * * Returns 0 if the value is valid, a positive error code * number otherwise and -1 in case of an internal or API error. */ static int xmlSchemaPValAttrNode(xmlSchemaParserCtxtPtr ctxt, xmlSchemaBasicItemPtr ownerItem, xmlAttrPtr attr, xmlSchemaTypePtr type, const xmlChar **value) { const xmlChar *val; if ((ctxt == NULL) || (type == NULL) || (attr == NULL)) return (-1); val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (value != NULL) *value = val; return (xmlSchemaPValAttrNodeValue(ctxt, ownerItem, attr, val, type)); } /** * xmlSchemaPValAttr: * * @ctxt: a schema parser context * @node: the element node of the attribute * @ownerItem: the schema object owner if existent * @ownerElem: the owner element node * @name: the name of the schema attribute node * @type: the built-in type to be validated against * @value: the resulting value if any * * Extracts and validates a value against the given built-in type. * This one is intended to be used internally for validation * of schema attribute values during parsing of the schema. * * Returns 0 if the value is valid, a positive error code * number otherwise and -1 in case of an internal or API error. */ static int xmlSchemaPValAttr(xmlSchemaParserCtxtPtr ctxt, xmlSchemaBasicItemPtr ownerItem, xmlNodePtr ownerElem, const char *name, xmlSchemaTypePtr type, const xmlChar **value) { xmlAttrPtr attr; if ((ctxt == NULL) || (type == NULL)) { if (value != NULL) *value = NULL; return (-1); } if (type->type != XML_SCHEMA_TYPE_BASIC) { if (value != NULL) *value = NULL; xmlSchemaPErr(ctxt, ownerElem, XML_SCHEMAP_INTERNAL, "Internal error: xmlSchemaPValAttr, the given " "type '%s' is not a built-in type.\n", type->name, NULL); return (-1); } attr = xmlSchemaGetPropNode(ownerElem, name); if (attr == NULL) { if (value != NULL) *value = NULL; return (0); } return (xmlSchemaPValAttrNode(ctxt, ownerItem, attr, type, value)); } static int xmlSchemaCheckReference(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema ATTRIBUTE_UNUSED, xmlNodePtr node, xmlAttrPtr attr, const xmlChar *namespaceName) { /* TODO: Pointer comparison instead? */ if (xmlStrEqual(pctxt->targetNamespace, namespaceName)) return (0); if (xmlStrEqual(xmlSchemaNs, namespaceName)) return (0); /* * Check if the referenced namespace was ed. */ if (WXS_BUCKET(pctxt)->relations != NULL) { xmlSchemaSchemaRelationPtr rel; rel = WXS_BUCKET(pctxt)->relations; do { if (WXS_IS_BUCKET_IMPMAIN(rel->type) && xmlStrEqual(namespaceName, rel->importNamespace)) return (0); rel = rel->next; } while (rel != NULL); } /* * No matching ed namespace found. */ { xmlNodePtr n = (attr != NULL) ? (xmlNodePtr) attr : node; if (namespaceName == NULL) xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_SRC_RESOLVE, n, NULL, "References from this schema to components in no " "namespace are not allowed, since not indicated by an " "import statement", NULL, NULL); else xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_SRC_RESOLVE, n, NULL, "References from this schema to components in the " "namespace '%s' are not allowed, since not indicated by an " "import statement", namespaceName, NULL); } return (XML_SCHEMAP_SRC_RESOLVE); } /** * xmlSchemaParseLocalAttributes: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * @type: the hosting type where the attributes will be anchored * * Parses attribute uses and attribute declarations and * attribute group references. */ static int xmlSchemaParseLocalAttributes(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr *child, xmlSchemaItemListPtr *list, int parentType, int *hasRefs) { void *item; while ((IS_SCHEMA((*child), "attribute")) || (IS_SCHEMA((*child), "attributeGroup"))) { if (IS_SCHEMA((*child), "attribute")) { item = xmlSchemaParseLocalAttribute(ctxt, schema, *child, *list, parentType); } else { item = xmlSchemaParseAttributeGroupRef(ctxt, schema, *child); if ((item != NULL) && (hasRefs != NULL)) *hasRefs = 1; } if (item != NULL) { if (*list == NULL) { /* TODO: Customize grow factor. */ *list = xmlSchemaItemListCreate(); if (*list == NULL) return(-1); } if (xmlSchemaItemListAddSize(*list, 2, item) == -1) return(-1); } *child = (*child)->next; } return (0); } /** * xmlSchemaParseAnnotation: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Attribute declaration * *WARNING* this interface is highly subject to change * * Returns -1 in case of error, 0 if the declaration is improper and * 1 in case of success. */ static xmlSchemaAnnotPtr xmlSchemaParseAnnotation(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int needed) { xmlSchemaAnnotPtr ret; xmlNodePtr child = NULL; xmlAttrPtr attr; int barked = 0; /* * INFO: S4S completed. */ /* * id = ID * {any attributes with non-schema namespace . . .}> * Content: (appinfo | documentation)* */ if ((ctxt == NULL) || (node == NULL)) return (NULL); if (needed) ret = xmlSchemaNewAnnot(ctxt, node); else ret = NULL; attr = node->properties; while (attr != NULL) { if (((attr->ns == NULL) && (!xmlStrEqual(attr->name, BAD_CAST "id"))) || ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, xmlSchemaNs))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * And now for the children... */ child = node->children; while (child != NULL) { if (IS_SCHEMA(child, "appinfo")) { /* TODO: make available the content of "appinfo". */ /* * source = anyURI * {any attributes with non-schema namespace . . .}> * Content: ({any})* */ attr = child->properties; while (attr != NULL) { if (((attr->ns == NULL) && (!xmlStrEqual(attr->name, BAD_CAST "source"))) || ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, xmlSchemaNs))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttr(ctxt, NULL, child, "source", xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), NULL); child = child->next; } else if (IS_SCHEMA(child, "documentation")) { /* TODO: make available the content of "documentation". */ /* * source = anyURI * {any attributes with non-schema namespace . . .}> * Content: ({any})* */ attr = child->properties; while (attr != NULL) { if (attr->ns == NULL) { if (!xmlStrEqual(attr->name, BAD_CAST "source")) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else { if (xmlStrEqual(attr->ns->href, xmlSchemaNs) || (xmlStrEqual(attr->name, BAD_CAST "lang") && (!xmlStrEqual(attr->ns->href, XML_XML_NAMESPACE)))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } attr = attr->next; } /* * Attribute "xml:lang". */ attr = xmlSchemaGetPropNodeNs(child, (const char *) XML_XML_NAMESPACE, "lang"); if (attr != NULL) xmlSchemaPValAttrNode(ctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_LANGUAGE), NULL); child = child->next; } else { if (!barked) xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(appinfo | documentation)*"); barked = 1; child = child->next; } } return (ret); } /** * xmlSchemaParseFacet: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Facet declaration * *WARNING* this interface is highly subject to change * * Returns the new type structure or NULL in case of error */ static xmlSchemaFacetPtr xmlSchemaParseFacet(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaFacetPtr facet; xmlNodePtr child = NULL; const xmlChar *value; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); facet = xmlSchemaNewFacet(); if (facet == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } facet->node = node; value = xmlSchemaGetProp(ctxt, node, "value"); if (value == NULL) { xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_FACET_NO_VALUE, "Facet %s has no value\n", node->name, NULL); xmlSchemaFreeFacet(facet); return (NULL); } if (IS_SCHEMA(node, "minInclusive")) { facet->type = XML_SCHEMA_FACET_MININCLUSIVE; } else if (IS_SCHEMA(node, "minExclusive")) { facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE; } else if (IS_SCHEMA(node, "maxInclusive")) { facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE; } else if (IS_SCHEMA(node, "maxExclusive")) { facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE; } else if (IS_SCHEMA(node, "totalDigits")) { facet->type = XML_SCHEMA_FACET_TOTALDIGITS; } else if (IS_SCHEMA(node, "fractionDigits")) { facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS; } else if (IS_SCHEMA(node, "pattern")) { facet->type = XML_SCHEMA_FACET_PATTERN; } else if (IS_SCHEMA(node, "enumeration")) { facet->type = XML_SCHEMA_FACET_ENUMERATION; } else if (IS_SCHEMA(node, "whiteSpace")) { facet->type = XML_SCHEMA_FACET_WHITESPACE; } else if (IS_SCHEMA(node, "length")) { facet->type = XML_SCHEMA_FACET_LENGTH; } else if (IS_SCHEMA(node, "maxLength")) { facet->type = XML_SCHEMA_FACET_MAXLENGTH; } else if (IS_SCHEMA(node, "minLength")) { facet->type = XML_SCHEMA_FACET_MINLENGTH; } else { xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_UNKNOWN_FACET_TYPE, "Unknown facet type %s\n", node->name, NULL); xmlSchemaFreeFacet(facet); return (NULL); } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); facet->value = value; if ((facet->type != XML_SCHEMA_FACET_PATTERN) && (facet->type != XML_SCHEMA_FACET_ENUMERATION)) { const xmlChar *fixed; fixed = xmlSchemaGetProp(ctxt, node, "fixed"); if (fixed != NULL) { if (xmlStrEqual(fixed, BAD_CAST "true")) facet->fixed = 1; } } child = node->children; if (IS_SCHEMA(child, "annotation")) { facet->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (child != NULL) { xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_UNKNOWN_FACET_CHILD, "Facet %s has unexpected child content\n", node->name, NULL); } return (facet); } /** * xmlSchemaParseWildcardNs: * @ctxt: a schema parser context * @wildc: the wildcard, already created * @node: a subtree containing XML Schema information * * Parses the attribute "processContents" and "namespace" * of a xsd:anyAttribute and xsd:any. * *WARNING* this interface is highly subject to change * * Returns 0 if everything goes fine, a positive error code * if something is not valid and -1 if an internal error occurs. */ static int xmlSchemaParseWildcardNs(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema ATTRIBUTE_UNUSED, xmlSchemaWildcardPtr wildc, xmlNodePtr node) { const xmlChar *pc, *ns, *dictnsItem; int ret = 0; xmlChar *nsItem; xmlSchemaWildcardNsPtr tmp, lastNs = NULL; xmlAttrPtr attr; pc = xmlSchemaGetProp(ctxt, node, "processContents"); if ((pc == NULL) || (xmlStrEqual(pc, (const xmlChar *) "strict"))) { wildc->processContents = XML_SCHEMAS_ANY_STRICT; } else if (xmlStrEqual(pc, (const xmlChar *) "skip")) { wildc->processContents = XML_SCHEMAS_ANY_SKIP; } else if (xmlStrEqual(pc, (const xmlChar *) "lax")) { wildc->processContents = XML_SCHEMAS_ANY_LAX; } else { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, node, NULL, "(strict | skip | lax)", pc, NULL, NULL, NULL); wildc->processContents = XML_SCHEMAS_ANY_STRICT; ret = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; } /* * Build the namespace constraints. */ attr = xmlSchemaGetPropNode(node, "namespace"); ns = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (ns == NULL) return (-1); if ((attr == NULL) || (xmlStrEqual(ns, BAD_CAST "##any"))) wildc->any = 1; else if (xmlStrEqual(ns, BAD_CAST "##other")) { wildc->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); if (wildc->negNsSet == NULL) { return (-1); } wildc->negNsSet->value = ctxt->targetNamespace; } else { const xmlChar *end, *cur; cur = ns; do { while (IS_BLANK_CH(*cur)) cur++; end = cur; while ((*end != 0) && (!(IS_BLANK_CH(*end)))) end++; if (end == cur) break; nsItem = xmlStrndup(cur, end - cur); if ((xmlStrEqual(nsItem, BAD_CAST "##other")) || (xmlStrEqual(nsItem, BAD_CAST "##any"))) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_WILDCARD_INVALID_NS_MEMBER, NULL, (xmlNodePtr) attr, NULL, "((##any | ##other) | List of (xs:anyURI | " "(##targetNamespace | ##local)))", nsItem, NULL, NULL, NULL); ret = XML_SCHEMAP_WILDCARD_INVALID_NS_MEMBER; } else { if (xmlStrEqual(nsItem, BAD_CAST "##targetNamespace")) { dictnsItem = ctxt->targetNamespace; } else if (xmlStrEqual(nsItem, BAD_CAST "##local")) { dictnsItem = NULL; } else { /* * Validate the item (anyURI). */ xmlSchemaPValAttrNodeValue(ctxt, NULL, attr, nsItem, xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI)); dictnsItem = xmlDictLookup(ctxt->dict, nsItem, -1); } /* * Avoid duplicate namespaces. */ tmp = wildc->nsSet; while (tmp != NULL) { if (dictnsItem == tmp->value) break; tmp = tmp->next; } if (tmp == NULL) { tmp = xmlSchemaNewWildcardNsConstraint(ctxt); if (tmp == NULL) { xmlFree(nsItem); return (-1); } tmp->value = dictnsItem; tmp->next = NULL; if (wildc->nsSet == NULL) wildc->nsSet = tmp; else if (lastNs != NULL) lastNs->next = tmp; lastNs = tmp; } } xmlFree(nsItem); cur = end; } while (*cur != 0); } return (ret); } static int xmlSchemaPCheckParticleCorrect_2(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr item ATTRIBUTE_UNUSED, xmlNodePtr node, int minOccurs, int maxOccurs) { if ((maxOccurs == 0) && ( minOccurs == 0)) return (0); if (maxOccurs != UNBOUNDED) { /* * TODO: Maybe we should better not create the particle, * if min/max is invalid, since it could confuse the build of the * content model. */ /* * 3.9.6 Schema Component Constraint: Particle Correct * */ if (maxOccurs < 1) { /* * 2.2 {max occurs} must be greater than or equal to 1. */ xmlSchemaPCustomAttrErr(ctxt, XML_SCHEMAP_P_PROPS_CORRECT_2_2, NULL, NULL, xmlSchemaGetPropNode(node, "maxOccurs"), "The value must be greater than or equal to 1"); return (XML_SCHEMAP_P_PROPS_CORRECT_2_2); } else if (minOccurs > maxOccurs) { /* * 2.1 {min occurs} must not be greater than {max occurs}. */ xmlSchemaPCustomAttrErr(ctxt, XML_SCHEMAP_P_PROPS_CORRECT_2_1, NULL, NULL, xmlSchemaGetPropNode(node, "minOccurs"), "The value must not be greater than the value of 'maxOccurs'"); return (XML_SCHEMAP_P_PROPS_CORRECT_2_1); } } return (0); } /** * xmlSchemaParseAny: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * Parsea a XML schema element. A particle and wildcard * will be created (except if minOccurs==maxOccurs==0, in this case * nothing will be created). * *WARNING* this interface is highly subject to change * * Returns the particle or NULL in case of error or if minOccurs==maxOccurs==0 */ static xmlSchemaParticlePtr xmlSchemaParseAny(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaParticlePtr particle; xmlNodePtr child = NULL; xmlSchemaWildcardPtr wild; int min, max; xmlAttrPtr attr; xmlSchemaAnnotPtr annot = NULL; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "minOccurs")) && (!xmlStrEqual(attr->name, BAD_CAST "maxOccurs")) && (!xmlStrEqual(attr->name, BAD_CAST "namespace")) && (!xmlStrEqual(attr->name, BAD_CAST "processContents"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * minOccurs/maxOccurs. */ max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, "(xs:nonNegativeInteger | unbounded)"); min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, "xs:nonNegativeInteger"); xmlSchemaPCheckParticleCorrect_2(ctxt, NULL, node, min, max); /* * Create & parse the wildcard. */ wild = xmlSchemaAddWildcard(ctxt, schema, XML_SCHEMA_TYPE_ANY, node); if (wild == NULL) return (NULL); xmlSchemaParseWildcardNs(ctxt, schema, wild, node); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } /* * No component if minOccurs==maxOccurs==0. */ if ((min == 0) && (max == 0)) { /* Don't free the wildcard, since it's already on the list. */ return (NULL); } /* * Create the particle. */ particle = xmlSchemaAddParticle(ctxt, node, min, max); if (particle == NULL) return (NULL); particle->annot = annot; particle->children = (xmlSchemaTreeItemPtr) wild; return (particle); } /** * xmlSchemaParseNotation: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Notation declaration * * Returns the new structure or NULL in case of error */ static xmlSchemaNotationPtr xmlSchemaParseNotation(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { const xmlChar *name; xmlSchemaNotationPtr ret; xmlNodePtr child = NULL; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); name = xmlSchemaGetProp(ctxt, node, "name"); if (name == NULL) { xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_NOTATION_NO_NAME, "Notation has no name\n", NULL, NULL); return (NULL); } ret = xmlSchemaAddNotation(ctxt, schema, name, ctxt->targetNamespace, node); if (ret == NULL) return (NULL); xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); child = node->children; if (IS_SCHEMA(child, "annotation")) { ret->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } return (ret); } /** * xmlSchemaParseAnyAttribute: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema AnyAttribute declaration * *WARNING* this interface is highly subject to change * * Returns a wildcard or NULL. */ static xmlSchemaWildcardPtr xmlSchemaParseAnyAttribute(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaWildcardPtr ret; xmlNodePtr child = NULL; xmlAttrPtr attr; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); ret = xmlSchemaAddWildcard(ctxt, schema, XML_SCHEMA_TYPE_ANY_ATTRIBUTE, node); if (ret == NULL) { return (NULL); } /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "namespace")) && (!xmlStrEqual(attr->name, BAD_CAST "processContents"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * Parse the namespace list. */ if (xmlSchemaParseWildcardNs(ctxt, schema, ret, node) != 0) return (NULL); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { ret->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } return (ret); } /** * xmlSchemaParseAttribute: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Attribute declaration * *WARNING* this interface is highly subject to change * * Returns the attribute declaration. */ static xmlSchemaBasicItemPtr xmlSchemaParseLocalAttribute(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaItemListPtr uses, int parentType) { const xmlChar *attrValue, *name = NULL, *ns = NULL; xmlSchemaAttributeUsePtr use = NULL; xmlNodePtr child = NULL; xmlAttrPtr attr; const xmlChar *tmpNs = NULL, *tmpName = NULL, *defValue = NULL; int isRef = 0, occurs = XML_SCHEMAS_ATTR_USE_OPTIONAL; int nberrors, hasForm = 0, defValueType = 0; #define WXS_ATTR_DEF_VAL_DEFAULT 1 #define WXS_ATTR_DEF_VAL_FIXED 2 /* * 3.2.3 Constraints on XML Representations of Attribute Declarations */ if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); attr = xmlSchemaGetPropNode(node, "ref"); if (attr != NULL) { if (xmlSchemaPValAttrNodeQName(pctxt, schema, NULL, attr, &tmpNs, &tmpName) != 0) { return (NULL); } if (xmlSchemaCheckReference(pctxt, schema, node, attr, tmpNs) != 0) return(NULL); isRef = 1; } nberrors = pctxt->nberrors; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if (isRef) { if (xmlStrEqual(attr->name, BAD_CAST "id")) { xmlSchemaPValAttrNodeID(pctxt, attr); goto attr_next; } else if (xmlStrEqual(attr->name, BAD_CAST "ref")) { goto attr_next; } } else { if (xmlStrEqual(attr->name, BAD_CAST "name")) { goto attr_next; } else if (xmlStrEqual(attr->name, BAD_CAST "id")) { xmlSchemaPValAttrNodeID(pctxt, attr); goto attr_next; } else if (xmlStrEqual(attr->name, BAD_CAST "type")) { xmlSchemaPValAttrNodeQName(pctxt, schema, NULL, attr, &tmpNs, &tmpName); goto attr_next; } else if (xmlStrEqual(attr->name, BAD_CAST "form")) { /* * Evaluate the target namespace */ hasForm = 1; attrValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); if (xmlStrEqual(attrValue, BAD_CAST "qualified")) { ns = pctxt->targetNamespace; } else if (!xmlStrEqual(attrValue, BAD_CAST "unqualified")) { xmlSchemaPSimpleTypeErr(pctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(qualified | unqualified)", attrValue, NULL, NULL, NULL); } goto attr_next; } } if (xmlStrEqual(attr->name, BAD_CAST "use")) { attrValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); /* TODO: Maybe we need to normalize the value beforehand. */ if (xmlStrEqual(attrValue, BAD_CAST "optional")) occurs = XML_SCHEMAS_ATTR_USE_OPTIONAL; else if (xmlStrEqual(attrValue, BAD_CAST "prohibited")) occurs = XML_SCHEMAS_ATTR_USE_PROHIBITED; else if (xmlStrEqual(attrValue, BAD_CAST "required")) occurs = XML_SCHEMAS_ATTR_USE_REQUIRED; else { xmlSchemaPSimpleTypeErr(pctxt, XML_SCHEMAP_INVALID_ATTR_USE, NULL, (xmlNodePtr) attr, NULL, "(optional | prohibited | required)", attrValue, NULL, NULL, NULL); } goto attr_next; } else if (xmlStrEqual(attr->name, BAD_CAST "default")) { /* * 3.2.3 : 1 * default and fixed must not both be present. */ if (defValue) { xmlSchemaPMutualExclAttrErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_1, NULL, attr, "default", "fixed"); } else { defValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); defValueType = WXS_ATTR_DEF_VAL_DEFAULT; } goto attr_next; } else if (xmlStrEqual(attr->name, BAD_CAST "fixed")) { /* * 3.2.3 : 1 * default and fixed must not both be present. */ if (defValue) { xmlSchemaPMutualExclAttrErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_1, NULL, attr, "default", "fixed"); } else { defValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); defValueType = WXS_ATTR_DEF_VAL_FIXED; } goto attr_next; } } else if (! xmlStrEqual(attr->ns->href, xmlSchemaNs)) goto attr_next; xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); attr_next: attr = attr->next; } /* * 3.2.3 : 2 * If default and use are both present, use must have * the actual value optional. */ if ((defValueType == WXS_ATTR_DEF_VAL_DEFAULT) && (occurs != XML_SCHEMAS_ATTR_USE_OPTIONAL)) { xmlSchemaPSimpleTypeErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_2, NULL, node, NULL, "(optional | prohibited | required)", NULL, "The value of the attribute 'use' must be 'optional' " "if the attribute 'default' is present", NULL, NULL); } /* * We want correct attributes. */ if (nberrors != pctxt->nberrors) return(NULL); if (! isRef) { xmlSchemaAttributePtr attrDecl; /* TODO: move XML_SCHEMAS_QUALIF_ATTR to the parser. */ if ((! hasForm) && (schema->flags & XML_SCHEMAS_QUALIF_ATTR)) ns = pctxt->targetNamespace; /* * 3.2.6 Schema Component Constraint: xsi: Not Allowed * TODO: Move this to the component layer. */ if (xmlStrEqual(ns, xmlSchemaInstanceNs)) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_NO_XSI, node, NULL, "The target namespace must not match '%s'", xmlSchemaInstanceNs, NULL); } attr = xmlSchemaGetPropNode(node, "name"); if (attr == NULL) { xmlSchemaPMissingAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } if (xmlSchemaPValAttrNode(pctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { return (NULL); } /* * 3.2.6 Schema Component Constraint: xmlns Not Allowed * TODO: Move this to the component layer. */ if (xmlStrEqual(name, BAD_CAST "xmlns")) { xmlSchemaPSimpleTypeErr(pctxt, XML_SCHEMAP_NO_XMLNS, NULL, (xmlNodePtr) attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), NULL, NULL, "The value of the attribute must not match 'xmlns'", NULL, NULL); return (NULL); } if (occurs == XML_SCHEMAS_ATTR_USE_PROHIBITED) goto check_children; /* * Create the attribute use component. */ use = xmlSchemaAddAttributeUse(pctxt, node); if (use == NULL) return(NULL); use->occurs = occurs; /* * Create the attribute declaration. */ attrDecl = xmlSchemaAddAttribute(pctxt, schema, name, ns, node, 0); if (attrDecl == NULL) return (NULL); if (tmpName != NULL) { attrDecl->typeName = tmpName; attrDecl->typeNs = tmpNs; } use->attrDecl = attrDecl; /* * Value constraint. */ if (defValue != NULL) { attrDecl->defValue = defValue; if (defValueType == WXS_ATTR_DEF_VAL_FIXED) attrDecl->flags |= XML_SCHEMAS_ATTR_FIXED; } } else if (occurs != XML_SCHEMAS_ATTR_USE_PROHIBITED) { xmlSchemaQNameRefPtr ref; /* * Create the attribute use component. */ use = xmlSchemaAddAttributeUse(pctxt, node); if (use == NULL) return(NULL); /* * We need to resolve the reference at later stage. */ WXS_ADD_PENDING(pctxt, use); use->occurs = occurs; /* * Create a QName reference to the attribute declaration. */ ref = xmlSchemaNewQNameRef(pctxt, XML_SCHEMA_TYPE_ATTRIBUTE, tmpName, tmpNs); if (ref == NULL) return(NULL); /* * Assign the reference. This will be substituted for the * referenced attribute declaration when the QName is resolved. */ use->attrDecl = WXS_ATTR_CAST ref; /* * Value constraint. */ if (defValue != NULL) use->defValue = defValue; if (defValueType == WXS_ATTR_DEF_VAL_FIXED) use->flags |= XML_SCHEMA_ATTR_USE_FIXED; } check_children: /* * And now for the children... */ child = node->children; if (occurs == XML_SCHEMAS_ATTR_USE_PROHIBITED) { xmlSchemaAttributeUseProhibPtr prohib; if (IS_SCHEMA(child, "annotation")) { xmlSchemaParseAnnotation(pctxt, child, 0); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } /* * Check for pointlessness of attribute prohibitions. */ if (parentType == XML_SCHEMA_TYPE_ATTRIBUTEGROUP) { xmlSchemaCustomWarning(ACTXT_CAST pctxt, XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, node, NULL, "Skipping attribute use prohibition, since it is " "pointless inside an ", NULL, NULL, NULL); return(NULL); } else if (parentType == XML_SCHEMA_TYPE_EXTENSION) { xmlSchemaCustomWarning(ACTXT_CAST pctxt, XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, node, NULL, "Skipping attribute use prohibition, since it is " "pointless when extending a type", NULL, NULL, NULL); return(NULL); } if (! isRef) { tmpName = name; tmpNs = ns; } /* * Check for duplicate attribute prohibitions. */ if (uses) { int i; for (i = 0; i < uses->nbItems; i++) { use = uses->items[i]; if ((use->type == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB) && (tmpName == (WXS_ATTR_PROHIB_CAST use)->name) && (tmpNs == (WXS_ATTR_PROHIB_CAST use)->targetNamespace)) { xmlChar *str = NULL; xmlSchemaCustomWarning(ACTXT_CAST pctxt, XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, node, NULL, "Skipping duplicate attribute use prohibition '%s'", xmlSchemaFormatQName(&str, tmpNs, tmpName), NULL, NULL); FREE_AND_NULL(str) return(NULL); } } } /* * Create the attribute prohibition helper component. */ prohib = xmlSchemaAddAttributeUseProhib(pctxt); if (prohib == NULL) return(NULL); prohib->node = node; prohib->name = tmpName; prohib->targetNamespace = tmpNs; if (isRef) { /* * We need at least to resolve to the attribute declaration. */ WXS_ADD_PENDING(pctxt, prohib); } return(WXS_BASIC_CAST prohib); } else { if (IS_SCHEMA(child, "annotation")) { /* * TODO: Should this go into the attr decl? */ use->annot = xmlSchemaParseAnnotation(pctxt, child, 1); child = child->next; } if (isRef) { if (child != NULL) { if (IS_SCHEMA(child, "simpleType")) /* * 3.2.3 : 3.2 * If ref is present, then all of , * form and type must be absent. */ xmlSchemaPContentErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_3_2, NULL, node, child, NULL, "(annotation?)"); else xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } } else { if (IS_SCHEMA(child, "simpleType")) { if (WXS_ATTRUSE_DECL(use)->typeName != NULL) { /* * 3.2.3 : 4 * type and must not both be present. */ xmlSchemaPContentErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_4, NULL, node, child, "The attribute 'type' and the child " "are mutually exclusive", NULL); } else WXS_ATTRUSE_TYPEDEF(use) = xmlSchemaParseSimpleType(pctxt, schema, child, 0); child = child->next; } if (child != NULL) xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, simpleType?)"); } } return (WXS_BASIC_CAST use); } static xmlSchemaAttributePtr xmlSchemaParseGlobalAttribute(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node) { const xmlChar *attrValue; xmlSchemaAttributePtr ret; xmlNodePtr child = NULL; xmlAttrPtr attr; /* * Note that the w3c spec assumes the schema to be validated with schema * for schemas beforehand. * * 3.2.3 Constraints on XML Representations of Attribute Declarations */ if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); /* * 3.2.3 : 3.1 * One of ref or name must be present, but not both */ attr = xmlSchemaGetPropNode(node, "name"); if (attr == NULL) { xmlSchemaPMissingAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } if (xmlSchemaPValAttrNode(pctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &attrValue) != 0) { return (NULL); } /* * 3.2.6 Schema Component Constraint: xmlns Not Allowed * TODO: Move this to the component layer. */ if (xmlStrEqual(attrValue, BAD_CAST "xmlns")) { xmlSchemaPSimpleTypeErr(pctxt, XML_SCHEMAP_NO_XMLNS, NULL, (xmlNodePtr) attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), NULL, NULL, "The value of the attribute must not match 'xmlns'", NULL, NULL); return (NULL); } /* * 3.2.6 Schema Component Constraint: xsi: Not Allowed * TODO: Move this to the component layer. * Or better leave it here and add it to the component layer * if we have a schema construction API. */ if (xmlStrEqual(pctxt->targetNamespace, xmlSchemaInstanceNs)) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_NO_XSI, node, NULL, "The target namespace must not match '%s'", xmlSchemaInstanceNs, NULL); } ret = xmlSchemaAddAttribute(pctxt, schema, attrValue, pctxt->targetNamespace, node, 1); if (ret == NULL) return (NULL); ret->flags |= XML_SCHEMAS_ATTR_GLOBAL; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "default")) && (!xmlStrEqual(attr->name, BAD_CAST "fixed")) && (!xmlStrEqual(attr->name, BAD_CAST "name")) && (!xmlStrEqual(attr->name, BAD_CAST "type"))) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrQName(pctxt, schema, NULL, node, "type", &ret->typeNs, &ret->typeName); xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); /* * Attribute "fixed". */ ret->defValue = xmlSchemaGetProp(pctxt, node, "fixed"); if (ret->defValue != NULL) ret->flags |= XML_SCHEMAS_ATTR_FIXED; /* * Attribute "default". */ attr = xmlSchemaGetPropNode(node, "default"); if (attr != NULL) { /* * 3.2.3 : 1 * default and fixed must not both be present. */ if (ret->flags & XML_SCHEMAS_ATTR_FIXED) { xmlSchemaPMutualExclAttrErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_1, WXS_BASIC_CAST ret, attr, "default", "fixed"); } else ret->defValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); } /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { ret->annot = xmlSchemaParseAnnotation(pctxt, child, 1); child = child->next; } if (IS_SCHEMA(child, "simpleType")) { if (ret->typeName != NULL) { /* * 3.2.3 : 4 * type and must not both be present. */ xmlSchemaPContentErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_4, NULL, node, child, "The attribute 'type' and the child " "are mutually exclusive", NULL); } else ret->subtypes = xmlSchemaParseSimpleType(pctxt, schema, child, 0); child = child->next; } if (child != NULL) xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, simpleType?)"); return (ret); } /** * xmlSchemaParseAttributeGroupRef: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * Parse an attribute group definition reference. * Note that a reference to an attribute group does not * correspond to any component at all. * *WARNING* this interface is highly subject to change * * Returns the attribute group or NULL in case of error. */ static xmlSchemaQNameRefPtr xmlSchemaParseAttributeGroupRef(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaQNameRefPtr ret; xmlNodePtr child = NULL; xmlAttrPtr attr; const xmlChar *refNs = NULL, *ref = NULL; if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); attr = xmlSchemaGetPropNode(node, "ref"); if (attr == NULL) { xmlSchemaPMissingAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "ref", NULL); return (NULL); } xmlSchemaPValAttrNodeQName(pctxt, schema, NULL, attr, &refNs, &ref); if (xmlSchemaCheckReference(pctxt, schema, node, attr, refNs) != 0) return(NULL); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "ref")) && (!xmlStrEqual(attr->name, BAD_CAST "id"))) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* Attribute ID */ xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * TODO: We do not have a place to store the annotation, do we? */ xmlSchemaParseAnnotation(pctxt, child, 0); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } /* * Handle attribute group redefinitions. */ if (pctxt->isRedefine && pctxt->redef && (pctxt->redef->item->type == XML_SCHEMA_TYPE_ATTRIBUTEGROUP) && (ref == pctxt->redef->refName) && (refNs == pctxt->redef->refTargetNs)) { /* * SPEC src-redefine: * (7.1) "If it has an among its contents * the `actual value` of whose ref [attribute] is the same * as the `actual value` of its own name attribute plus * target namespace, then it must have exactly one such group." */ if (pctxt->redefCounter != 0) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_SRC_REDEFINE, node, NULL, "The redefining attribute group definition " "'%s' must not contain more than one " "reference to the redefined definition", xmlSchemaFormatQName(&str, refNs, ref), NULL); FREE_AND_NULL(str); return(NULL); } pctxt->redefCounter++; /* * URGENT TODO: How to ensure that the reference will not be * handled by the normal component resolution mechanism? */ ret = xmlSchemaNewQNameRef(pctxt, XML_SCHEMA_TYPE_ATTRIBUTEGROUP, ref, refNs); if (ret == NULL) return(NULL); ret->node = node; pctxt->redef->reference = WXS_BASIC_CAST ret; } else { /* * Create a QName-reference helper component. We will substitute this * component for the attribute uses of the referenced attribute group * definition. */ ret = xmlSchemaNewQNameRef(pctxt, XML_SCHEMA_TYPE_ATTRIBUTEGROUP, ref, refNs); if (ret == NULL) return(NULL); ret->node = node; /* Add to pending items, to be able to resolve the reference. */ WXS_ADD_PENDING(pctxt, ret); } return (ret); } /** * xmlSchemaParseAttributeGroupDefinition: * @pctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Attribute Group declaration * *WARNING* this interface is highly subject to change * * Returns the attribute group definition or NULL in case of error. */ static xmlSchemaAttributeGroupPtr xmlSchemaParseAttributeGroupDefinition(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node) { const xmlChar *name; xmlSchemaAttributeGroupPtr ret; xmlNodePtr child = NULL; xmlAttrPtr attr; int hasRefs = 0; if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); attr = xmlSchemaGetPropNode(node, "name"); if (attr == NULL) { xmlSchemaPMissingAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } /* * The name is crucial, exit if invalid. */ if (xmlSchemaPValAttrNode(pctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { return (NULL); } ret = xmlSchemaAddAttributeGroupDefinition(pctxt, schema, name, pctxt->targetNamespace, node); if (ret == NULL) return (NULL); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "name")) && (!xmlStrEqual(attr->name, BAD_CAST "id"))) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* Attribute ID */ xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { ret->annot = xmlSchemaParseAnnotation(pctxt, child, 1); child = child->next; } /* * Parse contained attribute decls/refs. */ if (xmlSchemaParseLocalAttributes(pctxt, schema, &child, (xmlSchemaItemListPtr *) &(ret->attrUses), XML_SCHEMA_TYPE_ATTRIBUTEGROUP, &hasRefs) == -1) return(NULL); if (hasRefs) ret->flags |= XML_SCHEMAS_ATTRGROUP_HAS_REFS; /* * Parse the attribute wildcard. */ if (IS_SCHEMA(child, "anyAttribute")) { ret->attributeWildcard = xmlSchemaParseAnyAttribute(pctxt, schema, child); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, ((attribute | attributeGroup)*, anyAttribute?))"); } return (ret); } /** * xmlSchemaPValAttrFormDefault: * @value: the value * @flags: the flags to be modified * @flagQualified: the specific flag for "qualified" * * Returns 0 if the value is valid, 1 otherwise. */ static int xmlSchemaPValAttrFormDefault(const xmlChar *value, int *flags, int flagQualified) { if (xmlStrEqual(value, BAD_CAST "qualified")) { if ((*flags & flagQualified) == 0) *flags |= flagQualified; } else if (!xmlStrEqual(value, BAD_CAST "unqualified")) return (1); return (0); } /** * xmlSchemaPValAttrBlockFinal: * @value: the value * @flags: the flags to be modified * @flagAll: the specific flag for "#all" * @flagExtension: the specific flag for "extension" * @flagRestriction: the specific flag for "restriction" * @flagSubstitution: the specific flag for "substitution" * @flagList: the specific flag for "list" * @flagUnion: the specific flag for "union" * * Validates the value of the attribute "final" and "block". The value * is converted into the specified flag values and returned in @flags. * * Returns 0 if the value is valid, 1 otherwise. */ static int xmlSchemaPValAttrBlockFinal(const xmlChar *value, int *flags, int flagAll, int flagExtension, int flagRestriction, int flagSubstitution, int flagList, int flagUnion) { int ret = 0; /* * TODO: This does not check for duplicate entries. */ if ((flags == NULL) || (value == NULL)) return (-1); if (value[0] == 0) return (0); if (xmlStrEqual(value, BAD_CAST "#all")) { if (flagAll != -1) *flags |= flagAll; else { if (flagExtension != -1) *flags |= flagExtension; if (flagRestriction != -1) *flags |= flagRestriction; if (flagSubstitution != -1) *flags |= flagSubstitution; if (flagList != -1) *flags |= flagList; if (flagUnion != -1) *flags |= flagUnion; } } else { const xmlChar *end, *cur = value; xmlChar *item; do { while (IS_BLANK_CH(*cur)) cur++; end = cur; while ((*end != 0) && (!(IS_BLANK_CH(*end)))) end++; if (end == cur) break; item = xmlStrndup(cur, end - cur); if (xmlStrEqual(item, BAD_CAST "extension")) { if (flagExtension != -1) { if ((*flags & flagExtension) == 0) *flags |= flagExtension; } else ret = 1; } else if (xmlStrEqual(item, BAD_CAST "restriction")) { if (flagRestriction != -1) { if ((*flags & flagRestriction) == 0) *flags |= flagRestriction; } else ret = 1; } else if (xmlStrEqual(item, BAD_CAST "substitution")) { if (flagSubstitution != -1) { if ((*flags & flagSubstitution) == 0) *flags |= flagSubstitution; } else ret = 1; } else if (xmlStrEqual(item, BAD_CAST "list")) { if (flagList != -1) { if ((*flags & flagList) == 0) *flags |= flagList; } else ret = 1; } else if (xmlStrEqual(item, BAD_CAST "union")) { if (flagUnion != -1) { if ((*flags & flagUnion) == 0) *flags |= flagUnion; } else ret = 1; } else ret = 1; if (item != NULL) xmlFree(item); cur = end; } while ((ret == 0) && (*cur != 0)); } return (ret); } static int xmlSchemaCheckCSelectorXPath(xmlSchemaParserCtxtPtr ctxt, xmlSchemaIDCPtr idc, xmlSchemaIDCSelectPtr selector, xmlAttrPtr attr, int isField) { xmlNodePtr node; /* * c-selector-xpath: * Schema Component Constraint: Selector Value OK * * TODO: 1 The {selector} must be a valid XPath expression, as defined * in [XPath]. */ if (selector == NULL) { xmlSchemaPErr(ctxt, idc->node, XML_SCHEMAP_INTERNAL, "Internal error: xmlSchemaCheckCSelectorXPath, " "the selector is not specified.\n", NULL, NULL); return (-1); } if (attr == NULL) node = idc->node; else node = (xmlNodePtr) attr; if (selector->xpath == NULL) { xmlSchemaPCustomErr(ctxt, /* TODO: Adjust error code. */ XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, node, "The XPath expression of the selector is not valid", NULL); return (XML_SCHEMAP_S4S_ATTR_INVALID_VALUE); } else { const xmlChar **nsArray = NULL; xmlNsPtr *nsList = NULL; /* * Compile the XPath expression. */ /* * TODO: We need the array of in-scope namespaces for compilation. * TODO: Call xmlPatterncompile with different options for selector/ * field. */ if (attr == NULL) nsList = NULL; else nsList = xmlGetNsList(attr->doc, attr->parent); /* * Build an array of prefixes and namespaces. */ if (nsList != NULL) { int i, count = 0; for (i = 0; nsList[i] != NULL; i++) count++; nsArray = (const xmlChar **) xmlMalloc( (count * 2 + 1) * sizeof(const xmlChar *)); if (nsArray == NULL) { xmlSchemaPErrMemory(ctxt); xmlFree(nsList); return (-1); } for (i = 0; i < count; i++) { nsArray[2 * i] = nsList[i]->href; nsArray[2 * i + 1] = nsList[i]->prefix; } nsArray[count * 2] = NULL; xmlFree(nsList); } /* * TODO: Differentiate between "selector" and "field". */ if (isField) selector->xpathComp = (void *) xmlPatterncompile(selector->xpath, NULL, XML_PATTERN_XSFIELD, nsArray); else selector->xpathComp = (void *) xmlPatterncompile(selector->xpath, NULL, XML_PATTERN_XSSEL, nsArray); if (nsArray != NULL) xmlFree((xmlChar **) nsArray); if (selector->xpathComp == NULL) { xmlSchemaPCustomErr(ctxt, /* TODO: Adjust error code? */ XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, node, "The XPath expression '%s' could not be " "compiled", selector->xpath); return (XML_SCHEMAP_S4S_ATTR_INVALID_VALUE); } } return (0); } #define ADD_ANNOTATION(annot) \ xmlSchemaAnnotPtr cur = item->annot; \ if (item->annot == NULL) { \ item->annot = annot; \ return (annot); \ } \ cur = item->annot; \ while (cur->next != NULL) { \ cur = cur->next; \ } \ cur->next = annot; /** * xmlSchemaAssignAnnotation: * @item: the schema component * @annot: the annotation * * Adds the annotation to the given schema component. * * Returns the given annotation. */ static xmlSchemaAnnotPtr xmlSchemaAddAnnotation(xmlSchemaAnnotItemPtr annItem, xmlSchemaAnnotPtr annot) { if ((annItem == NULL) || (annot == NULL)) return (NULL); switch (annItem->type) { case XML_SCHEMA_TYPE_ELEMENT: { xmlSchemaElementPtr item = (xmlSchemaElementPtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_ATTRIBUTE: { xmlSchemaAttributePtr item = (xmlSchemaAttributePtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: case XML_SCHEMA_TYPE_ANY: { xmlSchemaWildcardPtr item = (xmlSchemaWildcardPtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_PARTICLE: case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_KEYREF: case XML_SCHEMA_TYPE_IDC_UNIQUE: { xmlSchemaAnnotItemPtr item = (xmlSchemaAnnotItemPtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: { xmlSchemaAttributeGroupPtr item = (xmlSchemaAttributeGroupPtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_NOTATION: { xmlSchemaNotationPtr item = (xmlSchemaNotationPtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_FACET_MININCLUSIVE: case XML_SCHEMA_FACET_MINEXCLUSIVE: case XML_SCHEMA_FACET_MAXINCLUSIVE: case XML_SCHEMA_FACET_MAXEXCLUSIVE: case XML_SCHEMA_FACET_TOTALDIGITS: case XML_SCHEMA_FACET_FRACTIONDIGITS: case XML_SCHEMA_FACET_PATTERN: case XML_SCHEMA_FACET_ENUMERATION: case XML_SCHEMA_FACET_WHITESPACE: case XML_SCHEMA_FACET_LENGTH: case XML_SCHEMA_FACET_MAXLENGTH: case XML_SCHEMA_FACET_MINLENGTH: { xmlSchemaFacetPtr item = (xmlSchemaFacetPtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_SIMPLE: case XML_SCHEMA_TYPE_COMPLEX: { xmlSchemaTypePtr item = (xmlSchemaTypePtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_GROUP: { xmlSchemaModelGroupDefPtr item = (xmlSchemaModelGroupDefPtr) annItem; ADD_ANNOTATION(annot) } break; case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: case XML_SCHEMA_TYPE_ALL: { xmlSchemaModelGroupPtr item = (xmlSchemaModelGroupPtr) annItem; ADD_ANNOTATION(annot) } break; default: xmlSchemaPCustomErr(NULL, XML_SCHEMAP_INTERNAL, NULL, NULL, "Internal error: xmlSchemaAddAnnotation, " "The item is not a annotated schema component", NULL); break; } return (annot); } /** * xmlSchemaParseIDCSelectorAndField: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * Parses a XML Schema identity-constraint definition's * and elements. * * Returns the parsed identity-constraint definition. */ static xmlSchemaIDCSelectPtr xmlSchemaParseIDCSelectorAndField(xmlSchemaParserCtxtPtr ctxt, xmlSchemaIDCPtr idc, xmlNodePtr node, int isField) { xmlSchemaIDCSelectPtr item; xmlNodePtr child = NULL; xmlAttrPtr attr; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "xpath"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* * Create the item. */ item = (xmlSchemaIDCSelectPtr) xmlMalloc(sizeof(xmlSchemaIDCSelect)); if (item == NULL) { xmlSchemaPErrMemory(ctxt); return (NULL); } memset(item, 0, sizeof(xmlSchemaIDCSelect)); /* * Attribute "xpath" (mandatory). */ attr = xmlSchemaGetPropNode(node, "xpath"); if (attr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); } else { item->xpath = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); /* * URGENT TODO: "field"s have an other syntax than "selector"s. */ if (xmlSchemaCheckCSelectorXPath(ctxt, idc, item, attr, isField) == -1) { xmlSchemaPErr(ctxt, (xmlNodePtr) attr, XML_SCHEMAP_INTERNAL, "Internal error: xmlSchemaParseIDCSelectorAndField, " "validating the XPath expression of a IDC selector.\n", NULL, NULL); } } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * Add the annotation to the parent IDC. */ xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) idc, xmlSchemaParseAnnotation(ctxt, child, 1)); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } return (item); } /** * xmlSchemaParseIDC: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * Parses a XML Schema identity-constraint definition. * * Returns the parsed identity-constraint definition. */ static xmlSchemaIDCPtr xmlSchemaParseIDC(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaTypeType idcCategory, const xmlChar *targetNamespace) { xmlSchemaIDCPtr item = NULL; xmlNodePtr child = NULL; xmlAttrPtr attr; const xmlChar *name = NULL; xmlSchemaIDCSelectPtr field = NULL, lastField = NULL; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "name")) && ((idcCategory != XML_SCHEMA_TYPE_IDC_KEYREF) || (!xmlStrEqual(attr->name, BAD_CAST "refer")))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* * Attribute "name" (mandatory). */ attr = xmlSchemaGetPropNode(node, "name"); if (attr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } else if (xmlSchemaPValAttrNode(ctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { return (NULL); } /* Create the component. */ item = xmlSchemaAddIDC(ctxt, schema, name, targetNamespace, idcCategory, node); if (item == NULL) return(NULL); xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); if (idcCategory == XML_SCHEMA_TYPE_IDC_KEYREF) { /* * Attribute "refer" (mandatory). */ attr = xmlSchemaGetPropNode(node, "refer"); if (attr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "refer", NULL); } else { /* * Create a reference item. */ item->ref = xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_IDC_KEY, NULL, NULL); if (item->ref == NULL) return (NULL); xmlSchemaPValAttrNodeQName(ctxt, schema, NULL, attr, &(item->ref->targetNamespace), &(item->ref->name)); xmlSchemaCheckReference(ctxt, schema, node, attr, item->ref->targetNamespace); } } /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (child == NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_MISSING, NULL, node, child, "A child element is missing", "(annotation?, (selector, field+))"); } /* * Child element . */ if (IS_SCHEMA(child, "selector")) { item->selector = xmlSchemaParseIDCSelectorAndField(ctxt, item, child, 0); child = child->next; /* * Child elements . */ if (IS_SCHEMA(child, "field")) { do { field = xmlSchemaParseIDCSelectorAndField(ctxt, item, child, 1); if (field != NULL) { field->index = item->nbFields; item->nbFields++; if (lastField != NULL) lastField->next = field; else item->fields = field; lastField = field; } child = child->next; } while (IS_SCHEMA(child, "field")); } else { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (selector, field+))"); } } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (selector, field+))"); } return (item); } /** * xmlSchemaParseElement: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * @topLevel: indicates if this is global declaration * * Parses a XML schema element declaration. * *WARNING* this interface is highly subject to change * * Returns the element declaration or a particle; NULL in case * of an error or if the particle has minOccurs==maxOccurs==0. */ static xmlSchemaBasicItemPtr xmlSchemaParseElement(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, int *isElemRef, int topLevel) { xmlSchemaElementPtr decl = NULL; xmlSchemaParticlePtr particle = NULL; xmlSchemaAnnotPtr annot = NULL; xmlNodePtr child = NULL; xmlAttrPtr attr, nameAttr; int min, max, isRef = 0; xmlChar *des = NULL; /* 3.3.3 Constraints on XML Representations of Element Declarations */ /* TODO: Complete implementation of 3.3.6 */ if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); if (isElemRef != NULL) *isElemRef = 0; /* * If we get a "ref" attribute on a local we will assume it's * a reference - even if there's a "name" attribute; this seems to be more * robust. */ nameAttr = xmlSchemaGetPropNode(node, "name"); attr = xmlSchemaGetPropNode(node, "ref"); if ((topLevel) || (attr == NULL)) { if (nameAttr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } } else isRef = 1; xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); child = node->children; if (IS_SCHEMA(child, "annotation")) { annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } /* * Skip particle part if a global declaration. */ if (topLevel) goto declaration_part; /* * The particle part ================================================== */ min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, "xs:nonNegativeInteger"); max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, "(xs:nonNegativeInteger | unbounded)"); xmlSchemaPCheckParticleCorrect_2(ctxt, NULL, node, min, max); particle = xmlSchemaAddParticle(ctxt, node, min, max); if (particle == NULL) goto return_null; /* ret->flags |= XML_SCHEMAS_ELEM_REF; */ if (isRef) { const xmlChar *refNs = NULL, *ref = NULL; xmlSchemaQNameRefPtr refer = NULL; /* * The reference part ============================================= */ if (isElemRef != NULL) *isElemRef = 1; xmlSchemaPValAttrNodeQName(ctxt, schema, NULL, attr, &refNs, &ref); xmlSchemaCheckReference(ctxt, schema, node, attr, refNs); /* * SPEC (3.3.3 : 2.1) "One of ref or name must be present, but not both" */ if (nameAttr != NULL) { xmlSchemaPMutualExclAttrErr(ctxt, XML_SCHEMAP_SRC_ELEMENT_2_1, NULL, nameAttr, "ref", "name"); } /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if (xmlStrEqual(attr->name, BAD_CAST "ref") || xmlStrEqual(attr->name, BAD_CAST "name") || xmlStrEqual(attr->name, BAD_CAST "id") || xmlStrEqual(attr->name, BAD_CAST "maxOccurs") || xmlStrEqual(attr->name, BAD_CAST "minOccurs")) { attr = attr->next; continue; } else { /* SPEC (3.3.3 : 2.2) */ xmlSchemaPCustomAttrErr(ctxt, XML_SCHEMAP_SRC_ELEMENT_2_2, NULL, NULL, attr, "Only the attributes 'minOccurs', 'maxOccurs' and " "'id' are allowed in addition to 'ref'"); break; } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* * No children except expected. */ if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } if ((min == 0) && (max == 0)) goto return_null; /* * Create the reference item and attach it to the particle. */ refer = xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_ELEMENT, ref, refNs); if (refer == NULL) goto return_null; particle->children = (xmlSchemaTreeItemPtr) refer; particle->annot = annot; /* * Add the particle to pending components, since the reference * need to be resolved. */ WXS_ADD_PENDING(ctxt, particle); return ((xmlSchemaBasicItemPtr) particle); } /* * The declaration part =============================================== */ declaration_part: { const xmlChar *ns = NULL, *fixed, *name, *attrValue; xmlSchemaIDCPtr curIDC = NULL, lastIDC = NULL; if (xmlSchemaPValAttrNode(ctxt, NULL, nameAttr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) goto return_null; /* * Evaluate the target namespace. */ if (topLevel) { ns = ctxt->targetNamespace; } else { attr = xmlSchemaGetPropNode(node, "form"); if (attr != NULL) { attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (xmlStrEqual(attrValue, BAD_CAST "qualified")) { ns = ctxt->targetNamespace; } else if (!xmlStrEqual(attrValue, BAD_CAST "unqualified")) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(qualified | unqualified)", attrValue, NULL, NULL, NULL); } } else if (schema->flags & XML_SCHEMAS_QUALIF_ELEM) ns = ctxt->targetNamespace; } decl = xmlSchemaAddElement(ctxt, name, ns, node, topLevel); if (decl == NULL) { goto return_null; } /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "name")) && (!xmlStrEqual(attr->name, BAD_CAST "type")) && (!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "default")) && (!xmlStrEqual(attr->name, BAD_CAST "fixed")) && (!xmlStrEqual(attr->name, BAD_CAST "block")) && (!xmlStrEqual(attr->name, BAD_CAST "nillable"))) { if (topLevel == 0) { if ((!xmlStrEqual(attr->name, BAD_CAST "maxOccurs")) && (!xmlStrEqual(attr->name, BAD_CAST "minOccurs")) && (!xmlStrEqual(attr->name, BAD_CAST "form"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if ((!xmlStrEqual(attr->name, BAD_CAST "final")) && (!xmlStrEqual(attr->name, BAD_CAST "abstract")) && (!xmlStrEqual(attr->name, BAD_CAST "substitutionGroup"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* * Extract/validate attributes. */ if (topLevel) { /* * Process top attributes of global element declarations here. */ decl->flags |= XML_SCHEMAS_ELEM_GLOBAL; decl->flags |= XML_SCHEMAS_ELEM_TOPLEVEL; xmlSchemaPValAttrQName(ctxt, schema, NULL, node, "substitutionGroup", &(decl->substGroupNs), &(decl->substGroup)); if (xmlGetBooleanProp(ctxt, node, "abstract", 0)) decl->flags |= XML_SCHEMAS_ELEM_ABSTRACT; /* * Attribute "final". */ attr = xmlSchemaGetPropNode(node, "final"); if (attr == NULL) { if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_EXTENSION) decl->flags |= XML_SCHEMAS_ELEM_FINAL_EXTENSION; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) decl->flags |= XML_SCHEMAS_ELEM_FINAL_RESTRICTION; } else { attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (xmlSchemaPValAttrBlockFinal(attrValue, &(decl->flags), -1, XML_SCHEMAS_ELEM_FINAL_EXTENSION, XML_SCHEMAS_ELEM_FINAL_RESTRICTION, -1, -1, -1) != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(#all | List of (extension | restriction))", attrValue, NULL, NULL, NULL); } } } /* * Attribute "block". */ attr = xmlSchemaGetPropNode(node, "block"); if (attr == NULL) { /* * Apply default "block" values. */ if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION) decl->flags |= XML_SCHEMAS_ELEM_BLOCK_RESTRICTION; if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION) decl->flags |= XML_SCHEMAS_ELEM_BLOCK_EXTENSION; if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION) decl->flags |= XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION; } else { attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (xmlSchemaPValAttrBlockFinal(attrValue, &(decl->flags), -1, XML_SCHEMAS_ELEM_BLOCK_EXTENSION, XML_SCHEMAS_ELEM_BLOCK_RESTRICTION, XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION, -1, -1) != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(#all | List of (extension | " "restriction | substitution))", attrValue, NULL, NULL, NULL); } } if (xmlGetBooleanProp(ctxt, node, "nillable", 0)) decl->flags |= XML_SCHEMAS_ELEM_NILLABLE; attr = xmlSchemaGetPropNode(node, "type"); if (attr != NULL) { xmlSchemaPValAttrNodeQName(ctxt, schema, NULL, attr, &(decl->namedTypeNs), &(decl->namedType)); xmlSchemaCheckReference(ctxt, schema, node, attr, decl->namedTypeNs); } decl->value = xmlSchemaGetProp(ctxt, node, "default"); attr = xmlSchemaGetPropNode(node, "fixed"); if (attr != NULL) { fixed = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (decl->value != NULL) { /* * 3.3.3 : 1 * default and fixed must not both be present. */ xmlSchemaPMutualExclAttrErr(ctxt, XML_SCHEMAP_SRC_ELEMENT_1, NULL, attr, "default", "fixed"); } else { decl->flags |= XML_SCHEMAS_ELEM_FIXED; decl->value = fixed; } } /* * And now for the children... */ if (IS_SCHEMA(child, "complexType")) { /* * 3.3.3 : 3 * "type" and either or are mutually * exclusive */ if (decl->namedType != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_SRC_ELEMENT_3, NULL, node, child, "The attribute 'type' and the child are " "mutually exclusive", NULL); } else WXS_ELEM_TYPEDEF(decl) = xmlSchemaParseComplexType(ctxt, schema, child, 0); child = child->next; } else if (IS_SCHEMA(child, "simpleType")) { /* * 3.3.3 : 3 * "type" and either or are * mutually exclusive */ if (decl->namedType != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_SRC_ELEMENT_3, NULL, node, child, "The attribute 'type' and the child are " "mutually exclusive", NULL); } else WXS_ELEM_TYPEDEF(decl) = xmlSchemaParseSimpleType(ctxt, schema, child, 0); child = child->next; } while ((IS_SCHEMA(child, "unique")) || (IS_SCHEMA(child, "key")) || (IS_SCHEMA(child, "keyref"))) { if (IS_SCHEMA(child, "unique")) { curIDC = xmlSchemaParseIDC(ctxt, schema, child, XML_SCHEMA_TYPE_IDC_UNIQUE, decl->targetNamespace); } else if (IS_SCHEMA(child, "key")) { curIDC = xmlSchemaParseIDC(ctxt, schema, child, XML_SCHEMA_TYPE_IDC_KEY, decl->targetNamespace); } else if (IS_SCHEMA(child, "keyref")) { curIDC = xmlSchemaParseIDC(ctxt, schema, child, XML_SCHEMA_TYPE_IDC_KEYREF, decl->targetNamespace); } if (lastIDC != NULL) lastIDC->next = curIDC; else decl->idcs = (void *) curIDC; lastIDC = curIDC; child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, ((simpleType | complexType)?, " "(unique | key | keyref)*))"); } decl->annot = annot; } /* * NOTE: Element Declaration Representation OK 4. will be checked at a * different layer. */ FREE_AND_NULL(des) if (topLevel) return ((xmlSchemaBasicItemPtr) decl); else { particle->children = (xmlSchemaTreeItemPtr) decl; return ((xmlSchemaBasicItemPtr) particle); } return_null: FREE_AND_NULL(des); if (annot != NULL) { if (particle != NULL) particle->annot = NULL; if (decl != NULL) decl->annot = NULL; xmlSchemaFreeAnnot(annot); } return (NULL); } /** * xmlSchemaParseUnion: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Union definition * *WARNING* this interface is highly subject to change * * Returns -1 in case of internal error, 0 in case of success and a positive * error code otherwise. */ static int xmlSchemaParseUnion(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaTypePtr type; xmlNodePtr child = NULL; xmlAttrPtr attr; const xmlChar *cur = NULL; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (-1); /* Not a component, don't create it. */ type = ctxt->ctxtType; /* * Mark the simple type as being of variety "union". */ type->flags |= XML_SCHEMAS_TYPE_VARIETY_UNION; /* * SPEC (Base type) (2) "If the or alternative is chosen, * then the `simple ur-type definition`." */ type->baseType = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYSIMPLETYPE); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "memberTypes"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * Attribute "memberTypes". This is a list of QNames. * TODO: Check the value to contain anything. */ attr = xmlSchemaGetPropNode(node, "memberTypes"); if (attr != NULL) { const xmlChar *end; xmlChar *tmp; const xmlChar *localName, *nsName; xmlSchemaTypeLinkPtr link, lastLink = NULL; xmlSchemaQNameRefPtr ref; cur = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (cur == NULL) return (-1); type->base = cur; do { while (IS_BLANK_CH(*cur)) cur++; end = cur; while ((*end != 0) && (!(IS_BLANK_CH(*end)))) end++; if (end == cur) break; tmp = xmlStrndup(cur, end - cur); if (tmp == NULL) { xmlSchemaPErrMemory(ctxt); return (-1); } if (xmlSchemaPValAttrNodeQNameValue(ctxt, schema, NULL, attr, BAD_CAST tmp, &nsName, &localName) == 0) { /* * Create the member type link. */ link = (xmlSchemaTypeLinkPtr) xmlMalloc(sizeof(xmlSchemaTypeLink)); if (link == NULL) { xmlSchemaPErrMemory(ctxt); FREE_AND_NULL(tmp) return (-1); } link->type = NULL; link->next = NULL; if (lastLink == NULL) type->memberTypes = link; else lastLink->next = link; lastLink = link; /* * Create a reference item. */ ref = xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_SIMPLE, localName, nsName); if (ref == NULL) { FREE_AND_NULL(tmp) return (-1); } /* * Assign the reference to the link, it will be resolved * later during fixup of the union simple type. */ link->type = (xmlSchemaTypePtr) ref; } FREE_AND_NULL(tmp) cur = end; } while (*cur != 0); } /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * Add the annotation to the simple type ancestor. */ xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, xmlSchemaParseAnnotation(ctxt, child, 1)); child = child->next; } if (IS_SCHEMA(child, "simpleType")) { xmlSchemaTypePtr subtype, last = NULL; /* * Anchor the member types in the "subtypes" field of the * simple type. */ while (IS_SCHEMA(child, "simpleType")) { subtype = (xmlSchemaTypePtr) xmlSchemaParseSimpleType(ctxt, schema, child, 0); if (subtype != NULL) { if (last == NULL) { type->subtypes = subtype; last = subtype; } else { last->next = subtype; last = subtype; } last->next = NULL; } child = child->next; } } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, simpleType*)"); } if ((attr == NULL) && (type->subtypes == NULL)) { /* * src-union-memberTypes-or-simpleTypes * Either the memberTypes [attribute] of the element must * be non-empty or there must be at least one simpleType [child]. */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_UNION_MEMBERTYPES_OR_SIMPLETYPES, NULL, node, "Either the attribute 'memberTypes' or " "at least one child must be present", NULL); } return (0); } /** * xmlSchemaParseList: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema List definition * *WARNING* this interface is highly subject to change * * Returns -1 in case of error, 0 if the declaration is improper and * 1 in case of success. */ static xmlSchemaTypePtr xmlSchemaParseList(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaTypePtr type; xmlNodePtr child = NULL; xmlAttrPtr attr; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); /* Not a component, don't create it. */ type = ctxt->ctxtType; /* * Mark the type as being of variety "list". */ type->flags |= XML_SCHEMAS_TYPE_VARIETY_LIST; /* * SPEC (Base type) (2) "If the or alternative is chosen, * then the `simple ur-type definition`." */ type->baseType = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYSIMPLETYPE); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "itemType"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * Attribute "itemType". NOTE that we will use the "ref" and "refNs" * fields for holding the reference to the itemType. * * REVAMP TODO: Use the "base" and "baseNs" fields, since we will remove * the "ref" fields. */ xmlSchemaPValAttrQName(ctxt, schema, NULL, node, "itemType", &(type->baseNs), &(type->base)); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, xmlSchemaParseAnnotation(ctxt, child, 1)); child = child->next; } if (IS_SCHEMA(child, "simpleType")) { /* * src-list-itemType-or-simpleType * Either the itemType [attribute] or the [child] of * the element must be present, but not both. */ if (type->base != NULL) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_SIMPLE_TYPE_1, NULL, node, "The attribute 'itemType' and the child " "are mutually exclusive", NULL); } else { type->subtypes = xmlSchemaParseSimpleType(ctxt, schema, child, 0); } child = child->next; } else if (type->base == NULL) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_SIMPLE_TYPE_1, NULL, node, "Either the attribute 'itemType' or the child " "must be present", NULL); } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, simpleType?)"); } if ((type->base == NULL) && (type->subtypes == NULL) && (xmlSchemaGetPropNode(node, "itemType") == NULL)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_SIMPLE_TYPE_1, NULL, node, "Either the attribute 'itemType' or the child " "must be present", NULL); } return (NULL); } /** * xmlSchemaParseSimpleType: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Simple Type definition * *WARNING* this interface is highly subject to change * * Returns -1 in case of error, 0 if the declaration is improper and * 1 in case of success. */ static xmlSchemaTypePtr xmlSchemaParseSimpleType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, int topLevel) { xmlSchemaTypePtr type, oldCtxtType; xmlNodePtr child = NULL; const xmlChar *attrValue = NULL; xmlAttrPtr attr; int hasRestriction = 0; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); if (topLevel) { attr = xmlSchemaGetPropNode(node, "name"); if (attr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } else { if (xmlSchemaPValAttrNode(ctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &attrValue) != 0) return (NULL); /* * Skip built-in types. */ if (ctxt->isS4S) { xmlSchemaTypePtr biType; if (ctxt->isRedefine) { /* * REDEFINE: Disallow redefinition of built-in-types. * TODO: It seems that the spec does not say anything * about this case. */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_REDEFINE, NULL, node, "Redefinition of built-in simple types is not " "supported", NULL); return(NULL); } biType = xmlSchemaGetPredefinedType(attrValue, xmlSchemaNs); if (biType != NULL) return (biType); } } } /* * TargetNamespace: * SPEC "The `actual value` of the targetNamespace [attribute] * of the ancestor element information item if present, * otherwise `absent`. */ if (topLevel == 0) { #ifdef ENABLE_NAMED_LOCALS char buf[40]; #endif /* * Parse as local simple type definition. */ #ifdef ENABLE_NAMED_LOCALS snprintf(buf, 39, "#ST%d", ctxt->counter++ + 1); type = xmlSchemaAddType(ctxt, schema, XML_SCHEMA_TYPE_SIMPLE, xmlDictLookup(ctxt->dict, (const xmlChar *)buf, -1), ctxt->targetNamespace, node, 0); #else type = xmlSchemaAddType(ctxt, schema, XML_SCHEMA_TYPE_SIMPLE, NULL, ctxt->targetNamespace, node, 0); #endif if (type == NULL) return (NULL); type->type = XML_SCHEMA_TYPE_SIMPLE; type->contentType = XML_SCHEMA_CONTENT_SIMPLE; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if (!xmlStrEqual(attr->name, BAD_CAST "id")) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } } else { /* * Parse as global simple type definition. * * Note that attrValue is the value of the attribute "name" here. */ type = xmlSchemaAddType(ctxt, schema, XML_SCHEMA_TYPE_SIMPLE, attrValue, ctxt->targetNamespace, node, 1); if (type == NULL) return (NULL); type->type = XML_SCHEMA_TYPE_SIMPLE; type->contentType = XML_SCHEMA_CONTENT_SIMPLE; type->flags |= XML_SCHEMAS_TYPE_GLOBAL; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "name")) && (!xmlStrEqual(attr->name, BAD_CAST "final"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* * Attribute "final". */ attr = xmlSchemaGetPropNode(node, "final"); if (attr == NULL) { if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) type->flags |= XML_SCHEMAS_TYPE_FINAL_RESTRICTION; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_LIST) type->flags |= XML_SCHEMAS_TYPE_FINAL_LIST; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_UNION) type->flags |= XML_SCHEMAS_TYPE_FINAL_UNION; } else { attrValue = xmlSchemaGetProp(ctxt, node, "final"); if (xmlSchemaPValAttrBlockFinal(attrValue, &(type->flags), -1, -1, XML_SCHEMAS_TYPE_FINAL_RESTRICTION, -1, XML_SCHEMAS_TYPE_FINAL_LIST, XML_SCHEMAS_TYPE_FINAL_UNION) != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, WXS_BASIC_CAST type, (xmlNodePtr) attr, NULL, "(#all | List of (list | union | restriction)", attrValue, NULL, NULL, NULL); } } } type->targetNamespace = ctxt->targetNamespace; xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * And now for the children... */ oldCtxtType = ctxt->ctxtType; ctxt->ctxtType = type; child = node->children; if (IS_SCHEMA(child, "annotation")) { type->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (child == NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_MISSING, NULL, node, child, NULL, "(annotation?, (restriction | list | union))"); } else if (IS_SCHEMA(child, "restriction")) { xmlSchemaParseRestriction(ctxt, schema, child, XML_SCHEMA_TYPE_SIMPLE); hasRestriction = 1; child = child->next; } else if (IS_SCHEMA(child, "list")) { xmlSchemaParseList(ctxt, schema, child); child = child->next; } else if (IS_SCHEMA(child, "union")) { xmlSchemaParseUnion(ctxt, schema, child); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (restriction | list | union))"); } /* * REDEFINE: SPEC src-redefine (5) * "Within the [children], each must have a * among its [children] ... the `actual value` of whose * base [attribute] must be the same as the `actual value` of its own * name attribute plus target namespace;" */ if (topLevel && ctxt->isRedefine && (! hasRestriction)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_REDEFINE, NULL, node, "This is a redefinition, thus the " " must have a child", NULL); } ctxt->ctxtType = oldCtxtType; return (type); } /** * xmlSchemaParseModelGroupDefRef: * @ctxt: the parser context * @schema: the schema being built * @node: the node * * Parses a reference to a model group definition. * * We will return a particle component with a qname-component or * NULL in case of an error. */ static xmlSchemaTreeItemPtr xmlSchemaParseModelGroupDefRef(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaParticlePtr item; xmlNodePtr child = NULL; xmlAttrPtr attr; const xmlChar *ref = NULL, *refNs = NULL; int min, max; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); attr = xmlSchemaGetPropNode(node, "ref"); if (attr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "ref", NULL); return (NULL); } else if (xmlSchemaPValAttrNodeQName(ctxt, schema, NULL, attr, &refNs, &ref) != 0) { return (NULL); } xmlSchemaCheckReference(ctxt, schema, node, attr, refNs); min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, "xs:nonNegativeInteger"); max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, "(xs:nonNegativeInteger | unbounded)"); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "ref")) && (!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "minOccurs")) && (!xmlStrEqual(attr->name, BAD_CAST "maxOccurs"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); item = xmlSchemaAddParticle(ctxt, node, min, max); if (item == NULL) return (NULL); /* * Create a qname-reference and set as the term; it will be substituted * for the model group after the reference has been resolved. */ item->children = (xmlSchemaTreeItemPtr) xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_GROUP, ref, refNs); xmlSchemaPCheckParticleCorrect_2(ctxt, item, node, min, max); /* * And now for the children... */ child = node->children; /* TODO: Is annotation even allowed for a model group reference? */ if (IS_SCHEMA(child, "annotation")) { /* * TODO: What to do exactly with the annotation? */ item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } /* * Corresponds to no component at all if minOccurs==maxOccurs==0. */ if ((min == 0) && (max == 0)) return (NULL); return ((xmlSchemaTreeItemPtr) item); } /** * xmlSchemaParseModelGroupDefinition: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * Parses a XML schema model group definition. * * Note that the constraint src-redefine (6.2) can't be applied until * references have been resolved. So we will do this at the * component fixup level. * * *WARNING* this interface is highly subject to change * * Returns -1 in case of error, 0 if the declaration is improper and * 1 in case of success. */ static xmlSchemaModelGroupDefPtr xmlSchemaParseModelGroupDefinition(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlSchemaModelGroupDefPtr item; xmlNodePtr child = NULL; xmlAttrPtr attr; const xmlChar *name; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); attr = xmlSchemaGetPropNode(node, "name"); if (attr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } else if (xmlSchemaPValAttrNode(ctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { return (NULL); } item = xmlSchemaAddModelGroupDefinition(ctxt, schema, name, ctxt->targetNamespace, node); if (item == NULL) return (NULL); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "name")) && (!xmlStrEqual(attr->name, BAD_CAST "id"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (IS_SCHEMA(child, "all")) { item->children = xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_ALL, 0); child = child->next; } else if (IS_SCHEMA(child, "choice")) { item->children = xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_CHOICE, 0); child = child->next; } else if (IS_SCHEMA(child, "sequence")) { item->children = xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_SEQUENCE, 0); child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (all | choice | sequence)?)"); } return (item); } /** * xmlSchemaCleanupDoc: * @ctxt: a schema validation context * @node: the root of the document. * * removes unwanted nodes in a schemas document tree */ static void xmlSchemaCleanupDoc(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr root) { xmlNodePtr delete, cur; if ((ctxt == NULL) || (root == NULL)) return; /* * Remove all the blank text nodes */ delete = NULL; cur = root; while (cur != NULL) { if (delete != NULL) { xmlUnlinkNode(delete); xmlFreeNode(delete); delete = NULL; } if (cur->type == XML_TEXT_NODE) { if (IS_BLANK_NODE(cur)) { if (xmlNodeGetSpacePreserve(cur) != 1) { delete = cur; } } } else if ((cur->type != XML_ELEMENT_NODE) && (cur->type != XML_CDATA_SECTION_NODE)) { delete = cur; goto skip_children; } /* * Skip to next node */ if (cur->children != NULL) { if ((cur->children->type != XML_ENTITY_DECL) && (cur->children->type != XML_ENTITY_REF_NODE) && (cur->children->type != XML_ENTITY_NODE)) { cur = cur->children; continue; } } skip_children: if (cur->next != NULL) { cur = cur->next; continue; } do { cur = cur->parent; if (cur == NULL) break; if (cur == root) { cur = NULL; break; } if (cur->next != NULL) { cur = cur->next; break; } } while (cur != NULL); } if (delete != NULL) { xmlUnlinkNode(delete); xmlFreeNode(delete); delete = NULL; } } static void xmlSchemaClearSchemaDefaults(xmlSchemaPtr schema) { if (schema->flags & XML_SCHEMAS_QUALIF_ELEM) schema->flags ^= XML_SCHEMAS_QUALIF_ELEM; if (schema->flags & XML_SCHEMAS_QUALIF_ATTR) schema->flags ^= XML_SCHEMAS_QUALIF_ATTR; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_EXTENSION) schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_EXTENSION; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_LIST) schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_LIST; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_UNION) schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_UNION; if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION) schema->flags ^= XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION; if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION) schema->flags ^= XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION; if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION) schema->flags ^= XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION; } static int xmlSchemaParseSchemaElement(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlAttrPtr attr; const xmlChar *val; int res = 0, oldErrs = ctxt->nberrors; /* * Those flags should be moved to the parser context flags, * since they are not visible at the component level. I.e. * they are used if processing schema *documents* only. */ res = xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); HFAILURE; /* * Since the version is of type xs:token, we won't bother to * check it. */ /* REMOVED: attr = xmlSchemaGetPropNode(node, "version"); if (attr != NULL) { res = xmlSchemaPValAttrNode(ctxt, NULL, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_TOKEN), &val); HFAILURE; } */ attr = xmlSchemaGetPropNode(node, "targetNamespace"); if (attr != NULL) { res = xmlSchemaPValAttrNode(ctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), NULL); HFAILURE; if (res != 0) { ctxt->stop = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; goto exit; } } attr = xmlSchemaGetPropNode(node, "elementFormDefault"); if (attr != NULL) { val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); res = xmlSchemaPValAttrFormDefault(val, &schema->flags, XML_SCHEMAS_QUALIF_ELEM); HFAILURE; if (res != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_ELEMFORMDEFAULT_VALUE, NULL, (xmlNodePtr) attr, NULL, "(qualified | unqualified)", val, NULL, NULL, NULL); } } attr = xmlSchemaGetPropNode(node, "attributeFormDefault"); if (attr != NULL) { val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); res = xmlSchemaPValAttrFormDefault(val, &schema->flags, XML_SCHEMAS_QUALIF_ATTR); HFAILURE; if (res != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_ATTRFORMDEFAULT_VALUE, NULL, (xmlNodePtr) attr, NULL, "(qualified | unqualified)", val, NULL, NULL, NULL); } } attr = xmlSchemaGetPropNode(node, "finalDefault"); if (attr != NULL) { val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); res = xmlSchemaPValAttrBlockFinal(val, &(schema->flags), -1, XML_SCHEMAS_FINAL_DEFAULT_EXTENSION, XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION, -1, XML_SCHEMAS_FINAL_DEFAULT_LIST, XML_SCHEMAS_FINAL_DEFAULT_UNION); HFAILURE; if (res != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(#all | List of (extension | restriction | list | union))", val, NULL, NULL, NULL); } } attr = xmlSchemaGetPropNode(node, "blockDefault"); if (attr != NULL) { val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); res = xmlSchemaPValAttrBlockFinal(val, &(schema->flags), -1, XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION, XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION, XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION, -1, -1); HFAILURE; if (res != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(#all | List of (extension | restriction | substitution))", val, NULL, NULL, NULL); } } exit: if (oldErrs != ctxt->nberrors) res = ctxt->err; return(res); exit_failure: return(-1); } /** * xmlSchemaParseSchemaTopLevel: * @ctxt: a schema validation context * @schema: the schemas * @nodes: the list of top level nodes * * Returns the internal XML Schema structure built from the resource or * NULL in case of error */ static int xmlSchemaParseSchemaTopLevel(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr nodes) { xmlNodePtr child; xmlSchemaAnnotPtr annot; int res = 0, oldErrs, tmpOldErrs; if ((ctxt == NULL) || (schema == NULL) || (nodes == NULL)) return(-1); oldErrs = ctxt->nberrors; child = nodes; while ((IS_SCHEMA(child, "include")) || (IS_SCHEMA(child, "import")) || (IS_SCHEMA(child, "redefine")) || (IS_SCHEMA(child, "annotation"))) { if (IS_SCHEMA(child, "annotation")) { annot = xmlSchemaParseAnnotation(ctxt, child, 1); if (schema->annot == NULL) schema->annot = annot; else xmlSchemaFreeAnnot(annot); } else if (IS_SCHEMA(child, "import")) { tmpOldErrs = ctxt->nberrors; res = xmlSchemaParseImport(ctxt, schema, child); HFAILURE; HSTOP(ctxt); if (tmpOldErrs != ctxt->nberrors) goto exit; } else if (IS_SCHEMA(child, "include")) { tmpOldErrs = ctxt->nberrors; res = xmlSchemaParseInclude(ctxt, schema, child); HFAILURE; HSTOP(ctxt); if (tmpOldErrs != ctxt->nberrors) goto exit; } else if (IS_SCHEMA(child, "redefine")) { tmpOldErrs = ctxt->nberrors; res = xmlSchemaParseRedefine(ctxt, schema, child); HFAILURE; HSTOP(ctxt); if (tmpOldErrs != ctxt->nberrors) goto exit; } child = child->next; } /* * URGENT TODO: Change the functions to return int results. * We need especially to catch internal errors. */ while (child != NULL) { if (IS_SCHEMA(child, "complexType")) { xmlSchemaParseComplexType(ctxt, schema, child, 1); child = child->next; } else if (IS_SCHEMA(child, "simpleType")) { xmlSchemaParseSimpleType(ctxt, schema, child, 1); child = child->next; } else if (IS_SCHEMA(child, "element")) { xmlSchemaParseElement(ctxt, schema, child, NULL, 1); child = child->next; } else if (IS_SCHEMA(child, "attribute")) { xmlSchemaParseGlobalAttribute(ctxt, schema, child); child = child->next; } else if (IS_SCHEMA(child, "attributeGroup")) { xmlSchemaParseAttributeGroupDefinition(ctxt, schema, child); child = child->next; } else if (IS_SCHEMA(child, "group")) { xmlSchemaParseModelGroupDefinition(ctxt, schema, child); child = child->next; } else if (IS_SCHEMA(child, "notation")) { xmlSchemaParseNotation(ctxt, schema, child); child = child->next; } else { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, child->parent, child, NULL, "((include | import | redefine | annotation)*, " "(((simpleType | complexType | group | attributeGroup) " "| element | attribute | notation), annotation*)*)"); child = child->next; } while (IS_SCHEMA(child, "annotation")) { /* * TODO: We should add all annotations. */ annot = xmlSchemaParseAnnotation(ctxt, child, 1); if (schema->annot == NULL) schema->annot = annot; else xmlSchemaFreeAnnot(annot); child = child->next; } } exit: ctxt->ctxtType = NULL; if (oldErrs != ctxt->nberrors) res = ctxt->err; return(res); exit_failure: return(-1); } static xmlSchemaSchemaRelationPtr xmlSchemaSchemaRelationCreate(void) { xmlSchemaSchemaRelationPtr ret; ret = (xmlSchemaSchemaRelationPtr) xmlMalloc(sizeof(xmlSchemaSchemaRelation)); if (ret == NULL) { xmlSchemaPErrMemory(NULL); return(NULL); } memset(ret, 0, sizeof(xmlSchemaSchemaRelation)); return(ret); } #if 0 static void xmlSchemaSchemaRelationFree(xmlSchemaSchemaRelationPtr rel) { xmlFree(rel); } #endif static void xmlSchemaRedefListFree(xmlSchemaRedefPtr redef) { xmlSchemaRedefPtr prev; while (redef != NULL) { prev = redef; redef = redef->next; xmlFree(prev); } } static void xmlSchemaConstructionCtxtFree(xmlSchemaConstructionCtxtPtr con) { /* * After the construction context has been freed, there will be * no schema graph available any more. Only the schema buckets * will stay alive, which are put into the "schemasImports" and * "includes" slots of the xmlSchema. */ if (con->buckets != NULL) xmlSchemaItemListFree(con->buckets); if (con->pending != NULL) xmlSchemaItemListFree(con->pending); if (con->substGroups != NULL) xmlHashFree(con->substGroups, xmlSchemaSubstGroupFreeEntry); if (con->redefs != NULL) xmlSchemaRedefListFree(con->redefs); if (con->dict != NULL) xmlDictFree(con->dict); xmlFree(con); } static xmlSchemaConstructionCtxtPtr xmlSchemaConstructionCtxtCreate(xmlDictPtr dict) { xmlSchemaConstructionCtxtPtr ret; ret = (xmlSchemaConstructionCtxtPtr) xmlMalloc(sizeof(xmlSchemaConstructionCtxt)); if (ret == NULL) { xmlSchemaPErrMemory(NULL); return (NULL); } memset(ret, 0, sizeof(xmlSchemaConstructionCtxt)); ret->buckets = xmlSchemaItemListCreate(); if (ret->buckets == NULL) { xmlSchemaPErrMemory(NULL); xmlFree(ret); return (NULL); } ret->pending = xmlSchemaItemListCreate(); if (ret->pending == NULL) { xmlSchemaPErrMemory(NULL); xmlSchemaConstructionCtxtFree(ret); return (NULL); } ret->dict = dict; xmlDictReference(dict); return(ret); } static xmlSchemaParserCtxtPtr xmlSchemaParserCtxtCreate(void) { xmlSchemaParserCtxtPtr ret; ret = (xmlSchemaParserCtxtPtr) xmlMalloc(sizeof(xmlSchemaParserCtxt)); if (ret == NULL) { xmlSchemaPErrMemory(NULL); return (NULL); } memset(ret, 0, sizeof(xmlSchemaParserCtxt)); ret->type = XML_SCHEMA_CTXT_PARSER; ret->attrProhibs = xmlSchemaItemListCreate(); if (ret->attrProhibs == NULL) { xmlFree(ret); return(NULL); } return(ret); } /** * xmlSchemaNewParserCtxtUseDict: * @URL: the location of the schema * @dict: the dictionary to be used * * Create an XML Schemas parse context for that file/resource expected * to contain an XML Schemas file. * * Returns the parser context or NULL in case of error */ static xmlSchemaParserCtxtPtr xmlSchemaNewParserCtxtUseDict(const char *URL, xmlDictPtr dict) { xmlSchemaParserCtxtPtr ret; ret = xmlSchemaParserCtxtCreate(); if (ret == NULL) return (NULL); ret->dict = dict; xmlDictReference(dict); if (URL != NULL) ret->URL = xmlDictLookup(dict, (const xmlChar *) URL, -1); return (ret); } static int xmlSchemaCreatePCtxtOnVCtxt(xmlSchemaValidCtxtPtr vctxt) { if (vctxt->pctxt == NULL) { if (vctxt->schema != NULL) vctxt->pctxt = xmlSchemaNewParserCtxtUseDict("*", vctxt->schema->dict); else vctxt->pctxt = xmlSchemaNewParserCtxt("*"); if (vctxt->pctxt == NULL) { VERROR_INT("xmlSchemaCreatePCtxtOnVCtxt", "failed to create a temp. parser context"); return (-1); } /* TODO: Pass user data. */ xmlSchemaSetParserErrors(vctxt->pctxt, vctxt->error, vctxt->warning, vctxt->errCtxt); xmlSchemaSetParserStructuredErrors(vctxt->pctxt, vctxt->serror, vctxt->errCtxt); } return (0); } /** * xmlSchemaGetSchemaBucket: * @pctxt: the schema parser context * @schemaLocation: the URI of the schema document * * Returns a schema bucket if it was already parsed. * * Returns a schema bucket if it was already parsed from * @schemaLocation, NULL otherwise. */ static xmlSchemaBucketPtr xmlSchemaGetSchemaBucket(xmlSchemaParserCtxtPtr pctxt, const xmlChar *schemaLocation) { xmlSchemaBucketPtr cur; xmlSchemaItemListPtr list; list = pctxt->constructor->buckets; if (list->nbItems == 0) return(NULL); else { int i; for (i = 0; i < list->nbItems; i++) { cur = (xmlSchemaBucketPtr) list->items[i]; /* Pointer comparison! */ if (cur->schemaLocation == schemaLocation) return(cur); } } return(NULL); } static xmlSchemaBucketPtr xmlSchemaGetChameleonSchemaBucket(xmlSchemaParserCtxtPtr pctxt, const xmlChar *schemaLocation, const xmlChar *targetNamespace) { xmlSchemaBucketPtr cur; xmlSchemaItemListPtr list; list = pctxt->constructor->buckets; if (list->nbItems == 0) return(NULL); else { int i; for (i = 0; i < list->nbItems; i++) { cur = (xmlSchemaBucketPtr) list->items[i]; /* Pointer comparison! */ if ((cur->origTargetNamespace == NULL) && (cur->schemaLocation == schemaLocation) && (cur->targetNamespace == targetNamespace)) return(cur); } } return(NULL); } #define IS_BAD_SCHEMA_DOC(b) \ (((b)->doc == NULL) && ((b)->schemaLocation != NULL)) static xmlSchemaBucketPtr xmlSchemaGetSchemaBucketByTNS(xmlSchemaParserCtxtPtr pctxt, const xmlChar *targetNamespace, int imported) { xmlSchemaBucketPtr cur; xmlSchemaItemListPtr list; list = pctxt->constructor->buckets; if (list->nbItems == 0) return(NULL); else { int i; for (i = 0; i < list->nbItems; i++) { cur = (xmlSchemaBucketPtr) list->items[i]; if ((! IS_BAD_SCHEMA_DOC(cur)) && (cur->origTargetNamespace == targetNamespace) && ((imported && cur->imported) || ((!imported) && (!cur->imported)))) return(cur); } } return(NULL); } static int xmlSchemaParseNewDocWithContext(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlSchemaBucketPtr bucket) { int oldFlags; xmlDocPtr oldDoc; xmlNodePtr node; int ret, oldErrs; xmlSchemaBucketPtr oldbucket = pctxt->constructor->bucket; /* * Save old values; reset the *main* schema. * URGENT TODO: This is not good; move the per-document information * to the parser. Get rid of passing the main schema to the * parsing functions. */ oldFlags = schema->flags; oldDoc = schema->doc; if (schema->flags != 0) xmlSchemaClearSchemaDefaults(schema); schema->doc = bucket->doc; pctxt->schema = schema; /* * Keep the current target namespace on the parser *not* on the * main schema. */ pctxt->targetNamespace = bucket->targetNamespace; WXS_CONSTRUCTOR(pctxt)->bucket = bucket; if ((bucket->targetNamespace != NULL) && xmlStrEqual(bucket->targetNamespace, xmlSchemaNs)) { /* * We are parsing the schema for schemas! */ pctxt->isS4S = 1; } /* Mark it as parsed, even if parsing fails. */ bucket->parsed++; /* Compile the schema doc. */ node = xmlDocGetRootElement(bucket->doc); ret = xmlSchemaParseSchemaElement(pctxt, schema, node); if (ret != 0) goto exit; /* An empty schema; just get out. */ if (node->children == NULL) goto exit; oldErrs = pctxt->nberrors; ret = xmlSchemaParseSchemaTopLevel(pctxt, schema, node->children); if (ret != 0) goto exit; /* * TODO: Not nice, but I'm not 100% sure we will get always an error * as a result of the above functions; so better rely on pctxt->err * as well. */ if ((ret == 0) && (oldErrs != pctxt->nberrors)) { ret = pctxt->err; goto exit; } exit: WXS_CONSTRUCTOR(pctxt)->bucket = oldbucket; /* Restore schema values. */ schema->doc = oldDoc; schema->flags = oldFlags; return(ret); } static int xmlSchemaParseNewDoc(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlSchemaBucketPtr bucket) { xmlSchemaParserCtxtPtr newpctxt; int res = 0; if (bucket == NULL) return(0); if (bucket->parsed) { PERROR_INT("xmlSchemaParseNewDoc", "reparsing a schema doc"); return(-1); } if (bucket->doc == NULL) { PERROR_INT("xmlSchemaParseNewDoc", "parsing a schema doc, but there's no doc"); return(-1); } if (pctxt->constructor == NULL) { PERROR_INT("xmlSchemaParseNewDoc", "no constructor"); return(-1); } /* Create and init the temporary parser context. */ newpctxt = xmlSchemaNewParserCtxtUseDict( (const char *) bucket->schemaLocation, pctxt->dict); if (newpctxt == NULL) return(-1); newpctxt->constructor = pctxt->constructor; /* * TODO: Can we avoid that the parser knows about the main schema? * It would be better if he knows about the current schema bucket * only. */ newpctxt->schema = schema; xmlSchemaSetParserErrors(newpctxt, pctxt->error, pctxt->warning, pctxt->errCtxt); xmlSchemaSetParserStructuredErrors(newpctxt, pctxt->serror, pctxt->errCtxt); newpctxt->counter = pctxt->counter; res = xmlSchemaParseNewDocWithContext(newpctxt, schema, bucket); /* Channel back errors and cleanup the temporary parser context. */ if (res != 0) pctxt->err = res; pctxt->nberrors += newpctxt->nberrors; pctxt->counter = newpctxt->counter; newpctxt->constructor = NULL; /* Free the parser context. */ xmlSchemaFreeParserCtxt(newpctxt); return(res); } static void xmlSchemaSchemaRelationAddChild(xmlSchemaBucketPtr bucket, xmlSchemaSchemaRelationPtr rel) { xmlSchemaSchemaRelationPtr cur = bucket->relations; if (cur == NULL) { bucket->relations = rel; return; } while (cur->next != NULL) cur = cur->next; cur->next = rel; } static const xmlChar * xmlSchemaBuildAbsoluteURI(xmlDictPtr dict, const xmlChar* location, xmlNodePtr ctxtNode) { /* * Build an absolute location URI. */ if (location != NULL) { if (ctxtNode == NULL) return(location); else { xmlChar *base, *URI; const xmlChar *ret = NULL; base = xmlNodeGetBase(ctxtNode->doc, ctxtNode); if (base == NULL) { URI = xmlBuildURI(location, ctxtNode->doc->URL); } else { URI = xmlBuildURI(location, base); xmlFree(base); } if (URI != NULL) { ret = xmlDictLookup(dict, URI, -1); xmlFree(URI); return(ret); } } } return(NULL); } /** * xmlSchemaAddSchemaDoc: * @pctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * Parse an included (and to-be-redefined) XML schema document. * * Returns 0 on success, a positive error code on errors and * -1 in case of an internal or API error. */ static int xmlSchemaAddSchemaDoc(xmlSchemaParserCtxtPtr pctxt, int type, /* import or include or redefine */ const xmlChar *schemaLocation, xmlDocPtr schemaDoc, const char *schemaBuffer, int schemaBufferLen, xmlNodePtr invokingNode, const xmlChar *sourceTargetNamespace, const xmlChar *importNamespace, xmlSchemaBucketPtr *bucket) { const xmlChar *targetNamespace = NULL; xmlSchemaSchemaRelationPtr relation = NULL; xmlDocPtr doc = NULL; int res = 0, err = 0, located = 0, preserveDoc = 0; xmlSchemaBucketPtr bkt = NULL; if (bucket != NULL) *bucket = NULL; switch (type) { case XML_SCHEMA_SCHEMA_IMPORT: case XML_SCHEMA_SCHEMA_MAIN: err = XML_SCHEMAP_SRC_IMPORT; break; case XML_SCHEMA_SCHEMA_INCLUDE: err = XML_SCHEMAP_SRC_INCLUDE; break; case XML_SCHEMA_SCHEMA_REDEFINE: err = XML_SCHEMAP_SRC_REDEFINE; break; } /* Special handling for the main schema: * skip the location and relation logic and just parse the doc. * We need just a bucket to be returned in this case. */ if ((type == XML_SCHEMA_SCHEMA_MAIN) || (! WXS_HAS_BUCKETS(pctxt))) goto doc_load; /* Note that we expect the location to be an absolute URI. */ if (schemaLocation != NULL) { bkt = xmlSchemaGetSchemaBucket(pctxt, schemaLocation); if ((bkt != NULL) && (pctxt->constructor->bucket == bkt)) { /* Report self-imports/inclusions/redefinitions. */ xmlSchemaCustomErr(ACTXT_CAST pctxt, err, invokingNode, NULL, "The schema must not import/include/redefine itself", NULL, NULL); goto exit; } } /* * Create a relation for the graph of schemas. */ relation = xmlSchemaSchemaRelationCreate(); if (relation == NULL) return(-1); xmlSchemaSchemaRelationAddChild(pctxt->constructor->bucket, relation); relation->type = type; /* * Save the namespace import information. */ if (WXS_IS_BUCKET_IMPMAIN(type)) { relation->importNamespace = importNamespace; if (schemaLocation == NULL) { /* * No location; this is just an import of the namespace. * Note that we don't assign a bucket to the relation * in this case. */ goto exit; } targetNamespace = importNamespace; } /* Did we already fetch the doc? */ if (bkt != NULL) { if ((WXS_IS_BUCKET_IMPMAIN(type)) && (! bkt->imported)) { /* * We included/redefined and then try to import a schema, * but the new location provided for import was different. */ if (schemaLocation == NULL) schemaLocation = BAD_CAST "in_memory_buffer"; if (!xmlStrEqual(schemaLocation, bkt->schemaLocation)) { xmlSchemaCustomErr(ACTXT_CAST pctxt, err, invokingNode, NULL, "The schema document '%s' cannot be imported, since " "it was already included or redefined", schemaLocation, NULL); goto exit; } } else if ((! WXS_IS_BUCKET_IMPMAIN(type)) && (bkt->imported)) { /* * We imported and then try to include/redefine a schema, * but the new location provided for the include/redefine * was different. */ if (schemaLocation == NULL) schemaLocation = BAD_CAST "in_memory_buffer"; if (!xmlStrEqual(schemaLocation, bkt->schemaLocation)) { xmlSchemaCustomErr(ACTXT_CAST pctxt, err, invokingNode, NULL, "The schema document '%s' cannot be included or " "redefined, since it was already imported", schemaLocation, NULL); goto exit; } } } if (WXS_IS_BUCKET_IMPMAIN(type)) { /* * Given that the schemaLocation [attribute] is only a hint, it is open * to applications to ignore all but the first for a given * namespace, regardless of the `actual value` of schemaLocation, but * such a strategy risks missing useful information when new * schemaLocations are offered. * * We will use the first that comes with a location. * Further s *with* a location, will result in an error. * TODO: Better would be to just report a warning here, but * we'll try it this way until someone complains. * * Schema Document Location Strategy: * 3 Based on the namespace name, identify an existing schema document, * either as a resource which is an XML document or a element * information item, in some local schema repository; * 5 Attempt to resolve the namespace name to locate such a resource. * * NOTE: (3) and (5) are not supported. */ if (bkt != NULL) { relation->bucket = bkt; goto exit; } bkt = xmlSchemaGetSchemaBucketByTNS(pctxt, importNamespace, 1); if (bkt != NULL) { relation->bucket = bkt; if (bkt->schemaLocation == NULL) { /* First given location of the schema; load the doc. */ bkt->schemaLocation = schemaLocation; } else { if (!xmlStrEqual(schemaLocation, bkt->schemaLocation)) { /* * Additional location given; just skip it. * URGENT TODO: We should report a warning here. * res = XML_SCHEMAP_SRC_IMPORT; */ if (schemaLocation == NULL) schemaLocation = BAD_CAST "in_memory_buffer"; xmlSchemaCustomWarning(ACTXT_CAST pctxt, XML_SCHEMAP_WARN_SKIP_SCHEMA, invokingNode, NULL, "Skipping import of schema located at '%s' for the " "namespace '%s', since this namespace was already " "imported with the schema located at '%s'", schemaLocation, importNamespace, bkt->schemaLocation); } goto exit; } } /* * No bucket + first location: load the doc and create a * bucket. */ } else { /* and */ if (bkt != NULL) { if ((bkt->origTargetNamespace == NULL) && (bkt->targetNamespace != sourceTargetNamespace)) { xmlSchemaBucketPtr chamel; /* * Chameleon include/redefine: skip loading only if it was * already build for the targetNamespace of the including * schema. */ /* * URGENT TODO: If the schema is a chameleon-include then copy * the components into the including schema and modify the * targetNamespace of those components, do nothing otherwise. * NOTE: This is currently worked-around by compiling the * chameleon for every distinct including targetNamespace; thus * not performant at the moment. * TODO: Check when the namespace in wildcards for chameleons * needs to be converted: before we built wildcard intersections * or after. * Answer: after! */ chamel = xmlSchemaGetChameleonSchemaBucket(pctxt, schemaLocation, sourceTargetNamespace); if (chamel != NULL) { /* A fitting chameleon was already parsed; NOP. */ relation->bucket = chamel; goto exit; } /* * We need to parse the chameleon again for a different * targetNamespace. * CHAMELEON TODO: Optimize this by only parsing the * chameleon once, and then copying the components to * the new targetNamespace. */ bkt = NULL; } else { relation->bucket = bkt; goto exit; } } } if ((bkt != NULL) && (bkt->doc != NULL)) { PERROR_INT("xmlSchemaAddSchemaDoc", "trying to load a schema doc, but a doc is already " "assigned to the schema bucket"); goto exit_failure; } doc_load: /* * Load the document. */ if (schemaDoc != NULL) { doc = schemaDoc; /* Don' free this one, since it was provided by the caller. */ preserveDoc = 1; /* TODO: Does the context or the doc hold the location? */ if (schemaDoc->URL != NULL) schemaLocation = xmlDictLookup(pctxt->dict, schemaDoc->URL, -1); else schemaLocation = BAD_CAST "in_memory_buffer"; } else if ((schemaLocation != NULL) || (schemaBuffer != NULL)) { xmlParserCtxtPtr parserCtxt; parserCtxt = xmlNewParserCtxt(); if (parserCtxt == NULL) { xmlSchemaPErrMemory(NULL); goto exit_failure; } if (pctxt->serror != NULL) xmlCtxtSetErrorHandler(parserCtxt, pctxt->serror, pctxt->errCtxt); if ((pctxt->dict != NULL) && (parserCtxt->dict != NULL)) { /* * TODO: Do we have to burden the schema parser dict with all * the content of the schema doc? */ xmlDictFree(parserCtxt->dict); parserCtxt->dict = pctxt->dict; xmlDictReference(parserCtxt->dict); } if (schemaLocation != NULL) { /* Parse from file. */ doc = xmlCtxtReadFile(parserCtxt, (const char *) schemaLocation, NULL, SCHEMAS_PARSE_OPTIONS); } else if (schemaBuffer != NULL) { /* Parse from memory buffer. */ doc = xmlCtxtReadMemory(parserCtxt, schemaBuffer, schemaBufferLen, NULL, NULL, SCHEMAS_PARSE_OPTIONS); schemaLocation = BAD_CAST "in_memory_buffer"; if (doc != NULL) doc->URL = xmlStrdup(schemaLocation); } /* * For : * 2.1 The referent is (a fragment of) a resource which is an * XML document (see clause 1.1), which in turn corresponds to * a element information item in a well-formed information * set, which in turn corresponds to a valid schema. * TODO: (2.1) fragments of XML documents are not supported. * * 2.2 The referent is a element information item in * a well-formed information set, which in turn corresponds * to a valid schema. * TODO: (2.2) is not supported. */ if (doc == NULL) { const xmlError *lerr; lerr = xmlGetLastError(); /* * Check if this a parser error, or if the document could * just not be located. * TODO: Try to find specific error codes to react only on * localisation failures. */ if ((lerr == NULL) || (lerr->domain != XML_FROM_IO)) { /* * We assume a parser error here. */ located = 1; /* TODO: Error code ?? */ res = XML_SCHEMAP_SRC_IMPORT_2_1; xmlSchemaCustomErr(ACTXT_CAST pctxt, res, invokingNode, NULL, "Failed to parse the XML resource '%s'", schemaLocation, NULL); } } xmlFreeParserCtxt(parserCtxt); if ((doc == NULL) && located) goto exit_error; } else { xmlSchemaPErr(pctxt, NULL, XML_SCHEMAP_NOTHING_TO_PARSE, "No information for parsing was provided with the " "given schema parser context.\n", NULL, NULL); goto exit_failure; } /* * Preprocess the document. */ if (doc != NULL) { xmlNodePtr docElem = NULL; located = 1; docElem = xmlDocGetRootElement(doc); if (docElem == NULL) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_NOROOT, invokingNode, NULL, "The document '%s' has no document element", schemaLocation, NULL); goto exit_error; } /* * Remove all the blank text nodes. */ xmlSchemaCleanupDoc(pctxt, docElem); /* * Check the schema's top level element. */ if (!IS_SCHEMA(docElem, "schema")) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_NOT_SCHEMA, invokingNode, NULL, "The XML document '%s' is not a schema document", schemaLocation, NULL); goto exit_error; } /* * Note that we don't apply a type check for the * targetNamespace value here. */ targetNamespace = xmlSchemaGetProp(pctxt, docElem, "targetNamespace"); } /* after_doc_loading: */ if ((bkt == NULL) && located) { /* Only create a bucket if the schema was located. */ bkt = xmlSchemaBucketCreate(pctxt, type, targetNamespace); if (bkt == NULL) goto exit_failure; } if (bkt != NULL) { bkt->schemaLocation = schemaLocation; bkt->located = located; if (doc != NULL) { bkt->doc = doc; bkt->targetNamespace = targetNamespace; bkt->origTargetNamespace = targetNamespace; if (preserveDoc) bkt->preserveDoc = 1; } if (WXS_IS_BUCKET_IMPMAIN(type)) bkt->imported++; /* * Add it to the graph of schemas. */ if (relation != NULL) relation->bucket = bkt; } exit: /* * Return the bucket explicitly; this is needed for the * main schema. */ if (bucket != NULL) *bucket = bkt; return (0); exit_error: if ((doc != NULL) && (! preserveDoc)) { xmlFreeDoc(doc); if (bkt != NULL) bkt->doc = NULL; } return(pctxt->err); exit_failure: if ((doc != NULL) && (! preserveDoc)) { xmlFreeDoc(doc); if (bkt != NULL) bkt->doc = NULL; } return (-1); } /** * xmlSchemaParseImport: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Import definition * *WARNING* this interface is highly subject to change * * Returns 0 in case of success, a positive error code if * not valid and -1 in case of an internal error. */ static int xmlSchemaParseImport(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node) { xmlNodePtr child; const xmlChar *namespaceName = NULL, *schemaLocation = NULL; const xmlChar *thisTargetNamespace; xmlAttrPtr attr; int ret = 0; xmlSchemaBucketPtr bucket = NULL; if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) return (-1); /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "namespace")) && (!xmlStrEqual(attr->name, BAD_CAST "schemaLocation"))) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* * Extract and validate attributes. */ if (xmlSchemaPValAttr(pctxt, NULL, node, "namespace", xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), &namespaceName) != 0) { xmlSchemaPSimpleTypeErr(pctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, node, xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), NULL, namespaceName, NULL, NULL, NULL); return (pctxt->err); } if (xmlSchemaPValAttr(pctxt, NULL, node, "schemaLocation", xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), &schemaLocation) != 0) { xmlSchemaPSimpleTypeErr(pctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, node, xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), NULL, schemaLocation, NULL, NULL, NULL); return (pctxt->err); } /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * the annotation here is simply discarded ... * TODO: really? */ child = child->next; } if (child != NULL) { xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?)"); } /* * Apply additional constraints. * * Note that it is important to use the original @targetNamespace * (or none at all), to rule out imports of schemas _with_ a * @targetNamespace if the importing schema is a chameleon schema * (with no @targetNamespace). */ thisTargetNamespace = WXS_BUCKET(pctxt)->origTargetNamespace; if (namespaceName != NULL) { /* * 1.1 If the namespace [attribute] is present, then its `actual value` * must not match the `actual value` of the enclosing 's * targetNamespace [attribute]. */ if (xmlStrEqual(thisTargetNamespace, namespaceName)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_SRC_IMPORT_1_1, NULL, node, "The value of the attribute 'namespace' must not match " "the target namespace '%s' of the importing schema", thisTargetNamespace); return (pctxt->err); } } else { /* * 1.2 If the namespace [attribute] is not present, then the enclosing * must have a targetNamespace [attribute]. */ if (thisTargetNamespace == NULL) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_SRC_IMPORT_1_2, NULL, node, "The attribute 'namespace' must be existent if " "the importing schema has no target namespace", NULL); return (pctxt->err); } } /* * Locate and acquire the schema document. */ if (schemaLocation != NULL) schemaLocation = xmlSchemaBuildAbsoluteURI(pctxt->dict, schemaLocation, node); ret = xmlSchemaAddSchemaDoc(pctxt, XML_SCHEMA_SCHEMA_IMPORT, schemaLocation, NULL, NULL, 0, node, thisTargetNamespace, namespaceName, &bucket); if (ret != 0) return(ret); /* * For : "It is *not* an error for the application * schema reference strategy to fail." * So just don't parse if no schema document was found. * Note that we will get no bucket if the schema could not be * located or if there was no schemaLocation. */ if ((bucket == NULL) && (schemaLocation != NULL)) { xmlSchemaCustomWarning(ACTXT_CAST pctxt, XML_SCHEMAP_WARN_UNLOCATED_SCHEMA, node, NULL, "Failed to locate a schema at location '%s'. " "Skipping the import", schemaLocation, NULL, NULL); } if ((bucket != NULL) && CAN_PARSE_SCHEMA(bucket)) { ret = xmlSchemaParseNewDoc(pctxt, schema, bucket); } return (ret); } static int xmlSchemaParseIncludeOrRedefineAttrs(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlChar **schemaLocation, int type) { xmlAttrPtr attr; if ((pctxt == NULL) || (schema == NULL) || (node == NULL) || (schemaLocation == NULL)) return (-1); *schemaLocation = NULL; /* * Check for illegal attributes. * Applies for both and . */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "schemaLocation"))) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); /* * Preliminary step, extract the URI-Reference and make an URI * from the base. */ /* * Attribute "schemaLocation" is mandatory. */ attr = xmlSchemaGetPropNode(node, "schemaLocation"); if (attr != NULL) { xmlChar *base = NULL; xmlChar *uri = NULL; if (xmlSchemaPValAttrNode(pctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), (const xmlChar **) schemaLocation) != 0) goto exit_error; base = xmlNodeGetBase(node->doc, node); if (base == NULL) { uri = xmlBuildURI(*schemaLocation, node->doc->URL); } else { uri = xmlBuildURI(*schemaLocation, base); xmlFree(base); } if (uri == NULL) { PERROR_INT("xmlSchemaParseIncludeOrRedefine", "could not build an URI from the schemaLocation") goto exit_failure; } (*schemaLocation) = (xmlChar *) xmlDictLookup(pctxt->dict, uri, -1); xmlFree(uri); } else { xmlSchemaPMissingAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "schemaLocation", NULL); goto exit_error; } /* * Report self-inclusion and self-redefinition. */ if (xmlStrEqual(*schemaLocation, pctxt->URL)) { if (type == XML_SCHEMA_SCHEMA_REDEFINE) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_SRC_REDEFINE, NULL, node, "The schema document '%s' cannot redefine itself.", *schemaLocation); } else { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_SRC_INCLUDE, NULL, node, "The schema document '%s' cannot include itself.", *schemaLocation); } goto exit_error; } return(0); exit_error: return(pctxt->err); exit_failure: return(-1); } static int xmlSchemaParseIncludeOrRedefine(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node, int type) { xmlNodePtr child = NULL; const xmlChar *schemaLocation = NULL; int res = 0; /* hasRedefinitions = 0 */ int isChameleon = 0, wasChameleon = 0; xmlSchemaBucketPtr bucket = NULL; if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) return (-1); /* * Parse attributes. Note that the returned schemaLocation will * be already converted to an absolute URI. */ res = xmlSchemaParseIncludeOrRedefineAttrs(pctxt, schema, node, (xmlChar **) (&schemaLocation), type); if (res != 0) return(res); /* * Load and add the schema document. */ res = xmlSchemaAddSchemaDoc(pctxt, type, schemaLocation, NULL, NULL, 0, node, pctxt->targetNamespace, NULL, &bucket); if (res != 0) return(res); /* * If we get no schema bucket back, then this means that the schema * document could not be located or was broken XML or was not * a schema document. */ if ((bucket == NULL) || (bucket->doc == NULL)) { if (type == XML_SCHEMA_SCHEMA_INCLUDE) { /* * WARNING for : * We will raise an error if the schema cannot be located * for inclusions, since the that was the feedback from the * schema people. I.e. the following spec piece will *not* be * satisfied: * SPEC src-include: "It is not an error for the `actual value` of the * schemaLocation [attribute] to fail to resolve it all, in which * case no corresponding inclusion is performed. * So do we need a warning report here?" */ res = XML_SCHEMAP_SRC_INCLUDE; xmlSchemaCustomErr(ACTXT_CAST pctxt, res, node, NULL, "Failed to load the document '%s' for inclusion", schemaLocation, NULL); } else { /* * NOTE: This was changed to raise an error even if no redefinitions * are specified. * * SPEC src-redefine (1) * "If there are any element information items among the [children] * other than then the `actual value` of the * schemaLocation [attribute] must successfully resolve." * TODO: Ask the WG if a the location has always to resolve * here as well! */ res = XML_SCHEMAP_SRC_REDEFINE; xmlSchemaCustomErr(ACTXT_CAST pctxt, res, node, NULL, "Failed to load the document '%s' for redefinition", schemaLocation, NULL); } } else { /* * Check targetNamespace sanity before parsing the new schema. * TODO: Note that we won't check further content if the * targetNamespace was bad. */ if (bucket->origTargetNamespace != NULL) { /* * SPEC src-include (2.1) * "SII has a targetNamespace [attribute], and its `actual * value` is identical to the `actual value` of the targetNamespace * [attribute] of SII' (which must have such an [attribute])." */ if (pctxt->targetNamespace == NULL) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_SRC_INCLUDE, node, NULL, "The target namespace of the included/redefined schema " "'%s' has to be absent, since the including/redefining " "schema has no target namespace", schemaLocation, NULL); goto exit_error; } else if (!xmlStrEqual(bucket->origTargetNamespace, pctxt->targetNamespace)) { /* TODO: Change error function. */ xmlSchemaPCustomErrExt(pctxt, XML_SCHEMAP_SRC_INCLUDE, NULL, node, "The target namespace '%s' of the included/redefined " "schema '%s' differs from '%s' of the " "including/redefining schema", bucket->origTargetNamespace, schemaLocation, pctxt->targetNamespace); goto exit_error; } } else if (pctxt->targetNamespace != NULL) { /* * Chameleons: the original target namespace will * differ from the resulting namespace. */ isChameleon = 1; bucket->targetNamespace = pctxt->targetNamespace; } } /* * Parse the schema. */ if (bucket && (!bucket->parsed) && (bucket->doc != NULL)) { if (isChameleon) { /* TODO: Get rid of this flag on the schema itself. */ if ((schema->flags & XML_SCHEMAS_INCLUDING_CONVERT_NS) == 0) { schema->flags |= XML_SCHEMAS_INCLUDING_CONVERT_NS; } else wasChameleon = 1; } xmlSchemaParseNewDoc(pctxt, schema, bucket); /* Restore chameleon flag. */ if (isChameleon && (!wasChameleon)) schema->flags ^= XML_SCHEMAS_INCLUDING_CONVERT_NS; } /* * And now for the children... */ child = node->children; if (type == XML_SCHEMA_SCHEMA_REDEFINE) { /* * Parse (simpleType | complexType | group | attributeGroup))* */ pctxt->redefined = bucket; /* * How to proceed if the redefined schema was not located? */ pctxt->isRedefine = 1; while (IS_SCHEMA(child, "annotation") || IS_SCHEMA(child, "simpleType") || IS_SCHEMA(child, "complexType") || IS_SCHEMA(child, "group") || IS_SCHEMA(child, "attributeGroup")) { if (IS_SCHEMA(child, "annotation")) { /* * TODO: discard or not? */ } else if (IS_SCHEMA(child, "simpleType")) { xmlSchemaParseSimpleType(pctxt, schema, child, 1); } else if (IS_SCHEMA(child, "complexType")) { xmlSchemaParseComplexType(pctxt, schema, child, 1); /* hasRedefinitions = 1; */ } else if (IS_SCHEMA(child, "group")) { /* hasRedefinitions = 1; */ xmlSchemaParseModelGroupDefinition(pctxt, schema, child); } else if (IS_SCHEMA(child, "attributeGroup")) { /* hasRedefinitions = 1; */ xmlSchemaParseAttributeGroupDefinition(pctxt, schema, child); } child = child->next; } pctxt->redefined = NULL; pctxt->isRedefine = 0; } else { if (IS_SCHEMA(child, "annotation")) { /* * TODO: discard or not? */ child = child->next; } } if (child != NULL) { res = XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED; if (type == XML_SCHEMA_SCHEMA_REDEFINE) { xmlSchemaPContentErr(pctxt, res, NULL, node, child, NULL, "(annotation | (simpleType | complexType | group | attributeGroup))*"); } else { xmlSchemaPContentErr(pctxt, res, NULL, node, child, NULL, "(annotation?)"); } } return(res); exit_error: return(pctxt->err); } static int xmlSchemaParseRedefine(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node) { int res; #ifndef ENABLE_REDEFINE TODO return(0); #endif res = xmlSchemaParseIncludeOrRedefine(pctxt, schema, node, XML_SCHEMA_SCHEMA_REDEFINE); if (res != 0) return(res); return(0); } static int xmlSchemaParseInclude(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, xmlNodePtr node) { int res; res = xmlSchemaParseIncludeOrRedefine(pctxt, schema, node, XML_SCHEMA_SCHEMA_INCLUDE); if (res != 0) return(res); return(0); } /** * xmlSchemaParseModelGroup: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * @type: the "compositor" type * @particleNeeded: if a a model group with a particle * * parse a XML schema Sequence definition. * Applies parts of: * Schema Representation Constraint: * Redefinition Constraints and Semantics (src-redefine) * (6.1), (6.1.1), (6.1.2) * * Schema Component Constraint: * All Group Limited (cos-all-limited) (2) * TODO: Actually this should go to component-level checks, * but is done here due to performance. Move it to an other layer * is schema construction via an API is implemented. * * *WARNING* this interface is highly subject to change * * Returns -1 in case of error, 0 if the declaration is improper and * 1 in case of success. */ static xmlSchemaTreeItemPtr xmlSchemaParseModelGroup(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaTypeType type, int withParticle) { xmlSchemaModelGroupPtr item; xmlSchemaParticlePtr particle = NULL; xmlNodePtr child = NULL; xmlAttrPtr attr; int min = 1, max = 1, isElemRef, hasRefs = 0; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); /* * Create a model group with the given compositor. */ item = xmlSchemaAddModelGroup(ctxt, schema, type, node); if (item == NULL) return (NULL); if (withParticle) { if (type == XML_SCHEMA_TYPE_ALL) { min = xmlGetMinOccurs(ctxt, node, 0, 1, 1, "(0 | 1)"); max = xmlGetMaxOccurs(ctxt, node, 1, 1, 1, "1"); } else { /* choice + sequence */ min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, "xs:nonNegativeInteger"); max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, "(xs:nonNegativeInteger | unbounded)"); } xmlSchemaPCheckParticleCorrect_2(ctxt, NULL, node, min, max); /* * Create a particle */ particle = xmlSchemaAddParticle(ctxt, node, min, max); if (particle == NULL) return (NULL); particle->children = (xmlSchemaTreeItemPtr) item; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "maxOccurs")) && (!xmlStrEqual(attr->name, BAD_CAST "minOccurs"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } } else { /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if (!xmlStrEqual(attr->name, BAD_CAST "id")) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } } /* * Extract and validate attributes. */ xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } if (type == XML_SCHEMA_TYPE_ALL) { xmlSchemaParticlePtr part, last = NULL; while (IS_SCHEMA(child, "element")) { part = (xmlSchemaParticlePtr) xmlSchemaParseElement(ctxt, schema, child, &isElemRef, 0); /* * SPEC cos-all-limited (2) * "The {max occurs} of all the particles in the {particles} * of the ('all') group must be 0 or 1. */ if (part != NULL) { if (isElemRef) hasRefs++; if (part->minOccurs > 1) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_COS_ALL_LIMITED, NULL, child, "Invalid value for minOccurs (must be 0 or 1)", NULL); /* Reset to 1. */ part->minOccurs = 1; } if (part->maxOccurs > 1) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_COS_ALL_LIMITED, NULL, child, "Invalid value for maxOccurs (must be 0 or 1)", NULL); /* Reset to 1. */ part->maxOccurs = 1; } if (last == NULL) item->children = (xmlSchemaTreeItemPtr) part; else last->next = (xmlSchemaTreeItemPtr) part; last = part; } child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (annotation?, element*)"); } } else { /* choice + sequence */ xmlSchemaTreeItemPtr part = NULL, last = NULL; while ((IS_SCHEMA(child, "element")) || (IS_SCHEMA(child, "group")) || (IS_SCHEMA(child, "any")) || (IS_SCHEMA(child, "choice")) || (IS_SCHEMA(child, "sequence"))) { if (IS_SCHEMA(child, "element")) { part = (xmlSchemaTreeItemPtr) xmlSchemaParseElement(ctxt, schema, child, &isElemRef, 0); if (part && isElemRef) hasRefs++; } else if (IS_SCHEMA(child, "group")) { part = xmlSchemaParseModelGroupDefRef(ctxt, schema, child); if (part != NULL) hasRefs++; /* * Handle redefinitions. */ if (ctxt->isRedefine && ctxt->redef && (ctxt->redef->item->type == XML_SCHEMA_TYPE_GROUP) && part && part->children) { if ((xmlSchemaGetQNameRefName(part->children) == ctxt->redef->refName) && (xmlSchemaGetQNameRefTargetNs(part->children) == ctxt->redef->refTargetNs)) { /* * SPEC src-redefine: * (6.1) "If it has a among its contents at * some level the `actual value` of whose ref * [attribute] is the same as the `actual value` of * its own name attribute plus target namespace, then * all of the following must be true:" * (6.1.1) "It must have exactly one such group." */ if (ctxt->redefCounter != 0) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_SRC_REDEFINE, child, NULL, "The redefining model group definition " "'%s' must not contain more than one " "reference to the redefined definition", xmlSchemaFormatQName(&str, ctxt->redef->refTargetNs, ctxt->redef->refName), NULL); FREE_AND_NULL(str) part = NULL; } else if (((WXS_PARTICLE(part))->minOccurs != 1) || ((WXS_PARTICLE(part))->maxOccurs != 1)) { xmlChar *str = NULL; /* * SPEC src-redefine: * (6.1.2) "The `actual value` of both that * group's minOccurs and maxOccurs [attribute] * must be 1 (or `absent`). */ xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_SRC_REDEFINE, child, NULL, "The redefining model group definition " "'%s' must not contain a reference to the " "redefined definition with a " "maxOccurs/minOccurs other than 1", xmlSchemaFormatQName(&str, ctxt->redef->refTargetNs, ctxt->redef->refName), NULL); FREE_AND_NULL(str) part = NULL; } ctxt->redef->reference = WXS_BASIC_CAST part; ctxt->redefCounter++; } } } else if (IS_SCHEMA(child, "any")) { part = (xmlSchemaTreeItemPtr) xmlSchemaParseAny(ctxt, schema, child); } else if (IS_SCHEMA(child, "choice")) { part = xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_CHOICE, 1); } else if (IS_SCHEMA(child, "sequence")) { part = xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_SEQUENCE, 1); } if (part != NULL) { if (last == NULL) item->children = part; else last->next = part; last = part; } child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (element | group | choice | sequence | any)*)"); } } if ((max == 0) && (min == 0)) return (NULL); if (hasRefs) { /* * We need to resolve references. */ WXS_ADD_PENDING(ctxt, item); } if (withParticle) return ((xmlSchemaTreeItemPtr) particle); else return ((xmlSchemaTreeItemPtr) item); } /** * xmlSchemaParseRestriction: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Restriction definition * *WARNING* this interface is highly subject to change * * Returns the type definition or NULL in case of error */ static xmlSchemaTypePtr xmlSchemaParseRestriction(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaTypeType parentType) { xmlSchemaTypePtr type; xmlNodePtr child = NULL; xmlAttrPtr attr; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); /* Not a component, don't create it. */ type = ctxt->ctxtType; type->flags |= XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "base"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } /* * Extract and validate attributes. */ xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * Attribute */ /* * Extract the base type. The "base" attribute is mandatory if inside * a complex type or if redefining. * * SPEC (1.2) "...otherwise ( has no " * among its [children]), the simple type definition which is * the {content type} of the type definition `resolved` to by * the `actual value` of the base [attribute]" */ if (xmlSchemaPValAttrQName(ctxt, schema, NULL, node, "base", &(type->baseNs), &(type->base)) == 0) { if ((type->base == NULL) && (type->type == XML_SCHEMA_TYPE_COMPLEX)) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "base", NULL); } else if ((ctxt->isRedefine) && (type->flags & XML_SCHEMAS_TYPE_GLOBAL)) { if (type->base == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "base", NULL); } else if ((! xmlStrEqual(type->base, type->name)) || (! xmlStrEqual(type->baseNs, type->targetNamespace))) { xmlChar *str1 = NULL, *str2 = NULL; /* * REDEFINE: SPEC src-redefine (5) * "Within the [children], each must have a * among its [children] ... the `actual value` of * whose base [attribute] must be the same as the `actual value` * of its own name attribute plus target namespace;" */ xmlSchemaPCustomErrExt(ctxt, XML_SCHEMAP_SRC_REDEFINE, NULL, node, "This is a redefinition, but the QName " "value '%s' of the 'base' attribute does not match the " "type's designation '%s'", xmlSchemaFormatQName(&str1, type->baseNs, type->base), xmlSchemaFormatQName(&str2, type->targetNamespace, type->name), NULL); FREE_AND_NULL(str1); FREE_AND_NULL(str2); /* Avoid confusion and erase the values. */ type->base = NULL; type->baseNs = NULL; } } } /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * Add the annotation to the simple type ancestor. */ xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, xmlSchemaParseAnnotation(ctxt, child, 1)); child = child->next; } if (parentType == XML_SCHEMA_TYPE_SIMPLE) { /* * Corresponds to . */ if (IS_SCHEMA(child, "simpleType")) { if (type->base != NULL) { /* * src-restriction-base-or-simpleType * Either the base [attribute] or the simpleType [child] of the * element must be present, but not both. */ xmlSchemaPContentErr(ctxt, XML_SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE, NULL, node, child, "The attribute 'base' and the child are " "mutually exclusive", NULL); } else { type->baseType = (xmlSchemaTypePtr) xmlSchemaParseSimpleType(ctxt, schema, child, 0); } child = child->next; } else if (type->base == NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE, NULL, node, child, "Either the attribute 'base' or a child " "must be present", NULL); } } else if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { /* * Corresponds to ... * followed by: * * Model groups , and . */ if (IS_SCHEMA(child, "all")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_ALL, 1); child = child->next; } else if (IS_SCHEMA(child, "choice")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_CHOICE, 1); child = child->next; } else if (IS_SCHEMA(child, "sequence")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_SEQUENCE, 1); child = child->next; /* * Model group reference . */ } else if (IS_SCHEMA(child, "group")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroupDefRef(ctxt, schema, child); /* * Note that the reference will be resolved in * xmlSchemaResolveTypeReferences(); */ child = child->next; } } else if (parentType == XML_SCHEMA_TYPE_SIMPLE_CONTENT) { /* * Corresponds to ... * * "1.1 the simple type definition corresponding to the * among the [children] of if there is one;" */ if (IS_SCHEMA(child, "simpleType")) { /* * We will store the to-be-restricted simple type in * type->contentTypeDef *temporarily*. */ type->contentTypeDef = (xmlSchemaTypePtr) xmlSchemaParseSimpleType(ctxt, schema, child, 0); if ( type->contentTypeDef == NULL) return (NULL); child = child->next; } } if ((parentType == XML_SCHEMA_TYPE_SIMPLE) || (parentType == XML_SCHEMA_TYPE_SIMPLE_CONTENT)) { xmlSchemaFacetPtr facet, lastfacet = NULL; /* * Corresponds to ... * ... */ /* * Add the facets to the simple type ancestor. */ /* * TODO: Datatypes: 4.1.3 Constraints on XML Representation of * Simple Type Definition Schema Representation Constraint: * *Single Facet Value* */ while ((IS_SCHEMA(child, "minInclusive")) || (IS_SCHEMA(child, "minExclusive")) || (IS_SCHEMA(child, "maxInclusive")) || (IS_SCHEMA(child, "maxExclusive")) || (IS_SCHEMA(child, "totalDigits")) || (IS_SCHEMA(child, "fractionDigits")) || (IS_SCHEMA(child, "pattern")) || (IS_SCHEMA(child, "enumeration")) || (IS_SCHEMA(child, "whiteSpace")) || (IS_SCHEMA(child, "length")) || (IS_SCHEMA(child, "maxLength")) || (IS_SCHEMA(child, "minLength"))) { facet = xmlSchemaParseFacet(ctxt, schema, child); if (facet != NULL) { if (lastfacet == NULL) type->facets = facet; else lastfacet->next = facet; lastfacet = facet; lastfacet->next = NULL; } child = child->next; } /* * Create links for derivation and validation. */ if (type->facets != NULL) { xmlSchemaFacetLinkPtr facetLink, lastFacetLink = NULL; facet = type->facets; do { facetLink = (xmlSchemaFacetLinkPtr) xmlMalloc(sizeof(xmlSchemaFacetLink)); if (facetLink == NULL) { xmlSchemaPErrMemory(ctxt); xmlFree(facetLink); return (NULL); } facetLink->facet = facet; facetLink->next = NULL; if (lastFacetLink == NULL) type->facetSet = facetLink; else lastFacetLink->next = facetLink; lastFacetLink = facetLink; facet = facet->next; } while (facet != NULL); } } if (type->type == XML_SCHEMA_TYPE_COMPLEX) { /* * Attribute uses/declarations. */ if (xmlSchemaParseLocalAttributes(ctxt, schema, &child, (xmlSchemaItemListPtr *) &(type->attrUses), XML_SCHEMA_TYPE_RESTRICTION, NULL) == -1) return(NULL); /* * Attribute wildcard. */ if (IS_SCHEMA(child, "anyAttribute")) { type->attributeWildcard = xmlSchemaParseAnyAttribute(ctxt, schema, child); child = child->next; } } if (child != NULL) { if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "annotation?, (group | all | choice | sequence)?, " "((attribute | attributeGroup)*, anyAttribute?))"); } else if (parentType == XML_SCHEMA_TYPE_SIMPLE_CONTENT) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (simpleType?, (minExclusive | minInclusive | " "maxExclusive | maxInclusive | totalDigits | fractionDigits | " "length | minLength | maxLength | enumeration | whiteSpace | " "pattern)*)?, ((attribute | attributeGroup)*, anyAttribute?))"); } else { /* Simple type */ xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (simpleType?, (minExclusive | minInclusive | " "maxExclusive | maxInclusive | totalDigits | fractionDigits | " "length | minLength | maxLength | enumeration | whiteSpace | " "pattern)*))"); } } return (NULL); } /** * xmlSchemaParseExtension: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * Parses an , which is found inside a * or . * *WARNING* this interface is highly subject to change. * * TODO: Returns the type definition or NULL in case of error */ static xmlSchemaTypePtr xmlSchemaParseExtension(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, xmlSchemaTypeType parentType) { xmlSchemaTypePtr type; xmlNodePtr child = NULL; xmlAttrPtr attr; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); /* Not a component, don't create it. */ type = ctxt->ctxtType; type->flags |= XML_SCHEMAS_TYPE_DERIVATION_METHOD_EXTENSION; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "base"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * Attribute "base" - mandatory. */ if ((xmlSchemaPValAttrQName(ctxt, schema, NULL, node, "base", &(type->baseNs), &(type->base)) == 0) && (type->base == NULL)) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "base", NULL); } /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * Add the annotation to the type ancestor. */ xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, xmlSchemaParseAnnotation(ctxt, child, 1)); child = child->next; } if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { /* * Corresponds to ... and: * * Model groups , , and . */ if (IS_SCHEMA(child, "all")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_ALL, 1); child = child->next; } else if (IS_SCHEMA(child, "choice")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_CHOICE, 1); child = child->next; } else if (IS_SCHEMA(child, "sequence")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_SEQUENCE, 1); child = child->next; } else if (IS_SCHEMA(child, "group")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroupDefRef(ctxt, schema, child); /* * Note that the reference will be resolved in * xmlSchemaResolveTypeReferences(); */ child = child->next; } } if (child != NULL) { /* * Attribute uses/declarations. */ if (xmlSchemaParseLocalAttributes(ctxt, schema, &child, (xmlSchemaItemListPtr *) &(type->attrUses), XML_SCHEMA_TYPE_EXTENSION, NULL) == -1) return(NULL); /* * Attribute wildcard. */ if (IS_SCHEMA(child, "anyAttribute")) { ctxt->ctxtType->attributeWildcard = xmlSchemaParseAnyAttribute(ctxt, schema, child); child = child->next; } } if (child != NULL) { if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { /* Complex content extension. */ xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, ((group | all | choice | sequence)?, " "((attribute | attributeGroup)*, anyAttribute?)))"); } else { /* Simple content extension. */ xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, ((attribute | attributeGroup)*, " "anyAttribute?))"); } } return (NULL); } /** * xmlSchemaParseSimpleContent: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema SimpleContent definition * *WARNING* this interface is highly subject to change * * Returns the type definition or NULL in case of error */ static int xmlSchemaParseSimpleContent(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, int *hasRestrictionOrExtension) { xmlSchemaTypePtr type; xmlNodePtr child = NULL; xmlAttrPtr attr; if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (hasRestrictionOrExtension == NULL)) return (-1); *hasRestrictionOrExtension = 0; /* Not a component, don't create it. */ type = ctxt->ctxtType; type->contentType = XML_SCHEMA_CONTENT_SIMPLE; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * Add the annotation to the complex type ancestor. */ xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, xmlSchemaParseAnnotation(ctxt, child, 1)); child = child->next; } if (child == NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_MISSING, NULL, node, NULL, NULL, "(annotation?, (restriction | extension))"); } if (child == NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_MISSING, NULL, node, NULL, NULL, "(annotation?, (restriction | extension))"); } if (IS_SCHEMA(child, "restriction")) { xmlSchemaParseRestriction(ctxt, schema, child, XML_SCHEMA_TYPE_SIMPLE_CONTENT); (*hasRestrictionOrExtension) = 1; child = child->next; } else if (IS_SCHEMA(child, "extension")) { xmlSchemaParseExtension(ctxt, schema, child, XML_SCHEMA_TYPE_SIMPLE_CONTENT); (*hasRestrictionOrExtension) = 1; child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (restriction | extension))"); } return (0); } /** * xmlSchemaParseComplexContent: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema ComplexContent definition * *WARNING* this interface is highly subject to change * * Returns the type definition or NULL in case of error */ static int xmlSchemaParseComplexContent(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, int *hasRestrictionOrExtension) { xmlSchemaTypePtr type; xmlNodePtr child = NULL; xmlAttrPtr attr; if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (hasRestrictionOrExtension == NULL)) return (-1); *hasRestrictionOrExtension = 0; /* Not a component, don't create it. */ type = ctxt->ctxtType; /* * Check for illegal attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && (!xmlStrEqual(attr->name, BAD_CAST "mixed"))) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); /* * Set the 'mixed' on the complex type ancestor. */ if (xmlGetBooleanProp(ctxt, node, "mixed", 0)) { if ((type->flags & XML_SCHEMAS_TYPE_MIXED) == 0) type->flags |= XML_SCHEMAS_TYPE_MIXED; } child = node->children; if (IS_SCHEMA(child, "annotation")) { /* * Add the annotation to the complex type ancestor. */ xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, xmlSchemaParseAnnotation(ctxt, child, 1)); child = child->next; } if (child == NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_MISSING, NULL, node, NULL, NULL, "(annotation?, (restriction | extension))"); } if (child == NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_MISSING, NULL, node, NULL, NULL, "(annotation?, (restriction | extension))"); } if (IS_SCHEMA(child, "restriction")) { xmlSchemaParseRestriction(ctxt, schema, child, XML_SCHEMA_TYPE_COMPLEX_CONTENT); (*hasRestrictionOrExtension) = 1; child = child->next; } else if (IS_SCHEMA(child, "extension")) { xmlSchemaParseExtension(ctxt, schema, child, XML_SCHEMA_TYPE_COMPLEX_CONTENT); (*hasRestrictionOrExtension) = 1; child = child->next; } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (restriction | extension))"); } return (0); } /** * xmlSchemaParseComplexType: * @ctxt: a schema validation context * @schema: the schema being built * @node: a subtree containing XML Schema information * * parse a XML schema Complex Type definition * *WARNING* this interface is highly subject to change * * Returns the type definition or NULL in case of error */ static xmlSchemaTypePtr xmlSchemaParseComplexType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, xmlNodePtr node, int topLevel) { xmlSchemaTypePtr type, ctxtType; xmlNodePtr child = NULL; const xmlChar *name = NULL; xmlAttrPtr attr; const xmlChar *attrValue; #ifdef ENABLE_NAMED_LOCALS char buf[40]; #endif int final = 0, block = 0, hasRestrictionOrExtension = 0; if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) return (NULL); ctxtType = ctxt->ctxtType; if (topLevel) { attr = xmlSchemaGetPropNode(node, "name"); if (attr == NULL) { xmlSchemaPMissingAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); return (NULL); } else if (xmlSchemaPValAttrNode(ctxt, NULL, attr, xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { return (NULL); } } if (topLevel == 0) { /* * Parse as local complex type definition. */ #ifdef ENABLE_NAMED_LOCALS snprintf(buf, 39, "#CT%d", ctxt->counter++ + 1); type = xmlSchemaAddType(ctxt, schema, XML_SCHEMA_TYPE_COMPLEX, xmlDictLookup(ctxt->dict, (const xmlChar *)buf, -1), ctxt->targetNamespace, node, 0); #else type = xmlSchemaAddType(ctxt, schema, XML_SCHEMA_TYPE_COMPLEX, NULL, ctxt->targetNamespace, node, 0); #endif if (type == NULL) return (NULL); name = type->name; type->node = node; type->type = XML_SCHEMA_TYPE_COMPLEX; /* * TODO: We need the target namespace. */ } else { /* * Parse as global complex type definition. */ type = xmlSchemaAddType(ctxt, schema, XML_SCHEMA_TYPE_COMPLEX, name, ctxt->targetNamespace, node, 1); if (type == NULL) return (NULL); type->node = node; type->type = XML_SCHEMA_TYPE_COMPLEX; type->flags |= XML_SCHEMAS_TYPE_GLOBAL; } type->targetNamespace = ctxt->targetNamespace; /* * Handle attributes. */ attr = node->properties; while (attr != NULL) { if (attr->ns == NULL) { if (xmlStrEqual(attr->name, BAD_CAST "id")) { /* * Attribute "id". */ xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); } else if (xmlStrEqual(attr->name, BAD_CAST "mixed")) { /* * Attribute "mixed". */ if (xmlSchemaPGetBoolNodeValue(ctxt, NULL, (xmlNodePtr) attr)) type->flags |= XML_SCHEMAS_TYPE_MIXED; } else if (topLevel) { /* * Attributes of global complex type definitions. */ if (xmlStrEqual(attr->name, BAD_CAST "name")) { /* Pass. */ } else if (xmlStrEqual(attr->name, BAD_CAST "abstract")) { /* * Attribute "abstract". */ if (xmlSchemaPGetBoolNodeValue(ctxt, NULL, (xmlNodePtr) attr)) type->flags |= XML_SCHEMAS_TYPE_ABSTRACT; } else if (xmlStrEqual(attr->name, BAD_CAST "final")) { /* * Attribute "final". */ attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (xmlSchemaPValAttrBlockFinal(attrValue, &(type->flags), -1, XML_SCHEMAS_TYPE_FINAL_EXTENSION, XML_SCHEMAS_TYPE_FINAL_RESTRICTION, -1, -1, -1) != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(#all | List of (extension | restriction))", attrValue, NULL, NULL, NULL); } else final = 1; } else if (xmlStrEqual(attr->name, BAD_CAST "block")) { /* * Attribute "block". */ attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); if (xmlSchemaPValAttrBlockFinal(attrValue, &(type->flags), -1, XML_SCHEMAS_TYPE_BLOCK_EXTENSION, XML_SCHEMAS_TYPE_BLOCK_RESTRICTION, -1, -1, -1) != 0) { xmlSchemaPSimpleTypeErr(ctxt, XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, NULL, (xmlNodePtr) attr, NULL, "(#all | List of (extension | restriction)) ", attrValue, NULL, NULL, NULL); } else block = 1; } else { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { xmlSchemaPIllegalAttrErr(ctxt, XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); } attr = attr->next; } if (! block) { /* * Apply default "block" values. */ if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION) type->flags |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION) type->flags |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; } if (! final) { /* * Apply default "block" values. */ if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) type->flags |= XML_SCHEMAS_TYPE_FINAL_RESTRICTION; if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_EXTENSION) type->flags |= XML_SCHEMAS_TYPE_FINAL_EXTENSION; } /* * And now for the children... */ child = node->children; if (IS_SCHEMA(child, "annotation")) { type->annot = xmlSchemaParseAnnotation(ctxt, child, 1); child = child->next; } ctxt->ctxtType = type; if (IS_SCHEMA(child, "simpleContent")) { /* * ... * 3.4.3 : 2.2 * Specifying mixed='true' when the * alternative is chosen has no effect */ if (type->flags & XML_SCHEMAS_TYPE_MIXED) type->flags ^= XML_SCHEMAS_TYPE_MIXED; xmlSchemaParseSimpleContent(ctxt, schema, child, &hasRestrictionOrExtension); child = child->next; } else if (IS_SCHEMA(child, "complexContent")) { /* * ... */ type->contentType = XML_SCHEMA_CONTENT_EMPTY; xmlSchemaParseComplexContent(ctxt, schema, child, &hasRestrictionOrExtension); child = child->next; } else { /* * E.g ... or ... etc. * * SPEC * "...the third alternative (neither nor * ) is chosen. This case is understood as shorthand * for complex content restricting the `ur-type definition`, and the * details of the mappings should be modified as necessary. */ type->baseType = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); type->flags |= XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION; /* * Parse model groups. */ if (IS_SCHEMA(child, "all")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_ALL, 1); child = child->next; } else if (IS_SCHEMA(child, "choice")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_CHOICE, 1); child = child->next; } else if (IS_SCHEMA(child, "sequence")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroup(ctxt, schema, child, XML_SCHEMA_TYPE_SEQUENCE, 1); child = child->next; } else if (IS_SCHEMA(child, "group")) { type->subtypes = (xmlSchemaTypePtr) xmlSchemaParseModelGroupDefRef(ctxt, schema, child); /* * Note that the reference will be resolved in * xmlSchemaResolveTypeReferences(); */ child = child->next; } /* * Parse attribute decls/refs. */ if (xmlSchemaParseLocalAttributes(ctxt, schema, &child, (xmlSchemaItemListPtr *) &(type->attrUses), XML_SCHEMA_TYPE_RESTRICTION, NULL) == -1) return(NULL); /* * Parse attribute wildcard. */ if (IS_SCHEMA(child, "anyAttribute")) { type->attributeWildcard = xmlSchemaParseAnyAttribute(ctxt, schema, child); child = child->next; } } if (child != NULL) { xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, NULL, node, child, NULL, "(annotation?, (simpleContent | complexContent | " "((group | all | choice | sequence)?, ((attribute | " "attributeGroup)*, anyAttribute?))))"); } /* * REDEFINE: SPEC src-redefine (5) */ if (topLevel && ctxt->isRedefine && (! hasRestrictionOrExtension)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_REDEFINE, NULL, node, "This is a redefinition, thus the " " must have a or " "grand-child", NULL); } ctxt->ctxtType = ctxtType; return (type); } /************************************************************************ * * * Validating using Schemas * * * ************************************************************************/ /************************************************************************ * * * Reading/Writing Schemas * * * ************************************************************************/ #if 0 /* Will be enabled if it is clear what options are needed. */ /** * xmlSchemaParserCtxtSetOptions: * @ctxt: a schema parser context * @options: a combination of xmlSchemaParserOption * * Sets the options to be used during the parse. * * Returns 0 in case of success, -1 in case of an * API error. */ static int xmlSchemaParserCtxtSetOptions(xmlSchemaParserCtxtPtr ctxt, int options) { int i; if (ctxt == NULL) return (-1); /* * WARNING: Change the start value if adding to the * xmlSchemaParseOption. */ for (i = 1; i < (int) sizeof(int) * 8; i++) { if (options & 1<options = options; return (0); } /** * xmlSchemaValidCtxtGetOptions: * @ctxt: a schema parser context * * Returns the option combination of the parser context. */ static int xmlSchemaParserCtxtGetOptions(xmlSchemaParserCtxtPtr ctxt) { if (ctxt == NULL) return (-1); else return (ctxt->options); } #endif /** * xmlSchemaNewParserCtxt: * @URL: the location of the schema * * Create an XML Schemas parse context for that file/resource expected * to contain an XML Schemas file. * * Returns the parser context or NULL in case of error */ xmlSchemaParserCtxtPtr xmlSchemaNewParserCtxt(const char *URL) { xmlSchemaParserCtxtPtr ret; if (URL == NULL) return (NULL); ret = xmlSchemaParserCtxtCreate(); if (ret == NULL) return(NULL); ret->dict = xmlDictCreate(); ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1); return (ret); } /** * xmlSchemaNewMemParserCtxt: * @buffer: a pointer to a char array containing the schemas * @size: the size of the array * * Create an XML Schemas parse context for that memory buffer expected * to contain an XML Schemas file. * * Returns the parser context or NULL in case of error */ xmlSchemaParserCtxtPtr xmlSchemaNewMemParserCtxt(const char *buffer, int size) { xmlSchemaParserCtxtPtr ret; if ((buffer == NULL) || (size <= 0)) return (NULL); ret = xmlSchemaParserCtxtCreate(); if (ret == NULL) return(NULL); ret->buffer = buffer; ret->size = size; ret->dict = xmlDictCreate(); return (ret); } /** * xmlSchemaNewDocParserCtxt: * @doc: a preparsed document tree * * Create an XML Schemas 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 */ xmlSchemaParserCtxtPtr xmlSchemaNewDocParserCtxt(xmlDocPtr doc) { xmlSchemaParserCtxtPtr ret; if (doc == NULL) return (NULL); ret = xmlSchemaParserCtxtCreate(); if (ret == NULL) return(NULL); ret->doc = doc; ret->dict = xmlDictCreate(); /* The application has responsibility for the document */ ret->preserve = 1; return (ret); } /** * xmlSchemaFreeParserCtxt: * @ctxt: the schema parser context * * Free the resources associated to the schema parser context */ void xmlSchemaFreeParserCtxt(xmlSchemaParserCtxtPtr ctxt) { if (ctxt == NULL) return; if (ctxt->doc != NULL && !ctxt->preserve) xmlFreeDoc(ctxt->doc); if (ctxt->vctxt != NULL) { xmlSchemaFreeValidCtxt(ctxt->vctxt); } if (ctxt->ownsConstructor && (ctxt->constructor != NULL)) { xmlSchemaConstructionCtxtFree(ctxt->constructor); ctxt->constructor = NULL; ctxt->ownsConstructor = 0; } if (ctxt->attrProhibs != NULL) xmlSchemaItemListFree(ctxt->attrProhibs); xmlDictFree(ctxt->dict); xmlFree(ctxt); } /************************************************************************ * * * Building the content models * * * ************************************************************************/ /** * xmlSchemaBuildContentModelForSubstGroup: * * Returns 1 if nillable, 0 otherwise */ static int xmlSchemaBuildContentModelForSubstGroup(xmlSchemaParserCtxtPtr pctxt, xmlSchemaParticlePtr particle, int counter, xmlAutomataStatePtr end) { xmlAutomataStatePtr start, tmp; xmlSchemaElementPtr elemDecl, member; xmlSchemaSubstGroupPtr substGroup; int i; int ret = 0; elemDecl = (xmlSchemaElementPtr) particle->children; /* * Wrap the substitution group with a CHOICE. */ start = pctxt->state; if (end == NULL) end = xmlAutomataNewState(pctxt->am); substGroup = xmlSchemaSubstGroupGet(pctxt, elemDecl); if (substGroup == NULL) { xmlSchemaPErr(pctxt, WXS_ITEM_NODE(particle), XML_SCHEMAP_INTERNAL, "Internal error: xmlSchemaBuildContentModelForSubstGroup, " "declaration is marked having a subst. group but none " "available.\n", elemDecl->name, NULL); return(0); } if (counter >= 0) { /* * NOTE that we put the declaration in, even if it's abstract. * However, an error will be raised during *validation* if an element * information item shall be validated against an abstract element * declaration. */ tmp = xmlAutomataNewCountedTrans(pctxt->am, start, NULL, counter); xmlAutomataNewTransition2(pctxt->am, tmp, end, elemDecl->name, elemDecl->targetNamespace, elemDecl); /* * Add subst. group members. */ for (i = 0; i < substGroup->members->nbItems; i++) { member = (xmlSchemaElementPtr) substGroup->members->items[i]; xmlAutomataNewTransition2(pctxt->am, tmp, end, member->name, member->targetNamespace, member); } } else if (particle->maxOccurs == 1) { /* * NOTE that we put the declaration in, even if it's abstract, */ xmlAutomataNewEpsilon(pctxt->am, xmlAutomataNewTransition2(pctxt->am, start, NULL, elemDecl->name, elemDecl->targetNamespace, elemDecl), end); /* * Add subst. group members. */ for (i = 0; i < substGroup->members->nbItems; i++) { member = (xmlSchemaElementPtr) substGroup->members->items[i]; /* * NOTE: This fixes bug #341150. xmlAutomataNewOnceTrans2() * was incorrectly used instead of xmlAutomataNewTransition2() * (seems like a copy&paste bug from the XML_SCHEMA_TYPE_ALL * section in xmlSchemaBuildAContentModel() ). * TODO: Check if xmlAutomataNewOnceTrans2() was instead * intended for the above "counter" section originally. I.e., * check xs:all with subst-groups. * * tmp = xmlAutomataNewOnceTrans2(pctxt->am, start, NULL, * member->name, member->targetNamespace, * 1, 1, member); */ tmp = xmlAutomataNewTransition2(pctxt->am, start, NULL, member->name, member->targetNamespace, member); xmlAutomataNewEpsilon(pctxt->am, tmp, end); } } else { xmlAutomataStatePtr hop; int maxOccurs = particle->maxOccurs == UNBOUNDED ? UNBOUNDED : particle->maxOccurs - 1; int minOccurs = particle->minOccurs < 1 ? 0 : particle->minOccurs - 1; counter = xmlAutomataNewCounter(pctxt->am, minOccurs, maxOccurs); hop = xmlAutomataNewState(pctxt->am); xmlAutomataNewEpsilon(pctxt->am, xmlAutomataNewTransition2(pctxt->am, start, NULL, elemDecl->name, elemDecl->targetNamespace, elemDecl), hop); /* * Add subst. group members. */ for (i = 0; i < substGroup->members->nbItems; i++) { member = (xmlSchemaElementPtr) substGroup->members->items[i]; xmlAutomataNewEpsilon(pctxt->am, xmlAutomataNewTransition2(pctxt->am, start, NULL, member->name, member->targetNamespace, member), hop); } xmlAutomataNewCountedTrans(pctxt->am, hop, start, counter); xmlAutomataNewCounterTrans(pctxt->am, hop, end, counter); } if (particle->minOccurs == 0) { xmlAutomataNewEpsilon(pctxt->am, start, end); ret = 1; } pctxt->state = end; return(ret); } /** * xmlSchemaBuildContentModelForElement: * * Returns 1 if nillable, 0 otherwise */ static int xmlSchemaBuildContentModelForElement(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr particle) { int ret = 0; if (((xmlSchemaElementPtr) particle->children)->flags & XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD) { /* * Substitution groups. */ ret = xmlSchemaBuildContentModelForSubstGroup(ctxt, particle, -1, NULL); } else { xmlSchemaElementPtr elemDecl; xmlAutomataStatePtr start; elemDecl = (xmlSchemaElementPtr) particle->children; if (elemDecl->flags & XML_SCHEMAS_ELEM_ABSTRACT) return(0); if (particle->maxOccurs == 1) { start = ctxt->state; ctxt->state = xmlAutomataNewTransition2(ctxt->am, start, NULL, elemDecl->name, elemDecl->targetNamespace, elemDecl); } else if ((particle->maxOccurs >= UNBOUNDED) && (particle->minOccurs < 2)) { /* Special case. */ start = ctxt->state; ctxt->state = xmlAutomataNewTransition2(ctxt->am, start, NULL, elemDecl->name, elemDecl->targetNamespace, elemDecl); ctxt->state = xmlAutomataNewTransition2(ctxt->am, ctxt->state, ctxt->state, elemDecl->name, elemDecl->targetNamespace, elemDecl); } else { int counter; int maxOccurs = particle->maxOccurs == UNBOUNDED ? UNBOUNDED : particle->maxOccurs - 1; int minOccurs = particle->minOccurs < 1 ? 0 : particle->minOccurs - 1; start = xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL); counter = xmlAutomataNewCounter(ctxt->am, minOccurs, maxOccurs); ctxt->state = xmlAutomataNewTransition2(ctxt->am, start, NULL, elemDecl->name, elemDecl->targetNamespace, elemDecl); xmlAutomataNewCountedTrans(ctxt->am, ctxt->state, start, counter); ctxt->state = xmlAutomataNewCounterTrans(ctxt->am, ctxt->state, NULL, counter); } if (particle->minOccurs == 0) { xmlAutomataNewEpsilon(ctxt->am, start, ctxt->state); ret = 1; } } return(ret); } /** * xmlSchemaBuildAContentModel: * @ctxt: the schema parser context * @particle: the particle component * @name: the complex type's name whose content is being built * * Create the automaton for the {content type} of a complex type. * * Returns 1 if the content is nillable, 0 otherwise */ static int xmlSchemaBuildAContentModel(xmlSchemaParserCtxtPtr pctxt, xmlSchemaParticlePtr particle) { int ret = 0, tmp2; if (particle == NULL) { PERROR_INT("xmlSchemaBuildAContentModel", "particle is NULL"); return(1); } if (particle->children == NULL) { /* * Just return in this case. A missing "term" of the particle * might arise due to an invalid "term" component. */ return(1); } switch (particle->children->type) { case XML_SCHEMA_TYPE_ANY: { xmlAutomataStatePtr start, end; xmlSchemaWildcardPtr wild; xmlSchemaWildcardNsPtr ns; wild = (xmlSchemaWildcardPtr) particle->children; start = pctxt->state; end = xmlAutomataNewState(pctxt->am); if (particle->maxOccurs == 1) { if (wild->any == 1) { /* * We need to add both transitions: * * 1. the {"*", "*"} for elements in a namespace. */ pctxt->state = xmlAutomataNewTransition2(pctxt->am, start, NULL, BAD_CAST "*", BAD_CAST "*", wild); xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); /* * 2. the {"*"} for elements in no namespace. */ pctxt->state = xmlAutomataNewTransition2(pctxt->am, start, NULL, BAD_CAST "*", NULL, wild); xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); } else if (wild->nsSet != NULL) { ns = wild->nsSet; do { pctxt->state = start; pctxt->state = xmlAutomataNewTransition2(pctxt->am, pctxt->state, NULL, BAD_CAST "*", ns->value, wild); xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); ns = ns->next; } while (ns != NULL); } else if (wild->negNsSet != NULL) { pctxt->state = xmlAutomataNewNegTrans(pctxt->am, start, end, BAD_CAST "*", wild->negNsSet->value, wild); } } else { int counter; xmlAutomataStatePtr hop; int maxOccurs = particle->maxOccurs == UNBOUNDED ? UNBOUNDED : particle->maxOccurs - 1; int minOccurs = particle->minOccurs < 1 ? 0 : particle->minOccurs - 1; counter = xmlAutomataNewCounter(pctxt->am, minOccurs, maxOccurs); hop = xmlAutomataNewState(pctxt->am); if (wild->any == 1) { pctxt->state = xmlAutomataNewTransition2(pctxt->am, start, NULL, BAD_CAST "*", BAD_CAST "*", wild); xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); pctxt->state = xmlAutomataNewTransition2(pctxt->am, start, NULL, BAD_CAST "*", NULL, wild); xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); } else if (wild->nsSet != NULL) { ns = wild->nsSet; do { pctxt->state = xmlAutomataNewTransition2(pctxt->am, start, NULL, BAD_CAST "*", ns->value, wild); xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); ns = ns->next; } while (ns != NULL); } else if (wild->negNsSet != NULL) { pctxt->state = xmlAutomataNewNegTrans(pctxt->am, start, hop, BAD_CAST "*", wild->negNsSet->value, wild); } xmlAutomataNewCountedTrans(pctxt->am, hop, start, counter); xmlAutomataNewCounterTrans(pctxt->am, hop, end, counter); } if (particle->minOccurs == 0) { xmlAutomataNewEpsilon(pctxt->am, start, end); ret = 1; } pctxt->state = end; break; } case XML_SCHEMA_TYPE_ELEMENT: ret = xmlSchemaBuildContentModelForElement(pctxt, particle); break; case XML_SCHEMA_TYPE_SEQUENCE:{ xmlSchemaTreeItemPtr sub; ret = 1; /* * If max and min occurrences are default (1) then * simply iterate over the particles of the . */ if ((particle->minOccurs == 1) && (particle->maxOccurs == 1)) { sub = particle->children->children; while (sub != NULL) { tmp2 = xmlSchemaBuildAContentModel(pctxt, (xmlSchemaParticlePtr) sub); if (tmp2 != 1) ret = 0; sub = sub->next; } } else { xmlAutomataStatePtr oldstate = pctxt->state; if (particle->maxOccurs >= UNBOUNDED) { if (particle->minOccurs > 1) { xmlAutomataStatePtr tmp; int counter; pctxt->state = xmlAutomataNewEpsilon(pctxt->am, oldstate, NULL); oldstate = pctxt->state; counter = xmlAutomataNewCounter(pctxt->am, particle->minOccurs - 1, UNBOUNDED); sub = particle->children->children; while (sub != NULL) { tmp2 = xmlSchemaBuildAContentModel(pctxt, (xmlSchemaParticlePtr) sub); if (tmp2 != 1) ret = 0; sub = sub->next; } tmp = pctxt->state; xmlAutomataNewCountedTrans(pctxt->am, tmp, oldstate, counter); pctxt->state = xmlAutomataNewCounterTrans(pctxt->am, tmp, NULL, counter); if (ret == 1) xmlAutomataNewEpsilon(pctxt->am, oldstate, pctxt->state); } else { pctxt->state = xmlAutomataNewEpsilon(pctxt->am, oldstate, NULL); oldstate = pctxt->state; sub = particle->children->children; while (sub != NULL) { tmp2 = xmlSchemaBuildAContentModel(pctxt, (xmlSchemaParticlePtr) sub); if (tmp2 != 1) ret = 0; sub = sub->next; } xmlAutomataNewEpsilon(pctxt->am, pctxt->state, oldstate); /* * epsilon needed to block previous trans from * being allowed to enter back from another * construct */ pctxt->state = xmlAutomataNewEpsilon(pctxt->am, pctxt->state, NULL); if (particle->minOccurs == 0) { xmlAutomataNewEpsilon(pctxt->am, oldstate, pctxt->state); ret = 1; } } } else if ((particle->maxOccurs > 1) || (particle->minOccurs > 1)) { xmlAutomataStatePtr tmp; int counter; pctxt->state = xmlAutomataNewEpsilon(pctxt->am, oldstate, NULL); oldstate = pctxt->state; counter = xmlAutomataNewCounter(pctxt->am, particle->minOccurs - 1, particle->maxOccurs - 1); sub = particle->children->children; while (sub != NULL) { tmp2 = xmlSchemaBuildAContentModel(pctxt, (xmlSchemaParticlePtr) sub); if (tmp2 != 1) ret = 0; sub = sub->next; } tmp = pctxt->state; xmlAutomataNewCountedTrans(pctxt->am, tmp, oldstate, counter); pctxt->state = xmlAutomataNewCounterTrans(pctxt->am, tmp, NULL, counter); if ((particle->minOccurs == 0) || (ret == 1)) { xmlAutomataNewEpsilon(pctxt->am, oldstate, pctxt->state); ret = 1; } } else { sub = particle->children->children; while (sub != NULL) { tmp2 = xmlSchemaBuildAContentModel(pctxt, (xmlSchemaParticlePtr) sub); if (tmp2 != 1) ret = 0; sub = sub->next; } /* * epsilon needed to block previous trans from * being allowed to enter back from another * construct */ pctxt->state = xmlAutomataNewEpsilon(pctxt->am, pctxt->state, NULL); if (particle->minOccurs == 0) { xmlAutomataNewEpsilon(pctxt->am, oldstate, pctxt->state); ret = 1; } } } break; } case XML_SCHEMA_TYPE_CHOICE:{ xmlSchemaTreeItemPtr sub; xmlAutomataStatePtr start, end; ret = 0; start = pctxt->state; end = xmlAutomataNewState(pctxt->am); /* * iterate over the subtypes and remerge the end with an * epsilon transition */ if (particle->maxOccurs == 1) { sub = particle->children->children; while (sub != NULL) { pctxt->state = start; tmp2 = xmlSchemaBuildAContentModel(pctxt, (xmlSchemaParticlePtr) sub); if (tmp2 == 1) ret = 1; xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); sub = sub->next; } } else { int counter; xmlAutomataStatePtr hop, base; int maxOccurs = particle->maxOccurs == UNBOUNDED ? UNBOUNDED : particle->maxOccurs - 1; int minOccurs = particle->minOccurs < 1 ? 0 : particle->minOccurs - 1; /* * use a counter to keep track of the number of transitions * which went through the choice. */ counter = xmlAutomataNewCounter(pctxt->am, minOccurs, maxOccurs); hop = xmlAutomataNewState(pctxt->am); base = xmlAutomataNewState(pctxt->am); sub = particle->children->children; while (sub != NULL) { pctxt->state = base; tmp2 = xmlSchemaBuildAContentModel(pctxt, (xmlSchemaParticlePtr) sub); if (tmp2 == 1) ret = 1; xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); sub = sub->next; } xmlAutomataNewEpsilon(pctxt->am, start, base); xmlAutomataNewCountedTrans(pctxt->am, hop, base, counter); xmlAutomataNewCounterTrans(pctxt->am, hop, end, counter); if (ret == 1) xmlAutomataNewEpsilon(pctxt->am, base, end); } if (particle->minOccurs == 0) { xmlAutomataNewEpsilon(pctxt->am, start, end); ret = 1; } pctxt->state = end; break; } case XML_SCHEMA_TYPE_ALL:{ xmlAutomataStatePtr start, tmp; xmlSchemaParticlePtr sub; xmlSchemaElementPtr elemDecl; ret = 1; sub = (xmlSchemaParticlePtr) particle->children->children; if (sub == NULL) break; ret = 0; start = pctxt->state; tmp = xmlAutomataNewState(pctxt->am); xmlAutomataNewEpsilon(pctxt->am, pctxt->state, tmp); pctxt->state = tmp; while (sub != NULL) { pctxt->state = tmp; elemDecl = (xmlSchemaElementPtr) sub->children; if (elemDecl == NULL) { PERROR_INT("xmlSchemaBuildAContentModel", " particle has no term"); return(ret); }; /* * NOTE: The {max occurs} of all the particles in the * {particles} of the group must be 0 or 1; this is * already ensured during the parse of the content of * . */ if (elemDecl->flags & XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD) { int counter; /* * This is an abstract group, we need to share * the same counter for all the element transitions * derived from the group */ counter = xmlAutomataNewCounter(pctxt->am, sub->minOccurs, sub->maxOccurs); xmlSchemaBuildContentModelForSubstGroup(pctxt, sub, counter, pctxt->state); } else { if ((sub->minOccurs == 1) && (sub->maxOccurs == 1)) { xmlAutomataNewOnceTrans2(pctxt->am, pctxt->state, pctxt->state, elemDecl->name, elemDecl->targetNamespace, 1, 1, elemDecl); } else if ((sub->minOccurs == 0) && (sub->maxOccurs == 1)) { xmlAutomataNewCountTrans2(pctxt->am, pctxt->state, pctxt->state, elemDecl->name, elemDecl->targetNamespace, 0, 1, elemDecl); } } sub = (xmlSchemaParticlePtr) sub->next; } pctxt->state = xmlAutomataNewAllTrans(pctxt->am, pctxt->state, NULL, 0); if (particle->minOccurs == 0) { xmlAutomataNewEpsilon(pctxt->am, start, pctxt->state); ret = 1; } break; } case XML_SCHEMA_TYPE_GROUP: /* * If we hit a model group definition, then this means that * it was empty, thus was not substituted for the containing * model group. Just do nothing in this case. * TODO: But the group should be substituted and not occur at * all in the content model at this point. Fix this. */ ret = 1; break; default: xmlSchemaInternalErr2(ACTXT_CAST pctxt, "xmlSchemaBuildAContentModel", "found unexpected term of type '%s' in content model", WXS_ITEM_TYPE_NAME(particle->children), NULL); return(ret); } return(ret); } /** * xmlSchemaBuildContentModel: * @ctxt: the schema parser context * @type: the complex type definition * @name: the element name * * Builds the content model of the complex type. */ static void xmlSchemaBuildContentModel(xmlSchemaTypePtr type, xmlSchemaParserCtxtPtr ctxt) { if ((type->type != XML_SCHEMA_TYPE_COMPLEX) || (type->contModel != NULL) || ((type->contentType != XML_SCHEMA_CONTENT_ELEMENTS) && (type->contentType != XML_SCHEMA_CONTENT_MIXED))) return; ctxt->am = NULL; ctxt->am = xmlNewAutomata(); if (ctxt->am == NULL) { xmlSchemaPErrMemory(ctxt); return; } ctxt->state = xmlAutomataGetInitState(ctxt->am); /* * Build the automaton. */ xmlSchemaBuildAContentModel(ctxt, WXS_TYPE_PARTICLE(type)); xmlAutomataSetFinalState(ctxt->am, ctxt->state); type->contModel = xmlAutomataCompile(ctxt->am); if (type->contModel == NULL) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_INTERNAL, WXS_BASIC_CAST type, type->node, "Failed to compile the content model", NULL); } else if (xmlRegexpIsDeterminist(type->contModel) != 1) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_NOT_DETERMINISTIC, /* XML_SCHEMAS_ERR_NOTDETERMINIST, */ WXS_BASIC_CAST type, type->node, "The content model is not determinist", NULL); } else { } ctxt->state = NULL; xmlFreeAutomata(ctxt->am); ctxt->am = NULL; } /** * xmlSchemaResolveElementReferences: * @elem: the schema element context * @ctxt: the schema parser context * * Resolves the references of an element declaration * or particle, which has an element declaration as it's * term. */ static void xmlSchemaResolveElementReferences(xmlSchemaElementPtr elemDecl, xmlSchemaParserCtxtPtr ctxt) { if ((ctxt == NULL) || (elemDecl == NULL) || ((elemDecl != NULL) && (elemDecl->flags & XML_SCHEMAS_ELEM_INTERNAL_RESOLVED))) return; elemDecl->flags |= XML_SCHEMAS_ELEM_INTERNAL_RESOLVED; if ((elemDecl->subtypes == NULL) && (elemDecl->namedType != NULL)) { xmlSchemaTypePtr type; /* (type definition) ... otherwise the type definition `resolved` * to by the `actual value` of the type [attribute] ... */ type = xmlSchemaGetType(ctxt->schema, elemDecl->namedType, elemDecl->namedTypeNs); if (type == NULL) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST elemDecl, elemDecl->node, "type", elemDecl->namedType, elemDecl->namedTypeNs, XML_SCHEMA_TYPE_BASIC, "type definition"); } else elemDecl->subtypes = type; } if (elemDecl->substGroup != NULL) { xmlSchemaElementPtr substHead; /* * FIXME TODO: Do we need a new field in _xmlSchemaElement for * substitutionGroup? */ substHead = xmlSchemaGetElem(ctxt->schema, elemDecl->substGroup, elemDecl->substGroupNs); if (substHead == NULL) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST elemDecl, NULL, "substitutionGroup", elemDecl->substGroup, elemDecl->substGroupNs, XML_SCHEMA_TYPE_ELEMENT, NULL); } else { xmlSchemaResolveElementReferences(substHead, ctxt); /* * Set the "substitution group affiliation". * NOTE that now we use the "refDecl" field for this. */ WXS_SUBST_HEAD(elemDecl) = substHead; /* * The type definitions is set to: * SPEC "...the {type definition} of the element * declaration `resolved` to by the `actual value` * of the substitutionGroup [attribute], if present" */ if (elemDecl->subtypes == NULL) { if (substHead->subtypes == NULL) { /* * This can happen with self-referencing substitution * groups. The cycle will be detected later, but we have * to set subtypes to avoid null-pointer dereferences. */ elemDecl->subtypes = xmlSchemaGetBuiltInType( XML_SCHEMAS_ANYTYPE); } else { elemDecl->subtypes = substHead->subtypes; } } } } /* * SPEC "The definition of anyType serves as the default type definition * for element declarations whose XML representation does not specify one." */ if ((elemDecl->subtypes == NULL) && (elemDecl->namedType == NULL) && (elemDecl->substGroup == NULL)) elemDecl->subtypes = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); } /** * xmlSchemaResolveUnionMemberTypes: * @ctxt: the schema parser context * @type: the schema simple type definition * * Checks and builds the "member type definitions" property of the union * simple type. This handles part (1), part (2) is done in * xmlSchemaFinishMemberTypeDefinitionsProperty() * * Returns -1 in case of an internal error, 0 otherwise. */ static int xmlSchemaResolveUnionMemberTypes(xmlSchemaParserCtxtPtr ctxt, xmlSchemaTypePtr type) { xmlSchemaTypeLinkPtr link, lastLink, newLink; xmlSchemaTypePtr memberType; /* * SPEC (1) "If the alternative is chosen, then [Definition:] * define the explicit members as the type definitions `resolved` * to by the items in the `actual value` of the memberTypes [attribute], * if any, followed by the type definitions corresponding to the * s among the [children] of , if any." */ /* * Resolve references. */ link = type->memberTypes; lastLink = NULL; while (link != NULL) { const xmlChar *name, *nsName; name = ((xmlSchemaQNameRefPtr) link->type)->name; nsName = ((xmlSchemaQNameRefPtr) link->type)->targetNamespace; memberType = xmlSchemaGetType(ctxt->schema, name, nsName); if ((memberType == NULL) || (! WXS_IS_SIMPLE(memberType))) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST type, type->node, "memberTypes", name, nsName, XML_SCHEMA_TYPE_SIMPLE, NULL); /* * Remove the member type link. */ if (lastLink == NULL) type->memberTypes = link->next; else lastLink->next = link->next; newLink = link; link = link->next; xmlFree(newLink); } else { link->type = memberType; lastLink = link; link = link->next; } } /* * Add local simple types, */ memberType = type->subtypes; while (memberType != NULL) { link = (xmlSchemaTypeLinkPtr) xmlMalloc(sizeof(xmlSchemaTypeLink)); if (link == NULL) { xmlSchemaPErrMemory(ctxt); return (-1); } link->type = memberType; link->next = NULL; if (lastLink == NULL) type->memberTypes = link; else lastLink->next = link; lastLink = link; memberType = memberType->next; } return (0); } /** * xmlSchemaIsDerivedFromBuiltInType: * @ctxt: the schema parser context * @type: the type definition * @valType: the value type * * * Returns 1 if the type has the given value type, or * is derived from such a type. */ static int xmlSchemaIsDerivedFromBuiltInType(xmlSchemaTypePtr type, int valType) { if (type == NULL) return (0); if (WXS_IS_COMPLEX(type)) return (0); if (type->type == XML_SCHEMA_TYPE_BASIC) { if (type->builtInType == valType) return(1); if ((type->builtInType == XML_SCHEMAS_ANYSIMPLETYPE) || (type->builtInType == XML_SCHEMAS_ANYTYPE)) return (0); return(xmlSchemaIsDerivedFromBuiltInType(type->subtypes, valType)); } return(xmlSchemaIsDerivedFromBuiltInType(type->subtypes, valType)); } #if 0 /** * xmlSchemaIsDerivedFromBuiltInType: * @ctxt: the schema parser context * @type: the type definition * @valType: the value type * * * Returns 1 if the type has the given value type, or * is derived from such a type. */ static int xmlSchemaIsUserDerivedFromBuiltInType(xmlSchemaTypePtr type, int valType) { if (type == NULL) return (0); if (WXS_IS_COMPLEX(type)) return (0); if (type->type == XML_SCHEMA_TYPE_BASIC) { if (type->builtInType == valType) return(1); return (0); } else return(xmlSchemaIsDerivedFromBuiltInType(type->subtypes, valType)); return (0); } static xmlSchemaTypePtr xmlSchemaQueryBuiltInType(xmlSchemaTypePtr type) { if (type == NULL) return (NULL); if (WXS_IS_COMPLEX(type)) return (NULL); if (type->type == XML_SCHEMA_TYPE_BASIC) return(type); return(xmlSchemaQueryBuiltInType(type->subtypes)); } #endif /** * xmlSchemaGetPrimitiveType: * @type: the simpleType definition * * Returns the primitive type of the given type or * NULL in case of error. */ static xmlSchemaTypePtr xmlSchemaGetPrimitiveType(xmlSchemaTypePtr type) { while (type != NULL) { /* * Note that anySimpleType is actually not a primitive type * but we need that here. */ if ((type->builtInType == XML_SCHEMAS_ANYSIMPLETYPE) || (type->flags & XML_SCHEMAS_TYPE_BUILTIN_PRIMITIVE)) return (type); type = type->baseType; } return (NULL); } #if 0 /** * xmlSchemaGetBuiltInTypeAncestor: * @type: the simpleType definition * * Returns the primitive type of the given type or * NULL in case of error. */ static xmlSchemaTypePtr xmlSchemaGetBuiltInTypeAncestor(xmlSchemaTypePtr type) { if (WXS_IS_LIST(type) || WXS_IS_UNION(type)) return (0); while (type != NULL) { if (type->type == XML_SCHEMA_TYPE_BASIC) return (type); type = type->baseType; } return (NULL); } #endif /** * xmlSchemaCloneWildcardNsConstraints: * @ctxt: the schema parser context * @dest: the destination wildcard * @source: the source wildcard * * Clones the namespace constraints of source * and assigns them to dest. * Returns -1 on internal error, 0 otherwise. */ static int xmlSchemaCloneWildcardNsConstraints(xmlSchemaParserCtxtPtr ctxt, xmlSchemaWildcardPtr dest, xmlSchemaWildcardPtr source) { xmlSchemaWildcardNsPtr cur, tmp, last; if ((source == NULL) || (dest == NULL)) return(-1); dest->any = source->any; cur = source->nsSet; last = NULL; while (cur != NULL) { tmp = xmlSchemaNewWildcardNsConstraint(ctxt); if (tmp == NULL) return(-1); tmp->value = cur->value; if (last == NULL) dest->nsSet = tmp; else last->next = tmp; last = tmp; cur = cur->next; } if (dest->negNsSet != NULL) xmlSchemaFreeWildcardNsSet(dest->negNsSet); if (source->negNsSet != NULL) { dest->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); if (dest->negNsSet == NULL) return(-1); dest->negNsSet->value = source->negNsSet->value; } else dest->negNsSet = NULL; return(0); } /** * xmlSchemaUnionWildcards: * @ctxt: the schema parser context * @completeWild: the first wildcard * @curWild: the second wildcard * * Unions the namespace constraints of the given wildcards. * @completeWild will hold the resulting union. * Returns a positive error code on failure, -1 in case of an * internal error, 0 otherwise. */ static int xmlSchemaUnionWildcards(xmlSchemaParserCtxtPtr ctxt, xmlSchemaWildcardPtr completeWild, xmlSchemaWildcardPtr curWild) { xmlSchemaWildcardNsPtr cur, curB, tmp; /* * 1 If O1 and O2 are the same value, then that value must be the * value. */ if ((completeWild->any == curWild->any) && ((completeWild->nsSet == NULL) == (curWild->nsSet == NULL)) && ((completeWild->negNsSet == NULL) == (curWild->negNsSet == NULL))) { if ((completeWild->negNsSet == NULL) || (completeWild->negNsSet->value == curWild->negNsSet->value)) { if (completeWild->nsSet != NULL) { int found = 0; /* * Check equality of sets. */ cur = completeWild->nsSet; while (cur != NULL) { found = 0; curB = curWild->nsSet; while (curB != NULL) { if (cur->value == curB->value) { found = 1; break; } curB = curB->next; } if (!found) break; cur = cur->next; } if (found) return(0); } else return(0); } } /* * 2 If either O1 or O2 is any, then any must be the value */ if (completeWild->any != curWild->any) { if (completeWild->any == 0) { completeWild->any = 1; if (completeWild->nsSet != NULL) { xmlSchemaFreeWildcardNsSet(completeWild->nsSet); completeWild->nsSet = NULL; } if (completeWild->negNsSet != NULL) { xmlFree(completeWild->negNsSet); completeWild->negNsSet = NULL; } } return (0); } /* * 3 If both O1 and O2 are sets of (namespace names or `absent`), * then the union of those sets must be the value. */ if ((completeWild->nsSet != NULL) && (curWild->nsSet != NULL)) { int found; xmlSchemaWildcardNsPtr start; cur = curWild->nsSet; start = completeWild->nsSet; while (cur != NULL) { found = 0; curB = start; while (curB != NULL) { if (cur->value == curB->value) { found = 1; break; } curB = curB->next; } if (!found) { tmp = xmlSchemaNewWildcardNsConstraint(ctxt); if (tmp == NULL) return (-1); tmp->value = cur->value; tmp->next = completeWild->nsSet; completeWild->nsSet = tmp; } cur = cur->next; } return(0); } /* * 4 If the two are negations of different values (namespace names * or `absent`), then a pair of not and `absent` must be the value. */ if ((completeWild->negNsSet != NULL) && (curWild->negNsSet != NULL) && (completeWild->negNsSet->value != curWild->negNsSet->value)) { completeWild->negNsSet->value = NULL; return(0); } /* * 5. */ if (((completeWild->negNsSet != NULL) && (completeWild->negNsSet->value != NULL) && (curWild->nsSet != NULL)) || ((curWild->negNsSet != NULL) && (curWild->negNsSet->value != NULL) && (completeWild->nsSet != NULL))) { int nsFound, absentFound = 0; if (completeWild->nsSet != NULL) { cur = completeWild->nsSet; curB = curWild->negNsSet; } else { cur = curWild->nsSet; curB = completeWild->negNsSet; } nsFound = 0; while (cur != NULL) { if (cur->value == NULL) absentFound = 1; else if (cur->value == curB->value) nsFound = 1; if (nsFound && absentFound) break; cur = cur->next; } if (nsFound && absentFound) { /* * 5.1 If the set S includes both the negated namespace * name and `absent`, then any must be the value. */ completeWild->any = 1; if (completeWild->nsSet != NULL) { xmlSchemaFreeWildcardNsSet(completeWild->nsSet); completeWild->nsSet = NULL; } if (completeWild->negNsSet != NULL) { xmlFree(completeWild->negNsSet); completeWild->negNsSet = NULL; } } else if (nsFound && (!absentFound)) { /* * 5.2 If the set S includes the negated namespace name * but not `absent`, then a pair of not and `absent` must * be the value. */ if (completeWild->nsSet != NULL) { xmlSchemaFreeWildcardNsSet(completeWild->nsSet); completeWild->nsSet = NULL; } if (completeWild->negNsSet == NULL) { completeWild->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); if (completeWild->negNsSet == NULL) return (-1); } completeWild->negNsSet->value = NULL; } else if ((!nsFound) && absentFound) { /* * 5.3 If the set S includes `absent` but not the negated * namespace name, then the union is not expressible. */ xmlSchemaPErr(ctxt, completeWild->node, XML_SCHEMAP_UNION_NOT_EXPRESSIBLE, "The union of the wildcard is not expressible.\n", NULL, NULL); return(XML_SCHEMAP_UNION_NOT_EXPRESSIBLE); } else if ((!nsFound) && (!absentFound)) { /* * 5.4 If the set S does not include either the negated namespace * name or `absent`, then whichever of O1 or O2 is a pair of not * and a namespace name must be the value. */ if (completeWild->negNsSet == NULL) { if (completeWild->nsSet != NULL) { xmlSchemaFreeWildcardNsSet(completeWild->nsSet); completeWild->nsSet = NULL; } completeWild->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); if (completeWild->negNsSet == NULL) return (-1); completeWild->negNsSet->value = curWild->negNsSet->value; } } return (0); } /* * 6. */ if (((completeWild->negNsSet != NULL) && (completeWild->negNsSet->value == NULL) && (curWild->nsSet != NULL)) || ((curWild->negNsSet != NULL) && (curWild->negNsSet->value == NULL) && (completeWild->nsSet != NULL))) { if (completeWild->nsSet != NULL) { cur = completeWild->nsSet; } else { cur = curWild->nsSet; } while (cur != NULL) { if (cur->value == NULL) { /* * 6.1 If the set S includes `absent`, then any must be the * value. */ completeWild->any = 1; if (completeWild->nsSet != NULL) { xmlSchemaFreeWildcardNsSet(completeWild->nsSet); completeWild->nsSet = NULL; } if (completeWild->negNsSet != NULL) { xmlFree(completeWild->negNsSet); completeWild->negNsSet = NULL; } return (0); } cur = cur->next; } if (completeWild->negNsSet == NULL) { /* * 6.2 If the set S does not include `absent`, then a pair of not * and `absent` must be the value. */ if (completeWild->nsSet != NULL) { xmlSchemaFreeWildcardNsSet(completeWild->nsSet); completeWild->nsSet = NULL; } completeWild->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); if (completeWild->negNsSet == NULL) return (-1); completeWild->negNsSet->value = NULL; } return (0); } return (0); } /** * xmlSchemaIntersectWildcards: * @ctxt: the schema parser context * @completeWild: the first wildcard * @curWild: the second wildcard * * Intersects the namespace constraints of the given wildcards. * @completeWild will hold the resulting intersection. * Returns a positive error code on failure, -1 in case of an * internal error, 0 otherwise. */ static int xmlSchemaIntersectWildcards(xmlSchemaParserCtxtPtr ctxt, xmlSchemaWildcardPtr completeWild, xmlSchemaWildcardPtr curWild) { xmlSchemaWildcardNsPtr cur, curB, prev, tmp; /* * 1 If O1 and O2 are the same value, then that value must be the * value. */ if ((completeWild->any == curWild->any) && ((completeWild->nsSet == NULL) == (curWild->nsSet == NULL)) && ((completeWild->negNsSet == NULL) == (curWild->negNsSet == NULL))) { if ((completeWild->negNsSet == NULL) || (completeWild->negNsSet->value == curWild->negNsSet->value)) { if (completeWild->nsSet != NULL) { int found = 0; /* * Check equality of sets. */ cur = completeWild->nsSet; while (cur != NULL) { found = 0; curB = curWild->nsSet; while (curB != NULL) { if (cur->value == curB->value) { found = 1; break; } curB = curB->next; } if (!found) break; cur = cur->next; } if (found) return(0); } else return(0); } } /* * 2 If either O1 or O2 is any, then the other must be the value. */ if ((completeWild->any != curWild->any) && (completeWild->any)) { if (xmlSchemaCloneWildcardNsConstraints(ctxt, completeWild, curWild) == -1) return(-1); return(0); } /* * 3 If either O1 or O2 is a pair of not and a value (a namespace * name or `absent`) and the other is a set of (namespace names or * `absent`), then that set, minus the negated value if it was in * the set, minus `absent` if it was in the set, must be the value. */ if (((completeWild->negNsSet != NULL) && (curWild->nsSet != NULL)) || ((curWild->negNsSet != NULL) && (completeWild->nsSet != NULL))) { const xmlChar *neg; if (completeWild->nsSet == NULL) { neg = completeWild->negNsSet->value; if (xmlSchemaCloneWildcardNsConstraints(ctxt, completeWild, curWild) == -1) return(-1); } else neg = curWild->negNsSet->value; /* * Remove absent and negated. */ prev = NULL; cur = completeWild->nsSet; while (cur != NULL) { if (cur->value == NULL) { if (prev == NULL) completeWild->nsSet = cur->next; else prev->next = cur->next; xmlFree(cur); break; } prev = cur; cur = cur->next; } if (neg != NULL) { prev = NULL; cur = completeWild->nsSet; while (cur != NULL) { if (cur->value == neg) { if (prev == NULL) completeWild->nsSet = cur->next; else prev->next = cur->next; xmlFree(cur); break; } prev = cur; cur = cur->next; } } return(0); } /* * 4 If both O1 and O2 are sets of (namespace names or `absent`), * then the intersection of those sets must be the value. */ if ((completeWild->nsSet != NULL) && (curWild->nsSet != NULL)) { int found; cur = completeWild->nsSet; prev = NULL; while (cur != NULL) { found = 0; curB = curWild->nsSet; while (curB != NULL) { if (cur->value == curB->value) { found = 1; break; } curB = curB->next; } if (!found) { if (prev == NULL) completeWild->nsSet = cur->next; else prev->next = cur->next; tmp = cur->next; xmlFree(cur); cur = tmp; continue; } prev = cur; cur = cur->next; } return(0); } /* 5 If the two are negations of different namespace names, * then the intersection is not expressible */ if ((completeWild->negNsSet != NULL) && (curWild->negNsSet != NULL) && (completeWild->negNsSet->value != curWild->negNsSet->value) && (completeWild->negNsSet->value != NULL) && (curWild->negNsSet->value != NULL)) { xmlSchemaPErr(ctxt, completeWild->node, XML_SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE, "The intersection of the wildcard is not expressible.\n", NULL, NULL); return(XML_SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE); } /* * 6 If the one is a negation of a namespace name and the other * is a negation of `absent`, then the one which is the negation * of a namespace name must be the value. */ if ((completeWild->negNsSet != NULL) && (curWild->negNsSet != NULL) && (completeWild->negNsSet->value != curWild->negNsSet->value) && (completeWild->negNsSet->value == NULL)) { completeWild->negNsSet->value = curWild->negNsSet->value; } return(0); } /** * xmlSchemaIsWildcardNsConstraintSubset: * @ctxt: the schema parser context * @sub: the first wildcard * @super: the second wildcard * * Schema Component Constraint: Wildcard Subset (cos-ns-subset) * * Returns 0 if the namespace constraint of @sub is an intensional * subset of @super, 1 otherwise. */ static int xmlSchemaCheckCOSNSSubset(xmlSchemaWildcardPtr sub, xmlSchemaWildcardPtr super) { /* * 1 super must be any. */ if (super->any) return (0); /* * 2.1 sub must be a pair of not and a namespace name or `absent`. * 2.2 super must be a pair of not and the same value. */ if ((sub->negNsSet != NULL) && (super->negNsSet != NULL) && (sub->negNsSet->value == super->negNsSet->value)) return (0); /* * 3.1 sub must be a set whose members are either namespace names or `absent`. */ if (sub->nsSet != NULL) { /* * 3.2.1 super must be the same set or a superset thereof. */ if (super->nsSet != NULL) { xmlSchemaWildcardNsPtr cur, curB; int found = 0; cur = sub->nsSet; while (cur != NULL) { found = 0; curB = super->nsSet; while (curB != NULL) { if (cur->value == curB->value) { found = 1; break; } curB = curB->next; } if (!found) return (1); cur = cur->next; } if (found) return (0); } else if (super->negNsSet != NULL) { xmlSchemaWildcardNsPtr cur; /* * 3.2.2 super must be a pair of not and a namespace name or * `absent` and that value must not be in sub's set. */ cur = sub->nsSet; while (cur != NULL) { if (cur->value == super->negNsSet->value) return (1); cur = cur->next; } return (0); } } return (1); } static int xmlSchemaGetEffectiveValueConstraint(xmlSchemaAttributeUsePtr attruse, int *fixed, const xmlChar **value, xmlSchemaValPtr *val) { *fixed = 0; *value = NULL; if (val != 0) *val = NULL; if (attruse->defValue != NULL) { *value = attruse->defValue; if (val != NULL) *val = attruse->defVal; if (attruse->flags & XML_SCHEMA_ATTR_USE_FIXED) *fixed = 1; return(1); } else if ((attruse->attrDecl != NULL) && (attruse->attrDecl->defValue != NULL)) { *value = attruse->attrDecl->defValue; if (val != NULL) *val = attruse->attrDecl->defVal; if (attruse->attrDecl->flags & XML_SCHEMAS_ATTR_FIXED) *fixed = 1; return(1); } return(0); } /** * xmlSchemaCheckCVCWildcardNamespace: * @wild: the wildcard * @ns: the namespace * * Validation Rule: Wildcard allows Namespace Name * (cvc-wildcard-namespace) * * Returns 0 if the given namespace matches the wildcard, * 1 otherwise and -1 on API errors. */ static int xmlSchemaCheckCVCWildcardNamespace(xmlSchemaWildcardPtr wild, const xmlChar* ns) { if (wild == NULL) return(-1); if (wild->any) return(0); else if (wild->nsSet != NULL) { xmlSchemaWildcardNsPtr cur; cur = wild->nsSet; while (cur != NULL) { if (xmlStrEqual(cur->value, ns)) return(0); cur = cur->next; } } else if ((wild->negNsSet != NULL) && (ns != NULL) && (!xmlStrEqual(wild->negNsSet->value, ns))) return(0); return(1); } #define XML_SCHEMA_ACTION_DERIVE 0 #define XML_SCHEMA_ACTION_REDEFINE 1 #define WXS_ACTION_STR(a) \ ((a) == XML_SCHEMA_ACTION_DERIVE) ? (const xmlChar *) "base" : (const xmlChar *) "redefined" /* * Schema Component Constraint: * Derivation Valid (Restriction, Complex) * derivation-ok-restriction (2) - (4) * * ATTENTION: * In XML Schema 1.1 this will be: * Validation Rule: * Checking complex type subsumption (practicalSubsumption) (1, 2 and 3) * */ static int xmlSchemaCheckDerivationOKRestriction2to4(xmlSchemaParserCtxtPtr pctxt, int action, xmlSchemaBasicItemPtr item, xmlSchemaBasicItemPtr baseItem, xmlSchemaItemListPtr uses, xmlSchemaItemListPtr baseUses, xmlSchemaWildcardPtr wild, xmlSchemaWildcardPtr baseWild) { xmlSchemaAttributeUsePtr cur = NULL, bcur; int i, j, found; /* err = 0; */ const xmlChar *bEffValue; int effFixed; if (uses != NULL) { for (i = 0; i < uses->nbItems; i++) { cur = uses->items[i]; found = 0; if (baseUses == NULL) goto not_found; for (j = 0; j < baseUses->nbItems; j++) { bcur = baseUses->items[j]; if ((WXS_ATTRUSE_DECL_NAME(cur) == WXS_ATTRUSE_DECL_NAME(bcur)) && (WXS_ATTRUSE_DECL_TNS(cur) == WXS_ATTRUSE_DECL_TNS(bcur))) { /* * (2.1) "If there is an attribute use in the {attribute * uses} of the {base type definition} (call this B) whose * {attribute declaration} has the same {name} and {target * namespace}, then all of the following must be true:" */ found = 1; if ((cur->occurs == XML_SCHEMAS_ATTR_USE_OPTIONAL) && (bcur->occurs == XML_SCHEMAS_ATTR_USE_REQUIRED)) { xmlChar *str = NULL; /* * (2.1.1) "one of the following must be true:" * (2.1.1.1) "B's {required} is false." * (2.1.1.2) "R's {required} is true." */ xmlSchemaPAttrUseErr4(pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_1, WXS_ITEM_NODE(item), item, cur, "The 'optional' attribute use is inconsistent " "with the corresponding 'required' attribute use of " "the %s %s", WXS_ACTION_STR(action), xmlSchemaGetComponentDesignation(&str, baseItem), NULL, NULL); FREE_AND_NULL(str); /* err = pctxt->err; */ } else if (xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST pctxt, WXS_ATTRUSE_TYPEDEF(cur), WXS_ATTRUSE_TYPEDEF(bcur), 0) != 0) { xmlChar *strA = NULL, *strB = NULL, *strC = NULL; /* * SPEC (2.1.2) "R's {attribute declaration}'s * {type definition} must be validly derived from * B's {type definition} given the empty set as * defined in Type Derivation OK (Simple) ($3.14.6)." */ xmlSchemaPAttrUseErr4(pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_2, WXS_ITEM_NODE(item), item, cur, "The attribute declaration's %s " "is not validly derived from " "the corresponding %s of the " "attribute declaration in the %s %s", xmlSchemaGetComponentDesignation(&strA, WXS_ATTRUSE_TYPEDEF(cur)), xmlSchemaGetComponentDesignation(&strB, WXS_ATTRUSE_TYPEDEF(bcur)), WXS_ACTION_STR(action), xmlSchemaGetComponentDesignation(&strC, baseItem)); /* xmlSchemaGetComponentDesignation(&str, baseItem), */ FREE_AND_NULL(strA); FREE_AND_NULL(strB); FREE_AND_NULL(strC); /* err = pctxt->err; */ } else { /* * 2.1.3 [Definition:] Let the effective value * constraint of an attribute use be its {value * constraint}, if present, otherwise its {attribute * declaration}'s {value constraint} . */ xmlSchemaGetEffectiveValueConstraint(bcur, &effFixed, &bEffValue, NULL); /* * 2.1.3 ... one of the following must be true * * 2.1.3.1 B's `effective value constraint` is * `absent` or default. */ if ((bEffValue != NULL) && (effFixed == 1)) { const xmlChar *rEffValue = NULL; xmlSchemaGetEffectiveValueConstraint(bcur, &effFixed, &rEffValue, NULL); /* * 2.1.3.2 R's `effective value constraint` is * fixed with the same string as B's. * MAYBE TODO: Compare the computed values. * Hmm, it says "same string" so * string-equality might really be sufficient. */ if ((effFixed == 0) || (! WXS_ARE_DEFAULT_STR_EQUAL(rEffValue, bEffValue))) { xmlChar *str = NULL; xmlSchemaPAttrUseErr4(pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_3, WXS_ITEM_NODE(item), item, cur, "The effective value constraint of the " "attribute use is inconsistent with " "its correspondent in the %s %s", WXS_ACTION_STR(action), xmlSchemaGetComponentDesignation(&str, baseItem), NULL, NULL); FREE_AND_NULL(str); /* err = pctxt->err; */ } } } break; } } not_found: if (!found) { /* * (2.2) "otherwise the {base type definition} must have an * {attribute wildcard} and the {target namespace} of the * R's {attribute declaration} must be `valid` with respect * to that wildcard, as defined in Wildcard allows Namespace * Name ($3.10.4)." */ if ((baseWild == NULL) || (xmlSchemaCheckCVCWildcardNamespace(baseWild, (WXS_ATTRUSE_DECL(cur))->targetNamespace) != 0)) { xmlChar *str = NULL; xmlSchemaPAttrUseErr4(pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_2, WXS_ITEM_NODE(item), item, cur, "Neither a matching attribute use, " "nor a matching wildcard exists in the %s %s", WXS_ACTION_STR(action), xmlSchemaGetComponentDesignation(&str, baseItem), NULL, NULL); FREE_AND_NULL(str); /* err = pctxt->err; */ } } } } /* * SPEC derivation-ok-restriction (3): * (3) "For each attribute use in the {attribute uses} of the {base type * definition} whose {required} is true, there must be an attribute * use with an {attribute declaration} with the same {name} and * {target namespace} as its {attribute declaration} in the {attribute * uses} of the complex type definition itself whose {required} is true. */ if (baseUses != NULL) { for (j = 0; j < baseUses->nbItems; j++) { bcur = baseUses->items[j]; if (bcur->occurs != XML_SCHEMAS_ATTR_USE_REQUIRED) continue; found = 0; if (uses != NULL) { for (i = 0; i < uses->nbItems; i++) { cur = uses->items[i]; if ((WXS_ATTRUSE_DECL_NAME(cur) == WXS_ATTRUSE_DECL_NAME(bcur)) && (WXS_ATTRUSE_DECL_TNS(cur) == WXS_ATTRUSE_DECL_TNS(bcur))) { found = 1; break; } } } if (!found) { xmlChar *strA = NULL, *strB = NULL; xmlSchemaCustomErr4(ACTXT_CAST pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_3, NULL, item, "A matching attribute use for the " "'required' %s of the %s %s is missing", xmlSchemaGetComponentDesignation(&strA, bcur), WXS_ACTION_STR(action), xmlSchemaGetComponentDesignation(&strB, baseItem), NULL); FREE_AND_NULL(strA); FREE_AND_NULL(strB); } } } /* * derivation-ok-restriction (4) */ if (wild != NULL) { /* * (4) "If there is an {attribute wildcard}, all of the * following must be true:" */ if (baseWild == NULL) { xmlChar *str = NULL; /* * (4.1) "The {base type definition} must also have one." */ xmlSchemaCustomErr4(ACTXT_CAST pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_1, NULL, item, "The %s has an attribute wildcard, " "but the %s %s '%s' does not have one", WXS_ITEM_TYPE_NAME(item), WXS_ACTION_STR(action), WXS_ITEM_TYPE_NAME(baseItem), xmlSchemaGetComponentQName(&str, baseItem)); FREE_AND_NULL(str); return(pctxt->err); } else if ((baseWild->any == 0) && xmlSchemaCheckCOSNSSubset(wild, baseWild)) { xmlChar *str = NULL; /* * (4.2) "The complex type definition's {attribute wildcard}'s * {namespace constraint} must be a subset of the {base type * definition}'s {attribute wildcard}'s {namespace constraint}, * as defined by Wildcard Subset ($3.10.6)." */ xmlSchemaCustomErr4(ACTXT_CAST pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_2, NULL, item, "The attribute wildcard is not a valid " "subset of the wildcard in the %s %s '%s'", WXS_ACTION_STR(action), WXS_ITEM_TYPE_NAME(baseItem), xmlSchemaGetComponentQName(&str, baseItem), NULL); FREE_AND_NULL(str); return(pctxt->err); } /* 4.3 Unless the {base type definition} is the `ur-type * definition`, the complex type definition's {attribute * wildcard}'s {process contents} must be identical to or * stronger than the {base type definition}'s {attribute * wildcard}'s {process contents}, where strict is stronger * than lax is stronger than skip. */ if ((! WXS_IS_ANYTYPE(baseItem)) && (wild->processContents < baseWild->processContents)) { xmlChar *str = NULL; xmlSchemaCustomErr4(ACTXT_CAST pctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_3, NULL, baseItem, "The {process contents} of the attribute wildcard is " "weaker than the one in the %s %s '%s'", WXS_ACTION_STR(action), WXS_ITEM_TYPE_NAME(baseItem), xmlSchemaGetComponentQName(&str, baseItem), NULL); FREE_AND_NULL(str) return(pctxt->err); } } return(0); } static int xmlSchemaExpandAttributeGroupRefs(xmlSchemaParserCtxtPtr pctxt, xmlSchemaBasicItemPtr item, xmlSchemaWildcardPtr *completeWild, xmlSchemaItemListPtr list, xmlSchemaItemListPtr prohibs); /** * xmlSchemaFixupTypeAttributeUses: * @ctxt: the schema parser context * @type: the complex type definition * * * Builds the wildcard and the attribute uses on the given complex type. * Returns -1 if an internal error occurs, 0 otherwise. * * ATTENTION TODO: Experimentally this uses pointer comparisons for * strings, so recheck this if we start to hardcode some schemata, since * they might not be in the same dict. * NOTE: It is allowed to "extend" the xs:anyType type. */ static int xmlSchemaFixupTypeAttributeUses(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { xmlSchemaTypePtr baseType = NULL; xmlSchemaAttributeUsePtr use; xmlSchemaItemListPtr uses, baseUses, prohibs = NULL; if (type->baseType == NULL) { PERROR_INT("xmlSchemaFixupTypeAttributeUses", "no base type"); return (-1); } baseType = type->baseType; if (WXS_IS_TYPE_NOT_FIXED(baseType)) if (xmlSchemaTypeFixup(baseType, ACTXT_CAST pctxt) == -1) return(-1); uses = type->attrUses; baseUses = baseType->attrUses; /* * Expand attribute group references. And build the 'complete' * wildcard, i.e. intersect multiple wildcards. * Move attribute prohibitions into a separate list. */ if (uses != NULL) { if (WXS_IS_RESTRICTION(type)) { /* * This one will transfer all attr. prohibitions * into pctxt->attrProhibs. */ if (xmlSchemaExpandAttributeGroupRefs(pctxt, WXS_BASIC_CAST type, &(type->attributeWildcard), uses, pctxt->attrProhibs) == -1) { PERROR_INT("xmlSchemaFixupTypeAttributeUses", "failed to expand attributes"); return(-1); } if (pctxt->attrProhibs->nbItems != 0) prohibs = pctxt->attrProhibs; } else { if (xmlSchemaExpandAttributeGroupRefs(pctxt, WXS_BASIC_CAST type, &(type->attributeWildcard), uses, NULL) == -1) { PERROR_INT("xmlSchemaFixupTypeAttributeUses", "failed to expand attributes"); return(-1); } } } /* * Inherit the attribute uses of the base type. */ if (baseUses != NULL) { int i, j; xmlSchemaAttributeUseProhibPtr pro; if (WXS_IS_RESTRICTION(type)) { int usesCount; xmlSchemaAttributeUsePtr tmp; if (uses != NULL) usesCount = uses->nbItems; else usesCount = 0; /* Restriction. */ for (i = 0; i < baseUses->nbItems; i++) { use = baseUses->items[i]; if (prohibs) { /* * Filter out prohibited uses. */ for (j = 0; j < prohibs->nbItems; j++) { pro = prohibs->items[j]; if ((WXS_ATTRUSE_DECL_NAME(use) == pro->name) && (WXS_ATTRUSE_DECL_TNS(use) == pro->targetNamespace)) { goto inherit_next; } } } if (usesCount) { /* * Filter out existing uses. */ for (j = 0; j < usesCount; j++) { tmp = uses->items[j]; if ((WXS_ATTRUSE_DECL_NAME(use) == WXS_ATTRUSE_DECL_NAME(tmp)) && (WXS_ATTRUSE_DECL_TNS(use) == WXS_ATTRUSE_DECL_TNS(tmp))) { goto inherit_next; } } } if (uses == NULL) { type->attrUses = xmlSchemaItemListCreate(); if (type->attrUses == NULL) goto exit_failure; uses = type->attrUses; } xmlSchemaItemListAddSize(uses, 2, use); inherit_next: {} } } else { /* Extension. */ for (i = 0; i < baseUses->nbItems; i++) { use = baseUses->items[i]; if (uses == NULL) { type->attrUses = xmlSchemaItemListCreate(); if (type->attrUses == NULL) goto exit_failure; uses = type->attrUses; } xmlSchemaItemListAddSize(uses, baseUses->nbItems, use); } } } /* * Shrink attr. uses. */ if (uses) { if (uses->nbItems == 0) { xmlSchemaItemListFree(uses); type->attrUses = NULL; } /* * TODO: We could shrink the size of the array * to fit the actual number of items. */ } /* * Compute the complete wildcard. */ if (WXS_IS_EXTENSION(type)) { if (baseType->attributeWildcard != NULL) { /* * (3.2.2.1) "If the `base wildcard` is non-`absent`, then * the appropriate case among the following:" */ if (type->attributeWildcard != NULL) { /* * Union the complete wildcard with the base wildcard. * SPEC {attribute wildcard} * (3.2.2.1.2) "otherwise a wildcard whose {process contents} * and {annotation} are those of the `complete wildcard`, * and whose {namespace constraint} is the intensional union * of the {namespace constraint} of the `complete wildcard` * and of the `base wildcard`, as defined in Attribute * Wildcard Union ($3.10.6)." */ if (xmlSchemaUnionWildcards(pctxt, type->attributeWildcard, baseType->attributeWildcard) == -1) goto exit_failure; } else { /* * (3.2.2.1.1) "If the `complete wildcard` is `absent`, * then the `base wildcard`." */ type->attributeWildcard = baseType->attributeWildcard; } } else { /* * (3.2.2.2) "otherwise (the `base wildcard` is `absent`) the * `complete wildcard`" * NOOP */ } } else { /* * SPEC {attribute wildcard} * (3.1) "If the alternative is chosen, then the * `complete wildcard`;" * NOOP */ } return (0); exit_failure: return(-1); } /** * xmlSchemaTypeFinalContains: * @schema: the schema * @type: the type definition * @final: the final * * Evaluates if a type definition contains the given "final". * This does take "finalDefault" into account as well. * * Returns 1 if the type does contain the given "final", * 0 otherwise. */ static int xmlSchemaTypeFinalContains(xmlSchemaTypePtr type, int final) { if (type == NULL) return (0); if (type->flags & final) return (1); else return (0); } /** * xmlSchemaGetUnionSimpleTypeMemberTypes: * @type: the Union Simple Type * * Returns a list of member types of @type if existing, * returns NULL otherwise. */ static xmlSchemaTypeLinkPtr xmlSchemaGetUnionSimpleTypeMemberTypes(xmlSchemaTypePtr type) { while ((type != NULL) && (type->type == XML_SCHEMA_TYPE_SIMPLE)) { if (type->memberTypes != NULL) return (type->memberTypes); else type = type->baseType; } return (NULL); } #if 0 /** * xmlSchemaGetParticleTotalRangeMin: * @particle: the particle * * Schema Component Constraint: Effective Total Range * (all and sequence) + (choice) * * Returns the minimum Effective Total Range. */ static int xmlSchemaGetParticleTotalRangeMin(xmlSchemaParticlePtr particle) { if ((particle->children == NULL) || (particle->minOccurs == 0)) return (0); if (particle->children->type == XML_SCHEMA_TYPE_CHOICE) { int min = -1, cur; xmlSchemaParticlePtr part = (xmlSchemaParticlePtr) particle->children->children; if (part == NULL) return (0); while (part != NULL) { if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || (part->children->type == XML_SCHEMA_TYPE_ANY)) cur = part->minOccurs; else cur = xmlSchemaGetParticleTotalRangeMin(part); if (cur == 0) return (0); if ((min > cur) || (min == -1)) min = cur; part = (xmlSchemaParticlePtr) part->next; } return (particle->minOccurs * min); } else { /* and */ int sum = 0; xmlSchemaParticlePtr part = (xmlSchemaParticlePtr) particle->children->children; if (part == NULL) return (0); do { if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || (part->children->type == XML_SCHEMA_TYPE_ANY)) sum += part->minOccurs; else sum += xmlSchemaGetParticleTotalRangeMin(part); part = (xmlSchemaParticlePtr) part->next; } while (part != NULL); return (particle->minOccurs * sum); } } /** * xmlSchemaGetParticleTotalRangeMax: * @particle: the particle * * Schema Component Constraint: Effective Total Range * (all and sequence) + (choice) * * Returns the maximum Effective Total Range. */ static int xmlSchemaGetParticleTotalRangeMax(xmlSchemaParticlePtr particle) { if ((particle->children == NULL) || (particle->children->children == NULL)) return (0); if (particle->children->type == XML_SCHEMA_TYPE_CHOICE) { int max = -1, cur; xmlSchemaParticlePtr part = (xmlSchemaParticlePtr) particle->children->children; for (; part != NULL; part = (xmlSchemaParticlePtr) part->next) { if (part->children == NULL) continue; if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || (part->children->type == XML_SCHEMA_TYPE_ANY)) cur = part->maxOccurs; else cur = xmlSchemaGetParticleTotalRangeMax(part); if (cur == UNBOUNDED) return (UNBOUNDED); if ((max < cur) || (max == -1)) max = cur; } /* TODO: Handle overflows? */ return (particle->maxOccurs * max); } else { /* and */ int sum = 0, cur; xmlSchemaParticlePtr part = (xmlSchemaParticlePtr) particle->children->children; for (; part != NULL; part = (xmlSchemaParticlePtr) part->next) { if (part->children == NULL) continue; if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || (part->children->type == XML_SCHEMA_TYPE_ANY)) cur = part->maxOccurs; else cur = xmlSchemaGetParticleTotalRangeMax(part); if (cur == UNBOUNDED) return (UNBOUNDED); if ((cur > 0) && (particle->maxOccurs == UNBOUNDED)) return (UNBOUNDED); sum += cur; } /* TODO: Handle overflows? */ return (particle->maxOccurs * sum); } } #endif /** * xmlSchemaGetParticleEmptiable: * @particle: the particle * * Returns 1 if emptiable, 0 otherwise. */ static int xmlSchemaGetParticleEmptiable(xmlSchemaParticlePtr particle) { xmlSchemaParticlePtr part; int emptiable; if ((particle->children == NULL) || (particle->minOccurs == 0)) return (1); part = (xmlSchemaParticlePtr) particle->children->children; if (part == NULL) return (1); while (part != NULL) { if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || (part->children->type == XML_SCHEMA_TYPE_ANY)) emptiable = (part->minOccurs == 0); else emptiable = xmlSchemaGetParticleEmptiable(part); if (particle->children->type == XML_SCHEMA_TYPE_CHOICE) { if (emptiable) return (1); } else { /* and */ if (!emptiable) return (0); } part = (xmlSchemaParticlePtr) part->next; } if (particle->children->type == XML_SCHEMA_TYPE_CHOICE) return (0); else return (1); } /** * xmlSchemaIsParticleEmptiable: * @particle: the particle * * Schema Component Constraint: Particle Emptiable * Checks whether the given particle is emptiable. * * Returns 1 if emptiable, 0 otherwise. */ static int xmlSchemaIsParticleEmptiable(xmlSchemaParticlePtr particle) { /* * SPEC (1) "Its {min occurs} is 0." */ if ((particle == NULL) || (particle->minOccurs == 0) || (particle->children == NULL)) return (1); /* * SPEC (2) "Its {term} is a group and the minimum part of the * effective total range of that group, [...] is 0." */ if (WXS_IS_MODEL_GROUP(particle->children)) return (xmlSchemaGetParticleEmptiable(particle)); return (0); } /** * xmlSchemaCheckCOSSTDerivedOK: * @actxt: a context * @type: the derived simple type definition * @baseType: the base type definition * @subset: the subset of ('restriction', etc.) * * Schema Component Constraint: * Type Derivation OK (Simple) (cos-st-derived-OK) * * Checks whether @type can be validly * derived from @baseType. * * Returns 0 on success, an positive error code otherwise. */ static int xmlSchemaCheckCOSSTDerivedOK(xmlSchemaAbstractCtxtPtr actxt, xmlSchemaTypePtr type, xmlSchemaTypePtr baseType, int subset) { /* * 1 They are the same type definition. * TODO: The identity check might have to be more complex than this. */ if (type == baseType) return (0); /* * 2.1 restriction is not in the subset, or in the {final} * of its own {base type definition}; * * NOTE that this will be used also via "xsi:type". * * TODO: Revise this, it looks strange. How can the "type" * not be fixed or *in* fixing? */ if (WXS_IS_TYPE_NOT_FIXED(type)) if (xmlSchemaTypeFixup(type, actxt) == -1) return(-1); if (WXS_IS_TYPE_NOT_FIXED(baseType)) if (xmlSchemaTypeFixup(baseType, actxt) == -1) return(-1); if ((subset & SUBSET_RESTRICTION) || (xmlSchemaTypeFinalContains(type->baseType, XML_SCHEMAS_TYPE_FINAL_RESTRICTION))) { return (XML_SCHEMAP_COS_ST_DERIVED_OK_2_1); } /* 2.2 */ if (type->baseType == baseType) { /* * 2.2.1 D's `base type definition` is B. */ return (0); } /* * 2.2.2 D's `base type definition` is not the `ur-type definition` * and is validly derived from B given the subset, as defined by this * constraint. */ if ((! WXS_IS_ANYTYPE(type->baseType)) && (xmlSchemaCheckCOSSTDerivedOK(actxt, type->baseType, baseType, subset) == 0)) { return (0); } /* * 2.2.3 D's {variety} is list or union and B is the `simple ur-type * definition`. */ if (WXS_IS_ANY_SIMPLE_TYPE(baseType) && (WXS_IS_LIST(type) || WXS_IS_UNION(type))) { return (0); } /* * 2.2.4 B's {variety} is union and D is validly derived from a type * definition in B's {member type definitions} given the subset, as * defined by this constraint. * * NOTE: This seems not to involve built-in types, since there is no * built-in Union Simple Type. */ if (WXS_IS_UNION(baseType)) { xmlSchemaTypeLinkPtr cur; cur = baseType->memberTypes; while (cur != NULL) { if (WXS_IS_TYPE_NOT_FIXED(cur->type)) if (xmlSchemaTypeFixup(cur->type, actxt) == -1) return(-1); if (xmlSchemaCheckCOSSTDerivedOK(actxt, type, cur->type, subset) == 0) { /* * It just has to be validly derived from at least one * member-type. */ return (0); } cur = cur->next; } } return (XML_SCHEMAP_COS_ST_DERIVED_OK_2_2); } /** * xmlSchemaCheckTypeDefCircularInternal: * @pctxt: the schema parser context * @ctxtType: the type definition * @ancestor: an ancestor of @ctxtType * * Checks st-props-correct (2) + ct-props-correct (3). * Circular type definitions are not allowed. * * Returns XML_SCHEMAP_ST_PROPS_CORRECT_2 if the given type is * circular, 0 otherwise. */ static int xmlSchemaCheckTypeDefCircularInternal(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr ctxtType, xmlSchemaTypePtr ancestor) { int ret; if ((ancestor == NULL) || (ancestor->type == XML_SCHEMA_TYPE_BASIC)) return (0); if (ctxtType == ancestor) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_ST_PROPS_CORRECT_2, WXS_BASIC_CAST ctxtType, WXS_ITEM_NODE(ctxtType), "The definition is circular", NULL); return (XML_SCHEMAP_ST_PROPS_CORRECT_2); } if (ancestor->flags & XML_SCHEMAS_TYPE_MARKED) { /* * Avoid infinite recursion on circular types not yet checked. */ return (0); } ancestor->flags |= XML_SCHEMAS_TYPE_MARKED; ret = xmlSchemaCheckTypeDefCircularInternal(pctxt, ctxtType, ancestor->baseType); ancestor->flags ^= XML_SCHEMAS_TYPE_MARKED; return (ret); } /** * xmlSchemaCheckTypeDefCircular: * @item: the complex/simple type definition * @ctxt: the parser context * @name: the name * * Checks for circular type definitions. */ static void xmlSchemaCheckTypeDefCircular(xmlSchemaTypePtr item, xmlSchemaParserCtxtPtr ctxt) { if ((item == NULL) || (item->type == XML_SCHEMA_TYPE_BASIC) || (item->baseType == NULL)) return; xmlSchemaCheckTypeDefCircularInternal(ctxt, item, item->baseType); } /* * Simple Type Definition Representation OK (src-simple-type) 4 * * "4 Circular union type definition is disallowed. That is, if the * alternative is chosen, there must not be any entries in the * memberTypes [attribute] at any depth which resolve to the component * corresponding to the ." * * Note that this should work on the *representation* of a component, * thus assumes any union types in the member types not being yet * substituted. At this stage we need the variety of the types * to be already computed. */ static int xmlSchemaCheckUnionTypeDefCircularRecur(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr ctxType, xmlSchemaTypeLinkPtr members) { xmlSchemaTypeLinkPtr member; xmlSchemaTypePtr memberType; member = members; while (member != NULL) { memberType = member->type; while ((memberType != NULL) && (memberType->type != XML_SCHEMA_TYPE_BASIC)) { if (memberType == ctxType) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_SRC_SIMPLE_TYPE_4, WXS_BASIC_CAST ctxType, NULL, "The union type definition is circular", NULL); return (XML_SCHEMAP_SRC_SIMPLE_TYPE_4); } if ((WXS_IS_UNION(memberType)) && ((memberType->flags & XML_SCHEMAS_TYPE_MARKED) == 0)) { int res; memberType->flags |= XML_SCHEMAS_TYPE_MARKED; res = xmlSchemaCheckUnionTypeDefCircularRecur(pctxt, ctxType, xmlSchemaGetUnionSimpleTypeMemberTypes(memberType)); memberType->flags ^= XML_SCHEMAS_TYPE_MARKED; if (res != 0) return(res); } memberType = memberType->baseType; } member = member->next; } return(0); } static int xmlSchemaCheckUnionTypeDefCircular(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { if (! WXS_IS_UNION(type)) return(0); return(xmlSchemaCheckUnionTypeDefCircularRecur(pctxt, type, type->memberTypes)); } /** * xmlSchemaResolveTypeReferences: * @item: the complex/simple type definition * @ctxt: the parser context * @name: the name * * Resolves type definition references */ static void xmlSchemaResolveTypeReferences(xmlSchemaTypePtr typeDef, xmlSchemaParserCtxtPtr ctxt) { if (typeDef == NULL) return; /* * Resolve the base type. */ if (typeDef->baseType == NULL) { typeDef->baseType = xmlSchemaGetType(ctxt->schema, typeDef->base, typeDef->baseNs); if (typeDef->baseType == NULL) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST typeDef, typeDef->node, "base", typeDef->base, typeDef->baseNs, XML_SCHEMA_TYPE_SIMPLE, NULL); return; } } if (WXS_IS_SIMPLE(typeDef)) { if (WXS_IS_UNION(typeDef)) { /* * Resolve the memberTypes. */ xmlSchemaResolveUnionMemberTypes(ctxt, typeDef); return; } else if (WXS_IS_LIST(typeDef)) { /* * Resolve the itemType. */ if ((typeDef->subtypes == NULL) && (typeDef->base != NULL)) { typeDef->subtypes = xmlSchemaGetType(ctxt->schema, typeDef->base, typeDef->baseNs); if ((typeDef->subtypes == NULL) || (! WXS_IS_SIMPLE(typeDef->subtypes))) { typeDef->subtypes = NULL; xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST typeDef, typeDef->node, "itemType", typeDef->base, typeDef->baseNs, XML_SCHEMA_TYPE_SIMPLE, NULL); } } return; } } /* * The ball of letters below means, that if we have a particle * which has a QName-helper component as its {term}, we want * to resolve it... */ else if ((WXS_TYPE_CONTENTTYPE(typeDef) != NULL) && ((WXS_TYPE_CONTENTTYPE(typeDef))->type == XML_SCHEMA_TYPE_PARTICLE) && (WXS_TYPE_PARTICLE_TERM(typeDef) != NULL) && ((WXS_TYPE_PARTICLE_TERM(typeDef))->type == XML_SCHEMA_EXTRA_QNAMEREF)) { xmlSchemaQNameRefPtr ref = WXS_QNAME_CAST WXS_TYPE_PARTICLE_TERM(typeDef); xmlSchemaModelGroupDefPtr groupDef; /* * URGENT TODO: Test this. */ WXS_TYPE_PARTICLE_TERM(typeDef) = NULL; /* * Resolve the MG definition reference. */ groupDef = WXS_MODEL_GROUPDEF_CAST xmlSchemaGetNamedComponent(ctxt->schema, ref->itemType, ref->name, ref->targetNamespace); if (groupDef == NULL) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, NULL, WXS_ITEM_NODE(WXS_TYPE_PARTICLE(typeDef)), "ref", ref->name, ref->targetNamespace, ref->itemType, NULL); /* Remove the particle. */ WXS_TYPE_CONTENTTYPE(typeDef) = NULL; } else if (WXS_MODELGROUPDEF_MODEL(groupDef) == NULL) /* Remove the particle. */ WXS_TYPE_CONTENTTYPE(typeDef) = NULL; else { /* * Assign the MG definition's {model group} to the * particle's {term}. */ WXS_TYPE_PARTICLE_TERM(typeDef) = WXS_MODELGROUPDEF_MODEL(groupDef); if (WXS_MODELGROUPDEF_MODEL(groupDef)->type == XML_SCHEMA_TYPE_ALL) { /* * SPEC cos-all-limited (1.2) * "1.2 the {term} property of a particle with * {max occurs}=1 which is part of a pair which constitutes * the {content type} of a complex type definition." */ if ((WXS_TYPE_PARTICLE(typeDef))->maxOccurs != 1) { xmlSchemaCustomErr(ACTXT_CAST ctxt, /* TODO: error code */ XML_SCHEMAP_COS_ALL_LIMITED, WXS_ITEM_NODE(WXS_TYPE_PARTICLE(typeDef)), NULL, "The particle's {max occurs} must be 1, since the " "reference resolves to an 'all' model group", NULL, NULL); } } } } } /** * xmlSchemaCheckSTPropsCorrect: * @ctxt: the schema parser context * @type: the simple type definition * * Checks st-props-correct. * * Returns 0 if the properties are correct, * if not, a positive error code and -1 on internal * errors. */ static int xmlSchemaCheckSTPropsCorrect(xmlSchemaParserCtxtPtr ctxt, xmlSchemaTypePtr type) { xmlSchemaTypePtr baseType = type->baseType; xmlChar *str = NULL; /* STATE: error funcs converted. */ /* * Schema Component Constraint: Simple Type Definition Properties Correct * * NOTE: This is somehow redundant, since we actually built a simple type * to have all the needed information; this acts as an self test. */ /* Base type: If the datatype has been `derived` by `restriction` * then the Simple Type Definition component from which it is `derived`, * otherwise the Simple Type Definition for anySimpleType ($4.1.6). */ if (baseType == NULL) { /* * TODO: Think about: "modulo the impact of Missing * Sub-components ($5.3)." */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_ST_PROPS_CORRECT_1, WXS_BASIC_CAST type, NULL, "No base type existent", NULL); return (XML_SCHEMAP_ST_PROPS_CORRECT_1); } if (! WXS_IS_SIMPLE(baseType)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_ST_PROPS_CORRECT_1, WXS_BASIC_CAST type, NULL, "The base type '%s' is not a simple type", xmlSchemaGetComponentQName(&str, baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_ST_PROPS_CORRECT_1); } if ((WXS_IS_LIST(type) || WXS_IS_UNION(type)) && (WXS_IS_RESTRICTION(type) == 0) && ((! WXS_IS_ANY_SIMPLE_TYPE(baseType)) && (baseType->type != XML_SCHEMA_TYPE_SIMPLE))) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_ST_PROPS_CORRECT_1, WXS_BASIC_CAST type, NULL, "A type, derived by list or union, must have " "the simple ur-type definition as base type, not '%s'", xmlSchemaGetComponentQName(&str, baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_ST_PROPS_CORRECT_1); } /* * Variety: One of {atomic, list, union}. */ if ((! WXS_IS_ATOMIC(type)) && (! WXS_IS_UNION(type)) && (! WXS_IS_LIST(type))) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_ST_PROPS_CORRECT_1, WXS_BASIC_CAST type, NULL, "The variety is absent", NULL); return (XML_SCHEMAP_ST_PROPS_CORRECT_1); } /* TODO: Finish this. Hmm, is this finished? */ /* * 3 The {final} of the {base type definition} must not contain restriction. */ if (xmlSchemaTypeFinalContains(baseType, XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_ST_PROPS_CORRECT_3, WXS_BASIC_CAST type, NULL, "The 'final' of its base type '%s' must not contain " "'restriction'", xmlSchemaGetComponentQName(&str, baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_ST_PROPS_CORRECT_3); } /* * 2 All simple type definitions must be derived ultimately from the `simple * ur-type definition` (so circular definitions are disallowed). That is, it * must be possible to reach a built-in primitive datatype or the `simple * ur-type definition` by repeatedly following the {base type definition}. * * NOTE: this is done in xmlSchemaCheckTypeDefCircular(). */ return (0); } /** * xmlSchemaCheckCOSSTRestricts: * @ctxt: the schema parser context * @type: the simple type definition * * Schema Component Constraint: * Derivation Valid (Restriction, Simple) (cos-st-restricts) * Checks if the given @type (simpleType) is derived validly by restriction. * STATUS: * * Returns -1 on internal errors, 0 if the type is validly derived, * a positive error code otherwise. */ static int xmlSchemaCheckCOSSTRestricts(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { xmlChar *str = NULL; if (type->type != XML_SCHEMA_TYPE_SIMPLE) { PERROR_INT("xmlSchemaCheckCOSSTRestricts", "given type is not a user-derived simpleType"); return (-1); } if (WXS_IS_ATOMIC(type)) { xmlSchemaTypePtr primitive; /* * 1.1 The {base type definition} must be an atomic simple * type definition or a built-in primitive datatype. */ if (! WXS_IS_ATOMIC(type->baseType)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_1_1, WXS_BASIC_CAST type, NULL, "The base type '%s' is not an atomic simple type", xmlSchemaGetComponentQName(&str, type->baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_1_1); } /* 1.2 The {final} of the {base type definition} must not contain * restriction. */ /* OPTIMIZE TODO : This is already done in xmlSchemaCheckStPropsCorrect */ if (xmlSchemaTypeFinalContains(type->baseType, XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_1_2, WXS_BASIC_CAST type, NULL, "The final of its base type '%s' must not contain 'restriction'", xmlSchemaGetComponentQName(&str, type->baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_1_2); } /* * 1.3.1 DF must be an allowed constraining facet for the {primitive * type definition}, as specified in the appropriate subsection of 3.2 * Primitive datatypes. */ if (type->facets != NULL) { xmlSchemaFacetPtr facet; int ok = 1; primitive = xmlSchemaGetPrimitiveType(type); if (primitive == NULL) { PERROR_INT("xmlSchemaCheckCOSSTRestricts", "failed to get primitive type"); return (-1); } facet = type->facets; do { if (xmlSchemaIsBuiltInTypeFacet(primitive, facet->type) == 0) { ok = 0; xmlSchemaPIllegalFacetAtomicErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_1_3_1, type, primitive, facet); } facet = facet->next; } while (facet != NULL); if (ok == 0) return (XML_SCHEMAP_COS_ST_RESTRICTS_1_3_1); } /* * SPEC (1.3.2) "If there is a facet of the same kind in the {facets} * of the {base type definition} (call this BF),then the DF's {value} * must be a valid restriction of BF's {value} as defined in * [XML Schemas: Datatypes]." * * NOTE (1.3.2) Facet derivation constraints are currently handled in * xmlSchemaDeriveAndValidateFacets() */ } else if (WXS_IS_LIST(type)) { xmlSchemaTypePtr itemType = NULL; itemType = type->subtypes; if ((itemType == NULL) || (! WXS_IS_SIMPLE(itemType))) { PERROR_INT("xmlSchemaCheckCOSSTRestricts", "failed to evaluate the item type"); return (-1); } if (WXS_IS_TYPE_NOT_FIXED(itemType)) xmlSchemaTypeFixup(itemType, ACTXT_CAST pctxt); /* * 2.1 The {item type definition} must have a {variety} of atomic or * union (in which case all the {member type definitions} * must be atomic). */ if ((! WXS_IS_ATOMIC(itemType)) && (! WXS_IS_UNION(itemType))) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_2_1, WXS_BASIC_CAST type, NULL, "The item type '%s' does not have a variety of atomic or union", xmlSchemaGetComponentQName(&str, itemType)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_2_1); } else if (WXS_IS_UNION(itemType)) { xmlSchemaTypeLinkPtr member; member = itemType->memberTypes; while (member != NULL) { if (! WXS_IS_ATOMIC(member->type)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_2_1, WXS_BASIC_CAST type, NULL, "The item type is a union type, but the " "member type '%s' of this item type is not atomic", xmlSchemaGetComponentQName(&str, member->type)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_2_1); } member = member->next; } } if (WXS_IS_ANY_SIMPLE_TYPE(type->baseType)) { xmlSchemaFacetPtr facet; /* * This is the case if we have: facets != NULL) { facet = type->facets; do { if (facet->type != XML_SCHEMA_FACET_WHITESPACE) { xmlSchemaPIllegalFacetListUnionErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_2, type, facet); return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_2); } facet = facet->next; } while (facet != NULL); } /* * MAYBE TODO: (Hmm, not really) Datatypes states: * A `list` datatype can be `derived` from an `atomic` datatype * whose `lexical space` allows space (such as string or anyURI)or * a `union` datatype any of whose {member type definitions}'s * `lexical space` allows space. */ } else { /* * This is the case if we have: baseType)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_1, WXS_BASIC_CAST type, NULL, "The base type '%s' must be a list type", xmlSchemaGetComponentQName(&str, type->baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_1); } /* * 2.3.2.2 The {final} of the {base type definition} must not * contain restriction. */ if (xmlSchemaTypeFinalContains(type->baseType, XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_2, WXS_BASIC_CAST type, NULL, "The 'final' of the base type '%s' must not contain 'restriction'", xmlSchemaGetComponentQName(&str, type->baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_2); } /* * 2.3.2.3 The {item type definition} must be validly derived * from the {base type definition}'s {item type definition} given * the empty set, as defined in Type Derivation OK (Simple) ($3.14.6). */ { xmlSchemaTypePtr baseItemType; baseItemType = type->baseType->subtypes; if ((baseItemType == NULL) || (! WXS_IS_SIMPLE(baseItemType))) { PERROR_INT("xmlSchemaCheckCOSSTRestricts", "failed to eval the item type of a base type"); return (-1); } if ((itemType != baseItemType) && (xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST pctxt, itemType, baseItemType, 0) != 0)) { xmlChar *strBIT = NULL, *strBT = NULL; xmlSchemaPCustomErrExt(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_3, WXS_BASIC_CAST type, NULL, "The item type '%s' is not validly derived from " "the item type '%s' of the base type '%s'", xmlSchemaGetComponentQName(&str, itemType), xmlSchemaGetComponentQName(&strBIT, baseItemType), xmlSchemaGetComponentQName(&strBT, type->baseType)); FREE_AND_NULL(str) FREE_AND_NULL(strBIT) FREE_AND_NULL(strBT) return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_3); } } if (type->facets != NULL) { xmlSchemaFacetPtr facet; int ok = 1; /* * 2.3.2.4 Only length, minLength, maxLength, whiteSpace, pattern * and enumeration facet components are allowed among the {facets}. */ facet = type->facets; do { switch (facet->type) { case XML_SCHEMA_FACET_LENGTH: case XML_SCHEMA_FACET_MINLENGTH: case XML_SCHEMA_FACET_MAXLENGTH: case XML_SCHEMA_FACET_WHITESPACE: /* * TODO: 2.5.1.2 List datatypes * The value of `whiteSpace` is fixed to the value collapse. */ case XML_SCHEMA_FACET_PATTERN: case XML_SCHEMA_FACET_ENUMERATION: break; default: { xmlSchemaPIllegalFacetListUnionErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_4, type, facet); /* * We could return, but it's nicer to report all * invalid facets. */ ok = 0; } } facet = facet->next; } while (facet != NULL); if (ok == 0) return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_4); /* * SPEC (2.3.2.5) (same as 1.3.2) * * NOTE (2.3.2.5) This is currently done in * xmlSchemaDeriveAndValidateFacets() */ } } } else if (WXS_IS_UNION(type)) { /* * 3.1 The {member type definitions} must all have {variety} of * atomic or list. */ xmlSchemaTypeLinkPtr member; member = type->memberTypes; while (member != NULL) { if (WXS_IS_TYPE_NOT_FIXED(member->type)) xmlSchemaTypeFixup(member->type, ACTXT_CAST pctxt); if ((! WXS_IS_ATOMIC(member->type)) && (! WXS_IS_LIST(member->type))) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_3_1, WXS_BASIC_CAST type, NULL, "The member type '%s' is neither an atomic, nor a list type", xmlSchemaGetComponentQName(&str, member->type)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_3_1); } member = member->next; } /* * 3.3.1 If the {base type definition} is the `simple ur-type * definition` */ if (type->baseType->builtInType == XML_SCHEMAS_ANYSIMPLETYPE) { /* * 3.3.1.1 All of the {member type definitions} must have a * {final} which does not contain union. */ member = type->memberTypes; while (member != NULL) { if (xmlSchemaTypeFinalContains(member->type, XML_SCHEMAS_TYPE_FINAL_UNION)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1, WXS_BASIC_CAST type, NULL, "The 'final' of member type '%s' contains 'union'", xmlSchemaGetComponentQName(&str, member->type)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1); } member = member->next; } /* * 3.3.1.2 The {facets} must be empty. */ if (type->facetSet != NULL) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1_2, WXS_BASIC_CAST type, NULL, "No facets allowed", NULL); return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1_2); } } else { /* * 3.3.2.1 The {base type definition} must have a {variety} of union. * I.e. the variety of "list" is inherited. */ if (! WXS_IS_UNION(type->baseType)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_1, WXS_BASIC_CAST type, NULL, "The base type '%s' is not a union type", xmlSchemaGetComponentQName(&str, type->baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_1); } /* * 3.3.2.2 The {final} of the {base type definition} must not contain restriction. */ if (xmlSchemaTypeFinalContains(type->baseType, XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_2, WXS_BASIC_CAST type, NULL, "The 'final' of its base type '%s' must not contain 'restriction'", xmlSchemaGetComponentQName(&str, type->baseType)); FREE_AND_NULL(str) return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_2); } /* * 3.3.2.3 The {member type definitions}, in order, must be validly * derived from the corresponding type definitions in the {base * type definition}'s {member type definitions} given the empty set, * as defined in Type Derivation OK (Simple) ($3.14.6). */ { xmlSchemaTypeLinkPtr baseMember; /* * OPTIMIZE: if the type is restricting, it has no local defined * member types and inherits the member types of the base type; * thus a check for equality can be skipped. */ /* * Even worse: I cannot see a scenario where a restricting * union simple type can have other member types as the member * types of it's base type. This check seems not necessary with * respect to the derivation process in libxml2. * But necessary if constructing types with an API. */ if (type->memberTypes != NULL) { member = type->memberTypes; baseMember = xmlSchemaGetUnionSimpleTypeMemberTypes(type->baseType); if ((member == NULL) && (baseMember != NULL)) { PERROR_INT("xmlSchemaCheckCOSSTRestricts", "different number of member types in base"); } while (member != NULL) { if (baseMember == NULL) { PERROR_INT("xmlSchemaCheckCOSSTRestricts", "different number of member types in base"); } else if ((member->type != baseMember->type) && (xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST pctxt, member->type, baseMember->type, 0) != 0)) { xmlChar *strBMT = NULL, *strBT = NULL; xmlSchemaPCustomErrExt(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_3, WXS_BASIC_CAST type, NULL, "The member type %s is not validly " "derived from its corresponding member " "type %s of the base type %s", xmlSchemaGetComponentQName(&str, member->type), xmlSchemaGetComponentQName(&strBMT, baseMember->type), xmlSchemaGetComponentQName(&strBT, type->baseType)); FREE_AND_NULL(str) FREE_AND_NULL(strBMT) FREE_AND_NULL(strBT) return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_3); } member = member->next; if (baseMember != NULL) baseMember = baseMember->next; } } } /* * 3.3.2.4 Only pattern and enumeration facet components are * allowed among the {facets}. */ if (type->facets != NULL) { xmlSchemaFacetPtr facet; int ok = 1; facet = type->facets; do { if ((facet->type != XML_SCHEMA_FACET_PATTERN) && (facet->type != XML_SCHEMA_FACET_ENUMERATION)) { xmlSchemaPIllegalFacetListUnionErr(pctxt, XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_4, type, facet); ok = 0; } facet = facet->next; } while (facet != NULL); if (ok == 0) return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_4); } /* * SPEC (3.3.2.5) (same as 1.3.2) * * NOTE (3.3.2.5) This is currently done in * xmlSchemaDeriveAndValidateFacets() */ } } return (0); } /** * xmlSchemaCheckSRCSimpleType: * @ctxt: the schema parser context * @type: the simple type definition * * Checks crc-simple-type constraints. * * Returns 0 if the constraints are satisfied, * if not a positive error code and -1 on internal * errors. */ #if 0 static int xmlSchemaCheckSRCSimpleType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaTypePtr type) { /* * src-simple-type.1 The corresponding simple type definition, if any, * must satisfy the conditions set out in Constraints on Simple Type * Definition Schema Components ($3.14.6). */ if (WXS_IS_RESTRICTION(type)) { /* * src-simple-type.2 "If the alternative is chosen, * either it must have a base [attribute] or a among its * [children], but not both." * NOTE: This is checked in the parse function of . */ /* * */ } else if (WXS_IS_LIST(type)) { /* src-simple-type.3 "If the alternative is chosen, either it must have * an itemType [attribute] or a among its [children], * but not both." * * NOTE: This is checked in the parse function of . */ } else if (WXS_IS_UNION(type)) { /* * src-simple-type.4 is checked in xmlSchemaCheckUnionTypeDefCircular(). */ } return (0); } #endif static int xmlSchemaCreateVCtxtOnPCtxt(xmlSchemaParserCtxtPtr ctxt) { if (ctxt->vctxt == NULL) { ctxt->vctxt = xmlSchemaNewValidCtxt(NULL); if (ctxt->vctxt == NULL) { xmlSchemaPErr(ctxt, NULL, XML_SCHEMAP_INTERNAL, "Internal error: xmlSchemaCreateVCtxtOnPCtxt, " "failed to create a temp. validation context.\n", NULL, NULL); return (-1); } /* TODO: Pass user data. */ xmlSchemaSetValidErrors(ctxt->vctxt, ctxt->error, ctxt->warning, ctxt->errCtxt); xmlSchemaSetValidStructuredErrors(ctxt->vctxt, ctxt->serror, ctxt->errCtxt); } return (0); } static int xmlSchemaVCheckCVCSimpleType(xmlSchemaAbstractCtxtPtr actxt, xmlNodePtr node, xmlSchemaTypePtr type, const xmlChar *value, xmlSchemaValPtr *retVal, int fireErrors, int normalize, int isNormalized); /** * xmlSchemaParseCheckCOSValidDefault: * @pctxt: the schema parser context * @type: the simple type definition * @value: the default value * @node: an optional node (the holder of the value) * * Schema Component Constraint: Element Default Valid (Immediate) * (cos-valid-default) * This will be used by the parser only. For the validator there's * an other version. * * Returns 0 if the constraints are satisfied, * if not, a positive error code and -1 on internal * errors. */ static int xmlSchemaParseCheckCOSValidDefault(xmlSchemaParserCtxtPtr pctxt, xmlNodePtr node, xmlSchemaTypePtr type, const xmlChar *value, xmlSchemaValPtr *val) { int ret = 0; /* * cos-valid-default: * Schema Component Constraint: Element Default Valid (Immediate) * For a string to be a valid default with respect to a type * definition the appropriate case among the following must be true: */ if WXS_IS_COMPLEX(type) { /* * Complex type. * * SPEC (2.1) "its {content type} must be a simple type definition * or mixed." * SPEC (2.2.2) "If the {content type} is mixed, then the {content * type}'s particle must be `emptiable` as defined by * Particle Emptiable ($3.9.6)." */ if ((! WXS_HAS_SIMPLE_CONTENT(type)) && ((! WXS_HAS_MIXED_CONTENT(type)) || (! WXS_EMPTIABLE(type)))) { /* NOTE that this covers (2.2.2) as well. */ xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_COS_VALID_DEFAULT_2_1, WXS_BASIC_CAST type, type->node, "For a string to be a valid default, the type definition " "must be a simple type or a complex type with mixed content " "and a particle emptiable", NULL); return(XML_SCHEMAP_COS_VALID_DEFAULT_2_1); } } /* * 1 If the type definition is a simple type definition, then the string * must be `valid` with respect to that definition as defined by String * Valid ($3.14.4). * * AND * * 2.2.1 If the {content type} is a simple type definition, then the * string must be `valid` with respect to that simple type definition * as defined by String Valid ($3.14.4). */ if (WXS_IS_SIMPLE(type)) ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST pctxt, node, type, value, val, 1, 1, 0); else if (WXS_HAS_SIMPLE_CONTENT(type)) ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST pctxt, node, type->contentTypeDef, value, val, 1, 1, 0); else return (ret); if (ret < 0) { PERROR_INT("xmlSchemaParseCheckCOSValidDefault", "calling xmlSchemaVCheckCVCSimpleType()"); } return (ret); } /** * xmlSchemaCheckCTPropsCorrect: * @ctxt: the schema parser context * @type: the complex type definition * *.(4.6) Constraints on Complex Type Definition Schema Components * Schema Component Constraint: * Complex Type Definition Properties Correct (ct-props-correct) * STATUS: (seems) complete * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckCTPropsCorrect(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { /* * TODO: Correct the error code; XML_SCHEMAP_SRC_CT_1 is used temporarily. * * SPEC (1) "The values of the properties of a complex type definition must * be as described in the property tableau in The Complex Type Definition * Schema Component ($3.4.1), modulo the impact of Missing * Sub-components ($5.3)." */ if ((type->baseType != NULL) && (WXS_IS_SIMPLE(type->baseType)) && (WXS_IS_EXTENSION(type) == 0)) { /* * SPEC (2) "If the {base type definition} is a simple type definition, * the {derivation method} must be extension." */ xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_SRC_CT_1, NULL, WXS_BASIC_CAST type, "If the base type is a simple type, the derivation method must be " "'extension'", NULL, NULL); return (XML_SCHEMAP_SRC_CT_1); } /* * SPEC (3) "Circular definitions are disallowed, except for the `ur-type * definition`. That is, it must be possible to reach the `ur-type * definition` by repeatedly following the {base type definition}." * * NOTE (3) is done in xmlSchemaCheckTypeDefCircular(). */ /* * NOTE that (4) and (5) need the following: * - attribute uses need to be already inherited (apply attr. prohibitions) * - attribute group references need to be expanded already * - simple types need to be typefixed already */ if (type->attrUses && (((xmlSchemaItemListPtr) type->attrUses)->nbItems > 1)) { xmlSchemaItemListPtr uses = (xmlSchemaItemListPtr) type->attrUses; xmlSchemaAttributeUsePtr use, tmp; int i, j, hasId = 0; for (i = uses->nbItems -1; i >= 0; i--) { use = uses->items[i]; /* * SPEC ct-props-correct * (4) "Two distinct attribute declarations in the * {attribute uses} must not have identical {name}s and * {target namespace}s." */ if (i > 0) { for (j = i -1; j >= 0; j--) { tmp = uses->items[j]; if ((WXS_ATTRUSE_DECL_NAME(use) == WXS_ATTRUSE_DECL_NAME(tmp)) && (WXS_ATTRUSE_DECL_TNS(use) == WXS_ATTRUSE_DECL_TNS(tmp))) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_AG_PROPS_CORRECT, NULL, WXS_BASIC_CAST type, "Duplicate %s", xmlSchemaGetComponentDesignation(&str, use), NULL); FREE_AND_NULL(str); /* * Remove the duplicate. */ if (xmlSchemaItemListRemove(uses, i) == -1) goto exit_failure; goto next_use; } } } /* * SPEC ct-props-correct * (5) "Two distinct attribute declarations in the * {attribute uses} must not have {type definition}s which * are or are derived from ID." */ if (WXS_ATTRUSE_TYPEDEF(use) != NULL) { if (xmlSchemaIsDerivedFromBuiltInType( WXS_ATTRUSE_TYPEDEF(use), XML_SCHEMAS_ID)) { if (hasId) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_AG_PROPS_CORRECT, NULL, WXS_BASIC_CAST type, "There must not exist more than one attribute " "declaration of type 'xs:ID' " "(or derived from 'xs:ID'). The %s violates this " "constraint", xmlSchemaGetComponentDesignation(&str, use), NULL); FREE_AND_NULL(str); if (xmlSchemaItemListRemove(uses, i) == -1) goto exit_failure; } hasId = 1; } } next_use: {} } } return (0); exit_failure: return(-1); } static int xmlSchemaAreEqualTypes(xmlSchemaTypePtr typeA, xmlSchemaTypePtr typeB) { /* * TODO: This should implement component-identity * in the future. */ if ((typeA == NULL) || (typeB == NULL)) return (0); return (typeA == typeB); } /** * xmlSchemaCheckCOSCTDerivedOK: * @ctxt: the schema parser context * @type: the to-be derived complex type definition * @baseType: the base complex type definition * @set: the given set * * Schema Component Constraint: * Type Derivation OK (Complex) (cos-ct-derived-ok) * * STATUS: completed * * Returns 0 if the constraints are satisfied, or 1 * if not. */ static int xmlSchemaCheckCOSCTDerivedOK(xmlSchemaAbstractCtxtPtr actxt, xmlSchemaTypePtr type, xmlSchemaTypePtr baseType, int set) { int equal = xmlSchemaAreEqualTypes(type, baseType); /* TODO: Error codes. */ /* * SPEC "For a complex type definition (call it D, for derived) * to be validly derived from a type definition (call this * B, for base) given a subset of {extension, restriction} * all of the following must be true:" */ if (! equal) { /* * SPEC (1) "If B and D are not the same type definition, then the * {derivation method} of D must not be in the subset." */ if (((set & SUBSET_EXTENSION) && (WXS_IS_EXTENSION(type))) || ((set & SUBSET_RESTRICTION) && (WXS_IS_RESTRICTION(type)))) return (1); } else { /* * SPEC (2.1) "B and D must be the same type definition." */ return (0); } /* * SPEC (2.2) "B must be D's {base type definition}." */ if (type->baseType == baseType) return (0); /* * SPEC (2.3.1) "D's {base type definition} must not be the `ur-type * definition`." */ if (WXS_IS_ANYTYPE(type->baseType)) return (1); if (WXS_IS_COMPLEX(type->baseType)) { /* * SPEC (2.3.2.1) "If D's {base type definition} is complex, then it * must be validly derived from B given the subset as defined by this * constraint." */ return (xmlSchemaCheckCOSCTDerivedOK(actxt, type->baseType, baseType, set)); } else { /* * SPEC (2.3.2.2) "If D's {base type definition} is simple, then it * must be validly derived from B given the subset as defined in Type * Derivation OK (Simple) ($3.14.6). */ return (xmlSchemaCheckCOSSTDerivedOK(actxt, type->baseType, baseType, set)); } } /** * xmlSchemaCheckCOSDerivedOK: * @type: the derived simple type definition * @baseType: the base type definition * * Calls: * Type Derivation OK (Simple) AND Type Derivation OK (Complex) * * Checks whether @type can be validly derived from @baseType. * * Returns 0 on success, an positive error code otherwise. */ static int xmlSchemaCheckCOSDerivedOK(xmlSchemaAbstractCtxtPtr actxt, xmlSchemaTypePtr type, xmlSchemaTypePtr baseType, int set) { if (WXS_IS_SIMPLE(type)) return (xmlSchemaCheckCOSSTDerivedOK(actxt, type, baseType, set)); else return (xmlSchemaCheckCOSCTDerivedOK(actxt, type, baseType, set)); } /** * xmlSchemaCheckCOSCTExtends: * @ctxt: the schema parser context * @type: the complex type definition * * (3.4.6) Constraints on Complex Type Definition Schema Components * Schema Component Constraint: * Derivation Valid (Extension) (cos-ct-extends) * * STATUS: * missing: * (1.5) * (1.4.3.2.2.2) "Particle Valid (Extension)" * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckCOSCTExtends(xmlSchemaParserCtxtPtr ctxt, xmlSchemaTypePtr type) { xmlSchemaTypePtr base = type->baseType; /* * TODO: Correct the error code; XML_SCHEMAP_COS_CT_EXTENDS_1_1 is used * temporarily only. */ /* * SPEC (1) "If the {base type definition} is a complex type definition, * then all of the following must be true:" */ if (WXS_IS_COMPLEX(base)) { /* * SPEC (1.1) "The {final} of the {base type definition} must not * contain extension." */ if (base->flags & XML_SCHEMAS_TYPE_FINAL_EXTENSION) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_1, WXS_BASIC_CAST type, NULL, "The 'final' of the base type definition " "contains 'extension'", NULL); return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); } /* * ATTENTION: The constrains (1.2) and (1.3) are not applied, * since they are automatically satisfied through the * inheriting mechanism. * Note that even if redefining components, the inheriting mechanism * is used. */ #if 0 /* * SPEC (1.2) "Its {attribute uses} must be a subset of the {attribute * uses} * of the complex type definition itself, that is, for every attribute * use in the {attribute uses} of the {base type definition}, there * must be an attribute use in the {attribute uses} of the complex * type definition itself whose {attribute declaration} has the same * {name}, {target namespace} and {type definition} as its attribute * declaration" */ if (base->attrUses != NULL) { int i, j, found; xmlSchemaAttributeUsePtr use, buse; for (i = 0; i < (WXS_LIST_CAST base->attrUses)->nbItems; i ++) { buse = (WXS_LIST_CAST base->attrUses)->items[i]; found = 0; if (type->attrUses != NULL) { use = (WXS_LIST_CAST type->attrUses)->items[j]; for (j = 0; j < (WXS_LIST_CAST type->attrUses)->nbItems; j ++) { if ((WXS_ATTRUSE_DECL_NAME(use) == WXS_ATTRUSE_DECL_NAME(buse)) && (WXS_ATTRUSE_DECL_TNS(use) == WXS_ATTRUSE_DECL_TNS(buse)) && (WXS_ATTRUSE_TYPEDEF(use) == WXS_ATTRUSE_TYPEDEF(buse)) { found = 1; break; } } } if (! found) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_2, NULL, WXS_BASIC_CAST type, /* * TODO: The report does not indicate that also the * type needs to be the same. */ "This type is missing a matching correspondent " "for its {base type}'s %s in its {attribute uses}", xmlSchemaGetComponentDesignation(&str, buse->children), NULL); FREE_AND_NULL(str) } } } /* * SPEC (1.3) "If it has an {attribute wildcard}, the complex type * definition must also have one, and the base type definition's * {attribute wildcard}'s {namespace constraint} must be a subset * of the complex type definition's {attribute wildcard}'s {namespace * constraint}, as defined by Wildcard Subset ($3.10.6)." */ /* * MAYBE TODO: Enable if ever needed. But this will be needed only * if created the type via a schema construction API. */ if (base->attributeWildcard != NULL) { if (type->attributeWildcard == NULL) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_3, NULL, type, "The base %s has an attribute wildcard, " "but this type is missing an attribute wildcard", xmlSchemaGetComponentDesignation(&str, base)); FREE_AND_NULL(str) } else if (xmlSchemaCheckCOSNSSubset( base->attributeWildcard, type->attributeWildcard)) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_3, NULL, type, "The attribute wildcard is not a valid " "superset of the one in the base %s", xmlSchemaGetComponentDesignation(&str, base)); FREE_AND_NULL(str) } } #endif /* * SPEC (1.4) "One of the following must be true:" */ if ((type->contentTypeDef != NULL) && (type->contentTypeDef == base->contentTypeDef)) { /* * SPEC (1.4.1) "The {content type} of the {base type definition} * and the {content type} of the complex type definition itself * must be the same simple type definition" * PASS */ } else if ((type->contentType == XML_SCHEMA_CONTENT_EMPTY) && (base->contentType == XML_SCHEMA_CONTENT_EMPTY) ) { /* * SPEC (1.4.2) "The {content type} of both the {base type * definition} and the complex type definition itself must * be empty." * PASS */ } else { /* * SPEC (1.4.3) "All of the following must be true:" */ if (type->subtypes == NULL) { /* * SPEC 1.4.3.1 The {content type} of the complex type * definition itself must specify a particle. */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_1, WXS_BASIC_CAST type, NULL, "The content type must specify a particle", NULL); return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); } /* * SPEC (1.4.3.2) "One of the following must be true:" */ if (base->contentType == XML_SCHEMA_CONTENT_EMPTY) { /* * SPEC (1.4.3.2.1) "The {content type} of the {base type * definition} must be empty. * PASS */ } else { /* * SPEC (1.4.3.2.2) "All of the following must be true:" */ if ((type->contentType != base->contentType) || ((type->contentType != XML_SCHEMA_CONTENT_MIXED) && (type->contentType != XML_SCHEMA_CONTENT_ELEMENTS))) { /* * SPEC (1.4.3.2.2.1) "Both {content type}s must be mixed * or both must be element-only." */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_1, WXS_BASIC_CAST type, NULL, "The content type of both, the type and its base " "type, must either 'mixed' or 'element-only'", NULL); return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); } /* * URGENT TODO SPEC (1.4.3.2.2.2) "The particle of the * complex type definition must be a `valid extension` * of the {base type definition}'s particle, as defined * in Particle Valid (Extension) ($3.9.6)." * * NOTE that we won't check "Particle Valid (Extension)", * since it is ensured by the derivation process in * xmlSchemaTypeFixup(). We need to implement this when heading * for a construction API * TODO: !! This is needed to be checked if redefining a type !! */ } /* * URGENT TODO (1.5) */ } } else { /* * SPEC (2) "If the {base type definition} is a simple type definition, * then all of the following must be true:" */ if (type->contentTypeDef != base) { /* * SPEC (2.1) "The {content type} must be the same simple type * definition." */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_1, WXS_BASIC_CAST type, NULL, "The content type must be the simple base type", NULL); return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); } if (base->flags & XML_SCHEMAS_TYPE_FINAL_EXTENSION) { /* * SPEC (2.2) "The {final} of the {base type definition} must not * contain extension" * NOTE that this is the same as (1.1). */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_COS_CT_EXTENDS_1_1, WXS_BASIC_CAST type, NULL, "The 'final' of the base type definition " "contains 'extension'", NULL); return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); } } return (0); } /** * xmlSchemaCheckDerivationOKRestriction: * @ctxt: the schema parser context * @type: the complex type definition * * (3.4.6) Constraints on Complex Type Definition Schema Components * Schema Component Constraint: * Derivation Valid (Restriction, Complex) (derivation-ok-restriction) * * STATUS: * missing: * (5.4.2) ??? * * ATTENTION: * In XML Schema 1.1 this will be: * Validation Rule: Checking complex type subsumption * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckDerivationOKRestriction(xmlSchemaParserCtxtPtr ctxt, xmlSchemaTypePtr type) { xmlSchemaTypePtr base; /* * TODO: Correct the error code; XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1 is used * temporarily only. */ base = type->baseType; if (! WXS_IS_COMPLEX(base)) { xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, type->node, WXS_BASIC_CAST type, "The base type must be a complex type", NULL, NULL); return(ctxt->err); } if (base->flags & XML_SCHEMAS_TYPE_FINAL_RESTRICTION) { /* * SPEC (1) "The {base type definition} must be a complex type * definition whose {final} does not contain restriction." */ xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, type->node, WXS_BASIC_CAST type, "The 'final' of the base type definition " "contains 'restriction'", NULL, NULL); return (ctxt->err); } /* * SPEC (2), (3) and (4) * Those are handled in a separate function, since the * same constraints are needed for redefinition of * attribute groups as well. */ if (xmlSchemaCheckDerivationOKRestriction2to4(ctxt, XML_SCHEMA_ACTION_DERIVE, WXS_BASIC_CAST type, WXS_BASIC_CAST base, type->attrUses, base->attrUses, type->attributeWildcard, base->attributeWildcard) == -1) { return(-1); } /* * SPEC (5) "One of the following must be true:" */ if (base->builtInType == XML_SCHEMAS_ANYTYPE) { /* * SPEC (5.1) "The {base type definition} must be the * `ur-type definition`." * PASS */ } else if ((type->contentType == XML_SCHEMA_CONTENT_SIMPLE) || (type->contentType == XML_SCHEMA_CONTENT_BASIC)) { /* * SPEC (5.2.1) "The {content type} of the complex type definition * must be a simple type definition" * * SPEC (5.2.2) "One of the following must be true:" */ if ((base->contentType == XML_SCHEMA_CONTENT_SIMPLE) || (base->contentType == XML_SCHEMA_CONTENT_BASIC)) { int err; /* * SPEC (5.2.2.1) "The {content type} of the {base type * definition} must be a simple type definition from which * the {content type} is validly derived given the empty * set as defined in Type Derivation OK (Simple) ($3.14.6)." * * ATTENTION TODO: This seems not needed if the type implicitly * derived from the base type. * */ err = xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST ctxt, type->contentTypeDef, base->contentTypeDef, 0); if (err != 0) { xmlChar *strA = NULL, *strB = NULL; if (err == -1) return(-1); xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, NULL, WXS_BASIC_CAST type, "The {content type} %s is not validly derived from the " "base type's {content type} %s", xmlSchemaGetComponentDesignation(&strA, type->contentTypeDef), xmlSchemaGetComponentDesignation(&strB, base->contentTypeDef)); FREE_AND_NULL(strA); FREE_AND_NULL(strB); return(ctxt->err); } } else if ((base->contentType == XML_SCHEMA_CONTENT_MIXED) && (xmlSchemaIsParticleEmptiable( (xmlSchemaParticlePtr) base->subtypes))) { /* * SPEC (5.2.2.2) "The {base type definition} must be mixed * and have a particle which is `emptiable` as defined in * Particle Emptiable ($3.9.6)." * PASS */ } else { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, WXS_BASIC_CAST type, NULL, "The content type of the base type must be either " "a simple type or 'mixed' and an emptiable particle", NULL); return (ctxt->err); } } else if (type->contentType == XML_SCHEMA_CONTENT_EMPTY) { /* * SPEC (5.3.1) "The {content type} of the complex type itself must * be empty" */ if (base->contentType == XML_SCHEMA_CONTENT_EMPTY) { /* * SPEC (5.3.2.1) "The {content type} of the {base type * definition} must also be empty." * PASS */ } else if (((base->contentType == XML_SCHEMA_CONTENT_ELEMENTS) || (base->contentType == XML_SCHEMA_CONTENT_MIXED)) && xmlSchemaIsParticleEmptiable( (xmlSchemaParticlePtr) base->subtypes)) { /* * SPEC (5.3.2.2) "The {content type} of the {base type * definition} must be elementOnly or mixed and have a particle * which is `emptiable` as defined in Particle Emptiable ($3.9.6)." * PASS */ } else { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, WXS_BASIC_CAST type, NULL, "The content type of the base type must be either " "empty or 'mixed' (or 'elements-only') and an emptiable " "particle", NULL); return (ctxt->err); } } else if ((type->contentType == XML_SCHEMA_CONTENT_ELEMENTS) || WXS_HAS_MIXED_CONTENT(type)) { /* * SPEC (5.4.1.1) "The {content type} of the complex type definition * itself must be element-only" */ if (WXS_HAS_MIXED_CONTENT(type) && (! WXS_HAS_MIXED_CONTENT(base))) { /* * SPEC (5.4.1.2) "The {content type} of the complex type * definition itself and of the {base type definition} must be * mixed" */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, WXS_BASIC_CAST type, NULL, "If the content type is 'mixed', then the content type of the " "base type must also be 'mixed'", NULL); return (ctxt->err); } /* * SPEC (5.4.2) "The particle of the complex type definition itself * must be a `valid restriction` of the particle of the {content * type} of the {base type definition} as defined in Particle Valid * (Restriction) ($3.9.6). * * URGENT TODO: (5.4.2) */ } else { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, WXS_BASIC_CAST type, NULL, "The type is not a valid restriction of its base type", NULL); return (ctxt->err); } return (0); } /** * xmlSchemaCheckCTComponent: * @ctxt: the schema parser context * @type: the complex type definition * * (3.4.6) Constraints on Complex Type Definition Schema Components * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckCTComponent(xmlSchemaParserCtxtPtr ctxt, xmlSchemaTypePtr type) { int ret; /* * Complex Type Definition Properties Correct */ ret = xmlSchemaCheckCTPropsCorrect(ctxt, type); if (ret != 0) return (ret); if (WXS_IS_EXTENSION(type)) ret = xmlSchemaCheckCOSCTExtends(ctxt, type); else ret = xmlSchemaCheckDerivationOKRestriction(ctxt, type); return (ret); } /** * xmlSchemaCheckSRCCT: * @ctxt: the schema parser context * @type: the complex type definition * * (3.4.3) Constraints on XML Representations of Complex Type Definitions: * Schema Representation Constraint: * Complex Type Definition Representation OK (src-ct) * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckSRCCT(xmlSchemaParserCtxtPtr ctxt, xmlSchemaTypePtr type) { xmlSchemaTypePtr base; int ret = 0; /* * TODO: Adjust the error codes here, as I used * XML_SCHEMAP_SRC_CT_1 only yet. */ base = type->baseType; if (! WXS_HAS_SIMPLE_CONTENT(type)) { /* * 1 If the alternative is chosen, the type definition * `resolved` to by the `actual value` of the base [attribute] * must be a complex type definition; */ if (! WXS_IS_COMPLEX(base)) { xmlChar *str = NULL; xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_CT_1, WXS_BASIC_CAST type, type->node, "If using , the base type is expected to be " "a complex type. The base type '%s' is a simple type", xmlSchemaFormatQName(&str, base->targetNamespace, base->name)); FREE_AND_NULL(str) return (XML_SCHEMAP_SRC_CT_1); } } else { /* * SPEC * 2 If the alternative is chosen, all of the * following must be true: * 2.1 The type definition `resolved` to by the `actual value` of the * base [attribute] must be one of the following: */ if (WXS_IS_SIMPLE(base)) { if (WXS_IS_EXTENSION(type) == 0) { xmlChar *str = NULL; /* * 2.1.3 only if the alternative is also * chosen, a simple type definition. */ /* TODO: Change error code to ..._SRC_CT_2_1_3. */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_CT_1, WXS_BASIC_CAST type, NULL, "If using and , the base " "type must be a complex type. The base type '%s' is " "a simple type", xmlSchemaFormatQName(&str, base->targetNamespace, base->name)); FREE_AND_NULL(str) return (XML_SCHEMAP_SRC_CT_1); } } else { /* Base type is a complex type. */ if ((base->contentType == XML_SCHEMA_CONTENT_SIMPLE) || (base->contentType == XML_SCHEMA_CONTENT_BASIC)) { /* * 2.1.1 a complex type definition whose {content type} is a * simple type definition; * PASS */ if (base->contentTypeDef == NULL) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_INTERNAL, WXS_BASIC_CAST type, NULL, "Internal error: xmlSchemaCheckSRCCT, " "'%s', base type has no content type", type->name); return (-1); } } else if ((base->contentType == XML_SCHEMA_CONTENT_MIXED) && (WXS_IS_RESTRICTION(type))) { /* * 2.1.2 only if the alternative is also * chosen, a complex type definition whose {content type} * is mixed and a particle emptiable. */ if (! xmlSchemaIsParticleEmptiable( (xmlSchemaParticlePtr) base->subtypes)) { ret = XML_SCHEMAP_SRC_CT_1; } else /* * Attention: at this point the child is in * ->contentTypeDef (put there during parsing). */ if (type->contentTypeDef == NULL) { xmlChar *str = NULL; /* * 2.2 If clause 2.1.2 above is satisfied, then there * must be a among the [children] of * . */ /* TODO: Change error code to ..._SRC_CT_2_2. */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_CT_1, WXS_BASIC_CAST type, NULL, "A is expected among the children " "of , if is used and " "the base type '%s' is a complex type", xmlSchemaFormatQName(&str, base->targetNamespace, base->name)); FREE_AND_NULL(str) return (XML_SCHEMAP_SRC_CT_1); } } else { ret = XML_SCHEMAP_SRC_CT_1; } } if (ret > 0) { xmlChar *str = NULL; if (WXS_IS_RESTRICTION(type)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_CT_1, WXS_BASIC_CAST type, NULL, "If and is used, the " "base type must be a simple type or a complex type with " "mixed content and particle emptiable. The base type " "'%s' is none of those", xmlSchemaFormatQName(&str, base->targetNamespace, base->name)); } else { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_CT_1, WXS_BASIC_CAST type, NULL, "If and is used, the " "base type must be a simple type. The base type '%s' " "is a complex type", xmlSchemaFormatQName(&str, base->targetNamespace, base->name)); } FREE_AND_NULL(str) } } /* * SPEC (3) "The corresponding complex type definition component must * satisfy the conditions set out in Constraints on Complex Type * Definition Schema Components ($3.4.6);" * NOTE (3) will be done in xmlSchemaTypeFixup(). */ /* * SPEC (4) If clause 2.2.1 or clause 2.2.2 in the correspondence specification * above for {attribute wildcard} is satisfied, the intensional * intersection must be expressible, as defined in Attribute Wildcard * Intersection ($3.10.6). * NOTE (4) is done in xmlSchemaFixupTypeAttributeUses(). */ return (ret); } #ifdef ENABLE_PARTICLE_RESTRICTION /** * xmlSchemaCheckParticleRangeOK: * @ctxt: the schema parser context * @type: the complex type definition * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Occurrence Range OK (range-ok) * * STATUS: complete * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckParticleRangeOK(int rmin, int rmax, int bmin, int bmax) { if (rmin < bmin) return (1); if ((bmax != UNBOUNDED) && (rmax > bmax)) return (1); return (0); } /** * xmlSchemaCheckRCaseNameAndTypeOK: * @ctxt: the schema parser context * @r: the restricting element declaration particle * @b: the base element declaration particle * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Particle Restriction OK (Elt:Elt -- NameAndTypeOK) * (rcase-NameAndTypeOK) * * STATUS: * MISSING (3.2.3) * CLARIFY: (3.2.2) * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckRCaseNameAndTypeOK(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr r, xmlSchemaParticlePtr b) { xmlSchemaElementPtr elemR, elemB; /* TODO: Error codes (rcase-NameAndTypeOK). */ elemR = (xmlSchemaElementPtr) r->children; elemB = (xmlSchemaElementPtr) b->children; /* * SPEC (1) "The declarations' {name}s and {target namespace}s are * the same." */ if ((elemR != elemB) && ((! xmlStrEqual(elemR->name, elemB->name)) || (! xmlStrEqual(elemR->targetNamespace, elemB->targetNamespace)))) return (1); /* * SPEC (2) "R's occurrence range is a valid restriction of B's * occurrence range as defined by Occurrence Range OK ($3.9.6)." */ if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, b->minOccurs, b->maxOccurs) != 0) return (1); /* * SPEC (3.1) "Both B's declaration's {scope} and R's declaration's * {scope} are global." */ if (elemR == elemB) return (0); /* * SPEC (3.2.1) "Either B's {nillable} is true or R's {nillable} is false." */ if (((elemB->flags & XML_SCHEMAS_ELEM_NILLABLE) == 0) && (elemR->flags & XML_SCHEMAS_ELEM_NILLABLE)) return (1); /* * SPEC (3.2.2) "either B's declaration's {value constraint} is absent, * or is not fixed, or R's declaration's {value constraint} is fixed * with the same value." */ if ((elemB->value != NULL) && (elemB->flags & XML_SCHEMAS_ELEM_FIXED) && ((elemR->value == NULL) || ((elemR->flags & XML_SCHEMAS_ELEM_FIXED) == 0) || /* TODO: Equality of the initial value or normalized or canonical? */ (! xmlStrEqual(elemR->value, elemB->value)))) return (1); /* * TODO: SPEC (3.2.3) "R's declaration's {identity-constraint * definitions} is a subset of B's declaration's {identity-constraint * definitions}, if any." */ if (elemB->idcs != NULL) { /* TODO */ } /* * SPEC (3.2.4) "R's declaration's {disallowed substitutions} is a * superset of B's declaration's {disallowed substitutions}." */ if (((elemB->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) && ((elemR->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) == 0)) || ((elemB->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) && ((elemR->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) == 0)) || ((elemB->flags & XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION) && ((elemR->flags & XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION) == 0))) return (1); /* * SPEC (3.2.5) "R's {type definition} is validly derived given * {extension, list, union} from B's {type definition}" * * BADSPEC TODO: What's the point of adding "list" and "union" to the * set, if the corresponding constraints handle "restriction" and * "extension" only? * */ { int set = 0; set |= SUBSET_EXTENSION; set |= SUBSET_LIST; set |= SUBSET_UNION; if (xmlSchemaCheckCOSDerivedOK(ACTXT_CAST ctxt, elemR->subtypes, elemB->subtypes, set) != 0) return (1); } return (0); } /** * xmlSchemaCheckRCaseNSCompat: * @ctxt: the schema parser context * @r: the restricting element declaration particle * @b: the base wildcard particle * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Particle Derivation OK (Elt:Any -- NSCompat) * (rcase-NSCompat) * * STATUS: complete * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckRCaseNSCompat(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr r, xmlSchemaParticlePtr b) { /* TODO:Error codes (rcase-NSCompat). */ /* * SPEC "For an element declaration particle to be a `valid restriction` * of a wildcard particle all of the following must be true:" * * SPEC (1) "The element declaration's {target namespace} is `valid` * with respect to the wildcard's {namespace constraint} as defined by * Wildcard allows Namespace Name ($3.10.4)." */ if (xmlSchemaCheckCVCWildcardNamespace((xmlSchemaWildcardPtr) b->children, ((xmlSchemaElementPtr) r->children)->targetNamespace) != 0) return (1); /* * SPEC (2) "R's occurrence range is a valid restriction of B's * occurrence range as defined by Occurrence Range OK ($3.9.6)." */ if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, b->minOccurs, b->maxOccurs) != 0) return (1); return (0); } /** * xmlSchemaCheckRCaseRecurseAsIfGroup: * @ctxt: the schema parser context * @r: the restricting element declaration particle * @b: the base model group particle * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Particle Derivation OK (Elt:All/Choice/Sequence -- RecurseAsIfGroup) * (rcase-RecurseAsIfGroup) * * STATUS: TODO * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckRCaseRecurseAsIfGroup(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr r, xmlSchemaParticlePtr b) { /* TODO: Error codes (rcase-RecurseAsIfGroup). */ TODO return (0); } /** * xmlSchemaCheckRCaseNSSubset: * @ctxt: the schema parser context * @r: the restricting wildcard particle * @b: the base wildcard particle * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Particle Derivation OK (Any:Any -- NSSubset) * (rcase-NSSubset) * * STATUS: complete * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckRCaseNSSubset(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr r, xmlSchemaParticlePtr b, int isAnyTypeBase) { /* TODO: Error codes (rcase-NSSubset). */ /* * SPEC (1) "R's occurrence range is a valid restriction of B's * occurrence range as defined by Occurrence Range OK ($3.9.6)." */ if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, b->minOccurs, b->maxOccurs)) return (1); /* * SPEC (2) "R's {namespace constraint} must be an intensional subset * of B's {namespace constraint} as defined by Wildcard Subset ($3.10.6)." */ if (xmlSchemaCheckCOSNSSubset((xmlSchemaWildcardPtr) r->children, (xmlSchemaWildcardPtr) b->children)) return (1); /* * SPEC (3) "Unless B is the content model wildcard of the `ur-type * definition`, R's {process contents} must be identical to or stronger * than B's {process contents}, where strict is stronger than lax is * stronger than skip." */ if (! isAnyTypeBase) { if ( ((xmlSchemaWildcardPtr) r->children)->processContents < ((xmlSchemaWildcardPtr) b->children)->processContents) return (1); } return (0); } /** * xmlSchemaCheckCOSParticleRestrict: * @ctxt: the schema parser context * @type: the complex type definition * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Particle Valid (Restriction) (cos-particle-restrict) * * STATUS: TODO * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckCOSParticleRestrict(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr r, xmlSchemaParticlePtr b) { int ret = 0; /*part = WXS_TYPE_PARTICLE(type); basePart = WXS_TYPE_PARTICLE(base); */ TODO /* * SPEC (1) "They are the same particle." */ if (r == b) return (0); return (0); } #if 0 /** * xmlSchemaCheckRCaseNSRecurseCheckCardinality: * @ctxt: the schema parser context * @r: the model group particle * @b: the base wildcard particle * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Particle Derivation OK (All/Choice/Sequence:Any -- * NSRecurseCheckCardinality) * (rcase-NSRecurseCheckCardinality) * * STATUS: TODO: subst-groups * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckRCaseNSRecurseCheckCardinality(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr r, xmlSchemaParticlePtr b) { xmlSchemaParticlePtr part; /* TODO: Error codes (rcase-NSRecurseCheckCardinality). */ if ((r->children == NULL) || (r->children->children == NULL)) return (-1); /* * SPEC "For a group particle to be a `valid restriction` of a * wildcard particle..." * * SPEC (1) "Every member of the {particles} of the group is a `valid * restriction` of the wildcard as defined by * Particle Valid (Restriction) ($3.9.6)." */ part = (xmlSchemaParticlePtr) r->children->children; do { if (xmlSchemaCheckCOSParticleRestrict(ctxt, part, b)) return (1); part = (xmlSchemaParticlePtr) part->next; } while (part != NULL); /* * SPEC (2) "The effective total range of the group [...] is a * valid restriction of B's occurrence range as defined by * Occurrence Range OK ($3.9.6)." */ if (xmlSchemaCheckParticleRangeOK( xmlSchemaGetParticleTotalRangeMin(r), xmlSchemaGetParticleTotalRangeMax(r), b->minOccurs, b->maxOccurs) != 0) return (1); return (0); } #endif /** * xmlSchemaCheckRCaseRecurse: * @ctxt: the schema parser context * @r: the or model group particle * @b: the base or model group particle * * (3.9.6) Constraints on Particle Schema Components * Schema Component Constraint: * Particle Derivation OK (All:All,Sequence:Sequence -- Recurse) * (rcase-Recurse) * * STATUS: ? * TODO: subst-groups * * Returns 0 if the constraints are satisfied, a positive * error code if not and -1 if an internal error occurred. */ static int xmlSchemaCheckRCaseRecurse(xmlSchemaParserCtxtPtr ctxt, xmlSchemaParticlePtr r, xmlSchemaParticlePtr b) { /* xmlSchemaParticlePtr part; */ /* TODO: Error codes (rcase-Recurse). */ if ((r->children == NULL) || (b->children == NULL) || (r->children->type != b->children->type)) return (-1); /* * SPEC "For an all or sequence group particle to be a `valid * restriction` of another group particle with the same {compositor}..." * * SPEC (1) "R's occurrence range is a valid restriction of B's * occurrence range as defined by Occurrence Range OK ($3.9.6)." */ if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, b->minOccurs, b->maxOccurs)) return (1); return (0); } #endif #define FACET_RESTR_MUTUAL_ERR(fac1, fac2) \ xmlSchemaPCustomErrExt(pctxt, \ XML_SCHEMAP_INVALID_FACET_VALUE, \ WXS_BASIC_CAST fac1, fac1->node, \ "It is an error for both '%s' and '%s' to be specified on the "\ "same type definition", \ BAD_CAST xmlSchemaFacetTypeToString(fac1->type), \ BAD_CAST xmlSchemaFacetTypeToString(fac2->type), NULL); #define FACET_RESTR_ERR(fac1, msg) \ xmlSchemaPCustomErr(pctxt, \ XML_SCHEMAP_INVALID_FACET_VALUE, \ WXS_BASIC_CAST fac1, fac1->node, \ msg, NULL); #define FACET_RESTR_FIXED_ERR(fac) \ xmlSchemaPCustomErr(pctxt, \ XML_SCHEMAP_INVALID_FACET_VALUE, \ WXS_BASIC_CAST fac, fac->node, \ "The base type's facet is 'fixed', thus the value must not " \ "differ", NULL); static void xmlSchemaDeriveFacetErr(xmlSchemaParserCtxtPtr pctxt, xmlSchemaFacetPtr facet1, xmlSchemaFacetPtr facet2, int lessGreater, int orEqual, int ofBase) { xmlChar *msg = NULL; msg = xmlStrdup(BAD_CAST "'"); msg = xmlStrcat(msg, xmlSchemaFacetTypeToString(facet1->type)); msg = xmlStrcat(msg, BAD_CAST "' has to be"); if (lessGreater == 0) msg = xmlStrcat(msg, BAD_CAST " equal to"); if (lessGreater == 1) msg = xmlStrcat(msg, BAD_CAST " greater than"); else msg = xmlStrcat(msg, BAD_CAST " less than"); if (orEqual) msg = xmlStrcat(msg, BAD_CAST " or equal to"); msg = xmlStrcat(msg, BAD_CAST " '"); msg = xmlStrcat(msg, xmlSchemaFacetTypeToString(facet2->type)); if (ofBase) msg = xmlStrcat(msg, BAD_CAST "' of the base type"); else msg = xmlStrcat(msg, BAD_CAST "'"); xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_INVALID_FACET_VALUE, WXS_BASIC_CAST facet1, NULL, (const char *) msg, NULL); if (msg != NULL) xmlFree(msg); } /* * xmlSchemaDeriveAndValidateFacets: * * Schema Component Constraint: Simple Type Restriction (Facets) * (st-restrict-facets) */ static int xmlSchemaDeriveAndValidateFacets(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { xmlSchemaTypePtr base = type->baseType; xmlSchemaFacetLinkPtr link, cur, last = NULL; xmlSchemaFacetPtr facet, bfacet, flength = NULL, ftotdig = NULL, ffracdig = NULL, fmaxlen = NULL, fminlen = NULL, /* facets of the current type */ fmininc = NULL, fmaxinc = NULL, fminexc = NULL, fmaxexc = NULL, bflength = NULL, bftotdig = NULL, bffracdig = NULL, bfmaxlen = NULL, bfminlen = NULL, /* facets of the base type */ bfmininc = NULL, bfmaxinc = NULL, bfminexc = NULL, bfmaxexc = NULL; int res; /* err = 0, fixedErr; */ /* * SPEC st-restrict-facets 1: * "The {variety} of R is the same as that of B." */ /* * SPEC st-restrict-facets 2: * "If {variety} is atomic, the {primitive type definition} * of R is the same as that of B." * * NOTE: we leave 1 & 2 out for now, since this will be * satisfied by the derivation process. * CONSTRUCTION TODO: Maybe needed if using a construction API. */ /* * SPEC st-restrict-facets 3: * "The {facets} of R are the union of S and the {facets} * of B, eliminating duplicates. To eliminate duplicates, * when a facet of the same kind occurs in both S and the * {facets} of B, the one in the {facets} of B is not * included, with the exception of enumeration and pattern * facets, for which multiple occurrences with distinct values * are allowed." */ if ((type->facetSet == NULL) && (base->facetSet == NULL)) return (0); last = type->facetSet; if (last != NULL) while (last->next != NULL) last = last->next; for (cur = type->facetSet; cur != NULL; cur = cur->next) { facet = cur->facet; switch (facet->type) { case XML_SCHEMA_FACET_LENGTH: flength = facet; break; case XML_SCHEMA_FACET_MINLENGTH: fminlen = facet; break; case XML_SCHEMA_FACET_MININCLUSIVE: fmininc = facet; break; case XML_SCHEMA_FACET_MINEXCLUSIVE: fminexc = facet; break; case XML_SCHEMA_FACET_MAXLENGTH: fmaxlen = facet; break; case XML_SCHEMA_FACET_MAXINCLUSIVE: fmaxinc = facet; break; case XML_SCHEMA_FACET_MAXEXCLUSIVE: fmaxexc = facet; break; case XML_SCHEMA_FACET_TOTALDIGITS: ftotdig = facet; break; case XML_SCHEMA_FACET_FRACTIONDIGITS: ffracdig = facet; break; default: break; } } for (cur = base->facetSet; cur != NULL; cur = cur->next) { facet = cur->facet; switch (facet->type) { case XML_SCHEMA_FACET_LENGTH: bflength = facet; break; case XML_SCHEMA_FACET_MINLENGTH: bfminlen = facet; break; case XML_SCHEMA_FACET_MININCLUSIVE: bfmininc = facet; break; case XML_SCHEMA_FACET_MINEXCLUSIVE: bfminexc = facet; break; case XML_SCHEMA_FACET_MAXLENGTH: bfmaxlen = facet; break; case XML_SCHEMA_FACET_MAXINCLUSIVE: bfmaxinc = facet; break; case XML_SCHEMA_FACET_MAXEXCLUSIVE: bfmaxexc = facet; break; case XML_SCHEMA_FACET_TOTALDIGITS: bftotdig = facet; break; case XML_SCHEMA_FACET_FRACTIONDIGITS: bffracdig = facet; break; default: break; } } /* * length and minLength or maxLength (2.2) + (3.2) */ if (flength && (fminlen || fmaxlen)) { FACET_RESTR_ERR(flength, "It is an error for both 'length' and " "either of 'minLength' or 'maxLength' to be specified on " "the same type definition") } /* * Mutual exclusions in the same derivation step. */ if ((fmaxinc) && (fmaxexc)) { /* * SCC "maxInclusive and maxExclusive" */ FACET_RESTR_MUTUAL_ERR(fmaxinc, fmaxexc) } if ((fmininc) && (fminexc)) { /* * SCC "minInclusive and minExclusive" */ FACET_RESTR_MUTUAL_ERR(fmininc, fminexc) } if (flength && bflength) { /* * SCC "length valid restriction" * The values have to be equal. */ res = xmlSchemaCompareValues(flength->val, bflength->val); if (res == -2) goto internal_error; if (res != 0) xmlSchemaDeriveFacetErr(pctxt, flength, bflength, 0, 0, 1); if ((res != 0) && (bflength->fixed)) { FACET_RESTR_FIXED_ERR(flength) } } if (fminlen && bfminlen) { /* * SCC "minLength valid restriction" * minLength >= BASE minLength */ res = xmlSchemaCompareValues(fminlen->val, bfminlen->val); if (res == -2) goto internal_error; if (res == -1) xmlSchemaDeriveFacetErr(pctxt, fminlen, bfminlen, 1, 1, 1); if ((res != 0) && (bfminlen->fixed)) { FACET_RESTR_FIXED_ERR(fminlen) } } if (fmaxlen && bfmaxlen) { /* * SCC "maxLength valid restriction" * maxLength <= BASE minLength */ res = xmlSchemaCompareValues(fmaxlen->val, bfmaxlen->val); if (res == -2) goto internal_error; if (res == 1) xmlSchemaDeriveFacetErr(pctxt, fmaxlen, bfmaxlen, -1, 1, 1); if ((res != 0) && (bfmaxlen->fixed)) { FACET_RESTR_FIXED_ERR(fmaxlen) } } /* * SCC "length and minLength or maxLength" */ if (! flength) flength = bflength; if (flength) { if (! fminlen) fminlen = bfminlen; if (fminlen) { /* (1.1) length >= minLength */ res = xmlSchemaCompareValues(flength->val, fminlen->val); if (res == -2) goto internal_error; if (res == -1) xmlSchemaDeriveFacetErr(pctxt, flength, fminlen, 1, 1, 0); } if (! fmaxlen) fmaxlen = bfmaxlen; if (fmaxlen) { /* (2.1) length <= maxLength */ res = xmlSchemaCompareValues(flength->val, fmaxlen->val); if (res == -2) goto internal_error; if (res == 1) xmlSchemaDeriveFacetErr(pctxt, flength, fmaxlen, -1, 1, 0); } } if (fmaxinc) { /* * "maxInclusive" */ if (fmininc) { /* SCC "maxInclusive >= minInclusive" */ res = xmlSchemaCompareValues(fmaxinc->val, fmininc->val); if (res == -2) goto internal_error; if (res == -1) { xmlSchemaDeriveFacetErr(pctxt, fmaxinc, fmininc, 1, 1, 0); } } /* * SCC "maxInclusive valid restriction" */ if (bfmaxinc) { /* maxInclusive <= BASE maxInclusive */ res = xmlSchemaCompareValues(fmaxinc->val, bfmaxinc->val); if (res == -2) goto internal_error; if (res == 1) xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfmaxinc, -1, 1, 1); if ((res != 0) && (bfmaxinc->fixed)) { FACET_RESTR_FIXED_ERR(fmaxinc) } } if (bfmaxexc) { /* maxInclusive < BASE maxExclusive */ res = xmlSchemaCompareValues(fmaxinc->val, bfmaxexc->val); if (res == -2) goto internal_error; if (res != -1) { xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfmaxexc, -1, 0, 1); } } if (bfmininc) { /* maxInclusive >= BASE minInclusive */ res = xmlSchemaCompareValues(fmaxinc->val, bfmininc->val); if (res == -2) goto internal_error; if (res == -1) { xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfmininc, 1, 1, 1); } } if (bfminexc) { /* maxInclusive > BASE minExclusive */ res = xmlSchemaCompareValues(fmaxinc->val, bfminexc->val); if (res == -2) goto internal_error; if (res != 1) { xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfminexc, 1, 0, 1); } } } if (fmaxexc) { /* * "maxExclusive >= minExclusive" */ if (fminexc) { res = xmlSchemaCompareValues(fmaxexc->val, fminexc->val); if (res == -2) goto internal_error; if (res == -1) { xmlSchemaDeriveFacetErr(pctxt, fmaxexc, fminexc, 1, 1, 0); } } /* * "maxExclusive valid restriction" */ if (bfmaxexc) { /* maxExclusive <= BASE maxExclusive */ res = xmlSchemaCompareValues(fmaxexc->val, bfmaxexc->val); if (res == -2) goto internal_error; if (res == 1) { xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfmaxexc, -1, 1, 1); } if ((res != 0) && (bfmaxexc->fixed)) { FACET_RESTR_FIXED_ERR(fmaxexc) } } if (bfmaxinc) { /* maxExclusive <= BASE maxInclusive */ res = xmlSchemaCompareValues(fmaxexc->val, bfmaxinc->val); if (res == -2) goto internal_error; if (res == 1) { xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfmaxinc, -1, 1, 1); } } if (bfmininc) { /* maxExclusive > BASE minInclusive */ res = xmlSchemaCompareValues(fmaxexc->val, bfmininc->val); if (res == -2) goto internal_error; if (res != 1) { xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfmininc, 1, 0, 1); } } if (bfminexc) { /* maxExclusive > BASE minExclusive */ res = xmlSchemaCompareValues(fmaxexc->val, bfminexc->val); if (res == -2) goto internal_error; if (res != 1) { xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfminexc, 1, 0, 1); } } } if (fminexc) { /* * "minExclusive < maxInclusive" */ if (fmaxinc) { res = xmlSchemaCompareValues(fminexc->val, fmaxinc->val); if (res == -2) goto internal_error; if (res != -1) { xmlSchemaDeriveFacetErr(pctxt, fminexc, fmaxinc, -1, 0, 0); } } /* * "minExclusive valid restriction" */ if (bfminexc) { /* minExclusive >= BASE minExclusive */ res = xmlSchemaCompareValues(fminexc->val, bfminexc->val); if (res == -2) goto internal_error; if (res == -1) { xmlSchemaDeriveFacetErr(pctxt, fminexc, bfminexc, 1, 1, 1); } if ((res != 0) && (bfminexc->fixed)) { FACET_RESTR_FIXED_ERR(fminexc) } } if (bfmaxinc) { /* minExclusive <= BASE maxInclusive */ res = xmlSchemaCompareValues(fminexc->val, bfmaxinc->val); if (res == -2) goto internal_error; if (res == 1) { xmlSchemaDeriveFacetErr(pctxt, fminexc, bfmaxinc, -1, 1, 1); } } if (bfmininc) { /* minExclusive >= BASE minInclusive */ res = xmlSchemaCompareValues(fminexc->val, bfmininc->val); if (res == -2) goto internal_error; if (res == -1) { xmlSchemaDeriveFacetErr(pctxt, fminexc, bfmininc, 1, 1, 1); } } if (bfmaxexc) { /* minExclusive < BASE maxExclusive */ res = xmlSchemaCompareValues(fminexc->val, bfmaxexc->val); if (res == -2) goto internal_error; if (res != -1) { xmlSchemaDeriveFacetErr(pctxt, fminexc, bfmaxexc, -1, 0, 1); } } } if (fmininc) { /* * "minInclusive < maxExclusive" */ if (fmaxexc) { res = xmlSchemaCompareValues(fmininc->val, fmaxexc->val); if (res == -2) goto internal_error; if (res != -1) { xmlSchemaDeriveFacetErr(pctxt, fmininc, fmaxexc, -1, 0, 0); } } /* * "minExclusive valid restriction" */ if (bfmininc) { /* minInclusive >= BASE minInclusive */ res = xmlSchemaCompareValues(fmininc->val, bfmininc->val); if (res == -2) goto internal_error; if (res == -1) { xmlSchemaDeriveFacetErr(pctxt, fmininc, bfmininc, 1, 1, 1); } if ((res != 0) && (bfmininc->fixed)) { FACET_RESTR_FIXED_ERR(fmininc) } } if (bfmaxinc) { /* minInclusive <= BASE maxInclusive */ res = xmlSchemaCompareValues(fmininc->val, bfmaxinc->val); if (res == -2) goto internal_error; if (res == 1) { xmlSchemaDeriveFacetErr(pctxt, fmininc, bfmaxinc, -1, 1, 1); } } if (bfminexc) { /* minInclusive > BASE minExclusive */ res = xmlSchemaCompareValues(fmininc->val, bfminexc->val); if (res == -2) goto internal_error; if (res != 1) xmlSchemaDeriveFacetErr(pctxt, fmininc, bfminexc, 1, 0, 1); } if (bfmaxexc) { /* minInclusive < BASE maxExclusive */ res = xmlSchemaCompareValues(fmininc->val, bfmaxexc->val); if (res == -2) goto internal_error; if (res != -1) xmlSchemaDeriveFacetErr(pctxt, fmininc, bfmaxexc, -1, 0, 1); } } if (ftotdig && bftotdig) { /* * SCC " totalDigits valid restriction" * totalDigits <= BASE totalDigits */ res = xmlSchemaCompareValues(ftotdig->val, bftotdig->val); if (res == -2) goto internal_error; if (res == 1) xmlSchemaDeriveFacetErr(pctxt, ftotdig, bftotdig, -1, 1, 1); if ((res != 0) && (bftotdig->fixed)) { FACET_RESTR_FIXED_ERR(ftotdig) } } if (ffracdig && bffracdig) { /* * SCC "fractionDigits valid restriction" * fractionDigits <= BASE fractionDigits */ res = xmlSchemaCompareValues(ffracdig->val, bffracdig->val); if (res == -2) goto internal_error; if (res == 1) xmlSchemaDeriveFacetErr(pctxt, ffracdig, bffracdig, -1, 1, 1); if ((res != 0) && (bffracdig->fixed)) { FACET_RESTR_FIXED_ERR(ffracdig) } } /* * SCC "fractionDigits less than or equal to totalDigits" */ if (! ftotdig) ftotdig = bftotdig; if (! ffracdig) ffracdig = bffracdig; if (ftotdig && ffracdig) { res = xmlSchemaCompareValues(ffracdig->val, ftotdig->val); if (res == -2) goto internal_error; if (res == 1) xmlSchemaDeriveFacetErr(pctxt, ffracdig, ftotdig, -1, 1, 0); } /* * *Enumerations* won' be added here, since only the first set * of enumerations in the ancestor-or-self axis is used * for validation, plus we need to use the base type of those * enumerations for whitespace. * * *Patterns*: won't be add here, since they are ORed at * type level and ANDed at ancestor level. This will * happen during validation by walking the base axis * of the type. */ for (cur = base->facetSet; cur != NULL; cur = cur->next) { bfacet = cur->facet; /* * Special handling of enumerations and patterns. * TODO: hmm, they should not appear in the set, so remove this. */ if ((bfacet->type == XML_SCHEMA_FACET_PATTERN) || (bfacet->type == XML_SCHEMA_FACET_ENUMERATION)) continue; /* * Search for a duplicate facet in the current type. */ link = type->facetSet; /* err = 0; */ /* fixedErr = 0; */ while (link != NULL) { facet = link->facet; if (facet->type == bfacet->type) { switch (facet->type) { case XML_SCHEMA_FACET_WHITESPACE: /* * The whitespace must be stronger. */ if (facet->whitespace < bfacet->whitespace) { FACET_RESTR_ERR(facet, "The 'whitespace' value has to be equal to " "or stronger than the 'whitespace' value of " "the base type") } if ((bfacet->fixed) && (facet->whitespace != bfacet->whitespace)) { FACET_RESTR_FIXED_ERR(facet) } break; default: break; } /* Duplicate found. */ break; } link = link->next; } /* * If no duplicate was found: add the base types's facet * to the set. */ if (link == NULL) { link = (xmlSchemaFacetLinkPtr) xmlMalloc(sizeof(xmlSchemaFacetLink)); if (link == NULL) { xmlSchemaPErrMemory(pctxt); return (-1); } link->facet = cur->facet; link->next = NULL; if (last == NULL) type->facetSet = link; else last->next = link; last = link; } } return (0); internal_error: PERROR_INT("xmlSchemaDeriveAndValidateFacets", "an error occurred"); return (-1); } static int xmlSchemaFinishMemberTypeDefinitionsProperty(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { xmlSchemaTypeLinkPtr link, lastLink, prevLink, subLink, newLink; /* * The actual value is then formed by replacing any union type * definition in the `explicit members` with the members of their * {member type definitions}, in order. * * TODO: There's a bug entry at * "http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005JulSep/0287.html" * which indicates that we'll keep the union types the future. */ link = type->memberTypes; while (link != NULL) { if (WXS_IS_TYPE_NOT_FIXED(link->type)) xmlSchemaTypeFixup(link->type, ACTXT_CAST pctxt); if (WXS_IS_UNION(link->type)) { subLink = xmlSchemaGetUnionSimpleTypeMemberTypes(link->type); if (subLink != NULL) { link->type = subLink->type; if (subLink->next != NULL) { lastLink = link->next; subLink = subLink->next; prevLink = link; while (subLink != NULL) { newLink = (xmlSchemaTypeLinkPtr) xmlMalloc(sizeof(xmlSchemaTypeLink)); if (newLink == NULL) { xmlSchemaPErrMemory(pctxt); return (-1); } newLink->type = subLink->type; prevLink->next = newLink; prevLink = newLink; newLink->next = lastLink; subLink = subLink->next; } } } } link = link->next; } return (0); } static void xmlSchemaTypeFixupOptimFacets(xmlSchemaTypePtr type) { int has = 0, needVal = 0, normVal = 0; has = (type->baseType->flags & XML_SCHEMAS_TYPE_HAS_FACETS) ? 1 : 0; if (has) { needVal = (type->baseType->flags & XML_SCHEMAS_TYPE_FACETSNEEDVALUE) ? 1 : 0; normVal = (type->baseType->flags & XML_SCHEMAS_TYPE_NORMVALUENEEDED) ? 1 : 0; } if (type->facets != NULL) { xmlSchemaFacetPtr fac; for (fac = type->facets; fac != NULL; fac = fac->next) { switch (fac->type) { case XML_SCHEMA_FACET_WHITESPACE: break; case XML_SCHEMA_FACET_PATTERN: normVal = 1; has = 1; break; case XML_SCHEMA_FACET_ENUMERATION: needVal = 1; normVal = 1; has = 1; break; default: has = 1; break; } } } if (normVal) type->flags |= XML_SCHEMAS_TYPE_NORMVALUENEEDED; if (needVal) type->flags |= XML_SCHEMAS_TYPE_FACETSNEEDVALUE; if (has) type->flags |= XML_SCHEMAS_TYPE_HAS_FACETS; if (has && (! needVal) && WXS_IS_ATOMIC(type)) { xmlSchemaTypePtr prim = xmlSchemaGetPrimitiveType(type); /* * OPTIMIZE VAL TODO: Some facets need a computed value. */ if ((prim->builtInType != XML_SCHEMAS_ANYSIMPLETYPE) && (prim->builtInType != XML_SCHEMAS_STRING)) { type->flags |= XML_SCHEMAS_TYPE_FACETSNEEDVALUE; } } } static int xmlSchemaTypeFixupWhitespace(xmlSchemaTypePtr type) { /* * Evaluate the whitespace-facet value. */ if (WXS_IS_LIST(type)) { type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE; return (0); } else if (WXS_IS_UNION(type)) return (0); if (type->facetSet != NULL) { xmlSchemaFacetLinkPtr lin; for (lin = type->facetSet; lin != NULL; lin = lin->next) { if (lin->facet->type == XML_SCHEMA_FACET_WHITESPACE) { switch (lin->facet->whitespace) { case XML_SCHEMAS_FACET_PRESERVE: type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE; break; case XML_SCHEMAS_FACET_REPLACE: type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_REPLACE; break; case XML_SCHEMAS_FACET_COLLAPSE: type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE; break; default: return (-1); } return (0); } } } /* * For all `atomic` datatypes other than string (and types `derived` * by `restriction` from it) the value of whiteSpace is fixed to * collapse */ { xmlSchemaTypePtr anc; for (anc = type->baseType; anc != NULL && anc->builtInType != XML_SCHEMAS_ANYTYPE; anc = anc->baseType) { if (anc->type == XML_SCHEMA_TYPE_BASIC) { if (anc->builtInType == XML_SCHEMAS_NORMSTRING) { type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_REPLACE; } else if ((anc->builtInType == XML_SCHEMAS_STRING) || (anc->builtInType == XML_SCHEMAS_ANYSIMPLETYPE)) { type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE; } else type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE; break; } } } return (0); } static int xmlSchemaFixupSimpleTypeStageOne(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { if (type->type != XML_SCHEMA_TYPE_SIMPLE) return(0); if (! WXS_IS_TYPE_NOT_FIXED_1(type)) return(0); type->flags |= XML_SCHEMAS_TYPE_FIXUP_1; if (WXS_IS_LIST(type)) { /* * Corresponds to ... */ if (type->subtypes == NULL) { /* * This one is really needed, so get out. */ PERROR_INT("xmlSchemaFixupSimpleTypeStageOne", "list type has no item-type assigned"); return(-1); } } else if (WXS_IS_UNION(type)) { /* * Corresponds to ... */ if (type->memberTypes == NULL) { /* * This one is really needed, so get out. */ PERROR_INT("xmlSchemaFixupSimpleTypeStageOne", "union type has no member-types assigned"); return(-1); } } else { /* * Corresponds to ... */ if (type->baseType == NULL) { PERROR_INT("xmlSchemaFixupSimpleTypeStageOne", "type has no base-type assigned"); return(-1); } if (WXS_IS_TYPE_NOT_FIXED_1(type->baseType)) if (xmlSchemaFixupSimpleTypeStageOne(pctxt, type->baseType) == -1) return(-1); /* * Variety * If the alternative is chosen, then the * {variety} of the {base type definition}. */ if (WXS_IS_ATOMIC(type->baseType)) type->flags |= XML_SCHEMAS_TYPE_VARIETY_ATOMIC; else if (WXS_IS_LIST(type->baseType)) { type->flags |= XML_SCHEMAS_TYPE_VARIETY_LIST; /* * Inherit the itemType. */ type->subtypes = type->baseType->subtypes; } else if (WXS_IS_UNION(type->baseType)) { type->flags |= XML_SCHEMAS_TYPE_VARIETY_UNION; /* * NOTE that we won't assign the memberTypes of the base, * since this will make trouble when freeing them; we will * use a lookup function to access them instead. */ } } return(0); } /* * 3.14.6 Constraints on Simple Type Definition Schema Components */ static int xmlSchemaFixupSimpleTypeStageTwo(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { int res, olderrs = pctxt->nberrors; if (type->type != XML_SCHEMA_TYPE_SIMPLE) return(-1); if (! WXS_IS_TYPE_NOT_FIXED(type)) return(0); type->flags |= XML_SCHEMAS_TYPE_INTERNAL_RESOLVED; type->contentType = XML_SCHEMA_CONTENT_SIMPLE; if (type->baseType == NULL) { PERROR_INT("xmlSchemaFixupSimpleTypeStageTwo", "missing baseType"); goto exit_failure; } if (WXS_IS_TYPE_NOT_FIXED(type->baseType)) xmlSchemaTypeFixup(type->baseType, ACTXT_CAST pctxt); /* * If a member type of a union is a union itself, we need to substitute * that member type for its member types. * NOTE that this might change in WXS 1.1; i.e. we will keep the union * types in WXS 1.1. */ if ((type->memberTypes != NULL) && (xmlSchemaFinishMemberTypeDefinitionsProperty(pctxt, type) == -1)) return(-1); /* * SPEC src-simple-type 1 * "The corresponding simple type definition, if any, must satisfy * the conditions set out in Constraints on Simple Type Definition * Schema Components ($3.14.6)." */ /* * Schema Component Constraint: Simple Type Definition Properties Correct * (st-props-correct) */ res = xmlSchemaCheckSTPropsCorrect(pctxt, type); HFAILURE HERROR /* * Schema Component Constraint: Derivation Valid (Restriction, Simple) * (cos-st-restricts) */ res = xmlSchemaCheckCOSSTRestricts(pctxt, type); HFAILURE HERROR /* * TODO: Removed the error report, since it got annoying to get an * extra error report, if anything failed until now. * Enable this if needed. * * xmlSchemaPErr(ctxt, type->node, * XML_SCHEMAP_SRC_SIMPLE_TYPE_1, * "Simple type '%s' does not satisfy the constraints " * "on simple type definitions.\n", * type->name, NULL); */ /* * Schema Component Constraint: Simple Type Restriction (Facets) * (st-restrict-facets) */ res = xmlSchemaCheckFacetValues(type, pctxt); HFAILURE HERROR if ((type->facetSet != NULL) || (type->baseType->facetSet != NULL)) { res = xmlSchemaDeriveAndValidateFacets(pctxt, type); HFAILURE HERROR } /* * Whitespace value. */ res = xmlSchemaTypeFixupWhitespace(type); HFAILURE HERROR xmlSchemaTypeFixupOptimFacets(type); exit_error: if (olderrs != pctxt->nberrors) return(pctxt->err); return(0); exit_failure: return(-1); } static int xmlSchemaFixupComplexType(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { int res = 0, olderrs = pctxt->nberrors; xmlSchemaTypePtr baseType = type->baseType; if (! WXS_IS_TYPE_NOT_FIXED(type)) return(0); type->flags |= XML_SCHEMAS_TYPE_INTERNAL_RESOLVED; if (baseType == NULL) { PERROR_INT("xmlSchemaFixupComplexType", "missing baseType"); goto exit_failure; } /* * Fixup the base type. */ if (WXS_IS_TYPE_NOT_FIXED(baseType)) xmlSchemaTypeFixup(baseType, ACTXT_CAST pctxt); if (baseType->flags & XML_SCHEMAS_TYPE_INTERNAL_INVALID) { /* * Skip fixup if the base type is invalid. * TODO: Generate a warning! */ return(0); } /* * This basically checks if the base type can be derived. */ res = xmlSchemaCheckSRCCT(pctxt, type); HFAILURE HERROR /* * Fixup the content type. */ if (type->contentType == XML_SCHEMA_CONTENT_SIMPLE) { /* * Corresponds to ... */ if ((WXS_IS_COMPLEX(baseType)) && (baseType->contentTypeDef != NULL) && (WXS_IS_RESTRICTION(type))) { xmlSchemaTypePtr contentBase, content; #ifdef ENABLE_NAMED_LOCALS char buf[30]; const xmlChar *tmpname; #endif /* * SPEC (1) If + base type is , * "whose own {content type} is a simple type..." */ if (type->contentTypeDef != NULL) { /* * SPEC (1.1) "the simple type definition corresponding to the * among the [children] of if there * is one;" * Note that this " among the [children]" was put * into ->contentTypeDef during parsing. */ contentBase = type->contentTypeDef; type->contentTypeDef = NULL; } else { /* * (1.2) "...otherwise ( has no * among its [children]), the simple type definition which * is the {content type} of the ... base type." */ contentBase = baseType->contentTypeDef; } /* * SPEC * "... a simple type definition which restricts the simple * type definition identified in clause 1.1 or clause 1.2 * with a set of facet components" * * Create the anonymous simple type, which will be the content * type of the complex type. */ #ifdef ENABLE_NAMED_LOCALS snprintf(buf, 29, "#scST%d", ++(pctxt->counter)); tmpname = xmlDictLookup(pctxt->dict, BAD_CAST buf, -1); content = xmlSchemaAddType(pctxt, pctxt->schema, XML_SCHEMA_TYPE_SIMPLE, tmpname, type->targetNamespace, type->node, 0); #else content = xmlSchemaAddType(pctxt, pctxt->schema, XML_SCHEMA_TYPE_SIMPLE, NULL, type->targetNamespace, type->node, 0); #endif if (content == NULL) goto exit_failure; /* * We will use the same node as for the * to have it somehow anchored in the schema doc. */ content->type = XML_SCHEMA_TYPE_SIMPLE; content->baseType = contentBase; /* * Move the facets, previously anchored on the * complexType during parsing. */ content->facets = type->facets; type->facets = NULL; content->facetSet = type->facetSet; type->facetSet = NULL; type->contentTypeDef = content; if (WXS_IS_TYPE_NOT_FIXED(contentBase)) xmlSchemaTypeFixup(contentBase, ACTXT_CAST pctxt); /* * Fixup the newly created type. We don't need to check * for circularity here. */ res = xmlSchemaFixupSimpleTypeStageOne(pctxt, content); HFAILURE HERROR res = xmlSchemaFixupSimpleTypeStageTwo(pctxt, content); HFAILURE HERROR } else if ((WXS_IS_COMPLEX(baseType)) && (baseType->contentType == XML_SCHEMA_CONTENT_MIXED) && (WXS_IS_RESTRICTION(type))) { /* * SPEC (2) If + base is a mixed with * an emptiable particle, then a simple type definition which * restricts the 's child. */ if ((type->contentTypeDef == NULL) || (type->contentTypeDef->baseType == NULL)) { /* * TODO: Check if this ever happens. */ xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_INTERNAL, WXS_BASIC_CAST type, NULL, "Internal error: xmlSchemaTypeFixup, " "complex type '%s': the " "is missing a child, but was not caught " "by xmlSchemaCheckSRCCT()", type->name); goto exit_failure; } } else if ((WXS_IS_COMPLEX(baseType)) && WXS_IS_EXTENSION(type)) { /* * SPEC (3) If + base is with * content, "...then the {content type} of that * complex type definition" */ if (baseType->contentTypeDef == NULL) { /* * TODO: Check if this ever happens. xmlSchemaCheckSRCCT * should have caught this already. */ xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_INTERNAL, WXS_BASIC_CAST type, NULL, "Internal error: xmlSchemaTypeFixup, " "complex type '%s': the ed base type is " "a complex type with no simple content type", type->name); goto exit_failure; } type->contentTypeDef = baseType->contentTypeDef; } else if ((WXS_IS_SIMPLE(baseType)) && WXS_IS_EXTENSION(type)) { /* * SPEC (4) + base is * "... then that simple type definition" */ type->contentTypeDef = baseType; } else { /* * TODO: Check if this ever happens. */ xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_INTERNAL, WXS_BASIC_CAST type, NULL, "Internal error: xmlSchemaTypeFixup, " "complex type '%s' with : unhandled " "derivation case", type->name); goto exit_failure; } } else { int dummySequence = 0; xmlSchemaParticlePtr particle = (xmlSchemaParticlePtr) type->subtypes; /* * Corresponds to ... * * NOTE that the effective mixed was already set during parsing of * and ; its flag value is * XML_SCHEMAS_TYPE_MIXED. * * Compute the "effective content": * (2.1.1) + (2.1.2) + (2.1.3) */ if ((particle == NULL) || ((particle->type == XML_SCHEMA_TYPE_PARTICLE) && ((particle->children->type == XML_SCHEMA_TYPE_ALL) || (particle->children->type == XML_SCHEMA_TYPE_SEQUENCE) || ((particle->children->type == XML_SCHEMA_TYPE_CHOICE) && (particle->minOccurs == 0))) && ( ((xmlSchemaTreeItemPtr) particle->children)->children == NULL))) { if (type->flags & XML_SCHEMAS_TYPE_MIXED) { /* * SPEC (2.1.4) "If the `effective mixed` is true, then * a particle whose properties are as follows:..." * * Empty sequence model group with * minOccurs/maxOccurs = 1 (i.e. a "particle emptiable"). * NOTE that we sill assign it the node to * somehow anchor it in the doc. */ if ((particle == NULL) || (particle->children->type != XML_SCHEMA_TYPE_SEQUENCE)) { /* * Create the particle. */ particle = xmlSchemaAddParticle(pctxt, type->node, 1, 1); if (particle == NULL) goto exit_failure; /* * Create the model group. */ /* URGENT TODO: avoid adding to pending items. */ particle->children = (xmlSchemaTreeItemPtr) xmlSchemaAddModelGroup(pctxt, pctxt->schema, XML_SCHEMA_TYPE_SEQUENCE, type->node); if (particle->children == NULL) goto exit_failure; type->subtypes = (xmlSchemaTypePtr) particle; } dummySequence = 1; type->contentType = XML_SCHEMA_CONTENT_ELEMENTS; } else { /* * SPEC (2.1.5) "otherwise empty" */ type->contentType = XML_SCHEMA_CONTENT_EMPTY; } } else { /* * SPEC (2.2) "otherwise the particle corresponding to the * , , or among the * [children]." */ type->contentType = XML_SCHEMA_CONTENT_ELEMENTS; } /* * Compute the "content type". */ if (WXS_IS_RESTRICTION(type)) { /* * SPEC (3.1) "If ..." * (3.1.1) + (3.1.2) */ if (type->contentType != XML_SCHEMA_CONTENT_EMPTY) { if (type->flags & XML_SCHEMAS_TYPE_MIXED) type->contentType = XML_SCHEMA_CONTENT_MIXED; } } else { /* * SPEC (3.2) "If ..." */ if (type->contentType == XML_SCHEMA_CONTENT_EMPTY) { /* * SPEC (3.2.1) * "If the `effective content` is empty, then the * {content type} of the [...] base ..." */ type->contentType = baseType->contentType; type->subtypes = baseType->subtypes; /* * Fixes bug #347316: * This is the case when the base type has a simple * type definition as content. */ type->contentTypeDef = baseType->contentTypeDef; /* * NOTE that the effective mixed is ignored here. */ } else if (baseType->contentType == XML_SCHEMA_CONTENT_EMPTY) { /* * SPEC (3.2.2) */ if (type->flags & XML_SCHEMAS_TYPE_MIXED) type->contentType = XML_SCHEMA_CONTENT_MIXED; } else { /* * SPEC (3.2.3) */ if (type->flags & XML_SCHEMAS_TYPE_MIXED) type->contentType = XML_SCHEMA_CONTENT_MIXED; /* * "A model group whose {compositor} is sequence and whose * {particles} are..." */ if ((WXS_TYPE_PARTICLE(type) != NULL) && (WXS_TYPE_PARTICLE_TERM(type) != NULL) && ((WXS_TYPE_PARTICLE_TERM(type))->type == XML_SCHEMA_TYPE_ALL)) { /* * SPEC cos-all-limited (1) */ xmlSchemaCustomErr(ACTXT_CAST pctxt, /* TODO: error code */ XML_SCHEMAP_COS_ALL_LIMITED, WXS_ITEM_NODE(type), NULL, "The type has an 'all' model group in its " "{content type} and thus cannot be derived from " "a non-empty type, since this would produce a " "'sequence' model group containing the 'all' " "model group; 'all' model groups are not " "allowed to appear inside other model groups", NULL, NULL); } else if ((WXS_TYPE_PARTICLE(baseType) != NULL) && (WXS_TYPE_PARTICLE_TERM(baseType) != NULL) && ((WXS_TYPE_PARTICLE_TERM(baseType))->type == XML_SCHEMA_TYPE_ALL)) { /* * SPEC cos-all-limited (1) */ xmlSchemaCustomErr(ACTXT_CAST pctxt, /* TODO: error code */ XML_SCHEMAP_COS_ALL_LIMITED, WXS_ITEM_NODE(type), NULL, "A type cannot be derived by extension from a type " "which has an 'all' model group in its " "{content type}, since this would produce a " "'sequence' model group containing the 'all' " "model group; 'all' model groups are not " "allowed to appear inside other model groups", NULL, NULL); } else if ((!dummySequence) && (baseType->subtypes != NULL)) { xmlSchemaTreeItemPtr effectiveContent = (xmlSchemaTreeItemPtr) type->subtypes; /* * Create the particle. */ particle = xmlSchemaAddParticle(pctxt, type->node, 1, 1); if (particle == NULL) goto exit_failure; /* * Create the "sequence" model group. */ particle->children = (xmlSchemaTreeItemPtr) xmlSchemaAddModelGroup(pctxt, pctxt->schema, XML_SCHEMA_TYPE_SEQUENCE, type->node); if (particle->children == NULL) goto exit_failure; WXS_TYPE_CONTENTTYPE(type) = (xmlSchemaTypePtr) particle; /* * SPEC "the particle of the {content type} of * the ... base ..." * Create a duplicate of the base type's particle * and assign its "term" to it. */ particle->children->children = (xmlSchemaTreeItemPtr) xmlSchemaAddParticle(pctxt, type->node, ((xmlSchemaParticlePtr) baseType->subtypes)->minOccurs, ((xmlSchemaParticlePtr) baseType->subtypes)->maxOccurs); if (particle->children->children == NULL) goto exit_failure; particle = (xmlSchemaParticlePtr) particle->children->children; particle->children = ((xmlSchemaParticlePtr) baseType->subtypes)->children; /* * SPEC "followed by the `effective content`." */ particle->next = effectiveContent; /* * This all will result in: * new-particle * --> new-sequence( * new-particle * --> base-model, * this-particle * --> this-model * ) */ } else { /* * This is the case when there is already an empty * with minOccurs==maxOccurs==1. * Just add the base types's content type. * NOTE that, although we miss to add an intermediate * , this should produce no difference to * neither the regex compilation of the content model, * nor to the complex type constraints. */ particle->children->children = (xmlSchemaTreeItemPtr) baseType->subtypes; } } } } /* * Now fixup attribute uses: * - expand attr. group references * - intersect attribute wildcards * - inherit attribute uses of the base type * - inherit or union attr. wildcards if extending * - apply attr. use prohibitions if restricting */ res = xmlSchemaFixupTypeAttributeUses(pctxt, type); HFAILURE HERROR /* * Apply the complex type component constraints; this will not * check attributes, since this is done in * xmlSchemaFixupTypeAttributeUses(). */ res = xmlSchemaCheckCTComponent(pctxt, type); HFAILURE HERROR if (olderrs != pctxt->nberrors) return(pctxt->err); else return(0); exit_error: type->flags |= XML_SCHEMAS_TYPE_INTERNAL_INVALID; return(pctxt->err); exit_failure: type->flags |= XML_SCHEMAS_TYPE_INTERNAL_INVALID; return(-1); } /** * xmlSchemaTypeFixup: * @typeDecl: the schema type definition * @ctxt: the schema parser context * * Fixes the content model of the type. * URGENT TODO: We need an int result! */ static int xmlSchemaTypeFixup(xmlSchemaTypePtr type, xmlSchemaAbstractCtxtPtr actxt) { if (type == NULL) return(0); if (actxt->type != XML_SCHEMA_CTXT_PARSER) { AERROR_INT("xmlSchemaTypeFixup", "this function needs a parser context"); return(-1); } if (! WXS_IS_TYPE_NOT_FIXED(type)) return(0); if (type->type == XML_SCHEMA_TYPE_COMPLEX) return(xmlSchemaFixupComplexType(PCTXT_CAST actxt, type)); else if (type->type == XML_SCHEMA_TYPE_SIMPLE) return(xmlSchemaFixupSimpleTypeStageTwo(PCTXT_CAST actxt, type)); return(0); } /** * xmlSchemaCheckFacet: * @facet: the facet * @typeDecl: the schema type definition * @pctxt: the schema parser context or NULL * @name: the optional name of the type * * Checks and computes the values of facets. * * Returns 0 if valid, a positive error code if not valid and * -1 in case of an internal or API error. */ int xmlSchemaCheckFacet(xmlSchemaFacetPtr facet, xmlSchemaTypePtr typeDecl, xmlSchemaParserCtxtPtr pctxt, const xmlChar * name ATTRIBUTE_UNUSED) { int ret = 0, ctxtGiven; if ((facet == NULL) || (typeDecl == NULL)) return(-1); /* * TODO: will the parser context be given if used from * the relaxNG module? */ if (pctxt == NULL) ctxtGiven = 0; else ctxtGiven = 1; switch (facet->type) { case XML_SCHEMA_FACET_MININCLUSIVE: case XML_SCHEMA_FACET_MINEXCLUSIVE: case XML_SCHEMA_FACET_MAXINCLUSIVE: case XML_SCHEMA_FACET_MAXEXCLUSIVE: case XML_SCHEMA_FACET_ENUMERATION: { /* * Okay we need to validate the value * at that point. */ xmlSchemaTypePtr base; /* 4.3.5.5 Constraints on enumeration Schema Components * Schema Component Constraint: enumeration valid restriction * It is an `error` if any member of {value} is not in the * `value space` of {base type definition}. * * minInclusive, maxInclusive, minExclusive, maxExclusive: * The value `must` be in the * `value space` of the `base type`. */ /* * This function is intended to deliver a compiled value * on the facet. In this implementation of XML Schemata the * type holding a facet, won't be a built-in type. * Thus to ensure that other API * calls (relaxng) do work, if the given type is a built-in * type, we will assume that the given built-in type *is * already* the base type. */ if (typeDecl->type != XML_SCHEMA_TYPE_BASIC) { base = typeDecl->baseType; if (base == NULL) { PERROR_INT("xmlSchemaCheckFacet", "a type user derived type has no base type"); return (-1); } } else base = typeDecl; if (! ctxtGiven) { /* * A context is needed if called from RelaxNG. */ pctxt = xmlSchemaNewParserCtxt("*"); if (pctxt == NULL) return (-1); } /* * NOTE: This call does not check the content nodes, * since they are not available: * facet->node is just the node holding the facet * definition, *not* the attribute holding the *value* * of the facet. */ ret = xmlSchemaVCheckCVCSimpleType( ACTXT_CAST pctxt, facet->node, base, facet->value, &(facet->val), 1, 1, 0); if (ret != 0) { if (ret < 0) { /* No error message for RelaxNG. */ if (ctxtGiven) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_INTERNAL, facet->node, NULL, "Internal error: xmlSchemaCheckFacet, " "failed to validate the value '%s' of the " "facet '%s' against the base type", facet->value, xmlSchemaFacetTypeToString(facet->type)); } goto internal_error; } ret = XML_SCHEMAP_INVALID_FACET_VALUE; /* No error message for RelaxNG. */ if (ctxtGiven) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, ret, facet->node, WXS_BASIC_CAST facet, "The value '%s' of the facet does not validate " "against the base type '%s'", facet->value, xmlSchemaFormatQName(&str, base->targetNamespace, base->name)); FREE_AND_NULL(str); } goto exit; } else if (facet->val == NULL) { if (ctxtGiven) { PERROR_INT("xmlSchemaCheckFacet", "value was not computed"); } /* TODO */ } break; } case XML_SCHEMA_FACET_PATTERN: facet->regexp = xmlRegexpCompile(facet->value); if (facet->regexp == NULL) { ret = XML_SCHEMAP_REGEXP_INVALID; /* No error message for RelaxNG. */ if (ctxtGiven) { xmlSchemaCustomErr(ACTXT_CAST pctxt, ret, facet->node, WXS_BASIC_CAST typeDecl, "The value '%s' of the facet 'pattern' is not a " "valid regular expression", facet->value, NULL); } } break; case XML_SCHEMA_FACET_TOTALDIGITS: case XML_SCHEMA_FACET_FRACTIONDIGITS: case XML_SCHEMA_FACET_LENGTH: case XML_SCHEMA_FACET_MAXLENGTH: case XML_SCHEMA_FACET_MINLENGTH: if (facet->type == XML_SCHEMA_FACET_TOTALDIGITS) { ret = xmlSchemaValidatePredefinedType( xmlSchemaGetBuiltInType(XML_SCHEMAS_PINTEGER), facet->value, &(facet->val)); } else { ret = xmlSchemaValidatePredefinedType( xmlSchemaGetBuiltInType(XML_SCHEMAS_NNINTEGER), facet->value, &(facet->val)); } if (ret != 0) { if (ret < 0) { /* No error message for RelaxNG. */ if (ctxtGiven) { PERROR_INT("xmlSchemaCheckFacet", "validating facet value"); } goto internal_error; } ret = XML_SCHEMAP_INVALID_FACET_VALUE; /* No error message for RelaxNG. */ if (ctxtGiven) { /* error code */ xmlSchemaCustomErr4(ACTXT_CAST pctxt, ret, facet->node, WXS_BASIC_CAST typeDecl, "The value '%s' of the facet '%s' is not a valid '%s'", facet->value, xmlSchemaFacetTypeToString(facet->type), (facet->type != XML_SCHEMA_FACET_TOTALDIGITS) ? BAD_CAST "nonNegativeInteger" : BAD_CAST "positiveInteger", NULL); } } break; case XML_SCHEMA_FACET_WHITESPACE:{ if (xmlStrEqual(facet->value, BAD_CAST "preserve")) { facet->whitespace = XML_SCHEMAS_FACET_PRESERVE; } else if (xmlStrEqual(facet->value, BAD_CAST "replace")) { facet->whitespace = XML_SCHEMAS_FACET_REPLACE; } else if (xmlStrEqual(facet->value, BAD_CAST "collapse")) { facet->whitespace = XML_SCHEMAS_FACET_COLLAPSE; } else { ret = XML_SCHEMAP_INVALID_FACET_VALUE; /* No error message for RelaxNG. */ if (ctxtGiven) { /* error was previously: XML_SCHEMAP_INVALID_WHITE_SPACE */ xmlSchemaCustomErr(ACTXT_CAST pctxt, ret, facet->node, WXS_BASIC_CAST typeDecl, "The value '%s' of the facet 'whitespace' is not " "valid", facet->value, NULL); } } } default: break; } exit: if ((! ctxtGiven) && (pctxt != NULL)) xmlSchemaFreeParserCtxt(pctxt); return (ret); internal_error: if ((! ctxtGiven) && (pctxt != NULL)) xmlSchemaFreeParserCtxt(pctxt); return (-1); } /** * xmlSchemaCheckFacetValues: * @typeDecl: the schema type definition * @ctxt: the schema parser context * * Checks the default values types, especially for facets */ static int xmlSchemaCheckFacetValues(xmlSchemaTypePtr typeDecl, xmlSchemaParserCtxtPtr pctxt) { int res, olderrs = pctxt->nberrors; const xmlChar *name = typeDecl->name; /* * NOTE: It is intended to use the facets list, instead * of facetSet. */ if (typeDecl->facets != NULL) { xmlSchemaFacetPtr facet = typeDecl->facets; /* * Temporarily assign the "schema" to the validation context * of the parser context. This is needed for NOTATION validation. */ if (pctxt->vctxt == NULL) { if (xmlSchemaCreateVCtxtOnPCtxt(pctxt) == -1) return(-1); } pctxt->vctxt->schema = pctxt->schema; while (facet != NULL) { res = xmlSchemaCheckFacet(facet, typeDecl, pctxt, name); HFAILURE facet = facet->next; } pctxt->vctxt->schema = NULL; } if (olderrs != pctxt->nberrors) return(pctxt->err); return(0); exit_failure: return(-1); } /** * xmlSchemaGetCircModelGrDefRef: * @ctxtMGroup: the searched model group * @selfMGroup: the second searched model group * @particle: the first particle * * This one is intended to be used by * xmlSchemaCheckGroupDefCircular only. * * Returns the particle with the circular model group definition reference, * otherwise NULL. */ static xmlSchemaTreeItemPtr xmlSchemaGetCircModelGrDefRef(xmlSchemaModelGroupDefPtr groupDef, xmlSchemaTreeItemPtr particle) { xmlSchemaTreeItemPtr circ = NULL; xmlSchemaTreeItemPtr term; xmlSchemaModelGroupDefPtr gdef; for (; particle != NULL; particle = particle->next) { term = particle->children; if (term == NULL) continue; switch (term->type) { case XML_SCHEMA_TYPE_GROUP: gdef = (xmlSchemaModelGroupDefPtr) term; if (gdef == groupDef) return (particle); /* * Mark this model group definition to avoid infinite * recursion on circular references not yet examined. */ if (gdef->flags & XML_SCHEMA_MODEL_GROUP_DEF_MARKED) continue; if (gdef->children != NULL) { gdef->flags |= XML_SCHEMA_MODEL_GROUP_DEF_MARKED; circ = xmlSchemaGetCircModelGrDefRef(groupDef, gdef->children->children); gdef->flags ^= XML_SCHEMA_MODEL_GROUP_DEF_MARKED; if (circ != NULL) return (circ); } break; case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: case XML_SCHEMA_TYPE_ALL: circ = xmlSchemaGetCircModelGrDefRef(groupDef, term->children); if (circ != NULL) return (circ); break; default: break; } } return (NULL); } /** * xmlSchemaCheckGroupDefCircular: * @item: the model group definition * @ctxt: the parser context * @name: the name * * Checks for circular references to model group definitions. */ static void xmlSchemaCheckGroupDefCircular(xmlSchemaModelGroupDefPtr item, xmlSchemaParserCtxtPtr ctxt) { /* * Schema Component Constraint: Model Group Correct * 2 Circular groups are disallowed. That is, within the {particles} * of a group there must not be at any depth a particle whose {term} * is the group itself. */ if ((item == NULL) || (item->type != XML_SCHEMA_TYPE_GROUP) || (item->children == NULL)) return; { xmlSchemaTreeItemPtr circ; circ = xmlSchemaGetCircModelGrDefRef(item, item->children->children); if (circ != NULL) { xmlChar *str = NULL; /* * TODO: The error report is not adequate: this constraint * is defined for model groups but not definitions, but since * there cannot be any circular model groups without a model group * definition (if not using a construction API), we check those * definitions only. */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_MG_PROPS_CORRECT_2, NULL, WXS_ITEM_NODE(circ), "Circular reference to the model group definition '%s' " "defined", xmlSchemaFormatQName(&str, item->targetNamespace, item->name)); FREE_AND_NULL(str) /* * NOTE: We will cut the reference to avoid further * confusion of the processor. This is a fatal error. */ circ->children = NULL; } } } /** * xmlSchemaModelGroupToModelGroupDefFixup: * @ctxt: the parser context * @mg: the model group * * Assigns the model group of model group definitions to the "term" * of the referencing particle. * In xmlSchemaResolveModelGroupParticleReferences the model group * definitions were assigned to the "term", since needed for the * circularity check. * * Schema Component Constraint: * All Group Limited (cos-all-limited) (1.2) */ static void xmlSchemaModelGroupToModelGroupDefFixup( xmlSchemaParserCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlSchemaModelGroupPtr mg) { xmlSchemaParticlePtr particle = WXS_MODELGROUP_PARTICLE(mg); while (particle != NULL) { if ((WXS_PARTICLE_TERM(particle) == NULL) || ((WXS_PARTICLE_TERM(particle))->type != XML_SCHEMA_TYPE_GROUP)) { particle = WXS_PTC_CAST particle->next; continue; } if (WXS_MODELGROUPDEF_MODEL(WXS_PARTICLE_TERM(particle)) == NULL) { /* * TODO: Remove the particle. */ WXS_PARTICLE_TERM(particle) = NULL; particle = WXS_PTC_CAST particle->next; continue; } /* * Assign the model group to the {term} of the particle. */ WXS_PARTICLE_TERM(particle) = WXS_TREE_CAST WXS_MODELGROUPDEF_MODEL(WXS_PARTICLE_TERM(particle)); particle = WXS_PTC_CAST particle->next; } } /** * xmlSchemaCheckAttrGroupCircularRecur: * @ctxtGr: the searched attribute group * @attr: the current attribute list to be processed * * This one is intended to be used by * xmlSchemaCheckAttrGroupCircular only. * * Returns the circular attribute group reference, otherwise NULL. */ static xmlSchemaQNameRefPtr xmlSchemaCheckAttrGroupCircularRecur(xmlSchemaAttributeGroupPtr ctxtGr, xmlSchemaItemListPtr list) { xmlSchemaAttributeGroupPtr gr; xmlSchemaQNameRefPtr ref, circ; int i; /* * We will search for an attribute group reference which * references the context attribute group. */ for (i = 0; i < list->nbItems; i++) { ref = list->items[i]; if ((ref->type == XML_SCHEMA_EXTRA_QNAMEREF) && (ref->itemType == XML_SCHEMA_TYPE_ATTRIBUTEGROUP) && (ref->item != NULL)) { gr = WXS_ATTR_GROUP_CAST ref->item; if (gr == ctxtGr) return(ref); if (gr->flags & XML_SCHEMAS_ATTRGROUP_MARKED) continue; /* * Mark as visited to avoid infinite recursion on * circular references not yet examined. */ if ((gr->attrUses) && (gr->flags & XML_SCHEMAS_ATTRGROUP_HAS_REFS)) { gr->flags |= XML_SCHEMAS_ATTRGROUP_MARKED; circ = xmlSchemaCheckAttrGroupCircularRecur(ctxtGr, (xmlSchemaItemListPtr) gr->attrUses); gr->flags ^= XML_SCHEMAS_ATTRGROUP_MARKED; if (circ != NULL) return (circ); } } } return (NULL); } /** * xmlSchemaCheckAttrGroupCircular: * attrGr: the attribute group definition * @ctxt: the parser context * @name: the name * * Checks for circular references of attribute groups. */ static int xmlSchemaCheckAttrGroupCircular(xmlSchemaAttributeGroupPtr attrGr, xmlSchemaParserCtxtPtr ctxt) { /* * Schema Representation Constraint: * Attribute Group Definition Representation OK * 3 Circular group reference is disallowed outside . * That is, unless this element information item's parent is * , then among the [children], if any, there must * not be an with ref [attribute] which resolves * to the component corresponding to this . Indirect * circularity is also ruled out. That is, when QName resolution * (Schema Document) ($3.15.3) is applied to a `QName` arising from * any s with a ref [attribute] among the [children], * it must not be the case that a `QName` is encountered at any depth * which resolves to the component corresponding to this . */ if (attrGr->attrUses == NULL) return(0); else if ((attrGr->flags & XML_SCHEMAS_ATTRGROUP_HAS_REFS) == 0) return(0); else { xmlSchemaQNameRefPtr circ; circ = xmlSchemaCheckAttrGroupCircularRecur(attrGr, (xmlSchemaItemListPtr) attrGr->attrUses); if (circ != NULL) { xmlChar *str = NULL; /* * TODO: Report the referenced attr group as QName. */ xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_3, NULL, WXS_ITEM_NODE(WXS_BASIC_CAST circ), "Circular reference to the attribute group '%s' " "defined", xmlSchemaGetComponentQName(&str, attrGr)); FREE_AND_NULL(str); /* * NOTE: We will cut the reference to avoid further * confusion of the processor. * BADSPEC TODO: The spec should define how to process in this case. */ circ->item = NULL; return(ctxt->err); } } return(0); } static int xmlSchemaAttributeGroupExpandRefs(xmlSchemaParserCtxtPtr pctxt, xmlSchemaAttributeGroupPtr attrGr); /** * xmlSchemaExpandAttributeGroupRefs: * @pctxt: the parser context * @node: the node of the component holding the attribute uses * @completeWild: the intersected wildcard to be returned * @list: the attribute uses * * Substitutes contained attribute group references * for their attribute uses. Wildcards are intersected. * Attribute use prohibitions are removed from the list * and returned via the @prohibs list. * Pointlessness of attr. prohibs, if a matching attr. decl * is existent a well, are checked. */ static int xmlSchemaExpandAttributeGroupRefs(xmlSchemaParserCtxtPtr pctxt, xmlSchemaBasicItemPtr item, xmlSchemaWildcardPtr *completeWild, xmlSchemaItemListPtr list, xmlSchemaItemListPtr prohibs) { xmlSchemaAttributeGroupPtr gr; xmlSchemaAttributeUsePtr use; xmlSchemaItemListPtr sublist; int i, j; int created = (*completeWild == NULL) ? 0 : 1; if (prohibs) prohibs->nbItems = 0; for (i = 0; i < list->nbItems; i++) { use = list->items[i]; if (use->type == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB) { if (prohibs == NULL) { PERROR_INT("xmlSchemaExpandAttributeGroupRefs", "unexpected attr prohibition found"); return(-1); } /* * Remove from attribute uses. */ if (xmlSchemaItemListRemove(list, i) == -1) return(-1); i--; /* * Note that duplicate prohibitions were already * handled at parsing time. */ /* * Add to list of prohibitions. */ xmlSchemaItemListAddSize(prohibs, 2, use); continue; } if ((use->type == XML_SCHEMA_EXTRA_QNAMEREF) && ((WXS_QNAME_CAST use)->itemType == XML_SCHEMA_TYPE_ATTRIBUTEGROUP)) { if ((WXS_QNAME_CAST use)->item == NULL) return(-1); gr = WXS_ATTR_GROUP_CAST (WXS_QNAME_CAST use)->item; /* * Expand the referenced attr. group. * TODO: remove this, this is done in a previous step, so * already done here. */ if ((gr->flags & XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED) == 0) { if (xmlSchemaAttributeGroupExpandRefs(pctxt, gr) == -1) return(-1); } /* * Build the 'complete' wildcard; i.e. intersect multiple * wildcards. */ if (gr->attributeWildcard != NULL) { if (*completeWild == NULL) { *completeWild = gr->attributeWildcard; } else { if (! created) { xmlSchemaWildcardPtr tmpWild; /* * Copy the first encountered wildcard as context, * except for the annotation. * * Although the complete wildcard might not correspond * to any node in the schema, we will anchor it on * the node of the owner component. */ tmpWild = xmlSchemaAddWildcard(pctxt, pctxt->schema, XML_SCHEMA_TYPE_ANY_ATTRIBUTE, WXS_ITEM_NODE(item)); if (tmpWild == NULL) return(-1); if (xmlSchemaCloneWildcardNsConstraints(pctxt, tmpWild, *completeWild) == -1) return (-1); tmpWild->processContents = (*completeWild)->processContents; *completeWild = tmpWild; created = 1; } if (xmlSchemaIntersectWildcards(pctxt, *completeWild, gr->attributeWildcard) == -1) return(-1); } } /* * Just remove the reference if the referenced group does not * contain any attribute uses. */ sublist = ((xmlSchemaItemListPtr) gr->attrUses); if ((sublist == NULL) || sublist->nbItems == 0) { if (xmlSchemaItemListRemove(list, i) == -1) return(-1); i--; continue; } /* * Add the attribute uses. */ list->items[i] = sublist->items[0]; if (sublist->nbItems != 1) { for (j = 1; j < sublist->nbItems; j++) { i++; if (xmlSchemaItemListInsert(list, sublist->items[j], i) == -1) return(-1); } } } } /* * Handle pointless prohibitions of declared attributes. */ if (prohibs && (prohibs->nbItems != 0) && (list->nbItems != 0)) { xmlSchemaAttributeUseProhibPtr prohib; for (i = prohibs->nbItems -1; i >= 0; i--) { prohib = prohibs->items[i]; for (j = 0; j < list->nbItems; j++) { use = list->items[j]; if ((prohib->name == WXS_ATTRUSE_DECL_NAME(use)) && (prohib->targetNamespace == WXS_ATTRUSE_DECL_TNS(use))) { xmlChar *str = NULL; xmlSchemaCustomWarning(ACTXT_CAST pctxt, XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, prohib->node, NULL, "Skipping pointless attribute use prohibition " "'%s', since a corresponding attribute use " "exists already in the type definition", xmlSchemaFormatQName(&str, prohib->targetNamespace, prohib->name), NULL, NULL); FREE_AND_NULL(str); /* * Remove the prohibition. */ if (xmlSchemaItemListRemove(prohibs, i) == -1) return(-1); break; } } } } return(0); } /** * xmlSchemaAttributeGroupExpandRefs: * @pctxt: the parser context * @attrGr: the attribute group definition * * Computation of: * {attribute uses} property * {attribute wildcard} property * * Substitutes contained attribute group references * for their attribute uses. Wildcards are intersected. */ static int xmlSchemaAttributeGroupExpandRefs(xmlSchemaParserCtxtPtr pctxt, xmlSchemaAttributeGroupPtr attrGr) { if ((attrGr->attrUses == NULL) || (attrGr->flags & XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED)) return(0); attrGr->flags |= XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED; if (xmlSchemaExpandAttributeGroupRefs(pctxt, WXS_BASIC_CAST attrGr, &(attrGr->attributeWildcard), attrGr->attrUses, NULL) == -1) return(-1); return(0); } /** * xmlSchemaAttributeGroupExpandRefs: * @pctxt: the parser context * @attrGr: the attribute group definition * * Substitutes contained attribute group references * for their attribute uses. Wildcards are intersected. * * Schema Component Constraint: * Attribute Group Definition Properties Correct (ag-props-correct) */ static int xmlSchemaCheckAGPropsCorrect(xmlSchemaParserCtxtPtr pctxt, xmlSchemaAttributeGroupPtr attrGr) { /* * SPEC ag-props-correct * (1) "The values of the properties of an attribute group definition * must be as described in the property tableau in The Attribute * Group Definition Schema Component ($3.6.1), modulo the impact of * Missing Sub-components ($5.3);" */ if ((attrGr->attrUses != NULL) && (WXS_LIST_CAST attrGr->attrUses)->nbItems > 1) { xmlSchemaItemListPtr uses = WXS_LIST_CAST attrGr->attrUses; xmlSchemaAttributeUsePtr use, tmp; int i, j, hasId = 0; for (i = uses->nbItems -1; i >= 0; i--) { use = uses->items[i]; /* * SPEC ag-props-correct * (2) "Two distinct members of the {attribute uses} must not have * {attribute declaration}s both of whose {name}s match and whose * {target namespace}s are identical." */ if (i > 0) { for (j = i -1; j >= 0; j--) { tmp = uses->items[j]; if ((WXS_ATTRUSE_DECL_NAME(use) == WXS_ATTRUSE_DECL_NAME(tmp)) && (WXS_ATTRUSE_DECL_TNS(use) == WXS_ATTRUSE_DECL_TNS(tmp))) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_AG_PROPS_CORRECT, attrGr->node, WXS_BASIC_CAST attrGr, "Duplicate %s", xmlSchemaGetComponentDesignation(&str, use), NULL); FREE_AND_NULL(str); /* * Remove the duplicate. */ if (xmlSchemaItemListRemove(uses, i) == -1) return(-1); goto next_use; } } } /* * SPEC ag-props-correct * (3) "Two distinct members of the {attribute uses} must not have * {attribute declaration}s both of whose {type definition}s are or * are derived from ID." * TODO: Does 'derived' include member-types of unions? */ if (WXS_ATTRUSE_TYPEDEF(use) != NULL) { if (xmlSchemaIsDerivedFromBuiltInType( WXS_ATTRUSE_TYPEDEF(use), XML_SCHEMAS_ID)) { if (hasId) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_AG_PROPS_CORRECT, attrGr->node, WXS_BASIC_CAST attrGr, "There must not exist more than one attribute " "declaration of type 'xs:ID' " "(or derived from 'xs:ID'). The %s violates this " "constraint", xmlSchemaGetComponentDesignation(&str, use), NULL); FREE_AND_NULL(str); if (xmlSchemaItemListRemove(uses, i) == -1) return(-1); } hasId = 1; } } next_use: {} } } return(0); } /** * xmlSchemaResolveAttrGroupReferences: * @attrgrpDecl: the schema attribute definition * @ctxt: the schema parser context * @name: the attribute name * * Resolves references to attribute group definitions. */ static int xmlSchemaResolveAttrGroupReferences(xmlSchemaQNameRefPtr ref, xmlSchemaParserCtxtPtr ctxt) { xmlSchemaAttributeGroupPtr group; if (ref->item != NULL) return(0); group = xmlSchemaGetAttributeGroup(ctxt->schema, ref->name, ref->targetNamespace); if (group == NULL) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, NULL, ref->node, "ref", ref->name, ref->targetNamespace, ref->itemType, NULL); return(ctxt->err); } ref->item = WXS_BASIC_CAST group; return(0); } /** * xmlSchemaCheckAttrPropsCorrect: * @item: an schema attribute declaration/use * @ctxt: a schema parser context * @name: the name of the attribute * * * Schema Component Constraint: * Attribute Declaration Properties Correct (a-props-correct) * * Validates the value constraints of an attribute declaration/use. * NOTE that this needs the simple type definitions to be already * built and checked. */ static int xmlSchemaCheckAttrPropsCorrect(xmlSchemaParserCtxtPtr pctxt, xmlSchemaAttributePtr attr) { /* * SPEC a-props-correct (1) * "The values of the properties of an attribute declaration must * be as described in the property tableau in The Attribute * Declaration Schema Component ($3.2.1), modulo the impact of * Missing Sub-components ($5.3)." */ if (WXS_ATTR_TYPEDEF(attr) == NULL) return(0); if (attr->defValue != NULL) { int ret; /* * SPEC a-props-correct (3) * "If the {type definition} is or is derived from ID then there * must not be a {value constraint}." */ if (xmlSchemaIsDerivedFromBuiltInType( WXS_ATTR_TYPEDEF(attr), XML_SCHEMAS_ID)) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_A_PROPS_CORRECT_3, NULL, WXS_BASIC_CAST attr, "Value constraints are not allowed if the type definition " "is or is derived from xs:ID", NULL, NULL); return(pctxt->err); } /* * SPEC a-props-correct (2) * "if there is a {value constraint}, the canonical lexical * representation of its value must be `valid` with respect * to the {type definition} as defined in String Valid ($3.14.4)." * TODO: Don't care about the *canonical* stuff here, this requirement * will be removed in WXS 1.1 anyway. */ ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST pctxt, attr->node, WXS_ATTR_TYPEDEF(attr), attr->defValue, &(attr->defVal), 1, 1, 0); if (ret != 0) { if (ret < 0) { PERROR_INT("xmlSchemaCheckAttrPropsCorrect", "calling xmlSchemaVCheckCVCSimpleType()"); return(-1); } xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_A_PROPS_CORRECT_2, NULL, WXS_BASIC_CAST attr, "The value of the value constraint is not valid", NULL, NULL); return(pctxt->err); } } return(0); } static xmlSchemaElementPtr xmlSchemaCheckSubstGroupCircular(xmlSchemaElementPtr elemDecl, xmlSchemaElementPtr ancestor) { xmlSchemaElementPtr ret; if (WXS_SUBST_HEAD(ancestor) == NULL) return (NULL); if (WXS_SUBST_HEAD(ancestor) == elemDecl) return (ancestor); if (WXS_SUBST_HEAD(ancestor)->flags & XML_SCHEMAS_ELEM_CIRCULAR) return (NULL); WXS_SUBST_HEAD(ancestor)->flags |= XML_SCHEMAS_ELEM_CIRCULAR; ret = xmlSchemaCheckSubstGroupCircular(elemDecl, WXS_SUBST_HEAD(ancestor)); WXS_SUBST_HEAD(ancestor)->flags ^= XML_SCHEMAS_ELEM_CIRCULAR; return (ret); } /** * xmlSchemaCheckElemPropsCorrect: * @ctxt: a schema parser context * @decl: the element declaration * @name: the name of the attribute * * Schema Component Constraint: * Element Declaration Properties Correct (e-props-correct) * * STATUS: * missing: (6) */ static int xmlSchemaCheckElemPropsCorrect(xmlSchemaParserCtxtPtr pctxt, xmlSchemaElementPtr elemDecl) { int ret = 0; xmlSchemaTypePtr typeDef = WXS_ELEM_TYPEDEF(elemDecl); /* * SPEC (1) "The values of the properties of an element declaration * must be as described in the property tableau in The Element * Declaration Schema Component ($3.3.1), modulo the impact of Missing * Sub-components ($5.3)." */ if (WXS_SUBST_HEAD(elemDecl) != NULL) { xmlSchemaElementPtr head = WXS_SUBST_HEAD(elemDecl), circ; xmlSchemaCheckElementDeclComponent(head, pctxt); /* * SPEC (3) "If there is a non-`absent` {substitution group * affiliation}, then {scope} must be global." */ if ((elemDecl->flags & XML_SCHEMAS_ELEM_GLOBAL) == 0) { xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_E_PROPS_CORRECT_3, WXS_BASIC_CAST elemDecl, NULL, "Only global element declarations can have a " "substitution group affiliation", NULL); ret = XML_SCHEMAP_E_PROPS_CORRECT_3; } /* * TODO: SPEC (6) "Circular substitution groups are disallowed. * That is, it must not be possible to return to an element declaration * by repeatedly following the {substitution group affiliation} * property." */ if (head == elemDecl) circ = head; else if (WXS_SUBST_HEAD(head) != NULL) circ = xmlSchemaCheckSubstGroupCircular(head, head); else circ = NULL; if (circ != NULL) { xmlChar *strA = NULL, *strB = NULL; xmlSchemaPCustomErrExt(pctxt, XML_SCHEMAP_E_PROPS_CORRECT_6, WXS_BASIC_CAST circ, NULL, "The element declaration '%s' defines a circular " "substitution group to element declaration '%s'", xmlSchemaGetComponentQName(&strA, circ), xmlSchemaGetComponentQName(&strB, head), NULL); FREE_AND_NULL(strA) FREE_AND_NULL(strB) ret = XML_SCHEMAP_E_PROPS_CORRECT_6; } /* * SPEC (4) "If there is a {substitution group affiliation}, * the {type definition} * of the element declaration must be validly derived from the {type * definition} of the {substitution group affiliation}, given the value * of the {substitution group exclusions} of the {substitution group * affiliation}, as defined in Type Derivation OK (Complex) ($3.4.6) * (if the {type definition} is complex) or as defined in * Type Derivation OK (Simple) ($3.14.6) (if the {type definition} is * simple)." * * NOTE: {substitution group exclusions} means the values of the * attribute "final". */ if (typeDef != WXS_ELEM_TYPEDEF(WXS_SUBST_HEAD(elemDecl))) { int set = 0; if (head->flags & XML_SCHEMAS_ELEM_FINAL_EXTENSION) set |= SUBSET_EXTENSION; if (head->flags & XML_SCHEMAS_ELEM_FINAL_RESTRICTION) set |= SUBSET_RESTRICTION; if (xmlSchemaCheckCOSDerivedOK(ACTXT_CAST pctxt, typeDef, WXS_ELEM_TYPEDEF(head), set) != 0) { xmlChar *strA = NULL, *strB = NULL, *strC = NULL; ret = XML_SCHEMAP_E_PROPS_CORRECT_4; xmlSchemaPCustomErrExt(pctxt, XML_SCHEMAP_E_PROPS_CORRECT_4, WXS_BASIC_CAST elemDecl, NULL, "The type definition '%s' was " "either rejected by the substitution group " "affiliation '%s', or not validly derived from its type " "definition '%s'", xmlSchemaGetComponentQName(&strA, typeDef), xmlSchemaGetComponentQName(&strB, head), xmlSchemaGetComponentQName(&strC, WXS_ELEM_TYPEDEF(head))); FREE_AND_NULL(strA) FREE_AND_NULL(strB) FREE_AND_NULL(strC) } } } /* * SPEC (5) "If the {type definition} or {type definition}'s * {content type} * is or is derived from ID then there must not be a {value constraint}. * Note: The use of ID as a type definition for elements goes beyond * XML 1.0, and should be avoided if backwards compatibility is desired" */ if ((elemDecl->value != NULL) && ((WXS_IS_SIMPLE(typeDef) && xmlSchemaIsDerivedFromBuiltInType(typeDef, XML_SCHEMAS_ID)) || (WXS_IS_COMPLEX(typeDef) && WXS_HAS_SIMPLE_CONTENT(typeDef) && xmlSchemaIsDerivedFromBuiltInType(typeDef->contentTypeDef, XML_SCHEMAS_ID)))) { ret = XML_SCHEMAP_E_PROPS_CORRECT_5; xmlSchemaPCustomErr(pctxt, XML_SCHEMAP_E_PROPS_CORRECT_5, WXS_BASIC_CAST elemDecl, NULL, "The type definition (or type definition's content type) is or " "is derived from ID; value constraints are not allowed in " "conjunction with such a type definition", NULL); } else if (elemDecl->value != NULL) { int vcret; xmlNodePtr node = NULL; /* * SPEC (2) "If there is a {value constraint}, the canonical lexical * representation of its value must be `valid` with respect to the * {type definition} as defined in Element Default Valid (Immediate) * ($3.3.6)." */ if (typeDef == NULL) { xmlSchemaPErr(pctxt, elemDecl->node, XML_SCHEMAP_INTERNAL, "Internal error: xmlSchemaCheckElemPropsCorrect, " "type is missing... skipping validation of " "the value constraint", NULL, NULL); return (-1); } if (elemDecl->node != NULL) { if (elemDecl->flags & XML_SCHEMAS_ELEM_FIXED) node = (xmlNodePtr) xmlHasProp(elemDecl->node, BAD_CAST "fixed"); else node = (xmlNodePtr) xmlHasProp(elemDecl->node, BAD_CAST "default"); } vcret = xmlSchemaParseCheckCOSValidDefault(pctxt, node, typeDef, elemDecl->value, &(elemDecl->defVal)); if (vcret != 0) { if (vcret < 0) { PERROR_INT("xmlSchemaElemCheckValConstr", "failed to validate the value constraint of an " "element declaration"); return (-1); } return (vcret); } } return (ret); } /** * xmlSchemaCheckElemSubstGroup: * @ctxt: a schema parser context * @decl: the element declaration * @name: the name of the attribute * * Schema Component Constraint: * Substitution Group (cos-equiv-class) * * In Libxml2 the subst. groups will be precomputed, in terms of that * a list will be built for each subst. group head, holding all direct * referents to this head. * NOTE that this function needs: * 1. circular subst. groups to be checked beforehand * 2. the declaration's type to be derived from the head's type * * STATUS: * */ static void xmlSchemaCheckElemSubstGroup(xmlSchemaParserCtxtPtr ctxt, xmlSchemaElementPtr elemDecl) { if ((WXS_SUBST_HEAD(elemDecl) == NULL) || /* SPEC (1) "Its {abstract} is false." */ (elemDecl->flags & XML_SCHEMAS_ELEM_ABSTRACT)) return; { xmlSchemaElementPtr head; xmlSchemaTypePtr headType, type; int set, methSet; /* * SPEC (2) "It is validly substitutable for HEAD subject to HEAD's * {disallowed substitutions} as the blocking constraint, as defined in * Substitution Group OK (Transitive) ($3.3.6)." */ for (head = WXS_SUBST_HEAD(elemDecl); head != NULL; head = WXS_SUBST_HEAD(head)) { set = 0; methSet = 0; /* * The blocking constraints. */ if (head->flags & XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION) continue; headType = head->subtypes; type = elemDecl->subtypes; if (headType == type) goto add_member; if (head->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) set |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; if (head->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) set |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; /* * SPEC: Substitution Group OK (Transitive) (2.3) * "The set of all {derivation method}s involved in the * derivation of D's {type definition} from C's {type definition} * does not intersect with the union of the blocking constraint, * C's {prohibited substitutions} (if C is complex, otherwise the * empty set) and the {prohibited substitutions} (respectively the * empty set) of any intermediate {type definition}s in the * derivation of D's {type definition} from C's {type definition}." */ /* * OPTIMIZE TODO: Optimize this a bit, since, if traversing the * subst.head axis, the methSet does not need to be computed for * the full depth over and over. */ /* * The set of all {derivation method}s involved in the derivation */ while ((type != NULL) && (type != headType) && (type != type->baseType)) { if ((WXS_IS_EXTENSION(type)) && ((methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0)) methSet |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; if (WXS_IS_RESTRICTION(type) && ((methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0)) methSet |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; type = type->baseType; } /* * The {prohibited substitutions} of all intermediate types + * the head's type. */ type = elemDecl->subtypes->baseType; while (type != NULL) { if (WXS_IS_COMPLEX(type)) { if ((type->flags & XML_SCHEMAS_TYPE_BLOCK_EXTENSION) && ((set & XML_SCHEMAS_TYPE_BLOCK_EXTENSION) == 0)) set |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; if ((type->flags & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) && ((set & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0)) set |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; } else break; if (type == headType) break; type = type->baseType; } if ((set != 0) && (((set & XML_SCHEMAS_TYPE_BLOCK_EXTENSION) && (methSet & XML_SCHEMAS_TYPE_BLOCK_EXTENSION)) || ((set & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) && (methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION)))) { continue; } add_member: xmlSchemaAddElementSubstitutionMember(ctxt, head, elemDecl); if ((head->flags & XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD) == 0) head->flags |= XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD; } } } #ifdef WXS_ELEM_DECL_CONS_ENABLED /* enable when finished */ /** * xmlSchemaCheckElementDeclComponent * @pctxt: the schema parser context * @ctxtComponent: the context component (an element declaration) * @ctxtParticle: the first particle of the context component * @searchParticle: the element declaration particle to be analysed * * Schema Component Constraint: Element Declarations Consistent */ static int xmlSchemaCheckElementDeclConsistent(xmlSchemaParserCtxtPtr pctxt, xmlSchemaBasicItemPtr ctxtComponent, xmlSchemaParticlePtr ctxtParticle, xmlSchemaParticlePtr searchParticle, xmlSchemaParticlePtr curParticle, int search) { return(0); int ret = 0; xmlSchemaParticlePtr cur = curParticle; if (curParticle == NULL) { return(0); } if (WXS_PARTICLE_TERM(curParticle) == NULL) { /* * Just return in this case. A missing "term" of the particle * might arise due to an invalid "term" component. */ return(0); } while (cur != NULL) { switch (WXS_PARTICLE_TERM(cur)->type) { case XML_SCHEMA_TYPE_ANY: break; case XML_SCHEMA_TYPE_ELEMENT: if (search == 0) { ret = xmlSchemaCheckElementDeclConsistent(pctxt, ctxtComponent, ctxtParticle, cur, ctxtParticle, 1); if (ret != 0) return(ret); } else { xmlSchemaElementPtr elem = WXS_ELEM_CAST(WXS_PARTICLE_TERM(cur)); /* * SPEC Element Declarations Consistent: * "If the {particles} contains, either directly, * indirectly (that is, within the {particles} of a * contained model group, recursively) or `implicitly` * two or more element declaration particles with * the same {name} and {target namespace}, then * all their type definitions must be the same * top-level definition [...]" */ if (xmlStrEqual(WXS_PARTICLE_TERM_AS_ELEM(cur)->name, WXS_PARTICLE_TERM_AS_ELEM(searchParticle)->name) && xmlStrEqual(WXS_PARTICLE_TERM_AS_ELEM(cur)->targetNamespace, WXS_PARTICLE_TERM_AS_ELEM(searchParticle)->targetNamespace)) { xmlChar *strA = NULL, *strB = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, /* TODO: error code */ XML_SCHEMAP_COS_NONAMBIG, WXS_ITEM_NODE(cur), NULL, "In the content model of %s, there are multiple " "element declarations for '%s' with different " "type definitions", xmlSchemaGetComponentDesignation(&strA, ctxtComponent), xmlSchemaFormatQName(&strB, WXS_PARTICLE_TERM_AS_ELEM(cur)->targetNamespace, WXS_PARTICLE_TERM_AS_ELEM(cur)->name)); FREE_AND_NULL(strA); FREE_AND_NULL(strB); return(XML_SCHEMAP_COS_NONAMBIG); } } break; case XML_SCHEMA_TYPE_SEQUENCE: { break; } case XML_SCHEMA_TYPE_CHOICE:{ /* xmlSchemaTreeItemPtr sub; sub = WXS_PARTICLE_TERM(particle)->children; (xmlSchemaParticlePtr) while (sub != NULL) { ret = xmlSchemaCheckElementDeclConsistent(pctxt, ctxtComponent, ctxtParticle, ctxtElem); if (ret != 0) return(ret); sub = sub->next; } */ break; } case XML_SCHEMA_TYPE_ALL: break; case XML_SCHEMA_TYPE_GROUP: break; default: xmlSchemaInternalErr2(ACTXT_CAST pctxt, "xmlSchemaCheckElementDeclConsistent", "found unexpected term of type '%s' in content model", WXS_ITEM_TYPE_NAME(WXS_PARTICLE_TERM(cur)), NULL); return(-1); } cur = (xmlSchemaParticlePtr) cur->next; } exit: return(ret); } #endif /** * xmlSchemaCheckElementDeclComponent * @item: an schema element declaration/particle * @ctxt: a schema parser context * @name: the name of the attribute * * Validates the value constraints of an element declaration. * Adds substitution group members. */ static void xmlSchemaCheckElementDeclComponent(xmlSchemaElementPtr elemDecl, xmlSchemaParserCtxtPtr ctxt) { if (elemDecl == NULL) return; if (elemDecl->flags & XML_SCHEMAS_ELEM_INTERNAL_CHECKED) return; elemDecl->flags |= XML_SCHEMAS_ELEM_INTERNAL_CHECKED; if (xmlSchemaCheckElemPropsCorrect(ctxt, elemDecl) == 0) { /* * Adds substitution group members. */ xmlSchemaCheckElemSubstGroup(ctxt, elemDecl); } } /** * xmlSchemaResolveModelGroupParticleReferences: * @particle: a particle component * @ctxt: a parser context * * Resolves references of a model group's {particles} to * model group definitions and to element declarations. */ static void xmlSchemaResolveModelGroupParticleReferences( xmlSchemaParserCtxtPtr ctxt, xmlSchemaModelGroupPtr mg) { xmlSchemaParticlePtr particle = WXS_MODELGROUP_PARTICLE(mg); xmlSchemaQNameRefPtr ref; xmlSchemaBasicItemPtr refItem; /* * URGENT TODO: Test this. */ while (particle != NULL) { if ((WXS_PARTICLE_TERM(particle) == NULL) || ((WXS_PARTICLE_TERM(particle))->type != XML_SCHEMA_EXTRA_QNAMEREF)) { goto next_particle; } ref = WXS_QNAME_CAST WXS_PARTICLE_TERM(particle); /* * Resolve the reference. * NULL the {term} by default. */ particle->children = NULL; refItem = xmlSchemaGetNamedComponent(ctxt->schema, ref->itemType, ref->name, ref->targetNamespace); if (refItem == NULL) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, NULL, WXS_ITEM_NODE(particle), "ref", ref->name, ref->targetNamespace, ref->itemType, NULL); /* TODO: remove the particle. */ goto next_particle; } if (refItem->type == XML_SCHEMA_TYPE_GROUP) { if (WXS_MODELGROUPDEF_MODEL(refItem) == NULL) /* TODO: remove the particle. */ goto next_particle; /* * NOTE that we will assign the model group definition * itself to the "term" of the particle. This will ease * the check for circular model group definitions. After * that the "term" will be assigned the model group of the * model group definition. */ if ((WXS_MODELGROUPDEF_MODEL(refItem))->type == XML_SCHEMA_TYPE_ALL) { /* * SPEC cos-all-limited (1) * SPEC cos-all-limited (1.2) * "It appears only as the value of one or both of the * following properties:" * (1.1) "the {model group} property of a model group * definition." * (1.2) "the {term} property of a particle [... of] the " * {content type} of a complex type definition." */ xmlSchemaCustomErr(ACTXT_CAST ctxt, /* TODO: error code */ XML_SCHEMAP_COS_ALL_LIMITED, WXS_ITEM_NODE(particle), NULL, "A model group definition is referenced, but " "it contains an 'all' model group, which " "cannot be contained by model groups", NULL, NULL); /* TODO: remove the particle. */ goto next_particle; } particle->children = (xmlSchemaTreeItemPtr) refItem; } else { /* * TODO: Are referenced element declarations the only * other components we expect here? */ particle->children = (xmlSchemaTreeItemPtr) refItem; } next_particle: particle = WXS_PTC_CAST particle->next; } } static int xmlSchemaAreValuesEqual(xmlSchemaValPtr x, xmlSchemaValPtr y) { xmlSchemaTypePtr tx, ty, ptx, pty; int ret; while (x != NULL) { /* Same types. */ tx = xmlSchemaGetBuiltInType(xmlSchemaGetValType(x)); ty = xmlSchemaGetBuiltInType(xmlSchemaGetValType(y)); ptx = xmlSchemaGetPrimitiveType(tx); pty = xmlSchemaGetPrimitiveType(ty); /* * (1) if a datatype T' is `derived` by `restriction` from an * atomic datatype T then the `value space` of T' is a subset of * the `value space` of T. */ /* * (2) if datatypes T' and T'' are `derived` by `restriction` * from a common atomic ancestor T then the `value space`s of T' * and T'' may overlap. */ if (ptx != pty) return(0); /* * We assume computed values to be normalized, so do a fast * string comparison for string based types. */ if ((ptx->builtInType == XML_SCHEMAS_STRING) || WXS_IS_ANY_SIMPLE_TYPE(ptx)) { if (! xmlStrEqual( xmlSchemaValueGetAsString(x), xmlSchemaValueGetAsString(y))) return (0); } else { ret = xmlSchemaCompareValuesWhtsp( x, XML_SCHEMA_WHITESPACE_PRESERVE, y, XML_SCHEMA_WHITESPACE_PRESERVE); if (ret == -2) return(-1); if (ret != 0) return(0); } /* * Lists. */ x = xmlSchemaValueGetNext(x); if (x != NULL) { y = xmlSchemaValueGetNext(y); if (y == NULL) return (0); } else if (xmlSchemaValueGetNext(y) != NULL) return (0); else return (1); } return (0); } /** * xmlSchemaResolveAttrUseReferences: * @item: an attribute use * @ctxt: a parser context * * Resolves the referenced attribute declaration. */ static int xmlSchemaResolveAttrUseReferences(xmlSchemaAttributeUsePtr ause, xmlSchemaParserCtxtPtr ctxt) { if ((ctxt == NULL) || (ause == NULL)) return(-1); if ((ause->attrDecl == NULL) || (ause->attrDecl->type != XML_SCHEMA_EXTRA_QNAMEREF)) return(0); { xmlSchemaQNameRefPtr ref = WXS_QNAME_CAST ause->attrDecl; /* * TODO: Evaluate, what errors could occur if the declaration is not * found. */ ause->attrDecl = xmlSchemaGetAttributeDecl(ctxt->schema, ref->name, ref->targetNamespace); if (ause->attrDecl == NULL) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST ause, ause->node, "ref", ref->name, ref->targetNamespace, XML_SCHEMA_TYPE_ATTRIBUTE, NULL); return(ctxt->err);; } } return(0); } /** * xmlSchemaCheckAttrUsePropsCorrect: * @ctxt: a parser context * @use: an attribute use * * Schema Component Constraint: * Attribute Use Correct (au-props-correct) * */ static int xmlSchemaCheckAttrUsePropsCorrect(xmlSchemaParserCtxtPtr ctxt, xmlSchemaAttributeUsePtr use) { if ((ctxt == NULL) || (use == NULL)) return(-1); if ((use->defValue == NULL) || (WXS_ATTRUSE_DECL(use) == NULL) || ((WXS_ATTRUSE_DECL(use))->type != XML_SCHEMA_TYPE_ATTRIBUTE)) return(0); /* * SPEC au-props-correct (1) * "The values of the properties of an attribute use must be as * described in the property tableau in The Attribute Use Schema * Component ($3.5.1), modulo the impact of Missing * Sub-components ($5.3)." */ if (((WXS_ATTRUSE_DECL(use))->defValue != NULL) && ((WXS_ATTRUSE_DECL(use))->flags & XML_SCHEMAS_ATTR_FIXED) && ((use->flags & XML_SCHEMA_ATTR_USE_FIXED) == 0)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_AU_PROPS_CORRECT_2, WXS_BASIC_CAST use, NULL, "The attribute declaration has a 'fixed' value constraint " ", thus the attribute use must also have a 'fixed' value " "constraint", NULL); return(ctxt->err); } /* * Compute and check the value constraint's value. */ if ((use->defVal != NULL) && (WXS_ATTRUSE_TYPEDEF(use) != NULL)) { int ret; /* * TODO: The spec seems to be missing a check of the * value constraint of the attribute use. We will do it here. */ /* * SPEC a-props-correct (3) */ if (xmlSchemaIsDerivedFromBuiltInType( WXS_ATTRUSE_TYPEDEF(use), XML_SCHEMAS_ID)) { xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_AU_PROPS_CORRECT, NULL, WXS_BASIC_CAST use, "Value constraints are not allowed if the type definition " "is or is derived from xs:ID", NULL, NULL); return(ctxt->err); } ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST ctxt, use->node, WXS_ATTRUSE_TYPEDEF(use), use->defValue, &(use->defVal), 1, 1, 0); if (ret != 0) { if (ret < 0) { PERROR_INT2("xmlSchemaCheckAttrUsePropsCorrect", "calling xmlSchemaVCheckCVCSimpleType()"); return(-1); } xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_AU_PROPS_CORRECT, NULL, WXS_BASIC_CAST use, "The value of the value constraint is not valid", NULL, NULL); return(ctxt->err); } } /* * SPEC au-props-correct (2) * "If the {attribute declaration} has a fixed * {value constraint}, then if the attribute use itself has a * {value constraint}, it must also be fixed and its value must match * that of the {attribute declaration}'s {value constraint}." */ if (((WXS_ATTRUSE_DECL(use))->defVal != NULL) && (((WXS_ATTRUSE_DECL(use))->flags & XML_SCHEMA_ATTR_USE_FIXED) == 0)) { if (! xmlSchemaAreValuesEqual(use->defVal, (WXS_ATTRUSE_DECL(use))->defVal)) { xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_AU_PROPS_CORRECT_2, WXS_BASIC_CAST use, NULL, "The 'fixed' value constraint of the attribute use " "must match the attribute declaration's value " "constraint '%s'", (WXS_ATTRUSE_DECL(use))->defValue); } return(ctxt->err); } return(0); } /** * xmlSchemaResolveAttrTypeReferences: * @item: an attribute declaration * @ctxt: a parser context * * Resolves the referenced type definition component. */ static int xmlSchemaResolveAttrTypeReferences(xmlSchemaAttributePtr item, xmlSchemaParserCtxtPtr ctxt) { /* * The simple type definition corresponding to the element * information item in the [children], if present, otherwise the simple * type definition `resolved` to by the `actual value` of the type * [attribute], if present, otherwise the `simple ur-type definition`. */ if (item->flags & XML_SCHEMAS_ATTR_INTERNAL_RESOLVED) return(0); item->flags |= XML_SCHEMAS_ATTR_INTERNAL_RESOLVED; if (item->subtypes != NULL) return(0); if (item->typeName != NULL) { xmlSchemaTypePtr type; type = xmlSchemaGetType(ctxt->schema, item->typeName, item->typeNs); if ((type == NULL) || (! WXS_IS_SIMPLE(type))) { xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST item, item->node, "type", item->typeName, item->typeNs, XML_SCHEMA_TYPE_SIMPLE, NULL); return(ctxt->err); } else item->subtypes = type; } else { /* * The type defaults to the xs:anySimpleType. */ item->subtypes = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYSIMPLETYPE); } return(0); } /** * xmlSchemaResolveIDCKeyReferences: * @idc: the identity-constraint definition * @ctxt: the schema parser context * @name: the attribute name * * Resolve keyRef references to key/unique IDCs. * Schema Component Constraint: * Identity-constraint Definition Properties Correct (c-props-correct) */ static int xmlSchemaResolveIDCKeyReferences(xmlSchemaIDCPtr idc, xmlSchemaParserCtxtPtr pctxt) { if (idc->type != XML_SCHEMA_TYPE_IDC_KEYREF) return(0); if (idc->ref->name != NULL) { idc->ref->item = (xmlSchemaBasicItemPtr) xmlSchemaGetIDC(pctxt->schema, idc->ref->name, idc->ref->targetNamespace); if (idc->ref->item == NULL) { /* * TODO: It is actually not an error to fail to resolve * at this stage. BUT we need to be that strict! */ xmlSchemaPResCompAttrErr(pctxt, XML_SCHEMAP_SRC_RESOLVE, WXS_BASIC_CAST idc, idc->node, "refer", idc->ref->name, idc->ref->targetNamespace, XML_SCHEMA_TYPE_IDC_KEY, NULL); return(pctxt->err); } else if (idc->ref->item->type == XML_SCHEMA_TYPE_IDC_KEYREF) { /* * SPEC c-props-correct (1) */ xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_C_PROPS_CORRECT, NULL, WXS_BASIC_CAST idc, "The keyref references a keyref", NULL, NULL); idc->ref->item = NULL; return(pctxt->err); } else { if (idc->nbFields != ((xmlSchemaIDCPtr) idc->ref->item)->nbFields) { xmlChar *str = NULL; xmlSchemaIDCPtr refer; refer = (xmlSchemaIDCPtr) idc->ref->item; /* * SPEC c-props-correct(2) * "If the {identity-constraint category} is keyref, * the cardinality of the {fields} must equal that of * the {fields} of the {referenced key}. */ xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_C_PROPS_CORRECT, NULL, WXS_BASIC_CAST idc, "The cardinality of the keyref differs from the " "cardinality of the referenced key/unique '%s'", xmlSchemaFormatQName(&str, refer->targetNamespace, refer->name), NULL); FREE_AND_NULL(str) return(pctxt->err); } } } return(0); } static int xmlSchemaResolveAttrUseProhibReferences(xmlSchemaAttributeUseProhibPtr prohib, xmlSchemaParserCtxtPtr pctxt) { if (xmlSchemaGetAttributeDecl(pctxt->schema, prohib->name, prohib->targetNamespace) == NULL) { xmlSchemaPResCompAttrErr(pctxt, XML_SCHEMAP_SRC_RESOLVE, NULL, prohib->node, "ref", prohib->name, prohib->targetNamespace, XML_SCHEMA_TYPE_ATTRIBUTE, NULL); return(XML_SCHEMAP_SRC_RESOLVE); } return(0); } #define WXS_REDEFINED_TYPE(c) \ (((xmlSchemaTypePtr) item)->flags & XML_SCHEMAS_TYPE_REDEFINED) #define WXS_REDEFINED_MODEL_GROUP_DEF(c) \ (((xmlSchemaModelGroupDefPtr) item)->flags & XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED) #define WXS_REDEFINED_ATTR_GROUP(c) \ (((xmlSchemaAttributeGroupPtr) item)->flags & XML_SCHEMAS_ATTRGROUP_REDEFINED) static int xmlSchemaCheckSRCRedefineFirst(xmlSchemaParserCtxtPtr pctxt) { int err = 0; xmlSchemaRedefPtr redef = WXS_CONSTRUCTOR(pctxt)->redefs; xmlSchemaBasicItemPtr prev, item; int wasRedefined; if (redef == NULL) return(0); do { item = redef->item; /* * First try to locate the redefined component in the * schema graph starting with the redefined schema. * NOTE: According to this schema bug entry: * http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005OctDec/0019.html * it's not clear if the referenced component needs to originate * from the d schema _document_ or the schema; the latter * would include all imported and included sub-schemas of the * d schema. Currently the latter approach is used. * SUPPLEMENT: It seems that the WG moves towards the latter * approach, so we are doing it right. * */ prev = xmlSchemaFindRedefCompInGraph( redef->targetBucket, item->type, redef->refName, redef->refTargetNs); if (prev == NULL) { xmlChar *str = NULL; xmlNodePtr node; /* * SPEC src-redefine: * (6.2.1) "The `actual value` of its own name attribute plus * target namespace must successfully `resolve` to a model * group definition in I." * (7.2.1) "The `actual value` of its own name attribute plus * target namespace must successfully `resolve` to an attribute * group definition in I." * * Note that, if we are redefining with the use of references * to components, the spec assumes the src-resolve to be used; * but this won't assure that we search only *inside* the * redefined schema. */ if (redef->reference) node = WXS_ITEM_NODE(redef->reference); else node = WXS_ITEM_NODE(item); xmlSchemaCustomErr(ACTXT_CAST pctxt, /* * TODO: error code. * Probably XML_SCHEMAP_SRC_RESOLVE, if this is using the * reference kind. */ XML_SCHEMAP_SRC_REDEFINE, node, NULL, "The %s '%s' to be redefined could not be found in " "the redefined schema", WXS_ITEM_TYPE_NAME(item), xmlSchemaFormatQName(&str, redef->refTargetNs, redef->refName)); FREE_AND_NULL(str); err = pctxt->err; redef = redef->next; continue; } /* * TODO: Obtaining and setting the redefinition state is really * clumsy. */ wasRedefined = 0; switch (item->type) { case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: if ((WXS_TYPE_CAST prev)->flags & XML_SCHEMAS_TYPE_REDEFINED) { wasRedefined = 1; break; } /* Mark it as redefined. */ (WXS_TYPE_CAST prev)->flags |= XML_SCHEMAS_TYPE_REDEFINED; /* * Assign the redefined type to the * base type of the redefining type. * TODO: How */ ((xmlSchemaTypePtr) item)->baseType = (xmlSchemaTypePtr) prev; break; case XML_SCHEMA_TYPE_GROUP: if ((WXS_MODEL_GROUPDEF_CAST prev)->flags & XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED) { wasRedefined = 1; break; } /* Mark it as redefined. */ (WXS_MODEL_GROUPDEF_CAST prev)->flags |= XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED; if (redef->reference != NULL) { /* * Overwrite the QName-reference with the * referenced model group def. */ (WXS_PTC_CAST redef->reference)->children = WXS_TREE_CAST prev; } redef->target = prev; break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: if ((WXS_ATTR_GROUP_CAST prev)->flags & XML_SCHEMAS_ATTRGROUP_REDEFINED) { wasRedefined = 1; break; } (WXS_ATTR_GROUP_CAST prev)->flags |= XML_SCHEMAS_ATTRGROUP_REDEFINED; if (redef->reference != NULL) { /* * Assign the redefined attribute group to the * QName-reference component. * This is the easy case, since we will just * expand the redefined group. */ (WXS_QNAME_CAST redef->reference)->item = prev; redef->target = NULL; } else { /* * This is the complicated case: we need * to apply src-redefine (7.2.2) at a later * stage, i.e. when attribute group references * have been expanded and simple types have * been fixed. */ redef->target = prev; } break; default: PERROR_INT("xmlSchemaResolveRedefReferences", "Unexpected redefined component type"); return(-1); } if (wasRedefined) { xmlChar *str = NULL; xmlNodePtr node; if (redef->reference) node = WXS_ITEM_NODE(redef->reference); else node = WXS_ITEM_NODE(redef->item); xmlSchemaCustomErr(ACTXT_CAST pctxt, /* TODO: error code. */ XML_SCHEMAP_SRC_REDEFINE, node, NULL, "The referenced %s was already redefined. Multiple " "redefinition of the same component is not supported", xmlSchemaGetComponentDesignation(&str, prev), NULL); FREE_AND_NULL(str) err = pctxt->err; redef = redef->next; continue; } redef = redef->next; } while (redef != NULL); return(err); } static int xmlSchemaCheckSRCRedefineSecond(xmlSchemaParserCtxtPtr pctxt) { int err = 0; xmlSchemaRedefPtr redef = WXS_CONSTRUCTOR(pctxt)->redefs; xmlSchemaBasicItemPtr item; if (redef == NULL) return(0); do { if (redef->target == NULL) { redef = redef->next; continue; } item = redef->item; switch (item->type) { case XML_SCHEMA_TYPE_SIMPLE: case XML_SCHEMA_TYPE_COMPLEX: /* * Since the spec wants the {name} of the redefined * type to be 'absent', we'll NULL it. */ (WXS_TYPE_CAST redef->target)->name = NULL; /* * TODO: Seems like there's nothing more to do. The normal * inheritance mechanism is used. But not 100% sure. */ break; case XML_SCHEMA_TYPE_GROUP: /* * URGENT TODO: * SPEC src-redefine: * (6.2.2) "The {model group} of the model group definition * which corresponds to it per XML Representation of Model * Group Definition Schema Components ($3.7.2) must be a * `valid restriction` of the {model group} of that model * group definition in I, as defined in Particle Valid * (Restriction) ($3.9.6)." */ break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: /* * SPEC src-redefine: * (7.2.2) "The {attribute uses} and {attribute wildcard} of * the attribute group definition which corresponds to it * per XML Representation of Attribute Group Definition Schema * Components ($3.6.2) must be `valid restrictions` of the * {attribute uses} and {attribute wildcard} of that attribute * group definition in I, as defined in clause 2, clause 3 and * clause 4 of Derivation Valid (Restriction, Complex) * ($3.4.6) (where references to the base type definition are * understood as references to the attribute group definition * in I)." */ err = xmlSchemaCheckDerivationOKRestriction2to4(pctxt, XML_SCHEMA_ACTION_REDEFINE, item, redef->target, (WXS_ATTR_GROUP_CAST item)->attrUses, (WXS_ATTR_GROUP_CAST redef->target)->attrUses, (WXS_ATTR_GROUP_CAST item)->attributeWildcard, (WXS_ATTR_GROUP_CAST redef->target)->attributeWildcard); if (err == -1) return(-1); break; default: break; } redef = redef->next; } while (redef != NULL); return(0); } static int xmlSchemaAddComponents(xmlSchemaParserCtxtPtr pctxt, xmlSchemaBucketPtr bucket) { xmlSchemaBasicItemPtr item; int err; xmlHashTablePtr *table; const xmlChar *name; int i; #define WXS_GET_GLOBAL_HASH(c, slot) { \ if (WXS_IS_BUCKET_IMPMAIN((c)->type)) \ table = &(WXS_IMPBUCKET((c))->schema->slot); \ else \ table = &(WXS_INCBUCKET((c))->ownerImport->schema->slot); } /* * Add global components to the schema's hash tables. * This is the place where duplicate components will be * detected. * TODO: I think normally we should support imports of the * same namespace from multiple locations. We don't do currently, * but if we do then according to: * http://www.w3.org/Bugs/Public/show_bug.cgi?id=2224 * we would need, if imported directly, to import redefined * components as well to be able to catch clashing components. * (I hope I'll still know what this means after some months :-() */ if (bucket == NULL) return(-1); if (bucket->flags & XML_SCHEMA_BUCKET_COMPS_ADDED) return(0); bucket->flags |= XML_SCHEMA_BUCKET_COMPS_ADDED; for (i = 0; i < bucket->globals->nbItems; i++) { item = bucket->globals->items[i]; table = NULL; switch (item->type) { case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: if (WXS_REDEFINED_TYPE(item)) continue; name = (WXS_TYPE_CAST item)->name; WXS_GET_GLOBAL_HASH(bucket, typeDecl) break; case XML_SCHEMA_TYPE_ELEMENT: name = (WXS_ELEM_CAST item)->name; WXS_GET_GLOBAL_HASH(bucket, elemDecl) break; case XML_SCHEMA_TYPE_ATTRIBUTE: name = (WXS_ATTR_CAST item)->name; WXS_GET_GLOBAL_HASH(bucket, attrDecl) break; case XML_SCHEMA_TYPE_GROUP: if (WXS_REDEFINED_MODEL_GROUP_DEF(item)) continue; name = (WXS_MODEL_GROUPDEF_CAST item)->name; WXS_GET_GLOBAL_HASH(bucket, groupDecl) break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: if (WXS_REDEFINED_ATTR_GROUP(item)) continue; name = (WXS_ATTR_GROUP_CAST item)->name; WXS_GET_GLOBAL_HASH(bucket, attrgrpDecl) break; case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEYREF: name = (WXS_IDC_CAST item)->name; WXS_GET_GLOBAL_HASH(bucket, idcDef) break; case XML_SCHEMA_TYPE_NOTATION: name = ((xmlSchemaNotationPtr) item)->name; WXS_GET_GLOBAL_HASH(bucket, notaDecl) break; default: PERROR_INT("xmlSchemaAddComponents", "Unexpected global component type"); continue; } if (*table == NULL) { *table = xmlHashCreateDict(10, pctxt->dict); if (*table == NULL) { PERROR_INT("xmlSchemaAddComponents", "failed to create a component hash table"); return(-1); } } err = xmlHashAddEntry(*table, name, item); if (err != 0) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_REDEFINED_TYPE, WXS_ITEM_NODE(item), WXS_BASIC_CAST item, "A global %s '%s' does already exist", WXS_ITEM_TYPE_NAME(item), xmlSchemaGetComponentQName(&str, item)); FREE_AND_NULL(str); } } /* * Process imported/included schemas. */ if (bucket->relations != NULL) { xmlSchemaSchemaRelationPtr rel = bucket->relations; do { if ((rel->bucket != NULL) && ((rel->bucket->flags & XML_SCHEMA_BUCKET_COMPS_ADDED) == 0)) { if (xmlSchemaAddComponents(pctxt, rel->bucket) == -1) return(-1); } rel = rel->next; } while (rel != NULL); } return(0); } static int xmlSchemaFixupComponents(xmlSchemaParserCtxtPtr pctxt, xmlSchemaBucketPtr rootBucket) { xmlSchemaConstructionCtxtPtr con = pctxt->constructor; xmlSchemaTreeItemPtr item, *items; int nbItems, i, ret = 0; xmlSchemaBucketPtr oldbucket = con->bucket; xmlSchemaElementPtr elemDecl; #define FIXHFAILURE if (pctxt->err == XML_SCHEMAP_INTERNAL) goto exit_failure; if ((con->pending == NULL) || (con->pending->nbItems == 0)) return(0); /* * Since xmlSchemaFixupComplexType() will create new particles * (local components), and those particle components need a bucket * on the constructor, we'll assure here that the constructor has * a bucket. * TODO: Think about storing locals _only_ on the main bucket. */ if (con->bucket == NULL) con->bucket = rootBucket; /* TODO: * SPEC (src-redefine): * (6.2) "If it has no such self-reference, then all of the * following must be true:" * (6.2.2) The {model group} of the model group definition which * corresponds to it per XML Representation of Model Group * Definition Schema Components ($3.7.2) must be a `valid * restriction` of the {model group} of that model group definition * in I, as defined in Particle Valid (Restriction) ($3.9.6)." */ xmlSchemaCheckSRCRedefineFirst(pctxt); /* * Add global components to the schemata's hash tables. */ xmlSchemaAddComponents(pctxt, rootBucket); pctxt->ctxtType = NULL; items = (xmlSchemaTreeItemPtr *) con->pending->items; nbItems = con->pending->nbItems; /* * Now that we have parsed *all* the schema document(s) and converted * them to schema components, we can resolve references, apply component * constraints, create the FSA from the content model, etc. */ /* * Resolve references of.. * * 1. element declarations: * - the type definition * - the substitution group affiliation * 2. simple/complex types: * - the base type definition * - the memberTypes of union types * - the itemType of list types * 3. attributes declarations and attribute uses: * - the type definition * - if an attribute use, then the attribute declaration * 4. attribute group references: * - the attribute group definition * 5. particles: * - the term of the particle (e.g. a model group) * 6. IDC key-references: * - the referenced IDC 'key' or 'unique' definition * 7. Attribute prohibitions which had a "ref" attribute. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_ELEMENT: xmlSchemaResolveElementReferences( (xmlSchemaElementPtr) item, pctxt); FIXHFAILURE; break; case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: xmlSchemaResolveTypeReferences( (xmlSchemaTypePtr) item, pctxt); FIXHFAILURE; break; case XML_SCHEMA_TYPE_ATTRIBUTE: xmlSchemaResolveAttrTypeReferences( (xmlSchemaAttributePtr) item, pctxt); FIXHFAILURE; break; case XML_SCHEMA_TYPE_ATTRIBUTE_USE: xmlSchemaResolveAttrUseReferences( (xmlSchemaAttributeUsePtr) item, pctxt); FIXHFAILURE; break; case XML_SCHEMA_EXTRA_QNAMEREF: if ((WXS_QNAME_CAST item)->itemType == XML_SCHEMA_TYPE_ATTRIBUTEGROUP) { xmlSchemaResolveAttrGroupReferences( WXS_QNAME_CAST item, pctxt); } FIXHFAILURE; break; case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: case XML_SCHEMA_TYPE_ALL: xmlSchemaResolveModelGroupParticleReferences(pctxt, WXS_MODEL_GROUP_CAST item); FIXHFAILURE; break; case XML_SCHEMA_TYPE_IDC_KEY: case XML_SCHEMA_TYPE_IDC_UNIQUE: case XML_SCHEMA_TYPE_IDC_KEYREF: xmlSchemaResolveIDCKeyReferences( (xmlSchemaIDCPtr) item, pctxt); FIXHFAILURE; break; case XML_SCHEMA_EXTRA_ATTR_USE_PROHIB: /* * Handle attribute prohibition which had a * "ref" attribute. */ xmlSchemaResolveAttrUseProhibReferences( WXS_ATTR_PROHIB_CAST item, pctxt); FIXHFAILURE; break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Now that all references are resolved we * can check for circularity of... * 1. the base axis of type definitions * 2. nested model group definitions * 3. nested attribute group definitions * TODO: check for circular substitution groups. */ for (i = 0; i < nbItems; i++) { item = items[i]; /* * Let's better stop on the first error here. */ switch (item->type) { case XML_SCHEMA_TYPE_COMPLEX: case XML_SCHEMA_TYPE_SIMPLE: xmlSchemaCheckTypeDefCircular( (xmlSchemaTypePtr) item, pctxt); FIXHFAILURE; if (pctxt->nberrors != 0) goto exit_error; break; case XML_SCHEMA_TYPE_GROUP: xmlSchemaCheckGroupDefCircular( (xmlSchemaModelGroupDefPtr) item, pctxt); FIXHFAILURE; if (pctxt->nberrors != 0) goto exit_error; break; case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: xmlSchemaCheckAttrGroupCircular( (xmlSchemaAttributeGroupPtr) item, pctxt); FIXHFAILURE; if (pctxt->nberrors != 0) goto exit_error; break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Model group definition references: * Such a reference is reflected by a particle at the component * level. Until now the 'term' of such particles pointed * to the model group definition; this was done, in order to * ease circularity checks. Now we need to set the 'term' of * such particles to the model group of the model group definition. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_SEQUENCE: case XML_SCHEMA_TYPE_CHOICE: xmlSchemaModelGroupToModelGroupDefFixup(pctxt, WXS_MODEL_GROUP_CAST item); break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Expand attribute group references of attribute group definitions. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: if ((! WXS_ATTR_GROUP_EXPANDED(item)) && WXS_ATTR_GROUP_HAS_REFS(item)) { xmlSchemaAttributeGroupExpandRefs(pctxt, WXS_ATTR_GROUP_CAST item); FIXHFAILURE; } break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * First compute the variety of simple types. This is needed as * a separate step, since otherwise we won't be able to detect * circular union types in all cases. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_SIMPLE: if (WXS_IS_TYPE_NOT_FIXED_1((xmlSchemaTypePtr) item)) { xmlSchemaFixupSimpleTypeStageOne(pctxt, (xmlSchemaTypePtr) item); FIXHFAILURE; } break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Detect circular union types. Note that this needs the variety to * be already computed. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_SIMPLE: if (((xmlSchemaTypePtr) item)->memberTypes != NULL) { xmlSchemaCheckUnionTypeDefCircular(pctxt, (xmlSchemaTypePtr) item); FIXHFAILURE; } break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Do the complete type fixup for simple types. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_SIMPLE: if (WXS_IS_TYPE_NOT_FIXED(WXS_TYPE_CAST item)) { xmlSchemaFixupSimpleTypeStageTwo(pctxt, WXS_TYPE_CAST item); FIXHFAILURE; } break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * At this point we need build and check all simple types. */ /* * Apply constraints for attribute declarations. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_ATTRIBUTE: xmlSchemaCheckAttrPropsCorrect(pctxt, WXS_ATTR_CAST item); FIXHFAILURE; break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Apply constraints for attribute uses. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_ATTRIBUTE_USE: if (((xmlSchemaAttributeUsePtr)item)->defValue != NULL) { xmlSchemaCheckAttrUsePropsCorrect(pctxt, WXS_ATTR_USE_CAST item); FIXHFAILURE; } break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Apply constraints for attribute group definitions. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: if (( (WXS_ATTR_GROUP_CAST item)->attrUses != NULL) && ( (WXS_LIST_CAST (WXS_ATTR_GROUP_CAST item)->attrUses)->nbItems > 1)) { xmlSchemaCheckAGPropsCorrect(pctxt, WXS_ATTR_GROUP_CAST item); FIXHFAILURE; } break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Apply constraints for redefinitions. */ if (WXS_CONSTRUCTOR(pctxt)->redefs != NULL) xmlSchemaCheckSRCRedefineSecond(pctxt); if (pctxt->nberrors != 0) goto exit_error; /* * Complex types are built and checked. */ for (i = 0; i < nbItems; i++) { item = con->pending->items[i]; switch (item->type) { case XML_SCHEMA_TYPE_COMPLEX: if (WXS_IS_TYPE_NOT_FIXED(WXS_TYPE_CAST item)) { xmlSchemaFixupComplexType(pctxt, WXS_TYPE_CAST item); FIXHFAILURE; } break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * The list could have changed, since xmlSchemaFixupComplexType() * will create particles and model groups in some cases. */ items = (xmlSchemaTreeItemPtr *) con->pending->items; nbItems = con->pending->nbItems; /* * Apply some constraints for element declarations. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_ELEMENT: elemDecl = (xmlSchemaElementPtr) item; if ((elemDecl->flags & XML_SCHEMAS_ELEM_INTERNAL_CHECKED) == 0) { xmlSchemaCheckElementDeclComponent( (xmlSchemaElementPtr) elemDecl, pctxt); FIXHFAILURE; } #ifdef WXS_ELEM_DECL_CONS_ENABLED /* * Schema Component Constraint: Element Declarations Consistent * Apply this constraint to local types of element declarations. */ if ((WXS_ELEM_TYPEDEF(elemDecl) != NULL) && (WXS_IS_COMPLEX(WXS_ELEM_TYPEDEF(elemDecl))) && (WXS_TYPE_IS_LOCAL(WXS_ELEM_TYPEDEF(elemDecl)))) { xmlSchemaCheckElementDeclConsistent(pctxt, WXS_BASIC_CAST elemDecl, WXS_TYPE_PARTICLE(WXS_ELEM_TYPEDEF(elemDecl)), NULL, NULL, 0); } #endif break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * Finally we can build the automaton from the content model of * complex types. */ for (i = 0; i < nbItems; i++) { item = items[i]; switch (item->type) { case XML_SCHEMA_TYPE_COMPLEX: xmlSchemaBuildContentModel((xmlSchemaTypePtr) item, pctxt); /* FIXHFAILURE; */ break; default: break; } } if (pctxt->nberrors != 0) goto exit_error; /* * URGENT TODO: cos-element-consistent */ goto exit; exit_error: ret = pctxt->err; goto exit; exit_failure: ret = -1; exit: /* * Reset the constructor. This is needed for XSI acquisition, since * those items will be processed over and over again for every XSI * if not cleared here. */ con->bucket = oldbucket; con->pending->nbItems = 0; if (con->substGroups != NULL) { xmlHashFree(con->substGroups, xmlSchemaSubstGroupFreeEntry); con->substGroups = NULL; } if (con->redefs != NULL) { xmlSchemaRedefListFree(con->redefs); con->redefs = NULL; } return(ret); } /** * xmlSchemaParse: * @ctxt: a schema validation context * * parse a schema definition resource and build an internal * XML Schema structure which can be used to validate instances. * * Returns the internal XML Schema structure built from the resource or * NULL in case of error */ xmlSchemaPtr xmlSchemaParse(xmlSchemaParserCtxtPtr ctxt) { xmlSchemaPtr mainSchema = NULL; xmlSchemaBucketPtr bucket = NULL; int res; /* * This one is used if the schema to be parsed was specified via * the API; i.e. not automatically by the validated instance document. */ if (xmlSchemaInitTypes() < 0) return (NULL); if (ctxt == NULL) return (NULL); /* TODO: Init the context. Is this all we need?*/ ctxt->nberrors = 0; ctxt->err = 0; ctxt->counter = 0; /* Create the *main* schema. */ mainSchema = xmlSchemaNewSchema(ctxt); if (mainSchema == NULL) goto exit_failure; /* * Create the schema constructor. */ if (ctxt->constructor == NULL) { ctxt->constructor = xmlSchemaConstructionCtxtCreate(ctxt->dict); if (ctxt->constructor == NULL) goto exit_failure; /* Take ownership of the constructor to be able to free it. */ ctxt->ownsConstructor = 1; } ctxt->constructor->mainSchema = mainSchema; /* * Locate and add the schema document. */ res = xmlSchemaAddSchemaDoc(ctxt, XML_SCHEMA_SCHEMA_MAIN, ctxt->URL, ctxt->doc, ctxt->buffer, ctxt->size, NULL, NULL, NULL, &bucket); if (res == -1) goto exit_failure; if (res != 0) goto exit; if (bucket == NULL) { /* TODO: Error code, actually we failed to *locate* the schema. */ if (ctxt->URL) xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_FAILED_LOAD, NULL, NULL, "Failed to locate the main schema resource at '%s'", ctxt->URL, NULL); else xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_FAILED_LOAD, NULL, NULL, "Failed to locate the main schema resource", NULL, NULL); goto exit; } /* Then do the parsing for good. */ if (xmlSchemaParseNewDocWithContext(ctxt, mainSchema, bucket) == -1) goto exit_failure; if (ctxt->nberrors != 0) goto exit; mainSchema->doc = bucket->doc; mainSchema->preserve = ctxt->preserve; ctxt->schema = mainSchema; if (xmlSchemaFixupComponents(ctxt, WXS_CONSTRUCTOR(ctxt)->mainBucket) == -1) goto exit_failure; /* * TODO: This is not nice, since we cannot distinguish from the * result if there was an internal error or not. */ exit: if (ctxt->nberrors != 0) { if (mainSchema) { xmlSchemaFree(mainSchema); mainSchema = NULL; } if (ctxt->constructor) { xmlSchemaConstructionCtxtFree(ctxt->constructor); ctxt->constructor = NULL; ctxt->ownsConstructor = 0; } } ctxt->schema = NULL; return(mainSchema); exit_failure: /* * Quite verbose, but should catch internal errors, which were * not communicated. */ if (mainSchema) { xmlSchemaFree(mainSchema); mainSchema = NULL; } if (ctxt->constructor) { xmlSchemaConstructionCtxtFree(ctxt->constructor); ctxt->constructor = NULL; ctxt->ownsConstructor = 0; } PERROR_INT2("xmlSchemaParse", "An internal error occurred"); ctxt->schema = NULL; return(NULL); } /** * xmlSchemaSetParserErrors: * @ctxt: a schema validation context * @err: the error callback * @warn: the warning callback * @ctx: contextual data for the callbacks * * DEPRECATED: Use xmlSchemaSetParserStructuredErrors. * * Set the callback functions used to handle errors for a validation context */ void xmlSchemaSetParserErrors(xmlSchemaParserCtxtPtr ctxt, xmlSchemaValidityErrorFunc err, xmlSchemaValidityWarningFunc warn, void *ctx) { if (ctxt == NULL) return; ctxt->error = err; ctxt->warning = warn; ctxt->errCtxt = ctx; if (ctxt->vctxt != NULL) xmlSchemaSetValidErrors(ctxt->vctxt, err, warn, ctx); } /** * xmlSchemaSetParserStructuredErrors: * @ctxt: a schema parser context * @serror: the structured error function * @ctx: the functions context * * Set the structured error callback */ void xmlSchemaSetParserStructuredErrors(xmlSchemaParserCtxtPtr ctxt, xmlStructuredErrorFunc serror, void *ctx) { if (ctxt == NULL) return; ctxt->serror = serror; ctxt->errCtxt = ctx; if (ctxt->vctxt != NULL) xmlSchemaSetValidStructuredErrors(ctxt->vctxt, serror, ctx); } /** * xmlSchemaGetParserErrors: * @ctxt: a XMl-Schema parser context * @err: the error callback result * @warn: the warning callback result * @ctx: contextual data for the callbacks result * * Get the callback information used to handle errors for a parser context * * Returns -1 in case of failure, 0 otherwise */ int xmlSchemaGetParserErrors(xmlSchemaParserCtxtPtr ctxt, xmlSchemaValidityErrorFunc * err, xmlSchemaValidityWarningFunc * warn, void **ctx) { if (ctxt == NULL) return(-1); if (err != NULL) *err = ctxt->error; if (warn != NULL) *warn = ctxt->warning; if (ctx != NULL) *ctx = ctxt->errCtxt; return(0); } /** * xmlSchemaFacetTypeToString: * @type: the facet type * * Convert the xmlSchemaTypeType to a char string. * * Returns the char string representation of the facet type if the * type is a facet and an "Internal Error" string otherwise. */ static const xmlChar * xmlSchemaFacetTypeToString(xmlSchemaTypeType type) { switch (type) { case XML_SCHEMA_FACET_PATTERN: return (BAD_CAST "pattern"); case XML_SCHEMA_FACET_MAXEXCLUSIVE: return (BAD_CAST "maxExclusive"); case XML_SCHEMA_FACET_MAXINCLUSIVE: return (BAD_CAST "maxInclusive"); case XML_SCHEMA_FACET_MINEXCLUSIVE: return (BAD_CAST "minExclusive"); case XML_SCHEMA_FACET_MININCLUSIVE: return (BAD_CAST "minInclusive"); case XML_SCHEMA_FACET_WHITESPACE: return (BAD_CAST "whiteSpace"); case XML_SCHEMA_FACET_ENUMERATION: return (BAD_CAST "enumeration"); case XML_SCHEMA_FACET_LENGTH: return (BAD_CAST "length"); case XML_SCHEMA_FACET_MAXLENGTH: return (BAD_CAST "maxLength"); case XML_SCHEMA_FACET_MINLENGTH: return (BAD_CAST "minLength"); case XML_SCHEMA_FACET_TOTALDIGITS: return (BAD_CAST "totalDigits"); case XML_SCHEMA_FACET_FRACTIONDIGITS: return (BAD_CAST "fractionDigits"); default: break; } return (BAD_CAST "Internal Error"); } static xmlSchemaWhitespaceValueType xmlSchemaGetWhiteSpaceFacetValue(xmlSchemaTypePtr type) { /* * The normalization type can be changed only for types which are derived * from xsd:string. */ if (type->type == XML_SCHEMA_TYPE_BASIC) { /* * Note that we assume a whitespace of preserve for anySimpleType. */ if ((type->builtInType == XML_SCHEMAS_STRING) || (type->builtInType == XML_SCHEMAS_ANYSIMPLETYPE)) return(XML_SCHEMA_WHITESPACE_PRESERVE); else if (type->builtInType == XML_SCHEMAS_NORMSTRING) return(XML_SCHEMA_WHITESPACE_REPLACE); else { /* * For all `atomic` datatypes other than string (and types `derived` * by `restriction` from it) the value of whiteSpace is fixed to * collapse * Note that this includes built-in list datatypes. */ return(XML_SCHEMA_WHITESPACE_COLLAPSE); } } else if (WXS_IS_LIST(type)) { /* * For list types the facet "whiteSpace" is fixed to "collapse". */ return (XML_SCHEMA_WHITESPACE_COLLAPSE); } else if (WXS_IS_UNION(type)) { return (XML_SCHEMA_WHITESPACE_UNKNOWN); } else if (WXS_IS_ATOMIC(type)) { if (type->flags & XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE) return (XML_SCHEMA_WHITESPACE_PRESERVE); else if (type->flags & XML_SCHEMAS_TYPE_WHITESPACE_REPLACE) return (XML_SCHEMA_WHITESPACE_REPLACE); else return (XML_SCHEMA_WHITESPACE_COLLAPSE); } return (-1); } /************************************************************************ * * * Simple type validation * * * ************************************************************************/ /************************************************************************ * * * DOM Validation code * * * ************************************************************************/ /** * xmlSchemaAssembleByLocation: * @pctxt: a schema parser context * @vctxt: a schema validation context * @schema: the existing schema * @node: the node that fired the assembling * @nsName: the namespace name of the new schema * @location: the location of the schema * * Expands an existing schema by an additional schema. * * Returns 0 if the new schema is correct, a positive error code * number otherwise and -1 in case of an internal or API error. */ static int xmlSchemaAssembleByLocation(xmlSchemaValidCtxtPtr vctxt, xmlSchemaPtr schema, xmlNodePtr node, const xmlChar *nsName, const xmlChar *location) { int ret = 0; xmlSchemaParserCtxtPtr pctxt; xmlSchemaBucketPtr bucket = NULL; if ((vctxt == NULL) || (schema == NULL)) return (-1); if (vctxt->pctxt == NULL) { VERROR_INT("xmlSchemaAssembleByLocation", "no parser context available"); return(-1); } pctxt = vctxt->pctxt; if (pctxt->constructor == NULL) { PERROR_INT("xmlSchemaAssembleByLocation", "no constructor"); return(-1); } /* * Acquire the schema document. */ location = xmlSchemaBuildAbsoluteURI(pctxt->dict, location, node); /* * Note that we pass XML_SCHEMA_SCHEMA_IMPORT here; * the process will automatically change this to * XML_SCHEMA_SCHEMA_MAIN if it is the first schema document. */ ret = xmlSchemaAddSchemaDoc(pctxt, XML_SCHEMA_SCHEMA_IMPORT, location, NULL, NULL, 0, node, NULL, nsName, &bucket); if (ret != 0) return(ret); if (bucket == NULL) { /* * Generate a warning that the document could not be located. */ xmlSchemaCustomWarning(ACTXT_CAST vctxt, XML_SCHEMAV_MISC, node, NULL, "The document at location '%s' could not be acquired", location, NULL, NULL); return(ret); } /* * The first located schema will be handled as if all other * schemas imported by XSI were imported by this first schema. */ if ((bucket != NULL) && (WXS_CONSTRUCTOR(pctxt)->bucket == NULL)) WXS_CONSTRUCTOR(pctxt)->bucket = bucket; /* * TODO: Is this handled like an import? I.e. is it not an error * if the schema cannot be located? */ if ((bucket == NULL) || (! CAN_PARSE_SCHEMA(bucket))) return(0); /* * We will reuse the parser context for every schema imported * directly via XSI. So reset the context. */ pctxt->nberrors = 0; pctxt->err = 0; pctxt->doc = bucket->doc; ret = xmlSchemaParseNewDocWithContext(pctxt, schema, bucket); if (ret == -1) { pctxt->doc = NULL; goto exit_failure; } /* Paranoid error channelling. */ if ((ret == 0) && (pctxt->nberrors != 0)) ret = pctxt->err; if (pctxt->nberrors == 0) { /* * Only bother to fixup pending components, if there was * no error yet. * For every XSI acquired schema (and its sub-schemata) we will * fixup the components. */ xmlSchemaFixupComponents(pctxt, bucket); ret = pctxt->err; /* * Not nice, but we need somehow to channel the schema parser * error to the validation context. */ if ((ret != 0) && (vctxt->err == 0)) vctxt->err = ret; vctxt->nberrors += pctxt->nberrors; } else { /* Add to validation error sum. */ vctxt->nberrors += pctxt->nberrors; } pctxt->doc = NULL; return(ret); exit_failure: pctxt->doc = NULL; return (-1); } static xmlSchemaAttrInfoPtr xmlSchemaGetMetaAttrInfo(xmlSchemaValidCtxtPtr vctxt, int metaType) { if (vctxt->nbAttrInfos == 0) return (NULL); { int i; xmlSchemaAttrInfoPtr iattr; for (i = 0; i < vctxt->nbAttrInfos; i++) { iattr = vctxt->attrInfos[i]; if (iattr->metaType == metaType) return (iattr); } } return (NULL); } /** * xmlSchemaAssembleByXSI: * @vctxt: a schema validation context * * Expands an existing schema by an additional schema using * the xsi:schemaLocation or xsi:noNamespaceSchemaLocation attribute * of an instance. If xsi:noNamespaceSchemaLocation is used, @noNamespace * must be set to 1. * * Returns 0 if the new schema is correct, a positive error code * number otherwise and -1 in case of an internal or API error. */ static int xmlSchemaAssembleByXSI(xmlSchemaValidCtxtPtr vctxt) { const xmlChar *cur, *end; const xmlChar *nsname = NULL, *location; int ret = 0; xmlSchemaAttrInfoPtr iattr; /* * Parse the value; we will assume an even number of values * to be given (this is how Xerces and XSV work). * * URGENT TODO: !! This needs to work for both * @noNamespaceSchemaLocation AND @schemaLocation on the same * element !! */ iattr = xmlSchemaGetMetaAttrInfo(vctxt, XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC); if (iattr == NULL) iattr = xmlSchemaGetMetaAttrInfo(vctxt, XML_SCHEMA_ATTR_INFO_META_XSI_NO_NS_SCHEMA_LOC); if (iattr == NULL) return (0); cur = iattr->value; do { /* * TODO: Move the string parsing mechanism away from here. */ if (iattr->metaType == XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC) { /* * Get the namespace name. */ while (IS_BLANK_CH(*cur)) cur++; end = cur; while ((*end != 0) && (!(IS_BLANK_CH(*end)))) end++; if (end == cur) break; /* TODO: Don't use the schema's dict. */ nsname = xmlDictLookup(vctxt->schema->dict, cur, end - cur); cur = end; } /* * Get the URI. */ while (IS_BLANK_CH(*cur)) cur++; end = cur; while ((*end != 0) && (!(IS_BLANK_CH(*end)))) end++; if (end == cur) { if (iattr->metaType == XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC) { /* * If using @schemaLocation then tuples are expected. * I.e. the namespace name *and* the document's URI. */ xmlSchemaCustomWarning(ACTXT_CAST vctxt, XML_SCHEMAV_MISC, iattr->node, NULL, "The value must consist of tuples: the target namespace " "name and the document's URI", NULL, NULL, NULL); } break; } /* TODO: Don't use the schema's dict. */ location = xmlDictLookup(vctxt->schema->dict, cur, end - cur); cur = end; ret = xmlSchemaAssembleByLocation(vctxt, vctxt->schema, iattr->node, nsname, location); if (ret == -1) { VERROR_INT("xmlSchemaAssembleByXSI", "assembling schemata"); return (-1); } } while (*cur != 0); return (ret); } static const xmlChar * xmlSchemaLookupNamespace(xmlSchemaValidCtxtPtr vctxt, const xmlChar *prefix) { if (vctxt->sax != NULL) { int i, j; xmlSchemaNodeInfoPtr inode; for (i = vctxt->depth; i >= 0; i--) { if (vctxt->elemInfos[i]->nbNsBindings != 0) { inode = vctxt->elemInfos[i]; for (j = 0; j < inode->nbNsBindings * 2; j += 2) { if (((prefix == NULL) && (inode->nsBindings[j] == NULL)) || ((prefix != NULL) && xmlStrEqual(prefix, inode->nsBindings[j]))) { /* * Note that the namespace bindings are already * in a string dict. */ return (inode->nsBindings[j+1]); } } } } return (NULL); #ifdef LIBXML_READER_ENABLED } else if (vctxt->reader != NULL) { xmlChar *nsName; nsName = xmlTextReaderLookupNamespace(vctxt->reader, prefix); if (nsName != NULL) { const xmlChar *ret; ret = xmlDictLookup(vctxt->dict, nsName, -1); xmlFree(nsName); return (ret); } else return (NULL); #endif } else { xmlNsPtr ns; if ((vctxt->inode->node == NULL) || (vctxt->inode->node->doc == NULL)) { VERROR_INT("xmlSchemaLookupNamespace", "no node or node's doc available"); return (NULL); } ns = xmlSearchNs(vctxt->inode->node->doc, vctxt->inode->node, prefix); if (ns != NULL) return (ns->href); return (NULL); } } /* * This one works on the schema of the validation context. */ static int xmlSchemaValidateNotation(xmlSchemaValidCtxtPtr vctxt, xmlSchemaPtr schema, xmlNodePtr node, const xmlChar *value, xmlSchemaValPtr *val, int valNeeded) { int ret; if (vctxt && (vctxt->schema == NULL)) { VERROR_INT("xmlSchemaValidateNotation", "a schema is needed on the validation context"); return (-1); } ret = xmlValidateQName(value, 1); if (ret != 0) return (ret); { xmlChar *localName = NULL; xmlChar *prefix = NULL; localName = xmlSplitQName2(value, &prefix); if (prefix != NULL) { const xmlChar *nsName = NULL; if (vctxt != NULL) nsName = xmlSchemaLookupNamespace(vctxt, BAD_CAST prefix); else if (node != NULL) { xmlNsPtr ns = xmlSearchNs(node->doc, node, prefix); if (ns != NULL) nsName = ns->href; } else { xmlFree(prefix); xmlFree(localName); return (1); } if (nsName == NULL) { xmlFree(prefix); xmlFree(localName); return (1); } if (xmlSchemaGetNotation(schema, localName, nsName) != NULL) { if ((valNeeded) && (val != NULL)) { (*val) = xmlSchemaNewNOTATIONValue(xmlStrdup(localName), xmlStrdup(nsName)); if (*val == NULL) ret = -1; } } else ret = 1; xmlFree(prefix); xmlFree(localName); } else { if (xmlSchemaGetNotation(schema, value, NULL) != NULL) { if (valNeeded && (val != NULL)) { (*val) = xmlSchemaNewNOTATIONValue( BAD_CAST xmlStrdup(value), NULL); if (*val == NULL) ret = -1; } } else return (1); } } return (ret); } static int xmlSchemaVAddNodeQName(xmlSchemaValidCtxtPtr vctxt, const xmlChar* lname, const xmlChar* nsname) { int i; lname = xmlDictLookup(vctxt->dict, lname, -1); if (lname == NULL) return(-1); if (nsname != NULL) { nsname = xmlDictLookup(vctxt->dict, nsname, -1); if (nsname == NULL) return(-1); } for (i = 0; i < vctxt->nodeQNames->nbItems; i += 2) { if ((vctxt->nodeQNames->items [i] == lname) && (vctxt->nodeQNames->items[i +1] == nsname)) /* Already there */ return(i); } /* Add new entry. */ i = vctxt->nodeQNames->nbItems; xmlSchemaItemListAdd(vctxt->nodeQNames, (void *) lname); xmlSchemaItemListAdd(vctxt->nodeQNames, (void *) nsname); return(i); } /************************************************************************ * * * Validation of identity-constraints (IDC) * * * ************************************************************************/ /** * xmlSchemaAugmentIDC: * @idcDef: the IDC definition * * Creates an augmented IDC definition item. * * Returns the item, or NULL on internal errors. */ static void xmlSchemaAugmentIDC(void *payload, void *data, const xmlChar *name ATTRIBUTE_UNUSED) { xmlSchemaIDCPtr idcDef = (xmlSchemaIDCPtr) payload; xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) data; xmlSchemaIDCAugPtr aidc; aidc = (xmlSchemaIDCAugPtr) xmlMalloc(sizeof(xmlSchemaIDCAug)); if (aidc == NULL) { xmlSchemaVErrMemory(vctxt); return; } aidc->keyrefDepth = -1; aidc->def = idcDef; aidc->next = NULL; if (vctxt->aidcs == NULL) vctxt->aidcs = aidc; else { aidc->next = vctxt->aidcs; vctxt->aidcs = aidc; } /* * Save if we have keyrefs at all. */ if ((vctxt->hasKeyrefs == 0) && (idcDef->type == XML_SCHEMA_TYPE_IDC_KEYREF)) vctxt->hasKeyrefs = 1; } /** * xmlSchemaAugmentImportedIDC: * @imported: the imported schema * * Creates an augmented IDC definition for the imported schema. */ static void xmlSchemaAugmentImportedIDC(void *payload, void *data, const xmlChar *name ATTRIBUTE_UNUSED) { xmlSchemaImportPtr imported = (xmlSchemaImportPtr) payload; xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) data; if (imported->schema->idcDef != NULL) { xmlHashScan(imported->schema->idcDef, xmlSchemaAugmentIDC, vctxt); } } /** * xmlSchemaIDCNewBinding: * @idcDef: the IDC definition of this binding * * Creates a new IDC binding. * * Returns the new IDC binding, NULL on internal errors. */ static xmlSchemaPSVIIDCBindingPtr xmlSchemaIDCNewBinding(xmlSchemaIDCPtr idcDef) { xmlSchemaPSVIIDCBindingPtr ret; ret = (xmlSchemaPSVIIDCBindingPtr) xmlMalloc( sizeof(xmlSchemaPSVIIDCBinding)); if (ret == NULL) { xmlSchemaVErrMemory(NULL); return (NULL); } memset(ret, 0, sizeof(xmlSchemaPSVIIDCBinding)); ret->definition = idcDef; return (ret); } /** * xmlSchemaIDCStoreNodeTableItem: * @vctxt: the WXS validation context * @item: the IDC node table item * * The validation context is used to store IDC node table items. * They are stored to avoid copying them if IDC node-tables are merged * with corresponding parent IDC node-tables (bubbling). * * Returns 0 if succeeded, -1 on internal errors. */ static int xmlSchemaIDCStoreNodeTableItem(xmlSchemaValidCtxtPtr vctxt, xmlSchemaPSVIIDCNodePtr item) { /* * Add to global list. */ if (vctxt->idcNodes == NULL) { vctxt->idcNodes = (xmlSchemaPSVIIDCNodePtr *) xmlMalloc(20 * sizeof(xmlSchemaPSVIIDCNodePtr)); if (vctxt->idcNodes == NULL) { xmlSchemaVErrMemory(vctxt); return (-1); } vctxt->sizeIdcNodes = 20; } else if (vctxt->sizeIdcNodes <= vctxt->nbIdcNodes) { vctxt->sizeIdcNodes *= 2; vctxt->idcNodes = (xmlSchemaPSVIIDCNodePtr *) xmlRealloc(vctxt->idcNodes, vctxt->sizeIdcNodes * sizeof(xmlSchemaPSVIIDCNodePtr)); if (vctxt->idcNodes == NULL) { xmlSchemaVErrMemory(vctxt); return (-1); } } vctxt->idcNodes[vctxt->nbIdcNodes++] = item; return (0); } /** * xmlSchemaIDCStoreKey: * @vctxt: the WXS validation context * @item: the IDC key * * The validation context is used to store an IDC key. * * Returns 0 if succeeded, -1 on internal errors. */ static int xmlSchemaIDCStoreKey(xmlSchemaValidCtxtPtr vctxt, xmlSchemaPSVIIDCKeyPtr key) { /* * Add to global list. */ if (vctxt->idcKeys == NULL) { vctxt->idcKeys = (xmlSchemaPSVIIDCKeyPtr *) xmlMalloc(40 * sizeof(xmlSchemaPSVIIDCKeyPtr)); if (vctxt->idcKeys == NULL) { xmlSchemaVErrMemory(vctxt); return (-1); } vctxt->sizeIdcKeys = 40; } else if (vctxt->sizeIdcKeys <= vctxt->nbIdcKeys) { vctxt->sizeIdcKeys *= 2; vctxt->idcKeys = (xmlSchemaPSVIIDCKeyPtr *) xmlRealloc(vctxt->idcKeys, vctxt->sizeIdcKeys * sizeof(xmlSchemaPSVIIDCKeyPtr)); if (vctxt->idcKeys == NULL) { xmlSchemaVErrMemory(vctxt); return (-1); } } vctxt->idcKeys[vctxt->nbIdcKeys++] = key; return (0); } /** * xmlSchemaIDCAppendNodeTableItem: * @bind: the IDC binding * @ntItem: the node-table item * * Appends the IDC node-table item to the binding. * * Returns 0 on success and -1 on internal errors. */ static int xmlSchemaIDCAppendNodeTableItem(xmlSchemaPSVIIDCBindingPtr bind, xmlSchemaPSVIIDCNodePtr ntItem) { if (bind->nodeTable == NULL) { bind->sizeNodes = 10; bind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) xmlMalloc(10 * sizeof(xmlSchemaPSVIIDCNodePtr)); if (bind->nodeTable == NULL) { xmlSchemaVErrMemory(NULL); return(-1); } } else if (bind->sizeNodes <= bind->nbNodes) { bind->sizeNodes *= 2; bind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) xmlRealloc(bind->nodeTable, bind->sizeNodes * sizeof(xmlSchemaPSVIIDCNodePtr)); if (bind->nodeTable == NULL) { xmlSchemaVErrMemory(NULL); return(-1); } } bind->nodeTable[bind->nbNodes++] = ntItem; return(0); } /** * xmlSchemaIDCAcquireBinding: * @vctxt: the WXS validation context * @matcher: the IDC matcher * * Looks up an PSVI IDC binding, for the IDC definition and * of the given matcher. If none found, a new one is created * and added to the IDC table. * * Returns an IDC binding or NULL on internal errors. */ static xmlSchemaPSVIIDCBindingPtr xmlSchemaIDCAcquireBinding(xmlSchemaValidCtxtPtr vctxt, xmlSchemaIDCMatcherPtr matcher) { xmlSchemaNodeInfoPtr ielem; ielem = vctxt->elemInfos[matcher->depth]; if (ielem->idcTable == NULL) { ielem->idcTable = xmlSchemaIDCNewBinding(matcher->aidc->def); if (ielem->idcTable == NULL) return (NULL); return(ielem->idcTable); } else { xmlSchemaPSVIIDCBindingPtr bind = NULL; bind = ielem->idcTable; do { if (bind->definition == matcher->aidc->def) return(bind); if (bind->next == NULL) { bind->next = xmlSchemaIDCNewBinding(matcher->aidc->def); if (bind->next == NULL) return (NULL); return(bind->next); } bind = bind->next; } while (bind != NULL); } return (NULL); } static xmlSchemaItemListPtr xmlSchemaIDCAcquireTargetList(xmlSchemaValidCtxtPtr vctxt ATTRIBUTE_UNUSED, xmlSchemaIDCMatcherPtr matcher) { if (matcher->targets == NULL) matcher->targets = xmlSchemaItemListCreate(); return(matcher->targets); } /** * xmlSchemaIDCFreeKey: * @key: the IDC key * * Frees an IDC key together with its compiled value. */ static void xmlSchemaIDCFreeKey(xmlSchemaPSVIIDCKeyPtr key) { if (key->val != NULL) xmlSchemaFreeValue(key->val); xmlFree(key); } /** * xmlSchemaIDCFreeBinding: * * Frees an IDC binding. Note that the node table-items * are not freed. */ static void xmlSchemaIDCFreeBinding(xmlSchemaPSVIIDCBindingPtr bind) { if (bind->nodeTable != NULL) xmlFree(bind->nodeTable); if (bind->dupls != NULL) xmlSchemaItemListFree(bind->dupls); xmlFree(bind); } /** * xmlSchemaIDCFreeIDCTable: * @bind: the first IDC binding in the list * * Frees an IDC table, i.e. all the IDC bindings in the list. */ static void xmlSchemaIDCFreeIDCTable(xmlSchemaPSVIIDCBindingPtr bind) { xmlSchemaPSVIIDCBindingPtr prev; while (bind != NULL) { prev = bind; bind = bind->next; xmlSchemaIDCFreeBinding(prev); } } static void xmlFreeIDCHashEntry (void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { xmlIDCHashEntryPtr e = payload, n; while (e) { n = e->next; xmlFree(e); e = n; } } /** * xmlSchemaIDCFreeMatcherList: * @matcher: the first IDC matcher in the list * * Frees a list of IDC matchers. */ static void xmlSchemaIDCFreeMatcherList(xmlSchemaIDCMatcherPtr matcher) { xmlSchemaIDCMatcherPtr next; while (matcher != NULL) { next = matcher->next; if (matcher->keySeqs != NULL) { int i; for (i = 0; i < matcher->sizeKeySeqs; i++) if (matcher->keySeqs[i] != NULL) xmlFree(matcher->keySeqs[i]); xmlFree(matcher->keySeqs); } if (matcher->targets != NULL) { if (matcher->idcType == XML_SCHEMA_TYPE_IDC_KEYREF) { int i; xmlSchemaPSVIIDCNodePtr idcNode; /* * Node-table items for keyrefs are not stored globally * to the validation context, since they are not bubbled. * We need to free them here. */ for (i = 0; i < matcher->targets->nbItems; i++) { idcNode = (xmlSchemaPSVIIDCNodePtr) matcher->targets->items[i]; xmlFree(idcNode->keys); xmlFree(idcNode); } } xmlSchemaItemListFree(matcher->targets); } if (matcher->htab != NULL) xmlHashFree(matcher->htab, xmlFreeIDCHashEntry); xmlFree(matcher); matcher = next; } } /** * xmlSchemaIDCReleaseMatcherList: * @vctxt: the WXS validation context * @matcher: the first IDC matcher in the list * * Caches a list of IDC matchers for reuse. */ static void xmlSchemaIDCReleaseMatcherList(xmlSchemaValidCtxtPtr vctxt, xmlSchemaIDCMatcherPtr matcher) { xmlSchemaIDCMatcherPtr next; while (matcher != NULL) { next = matcher->next; if (matcher->keySeqs != NULL) { int i; /* * Don't free the array, but only the content. */ for (i = 0; i < matcher->sizeKeySeqs; i++) if (matcher->keySeqs[i] != NULL) { xmlFree(matcher->keySeqs[i]); matcher->keySeqs[i] = NULL; } } if (matcher->targets) { if (matcher->idcType == XML_SCHEMA_TYPE_IDC_KEYREF) { int i; xmlSchemaPSVIIDCNodePtr idcNode; /* * Node-table items for keyrefs are not stored globally * to the validation context, since they are not bubbled. * We need to free them here. */ for (i = 0; i < matcher->targets->nbItems; i++) { idcNode = (xmlSchemaPSVIIDCNodePtr) matcher->targets->items[i]; xmlFree(idcNode->keys); xmlFree(idcNode); } } xmlSchemaItemListFree(matcher->targets); matcher->targets = NULL; } if (matcher->htab != NULL) { xmlHashFree(matcher->htab, xmlFreeIDCHashEntry); matcher->htab = NULL; } matcher->next = NULL; /* * Cache the matcher. */ if (vctxt->idcMatcherCache != NULL) matcher->nextCached = vctxt->idcMatcherCache; vctxt->idcMatcherCache = matcher; matcher = next; } } /** * xmlSchemaIDCAddStateObject: * @vctxt: the WXS validation context * @matcher: the IDC matcher * @sel: the XPath information * @parent: the parent "selector" state object if any * @type: "selector" or "field" * * Creates/reuses and activates state objects for the given * XPath information; if the XPath expression consists of unions, * multiple state objects are created for every unioned expression. * * Returns 0 on success and -1 on internal errors. */ static int xmlSchemaIDCAddStateObject(xmlSchemaValidCtxtPtr vctxt, xmlSchemaIDCMatcherPtr matcher, xmlSchemaIDCSelectPtr sel, int type) { xmlSchemaIDCStateObjPtr sto; /* * Reuse the state objects from the pool. */ if (vctxt->xpathStatePool != NULL) { sto = vctxt->xpathStatePool; vctxt->xpathStatePool = sto->next; sto->next = NULL; } else { /* * Create a new state object. */ sto = (xmlSchemaIDCStateObjPtr) xmlMalloc(sizeof(xmlSchemaIDCStateObj)); if (sto == NULL) { xmlSchemaVErrMemory(NULL); return (-1); } memset(sto, 0, sizeof(xmlSchemaIDCStateObj)); } /* * Add to global list. */ if (vctxt->xpathStates != NULL) sto->next = vctxt->xpathStates; vctxt->xpathStates = sto; /* * Free the old xpath validation context. */ if (sto->xpathCtxt != NULL) xmlFreeStreamCtxt((xmlStreamCtxtPtr) sto->xpathCtxt); /* * Create a new XPath (pattern) validation context. */ sto->xpathCtxt = (void *) xmlPatternGetStreamCtxt( (xmlPatternPtr) sel->xpathComp); if (sto->xpathCtxt == NULL) { VERROR_INT("xmlSchemaIDCAddStateObject", "failed to create an XPath validation context"); return (-1); } sto->type = type; sto->depth = vctxt->depth; sto->matcher = matcher; sto->sel = sel; sto->nbHistory = 0; return (0); } /** * xmlSchemaXPathEvaluate: * @vctxt: the WXS validation context * @nodeType: the nodeType of the current node * * Evaluates all active XPath state objects. * * Returns the number of IC "field" state objects which resolved to * this node, 0 if none resolved and -1 on internal errors. */ static int xmlSchemaXPathEvaluate(xmlSchemaValidCtxtPtr vctxt, xmlElementType nodeType) { xmlSchemaIDCStateObjPtr sto, head = NULL, first; int res, resolved = 0, depth = vctxt->depth; if (vctxt->xpathStates == NULL) return (0); if (nodeType == XML_ATTRIBUTE_NODE) depth++; /* * Process all active XPath state objects. */ first = vctxt->xpathStates; sto = first; while (sto != head) { if (nodeType == XML_ELEMENT_NODE) res = xmlStreamPush((xmlStreamCtxtPtr) sto->xpathCtxt, vctxt->inode->localName, vctxt->inode->nsName); else res = xmlStreamPushAttr((xmlStreamCtxtPtr) sto->xpathCtxt, vctxt->inode->localName, vctxt->inode->nsName); if (res == -1) { VERROR_INT("xmlSchemaXPathEvaluate", "calling xmlStreamPush()"); return (-1); } if (res == 0) goto next_sto; /* * Full match. */ /* * Register a match in the state object history. */ if (sto->history == NULL) { sto->history = (int *) xmlMalloc(5 * sizeof(int)); if (sto->history == NULL) { xmlSchemaVErrMemory(NULL); return(-1); } sto->sizeHistory = 5; } else if (sto->sizeHistory <= sto->nbHistory) { sto->sizeHistory *= 2; sto->history = (int *) xmlRealloc(sto->history, sto->sizeHistory * sizeof(int)); if (sto->history == NULL) { xmlSchemaVErrMemory(NULL); return(-1); } } sto->history[sto->nbHistory++] = depth; if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) { xmlSchemaIDCSelectPtr sel; /* * Activate state objects for the IDC fields of * the IDC selector. */ sel = sto->matcher->aidc->def->fields; while (sel != NULL) { if (xmlSchemaIDCAddStateObject(vctxt, sto->matcher, sel, XPATH_STATE_OBJ_TYPE_IDC_FIELD) == -1) return (-1); sel = sel->next; } } else if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_FIELD) { /* * An IDC key node was found by the IDC field. */ /* * Notify that the character value of this node is * needed. */ if (resolved == 0) { if ((vctxt->inode->flags & XML_SCHEMA_NODE_INFO_VALUE_NEEDED) == 0) vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_VALUE_NEEDED; } resolved++; } next_sto: if (sto->next == NULL) { /* * Evaluate field state objects created on this node as well. */ head = first; sto = vctxt->xpathStates; } else sto = sto->next; } return (resolved); } static const xmlChar * xmlSchemaFormatIDCKeySequence_1(xmlSchemaValidCtxtPtr vctxt, xmlChar **buf, xmlSchemaPSVIIDCKeyPtr *seq, int count, int for_hash) { int i, res; xmlChar *value = NULL; *buf = xmlStrdup(BAD_CAST "["); for (i = 0; i < count; i++) { *buf = xmlStrcat(*buf, BAD_CAST "'"); if (!for_hash) res = xmlSchemaGetCanonValueWhtspExt(seq[i]->val, xmlSchemaGetWhiteSpaceFacetValue(seq[i]->type), &value); else { res = xmlSchemaGetCanonValueHash(seq[i]->val, &value); } if (res == 0) *buf = xmlStrcat(*buf, BAD_CAST value); else { VERROR_INT("xmlSchemaFormatIDCKeySequence", "failed to compute a canonical value"); *buf = xmlStrcat(*buf, BAD_CAST "???"); } if (i < count -1) *buf = xmlStrcat(*buf, BAD_CAST "', "); else *buf = xmlStrcat(*buf, BAD_CAST "'"); if (value != NULL) { xmlFree(value); value = NULL; } } *buf = xmlStrcat(*buf, BAD_CAST "]"); return (BAD_CAST *buf); } static const xmlChar * xmlSchemaFormatIDCKeySequence(xmlSchemaValidCtxtPtr vctxt, xmlChar **buf, xmlSchemaPSVIIDCKeyPtr *seq, int count) { return xmlSchemaFormatIDCKeySequence_1(vctxt, buf, seq, count, 0); } static const xmlChar * xmlSchemaHashKeySequence(xmlSchemaValidCtxtPtr vctxt, xmlChar **buf, xmlSchemaPSVIIDCKeyPtr *seq, int count) { return xmlSchemaFormatIDCKeySequence_1(vctxt, buf, seq, count, 1); } /** * xmlSchemaXPathPop: * @vctxt: the WXS validation context * * Pops all XPath states. * * Returns 0 on success and -1 on internal errors. */ static int xmlSchemaXPathPop(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaIDCStateObjPtr sto; int res; if (vctxt->xpathStates == NULL) return(0); sto = vctxt->xpathStates; do { res = xmlStreamPop((xmlStreamCtxtPtr) sto->xpathCtxt); if (res == -1) return (-1); sto = sto->next; } while (sto != NULL); return(0); } /** * xmlSchemaXPathProcessHistory: * @vctxt: the WXS validation context * @type: the simple/complex type of the current node if any at all * @val: the precompiled value * * Processes and pops the history items of the IDC state objects. * IDC key-sequences are validated/created on IDC bindings. * * Returns 0 on success and -1 on internal errors. */ static int xmlSchemaXPathProcessHistory(xmlSchemaValidCtxtPtr vctxt, int depth) { xmlSchemaIDCStateObjPtr sto, nextsto; int res, matchDepth; xmlSchemaPSVIIDCKeyPtr key = NULL; xmlSchemaTypePtr type = vctxt->inode->typeDef, simpleType = NULL; if (vctxt->xpathStates == NULL) return (0); sto = vctxt->xpathStates; /* * Evaluate the state objects. */ while (sto != NULL) { res = xmlStreamPop((xmlStreamCtxtPtr) sto->xpathCtxt); if (res == -1) { VERROR_INT("xmlSchemaXPathProcessHistory", "calling xmlStreamPop()"); return (-1); } if (sto->nbHistory == 0) goto deregister_check; matchDepth = sto->history[sto->nbHistory -1]; /* * Only matches at the current depth are of interest. */ if (matchDepth != depth) { sto = sto->next; continue; } if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_FIELD) { /* * NOTE: According to * http://www.w3.org/Bugs/Public/show_bug.cgi?id=2198 * ... the simple-content of complex types is also allowed. */ if (WXS_IS_COMPLEX(type)) { if (WXS_HAS_SIMPLE_CONTENT(type)) { /* * Sanity check for complex types with simple content. */ simpleType = type->contentTypeDef; if (simpleType == NULL) { VERROR_INT("xmlSchemaXPathProcessHistory", "field resolves to a CT with simple content " "but the CT is missing the ST definition"); return (-1); } } else simpleType = NULL; } else simpleType = type; if (simpleType == NULL) { xmlChar *str = NULL; /* * Not qualified if the field resolves to a node of non * simple type. */ xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_IDC, NULL, WXS_BASIC_CAST sto->matcher->aidc->def, "The XPath '%s' of a field of %s does evaluate to a node of " "non-simple type", sto->sel->xpath, xmlSchemaGetIDCDesignation(&str, sto->matcher->aidc->def)); FREE_AND_NULL(str); sto->nbHistory--; goto deregister_check; } if ((key == NULL) && (vctxt->inode->val == NULL)) { /* * Failed to provide the normalized value; maybe * the value was invalid. */ VERROR(XML_SCHEMAV_CVC_IDC, WXS_BASIC_CAST sto->matcher->aidc->def, "Warning: No precomputed value available, the value " "was either invalid or something strange happened"); sto->nbHistory--; goto deregister_check; } else { xmlSchemaIDCMatcherPtr matcher = sto->matcher; xmlSchemaPSVIIDCKeyPtr *keySeq; int pos, idx; /* * The key will be anchored on the matcher's list of * key-sequences. The position in this list is determined * by the target node's depth relative to the matcher's * depth of creation (i.e. the depth of the scope element). * * Element Depth Pos List-entries * 0 NULL * 1 NULL * 2 2 target * * * * The size of the list is only dependent on the depth of * the tree. * An entry will be NULLed in selector_leave, i.e. when * we hit the target's */ pos = sto->depth - matcher->depth; idx = sto->sel->index; /* * Create/grow the array of key-sequences. */ if (matcher->keySeqs == NULL) { if (pos > 9) matcher->sizeKeySeqs = pos * 2; else matcher->sizeKeySeqs = 10; matcher->keySeqs = (xmlSchemaPSVIIDCKeyPtr **) xmlMalloc(matcher->sizeKeySeqs * sizeof(xmlSchemaPSVIIDCKeyPtr *)); if (matcher->keySeqs == NULL) { xmlSchemaVErrMemory(NULL); return(-1); } memset(matcher->keySeqs, 0, matcher->sizeKeySeqs * sizeof(xmlSchemaPSVIIDCKeyPtr *)); } else if (pos >= matcher->sizeKeySeqs) { int i = matcher->sizeKeySeqs; matcher->sizeKeySeqs = pos * 2; matcher->keySeqs = (xmlSchemaPSVIIDCKeyPtr **) xmlRealloc(matcher->keySeqs, matcher->sizeKeySeqs * sizeof(xmlSchemaPSVIIDCKeyPtr *)); if (matcher->keySeqs == NULL) { xmlSchemaVErrMemory(NULL); return (-1); } /* * The array needs to be NULLed. * TODO: Use memset? */ for (; i < matcher->sizeKeySeqs; i++) matcher->keySeqs[i] = NULL; } /* * Get/create the key-sequence. */ keySeq = matcher->keySeqs[pos]; if (keySeq == NULL) { goto create_sequence; } else if (keySeq[idx] != NULL) { xmlChar *str = NULL; /* * cvc-identity-constraint: * 3 For each node in the `target node set` all * of the {fields}, with that node as the context * node, evaluate to either an empty node-set or * a node-set with exactly one member, which must * have a simple type. * * The key was already set; report an error. */ xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_IDC, NULL, WXS_BASIC_CAST matcher->aidc->def, "The XPath '%s' of a field of %s evaluates to a " "node-set with more than one member", sto->sel->xpath, xmlSchemaGetIDCDesignation(&str, matcher->aidc->def)); FREE_AND_NULL(str); sto->nbHistory--; goto deregister_check; } else goto create_key; create_sequence: /* * Create a key-sequence. */ keySeq = (xmlSchemaPSVIIDCKeyPtr *) xmlMalloc( matcher->aidc->def->nbFields * sizeof(xmlSchemaPSVIIDCKeyPtr)); if (keySeq == NULL) { xmlSchemaVErrMemory(NULL); return(-1); } memset(keySeq, 0, matcher->aidc->def->nbFields * sizeof(xmlSchemaPSVIIDCKeyPtr)); matcher->keySeqs[pos] = keySeq; create_key: /* * Create a key once per node only. */ if (key == NULL) { key = (xmlSchemaPSVIIDCKeyPtr) xmlMalloc( sizeof(xmlSchemaPSVIIDCKey)); if (key == NULL) { xmlSchemaVErrMemory(NULL); xmlFree(keySeq); matcher->keySeqs[pos] = NULL; return(-1); } /* * Consume the compiled value. */ key->type = simpleType; key->val = vctxt->inode->val; vctxt->inode->val = NULL; /* * Store the key in a global list. */ if (xmlSchemaIDCStoreKey(vctxt, key) == -1) { xmlSchemaIDCFreeKey(key); return (-1); } } keySeq[idx] = key; } } else if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) { xmlSchemaPSVIIDCKeyPtr **keySeq = NULL; /* xmlSchemaPSVIIDCBindingPtr bind; */ xmlSchemaPSVIIDCNodePtr ntItem; xmlSchemaIDCMatcherPtr matcher; xmlSchemaIDCPtr idc; xmlSchemaItemListPtr targets; int pos, i, j, nbKeys; /* * Here we have the following scenario: * An IDC 'selector' state object resolved to a target node, * during the time this target node was in the * ancestor-or-self axis, the 'field' state object(s) looked * out for matching nodes to create a key-sequence for this * target node. Now we are back to this target node and need * to put the key-sequence, together with the target node * itself, into the node-table of the corresponding IDC * binding. */ matcher = sto->matcher; idc = matcher->aidc->def; nbKeys = idc->nbFields; pos = depth - matcher->depth; /* * Check if the matcher has any key-sequences at all, plus * if it has a key-sequence for the current target node. */ if ((matcher->keySeqs == NULL) || (matcher->sizeKeySeqs <= pos)) { if (idc->type == XML_SCHEMA_TYPE_IDC_KEY) goto selector_key_error; else goto selector_leave; } keySeq = &(matcher->keySeqs[pos]); if (*keySeq == NULL) { if (idc->type == XML_SCHEMA_TYPE_IDC_KEY) goto selector_key_error; else goto selector_leave; } for (i = 0; i < nbKeys; i++) { if ((*keySeq)[i] == NULL) { /* * Not qualified, if not all fields did resolve. */ if (idc->type == XML_SCHEMA_TYPE_IDC_KEY) { /* * All fields of a "key" IDC must resolve. */ goto selector_key_error; } goto selector_leave; } } /* * All fields did resolve. */ /* * 4.1 If the {identity-constraint category} is unique(/key), * then no two members of the `qualified node set` have * `key-sequences` whose members are pairwise equal, as * defined by Equal in [XML Schemas: Datatypes]. * * Get the IDC binding from the matcher and check for * duplicate key-sequences. */ #if 0 bind = xmlSchemaIDCAcquireBinding(vctxt, matcher); #endif targets = xmlSchemaIDCAcquireTargetList(vctxt, matcher); if ((idc->type != XML_SCHEMA_TYPE_IDC_KEYREF) && (targets->nbItems != 0)) { xmlSchemaPSVIIDCKeyPtr ckey, bkey, *bkeySeq; xmlIDCHashEntryPtr e; res = 0; if (!matcher->htab) e = NULL; else { xmlChar *value = NULL; xmlSchemaHashKeySequence(vctxt, &value, *keySeq, nbKeys); e = xmlHashLookup(matcher->htab, value); FREE_AND_NULL(value); } /* * Compare the key-sequences, key by key. */ for (;e; e = e->next) { bkeySeq = ((xmlSchemaPSVIIDCNodePtr) targets->items[e->index])->keys; for (j = 0; j < nbKeys; j++) { ckey = (*keySeq)[j]; bkey = bkeySeq[j]; res = xmlSchemaAreValuesEqual(ckey->val, bkey->val); if (res == -1) { return (-1); } else if (res == 0) { /* * One of the keys differs, so the key-sequence * won't be equal; get out. */ break; } } if (res == 1) { /* * Duplicate key-sequence found. */ break; } } if (e) { xmlChar *str = NULL, *strB = NULL; /* * TODO: Try to report the key-sequence. */ xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_IDC, NULL, WXS_BASIC_CAST idc, "Duplicate key-sequence %s in %s", xmlSchemaFormatIDCKeySequence(vctxt, &str, (*keySeq), nbKeys), xmlSchemaGetIDCDesignation(&strB, idc)); FREE_AND_NULL(str); FREE_AND_NULL(strB); goto selector_leave; } } /* * Add a node-table item to the IDC binding. */ ntItem = (xmlSchemaPSVIIDCNodePtr) xmlMalloc( sizeof(xmlSchemaPSVIIDCNode)); if (ntItem == NULL) { xmlSchemaVErrMemory(NULL); xmlFree(*keySeq); *keySeq = NULL; return(-1); } memset(ntItem, 0, sizeof(xmlSchemaPSVIIDCNode)); /* * Store the node-table item in a global list. */ if (idc->type != XML_SCHEMA_TYPE_IDC_KEYREF) { if (xmlSchemaIDCStoreNodeTableItem(vctxt, ntItem) == -1) { xmlFree(ntItem); xmlFree(*keySeq); *keySeq = NULL; return (-1); } ntItem->nodeQNameID = -1; } else { /* * Save a cached QName for this node on the IDC node, to be * able to report it, even if the node is not saved. */ ntItem->nodeQNameID = xmlSchemaVAddNodeQName(vctxt, vctxt->inode->localName, vctxt->inode->nsName); if (ntItem->nodeQNameID == -1) { xmlFree(ntItem); xmlFree(*keySeq); *keySeq = NULL; return (-1); } } /* * Init the node-table item: Save the node, position and * consume the key-sequence. */ ntItem->node = vctxt->node; ntItem->nodeLine = vctxt->inode->nodeLine; ntItem->keys = *keySeq; *keySeq = NULL; #if 0 if (xmlSchemaIDCAppendNodeTableItem(bind, ntItem) == -1) #endif if (xmlSchemaItemListAdd(targets, ntItem) == -1) { if (idc->type == XML_SCHEMA_TYPE_IDC_KEYREF) { /* * Free the item, since keyref items won't be * put on a global list. */ xmlFree(ntItem->keys); xmlFree(ntItem); } return (-1); } if (idc->type != XML_SCHEMA_TYPE_IDC_KEYREF) { xmlChar *value = NULL; xmlIDCHashEntryPtr r, e; if (!matcher->htab) matcher->htab = xmlHashCreate(4); xmlSchemaHashKeySequence(vctxt, &value, ntItem->keys, nbKeys); e = xmlMalloc(sizeof *e); e->index = targets->nbItems - 1; r = xmlHashLookup(matcher->htab, value); if (r) { e->next = r->next; r->next = e; } else { e->next = NULL; xmlHashAddEntry(matcher->htab, value, e); } FREE_AND_NULL(value); } goto selector_leave; selector_key_error: { xmlChar *str = NULL; /* * 4.2.1 (KEY) The `target node set` and the * `qualified node set` are equal, that is, every * member of the `target node set` is also a member * of the `qualified node set` and vice versa. */ xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_IDC, NULL, WXS_BASIC_CAST idc, "Not all fields of %s evaluate to a node", xmlSchemaGetIDCDesignation(&str, idc), NULL); FREE_AND_NULL(str); } selector_leave: /* * Free the key-sequence if not added to the IDC table. */ if ((keySeq != NULL) && (*keySeq != NULL)) { xmlFree(*keySeq); *keySeq = NULL; } } /* if selector */ sto->nbHistory--; deregister_check: /* * Deregister state objects if they reach the depth of creation. */ if ((sto->nbHistory == 0) && (sto->depth == depth)) { if (vctxt->xpathStates != sto) { VERROR_INT("xmlSchemaXPathProcessHistory", "The state object to be removed is not the first " "in the list"); } nextsto = sto->next; /* * Unlink from the list of active XPath state objects. */ vctxt->xpathStates = sto->next; sto->next = vctxt->xpathStatePool; /* * Link it to the pool of reusable state objects. */ vctxt->xpathStatePool = sto; sto = nextsto; } else sto = sto->next; } /* while (sto != NULL) */ return (0); } /** * xmlSchemaIDCRegisterMatchers: * @vctxt: the WXS validation context * @elemDecl: the element declaration * * Creates helper objects to evaluate IDC selectors/fields * successively. * * Returns 0 if OK and -1 on internal errors. */ static int xmlSchemaIDCRegisterMatchers(xmlSchemaValidCtxtPtr vctxt, xmlSchemaElementPtr elemDecl) { xmlSchemaIDCMatcherPtr matcher, last = NULL; xmlSchemaIDCPtr idc, refIdc; xmlSchemaIDCAugPtr aidc; idc = (xmlSchemaIDCPtr) elemDecl->idcs; if (idc == NULL) return (0); if (vctxt->inode->idcMatchers != NULL) { VERROR_INT("xmlSchemaIDCRegisterMatchers", "The chain of IDC matchers is expected to be empty"); return (-1); } do { if (idc->type == XML_SCHEMA_TYPE_IDC_KEYREF) { /* * Since IDCs bubbles are expensive we need to know the * depth at which the bubbles should stop; this will be * the depth of the top-most keyref IDC. If no keyref * references a key/unique IDC, the keyrefDepth will * be -1, indicating that no bubbles are needed. */ refIdc = (xmlSchemaIDCPtr) idc->ref->item; if (refIdc != NULL) { /* * Remember that we have keyrefs on this node. */ vctxt->inode->hasKeyrefs = 1; /* * Lookup the referenced augmented IDC info. */ aidc = vctxt->aidcs; while (aidc != NULL) { if (aidc->def == refIdc) break; aidc = aidc->next; } if (aidc == NULL) { VERROR_INT("xmlSchemaIDCRegisterMatchers", "Could not find an augmented IDC item for an IDC " "definition"); return (-1); } if ((aidc->keyrefDepth == -1) || (vctxt->depth < aidc->keyrefDepth)) aidc->keyrefDepth = vctxt->depth; } } /* * Lookup the augmented IDC item for the IDC definition. */ aidc = vctxt->aidcs; while (aidc != NULL) { if (aidc->def == idc) break; aidc = aidc->next; } if (aidc == NULL) { VERROR_INT("xmlSchemaIDCRegisterMatchers", "Could not find an augmented IDC item for an IDC definition"); return (-1); } /* * Create an IDC matcher for every IDC definition. */ if (vctxt->idcMatcherCache != NULL) { /* * Reuse a cached matcher. */ matcher = vctxt->idcMatcherCache; vctxt->idcMatcherCache = matcher->nextCached; matcher->nextCached = NULL; } else { matcher = (xmlSchemaIDCMatcherPtr) xmlMalloc(sizeof(xmlSchemaIDCMatcher)); if (matcher == NULL) { xmlSchemaVErrMemory(vctxt); return (-1); } memset(matcher, 0, sizeof(xmlSchemaIDCMatcher)); } if (last == NULL) vctxt->inode->idcMatchers = matcher; else last->next = matcher; last = matcher; matcher->type = IDC_MATCHER; matcher->depth = vctxt->depth; matcher->aidc = aidc; matcher->idcType = aidc->def->type; /* * Init the automaton state object. */ if (xmlSchemaIDCAddStateObject(vctxt, matcher, idc->selector, XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) == -1) return (-1); idc = idc->next; } while (idc != NULL); return (0); } static int xmlSchemaIDCFillNodeTables(xmlSchemaValidCtxtPtr vctxt, xmlSchemaNodeInfoPtr ielem) { xmlSchemaPSVIIDCBindingPtr bind; int res, i, j, k, nbTargets, nbFields, nbDupls, nbNodeTable; xmlSchemaPSVIIDCKeyPtr *keys, *ntkeys; xmlSchemaPSVIIDCNodePtr *targets, *dupls; xmlSchemaIDCMatcherPtr matcher = ielem->idcMatchers; /* vctxt->createIDCNodeTables */ while (matcher != NULL) { /* * Skip keyref IDCs and empty IDC target-lists. */ if ((matcher->aidc->def->type == XML_SCHEMA_TYPE_IDC_KEYREF) || WXS_ILIST_IS_EMPTY(matcher->targets)) { matcher = matcher->next; continue; } /* * If we _want_ the IDC node-table to be created in any case * then do so. Otherwise create them only if keyrefs need them. */ if ((! vctxt->createIDCNodeTables) && ((matcher->aidc->keyrefDepth == -1) || (matcher->aidc->keyrefDepth > vctxt->depth))) { matcher = matcher->next; continue; } /* * Get/create the IDC binding on this element for the IDC definition. */ bind = xmlSchemaIDCAcquireBinding(vctxt, matcher); if (bind == NULL) goto internal_error; if (! WXS_ILIST_IS_EMPTY(bind->dupls)) { dupls = (xmlSchemaPSVIIDCNodePtr *) bind->dupls->items; nbDupls = bind->dupls->nbItems; } else { dupls = NULL; nbDupls = 0; } if (bind->nodeTable != NULL) { nbNodeTable = bind->nbNodes; } else { nbNodeTable = 0; } if ((nbNodeTable == 0) && (nbDupls == 0)) { /* * Transfer all IDC target-nodes to the IDC node-table. */ bind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) matcher->targets->items; bind->sizeNodes = matcher->targets->sizeItems; bind->nbNodes = matcher->targets->nbItems; matcher->targets->items = NULL; matcher->targets->sizeItems = 0; matcher->targets->nbItems = 0; if (matcher->htab) { xmlHashFree(matcher->htab, xmlFreeIDCHashEntry); matcher->htab = NULL; } } else { /* * Compare the key-sequences and add to the IDC node-table. */ nbTargets = matcher->targets->nbItems; targets = (xmlSchemaPSVIIDCNodePtr *) matcher->targets->items; nbFields = matcher->aidc->def->nbFields; i = 0; do { keys = targets[i]->keys; if (nbDupls) { /* * Search in already found duplicates first. */ j = 0; do { if (nbFields == 1) { res = xmlSchemaAreValuesEqual(keys[0]->val, dupls[j]->keys[0]->val); if (res == -1) goto internal_error; if (res == 1) { /* * Equal key-sequence. */ goto next_target; } } else { res = 0; ntkeys = dupls[j]->keys; for (k = 0; k < nbFields; k++) { res = xmlSchemaAreValuesEqual(keys[k]->val, ntkeys[k]->val); if (res == -1) goto internal_error; if (res == 0) { /* * One of the keys differs. */ break; } } if (res == 1) { /* * Equal key-sequence found. */ goto next_target; } } j++; } while (j < nbDupls); } if (nbNodeTable) { j = 0; do { if (nbFields == 1) { res = xmlSchemaAreValuesEqual(keys[0]->val, bind->nodeTable[j]->keys[0]->val); if (res == -1) goto internal_error; if (res == 0) { /* * The key-sequence differs. */ goto next_node_table_entry; } } else { res = 0; ntkeys = bind->nodeTable[j]->keys; for (k = 0; k < nbFields; k++) { res = xmlSchemaAreValuesEqual(keys[k]->val, ntkeys[k]->val); if (res == -1) goto internal_error; if (res == 0) { /* * One of the keys differs. */ goto next_node_table_entry; } } } /* * Add the duplicate to the list of duplicates. */ if (bind->dupls == NULL) { bind->dupls = xmlSchemaItemListCreate(); if (bind->dupls == NULL) goto internal_error; } if (xmlSchemaItemListAdd(bind->dupls, bind->nodeTable[j]) == -1) goto internal_error; /* * Remove the duplicate entry from the IDC node-table. */ bind->nodeTable[j] = bind->nodeTable[bind->nbNodes -1]; bind->nbNodes--; goto next_target; next_node_table_entry: j++; } while (j < nbNodeTable); } /* * If everything is fine, then add the IDC target-node to * the IDC node-table. */ if (xmlSchemaIDCAppendNodeTableItem(bind, targets[i]) == -1) goto internal_error; next_target: i++; } while (i < nbTargets); } matcher = matcher->next; } return(0); internal_error: return(-1); } /** * xmlSchemaBubbleIDCNodeTables: * @depth: the current tree depth * * Merges IDC bindings of an element at @depth into the corresponding IDC * bindings of its parent element. If a duplicate note-table entry is found, * both, the parent node-table entry and child entry are discarded from the * node-table of the parent. * * Returns 0 if OK and -1 on internal errors. */ static int xmlSchemaBubbleIDCNodeTables(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaPSVIIDCBindingPtr bind; /* IDC bindings of the current node. */ xmlSchemaPSVIIDCBindingPtr *parTable, parBind = NULL; /* parent IDC bindings. */ xmlSchemaPSVIIDCNodePtr node, parNode = NULL, *dupls, *parNodes; /* node-table entries. */ xmlSchemaIDCAugPtr aidc; int i, j, k, ret = 0, nbFields, oldNum, oldDupls; bind = vctxt->inode->idcTable; if (bind == NULL) { /* Fine, no table, no bubbles. */ return (0); } parTable = &(vctxt->elemInfos[vctxt->depth -1]->idcTable); /* * Walk all bindings; create new or add to existing bindings. * Remove duplicate key-sequences. */ while (bind != NULL) { if ((bind->nbNodes == 0) && WXS_ILIST_IS_EMPTY(bind->dupls)) goto next_binding; /* * Check if the key/unique IDC table needs to be bubbled. */ if (! vctxt->createIDCNodeTables) { aidc = vctxt->aidcs; do { if (aidc->def == bind->definition) { if ((aidc->keyrefDepth == -1) || (aidc->keyrefDepth >= vctxt->depth)) { goto next_binding; } break; } aidc = aidc->next; } while (aidc != NULL); } if (parTable != NULL) parBind = *parTable; /* * Search a matching parent binding for the * IDC definition. */ while (parBind != NULL) { if (parBind->definition == bind->definition) break; parBind = parBind->next; } if (parBind != NULL) { /* * Compare every node-table entry of the child node, * i.e. the key-sequence within, ... */ oldNum = parBind->nbNodes; /* Skip newly added items. */ if (! WXS_ILIST_IS_EMPTY(parBind->dupls)) { oldDupls = parBind->dupls->nbItems; dupls = (xmlSchemaPSVIIDCNodePtr *) parBind->dupls->items; } else { dupls = NULL; oldDupls = 0; } parNodes = parBind->nodeTable; nbFields = bind->definition->nbFields; for (i = 0; i < bind->nbNodes; i++) { node = bind->nodeTable[i]; if (node == NULL) continue; /* * ...with every key-sequence of the parent node, already * evaluated to be a duplicate key-sequence. */ if (oldDupls) { j = 0; while (j < oldDupls) { if (nbFields == 1) { ret = xmlSchemaAreValuesEqual( node->keys[0]->val, dupls[j]->keys[0]->val); if (ret == -1) goto internal_error; if (ret == 0) { j++; continue; } } else { parNode = dupls[j]; for (k = 0; k < nbFields; k++) { ret = xmlSchemaAreValuesEqual( node->keys[k]->val, parNode->keys[k]->val); if (ret == -1) goto internal_error; if (ret == 0) break; } } if (ret == 1) /* Duplicate found. */ break; j++; } if (j != oldDupls) { /* Duplicate found. Skip this entry. */ continue; } } /* * ... and with every key-sequence of the parent node. */ if (oldNum) { j = 0; while (j < oldNum) { parNode = parNodes[j]; if (nbFields == 1) { ret = xmlSchemaAreValuesEqual( node->keys[0]->val, parNode->keys[0]->val); if (ret == -1) goto internal_error; if (ret == 0) { j++; continue; } } else { for (k = 0; k < nbFields; k++) { ret = xmlSchemaAreValuesEqual( node->keys[k]->val, parNode->keys[k]->val); if (ret == -1) goto internal_error; if (ret == 0) break; } } if (ret == 1) /* Duplicate found. */ break; j++; } if (j != oldNum) { /* * Handle duplicates. Move the duplicate in * the parent's node-table to the list of * duplicates. */ oldNum--; parBind->nbNodes--; /* * Move last old item to pos of duplicate. */ parNodes[j] = parNodes[oldNum]; if (parBind->nbNodes != oldNum) { /* * If new items exist, move last new item to * last of old items. */ parNodes[oldNum] = parNodes[parBind->nbNodes]; } if (parBind->dupls == NULL) { parBind->dupls = xmlSchemaItemListCreate(); if (parBind->dupls == NULL) goto internal_error; } xmlSchemaItemListAdd(parBind->dupls, parNode); } else { /* * Add the node-table entry (node and key-sequence) of * the child node to the node table of the parent node. */ if (parBind->nodeTable == NULL) { parBind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) xmlMalloc(10 * sizeof(xmlSchemaPSVIIDCNodePtr)); if (parBind->nodeTable == NULL) { xmlSchemaVErrMemory(NULL); goto internal_error; } parBind->sizeNodes = 1; } else if (parBind->nbNodes >= parBind->sizeNodes) { parBind->sizeNodes *= 2; parBind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) xmlRealloc(parBind->nodeTable, parBind->sizeNodes * sizeof(xmlSchemaPSVIIDCNodePtr)); if (parBind->nodeTable == NULL) { xmlSchemaVErrMemory(NULL); goto internal_error; } } parNodes = parBind->nodeTable; /* * Append the new node-table entry to the 'new node-table * entries' section. */ parNodes[parBind->nbNodes++] = node; } } } } else { /* * No binding for the IDC was found: create a new one and * copy all node-tables. */ parBind = xmlSchemaIDCNewBinding(bind->definition); if (parBind == NULL) goto internal_error; /* * TODO: Hmm, how to optimize the initial number of * allocated entries? */ if (bind->nbNodes != 0) { /* * Add all IDC node-table entries. */ if (! vctxt->psviExposeIDCNodeTables) { /* * Just move the entries. * NOTE: this is quite save here, since * all the keyref lookups have already been * performed. */ parBind->nodeTable = bind->nodeTable; bind->nodeTable = NULL; parBind->sizeNodes = bind->sizeNodes; bind->sizeNodes = 0; parBind->nbNodes = bind->nbNodes; bind->nbNodes = 0; } else { /* * Copy the entries. */ parBind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) xmlMalloc(bind->nbNodes * sizeof(xmlSchemaPSVIIDCNodePtr)); if (parBind->nodeTable == NULL) { xmlSchemaVErrMemory(NULL); xmlSchemaIDCFreeBinding(parBind); goto internal_error; } parBind->sizeNodes = bind->nbNodes; parBind->nbNodes = bind->nbNodes; memcpy(parBind->nodeTable, bind->nodeTable, bind->nbNodes * sizeof(xmlSchemaPSVIIDCNodePtr)); } } if (bind->dupls) { /* * Move the duplicates. */ if (parBind->dupls != NULL) xmlSchemaItemListFree(parBind->dupls); parBind->dupls = bind->dupls; bind->dupls = NULL; } if (parTable != NULL) { if (*parTable == NULL) *parTable = parBind; else { parBind->next = *parTable; *parTable = parBind; } } } next_binding: bind = bind->next; } return (0); internal_error: return(-1); } /** * xmlSchemaCheckCVCIDCKeyRef: * @vctxt: the WXS validation context * @elemDecl: the element declaration * * Check the cvc-idc-keyref constraints. */ static int xmlSchemaCheckCVCIDCKeyRef(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaIDCMatcherPtr matcher; xmlSchemaPSVIIDCBindingPtr bind; matcher = vctxt->inode->idcMatchers; /* * Find a keyref. */ while (matcher != NULL) { if ((matcher->idcType == XML_SCHEMA_TYPE_IDC_KEYREF) && matcher->targets && matcher->targets->nbItems) { int i, j, k, res, nbFields, hasDupls; xmlSchemaPSVIIDCKeyPtr *refKeys, *keys; xmlSchemaPSVIIDCNodePtr refNode = NULL; xmlHashTablePtr table = NULL; nbFields = matcher->aidc->def->nbFields; /* * Find the IDC node-table for the referenced IDC key/unique. */ bind = vctxt->inode->idcTable; while (bind != NULL) { if ((xmlSchemaIDCPtr) matcher->aidc->def->ref->item == bind->definition) break; bind = bind->next; } hasDupls = (bind && bind->dupls && bind->dupls->nbItems) ? 1 : 0; /* * Search for a matching key-sequences. */ if (bind) { table = xmlHashCreate(bind->nbNodes * 2); for (j = 0; j < bind->nbNodes; j++) { xmlChar *value; xmlIDCHashEntryPtr r, e; keys = bind->nodeTable[j]->keys; xmlSchemaHashKeySequence(vctxt, &value, keys, nbFields); e = xmlMalloc(sizeof *e); e->index = j; r = xmlHashLookup(table, value); if (r) { e->next = r->next; r->next = e; } else { e->next = NULL; xmlHashAddEntry(table, value, e); } FREE_AND_NULL(value); } } for (i = 0; i < matcher->targets->nbItems; i++) { res = 0; refNode = matcher->targets->items[i]; if (bind != NULL) { xmlChar *value; xmlIDCHashEntryPtr e; refKeys = refNode->keys; xmlSchemaHashKeySequence(vctxt, &value, refKeys, nbFields); e = xmlHashLookup(table, value); FREE_AND_NULL(value); res = 0; for (;e; e = e->next) { keys = bind->nodeTable[e->index]->keys; for (k = 0; k < nbFields; k++) { res = xmlSchemaAreValuesEqual(keys[k]->val, refKeys[k]->val); if (res == 0) break; else if (res == -1) { return (-1); } } if (res == 1) { /* * Match found. */ break; } } if ((res == 0) && hasDupls) { /* * Search in duplicates */ for (j = 0; j < bind->dupls->nbItems; j++) { keys = ((xmlSchemaPSVIIDCNodePtr) bind->dupls->items[j])->keys; for (k = 0; k < nbFields; k++) { res = xmlSchemaAreValuesEqual(keys[k]->val, refKeys[k]->val); if (res == 0) break; else if (res == -1) { return (-1); } } if (res == 1) { /* * Match in duplicates found. */ xmlChar *str = NULL, *strB = NULL; xmlSchemaKeyrefErr(vctxt, XML_SCHEMAV_CVC_IDC, refNode, (xmlSchemaTypePtr) matcher->aidc->def, "More than one match found for " "key-sequence %s of keyref '%s'", xmlSchemaFormatIDCKeySequence(vctxt, &str, refNode->keys, nbFields), xmlSchemaGetComponentQName(&strB, matcher->aidc->def)); FREE_AND_NULL(str); FREE_AND_NULL(strB); break; } } } } if (res == 0) { xmlChar *str = NULL, *strB = NULL; xmlSchemaKeyrefErr(vctxt, XML_SCHEMAV_CVC_IDC, refNode, (xmlSchemaTypePtr) matcher->aidc->def, "No match found for key-sequence %s of keyref '%s'", xmlSchemaFormatIDCKeySequence(vctxt, &str, refNode->keys, nbFields), xmlSchemaGetComponentQName(&strB, matcher->aidc->def)); FREE_AND_NULL(str); FREE_AND_NULL(strB); } } if (table) { xmlHashFree(table, xmlFreeIDCHashEntry); } } matcher = matcher->next; } /* TODO: Return an error if any error encountered. */ return (0); } /************************************************************************ * * * XML Reader validation code * * * ************************************************************************/ static xmlSchemaAttrInfoPtr xmlSchemaGetFreshAttrInfo(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaAttrInfoPtr iattr; /* * Grow/create list of attribute infos. */ if (vctxt->attrInfos == NULL) { vctxt->attrInfos = (xmlSchemaAttrInfoPtr *) xmlMalloc(sizeof(xmlSchemaAttrInfoPtr)); vctxt->sizeAttrInfos = 1; if (vctxt->attrInfos == NULL) { xmlSchemaVErrMemory(vctxt); return (NULL); } } else if (vctxt->sizeAttrInfos <= vctxt->nbAttrInfos) { vctxt->sizeAttrInfos++; vctxt->attrInfos = (xmlSchemaAttrInfoPtr *) xmlRealloc(vctxt->attrInfos, vctxt->sizeAttrInfos * sizeof(xmlSchemaAttrInfoPtr)); if (vctxt->attrInfos == NULL) { xmlSchemaVErrMemory(vctxt); return (NULL); } } else { iattr = vctxt->attrInfos[vctxt->nbAttrInfos++]; if (iattr->localName != NULL) { VERROR_INT("xmlSchemaGetFreshAttrInfo", "attr info not cleared"); return (NULL); } iattr->nodeType = XML_ATTRIBUTE_NODE; return (iattr); } /* * Create an attribute info. */ iattr = (xmlSchemaAttrInfoPtr) xmlMalloc(sizeof(xmlSchemaAttrInfo)); if (iattr == NULL) { xmlSchemaVErrMemory(vctxt); return (NULL); } memset(iattr, 0, sizeof(xmlSchemaAttrInfo)); iattr->nodeType = XML_ATTRIBUTE_NODE; vctxt->attrInfos[vctxt->nbAttrInfos++] = iattr; return (iattr); } static int xmlSchemaValidatorPushAttribute(xmlSchemaValidCtxtPtr vctxt, xmlNodePtr attrNode, int nodeLine, const xmlChar *localName, const xmlChar *nsName, int ownedNames, xmlChar *value, int ownedValue) { xmlSchemaAttrInfoPtr attr; attr = xmlSchemaGetFreshAttrInfo(vctxt); if (attr == NULL) { VERROR_INT("xmlSchemaPushAttribute", "calling xmlSchemaGetFreshAttrInfo()"); return (-1); } attr->node = attrNode; attr->nodeLine = nodeLine; attr->state = XML_SCHEMAS_ATTR_UNKNOWN; attr->localName = localName; attr->nsName = nsName; if (ownedNames) attr->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES; /* * Evaluate if it's an XSI attribute. */ if (nsName != NULL) { if (xmlStrEqual(localName, BAD_CAST "nil")) { if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_NIL; } } else if (xmlStrEqual(localName, BAD_CAST "type")) { if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_TYPE; } } else if (xmlStrEqual(localName, BAD_CAST "schemaLocation")) { if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC; } } else if (xmlStrEqual(localName, BAD_CAST "noNamespaceSchemaLocation")) { if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_NO_NS_SCHEMA_LOC; } } else if (xmlStrEqual(attr->nsName, xmlNamespaceNs)) { attr->metaType = XML_SCHEMA_ATTR_INFO_META_XMLNS; } } attr->value = value; if (ownedValue) attr->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; if (attr->metaType != 0) attr->state = XML_SCHEMAS_ATTR_META; return (0); } /** * xmlSchemaClearElemInfo: * @vctxt: the WXS validation context * @ielem: the element information item */ static void xmlSchemaClearElemInfo(xmlSchemaValidCtxtPtr vctxt, xmlSchemaNodeInfoPtr ielem) { ielem->hasKeyrefs = 0; ielem->appliedXPath = 0; if (ielem->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES) { FREE_AND_NULL(ielem->localName); FREE_AND_NULL(ielem->nsName); } else { ielem->localName = NULL; ielem->nsName = NULL; } if (ielem->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES) { FREE_AND_NULL(ielem->value); } else { ielem->value = NULL; } if (ielem->val != NULL) { /* * PSVI TODO: Be careful not to free it when the value is * exposed via PSVI. */ xmlSchemaFreeValue(ielem->val); ielem->val = NULL; } if (ielem->idcMatchers != NULL) { /* * REVISIT OPTIMIZE TODO: Use a pool of IDC matchers. * Does it work? */ xmlSchemaIDCReleaseMatcherList(vctxt, ielem->idcMatchers); #if 0 xmlSchemaIDCFreeMatcherList(ielem->idcMatchers); #endif ielem->idcMatchers = NULL; } if (ielem->idcTable != NULL) { /* * OPTIMIZE TODO: Use a pool of IDC tables??. */ xmlSchemaIDCFreeIDCTable(ielem->idcTable); ielem->idcTable = NULL; } if (ielem->regexCtxt != NULL) { xmlRegFreeExecCtxt(ielem->regexCtxt); ielem->regexCtxt = NULL; } if (ielem->nsBindings != NULL) { xmlFree((xmlChar **)ielem->nsBindings); ielem->nsBindings = NULL; ielem->nbNsBindings = 0; ielem->sizeNsBindings = 0; } } /** * xmlSchemaGetFreshElemInfo: * @vctxt: the schema validation context * * Creates/reuses and initializes the element info item for * the current tree depth. * * Returns the element info item or NULL on API or internal errors. */ static xmlSchemaNodeInfoPtr xmlSchemaGetFreshElemInfo(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaNodeInfoPtr info = NULL; if (vctxt->depth > vctxt->sizeElemInfos) { VERROR_INT("xmlSchemaGetFreshElemInfo", "inconsistent depth encountered"); return (NULL); } if (vctxt->elemInfos == NULL) { vctxt->elemInfos = (xmlSchemaNodeInfoPtr *) xmlMalloc(10 * sizeof(xmlSchemaNodeInfoPtr)); if (vctxt->elemInfos == NULL) { xmlSchemaVErrMemory(vctxt); return (NULL); } memset(vctxt->elemInfos, 0, 10 * sizeof(xmlSchemaNodeInfoPtr)); vctxt->sizeElemInfos = 10; } else if (vctxt->sizeElemInfos <= vctxt->depth) { int i = vctxt->sizeElemInfos; vctxt->sizeElemInfos *= 2; vctxt->elemInfos = (xmlSchemaNodeInfoPtr *) xmlRealloc(vctxt->elemInfos, vctxt->sizeElemInfos * sizeof(xmlSchemaNodeInfoPtr)); if (vctxt->elemInfos == NULL) { xmlSchemaVErrMemory(vctxt); return (NULL); } /* * We need the new memory to be NULLed. * TODO: Use memset instead? */ for (; i < vctxt->sizeElemInfos; i++) vctxt->elemInfos[i] = NULL; } else info = vctxt->elemInfos[vctxt->depth]; if (info == NULL) { info = (xmlSchemaNodeInfoPtr) xmlMalloc(sizeof(xmlSchemaNodeInfo)); if (info == NULL) { xmlSchemaVErrMemory(vctxt); return (NULL); } vctxt->elemInfos[vctxt->depth] = info; } else { if (info->localName != NULL) { VERROR_INT("xmlSchemaGetFreshElemInfo", "elem info has not been cleared"); return (NULL); } } memset(info, 0, sizeof(xmlSchemaNodeInfo)); info->nodeType = XML_ELEMENT_NODE; info->depth = vctxt->depth; return (info); } #define ACTIVATE_ATTRIBUTE(item) vctxt->inode = (xmlSchemaNodeInfoPtr) item; #define ACTIVATE_ELEM vctxt->inode = vctxt->elemInfos[vctxt->depth]; #define ACTIVATE_PARENT_ELEM vctxt->inode = vctxt->elemInfos[vctxt->depth -1]; static int xmlSchemaValidateFacets(xmlSchemaAbstractCtxtPtr actxt, xmlNodePtr node, xmlSchemaTypePtr type, xmlSchemaValType valType, const xmlChar * value, xmlSchemaValPtr val, unsigned long length, int fireErrors) { int ret, error = 0, found; xmlSchemaTypePtr tmpType; xmlSchemaFacetLinkPtr facetLink; xmlSchemaFacetPtr facet; unsigned long len = 0; xmlSchemaWhitespaceValueType ws; /* * In Libxml2, derived built-in types have currently no explicit facets. */ if (type->type == XML_SCHEMA_TYPE_BASIC) return (0); /* * NOTE: Do not jump away, if the facetSet of the given type is * empty: until now, "pattern" and "enumeration" facets of the * *base types* need to be checked as well. */ if (type->facetSet == NULL) goto pattern_and_enum; if (! WXS_IS_ATOMIC(type)) { if (WXS_IS_LIST(type)) goto WXS_IS_LIST; else goto pattern_and_enum; } /* * Whitespace handling is only of importance for string-based * types. */ tmpType = xmlSchemaGetPrimitiveType(type); if ((tmpType->builtInType == XML_SCHEMAS_STRING) || WXS_IS_ANY_SIMPLE_TYPE(tmpType)) { ws = xmlSchemaGetWhiteSpaceFacetValue(type); } else ws = XML_SCHEMA_WHITESPACE_COLLAPSE; /* * If the value was not computed (for string or * anySimpleType based types), then use the provided * type. */ if (val != NULL) valType = xmlSchemaGetValType(val); ret = 0; for (facetLink = type->facetSet; facetLink != NULL; facetLink = facetLink->next) { /* * Skip the pattern "whiteSpace": it is used to * format the character content beforehand. */ switch (facetLink->facet->type) { case XML_SCHEMA_FACET_WHITESPACE: case XML_SCHEMA_FACET_PATTERN: case XML_SCHEMA_FACET_ENUMERATION: continue; case XML_SCHEMA_FACET_LENGTH: case XML_SCHEMA_FACET_MINLENGTH: case XML_SCHEMA_FACET_MAXLENGTH: ret = xmlSchemaValidateLengthFacetWhtsp(facetLink->facet, valType, value, val, &len, ws); break; default: ret = xmlSchemaValidateFacetWhtsp(facetLink->facet, ws, valType, value, val, ws); break; } if (ret < 0) { AERROR_INT("xmlSchemaValidateFacets", "validating against a atomic type facet"); return (-1); } else if (ret > 0) { if (fireErrors) xmlSchemaFacetErr(actxt, ret, node, value, len, type, facetLink->facet, NULL, NULL, NULL); else return (ret); if (error == 0) error = ret; } ret = 0; } WXS_IS_LIST: if (! WXS_IS_LIST(type)) goto pattern_and_enum; /* * "length", "minLength" and "maxLength" of list types. */ ret = 0; for (facetLink = type->facetSet; facetLink != NULL; facetLink = facetLink->next) { switch (facetLink->facet->type) { case XML_SCHEMA_FACET_LENGTH: case XML_SCHEMA_FACET_MINLENGTH: case XML_SCHEMA_FACET_MAXLENGTH: ret = xmlSchemaValidateListSimpleTypeFacet(facetLink->facet, value, length, NULL); break; default: continue; } if (ret < 0) { AERROR_INT("xmlSchemaValidateFacets", "validating against a list type facet"); return (-1); } else if (ret > 0) { if (fireErrors) xmlSchemaFacetErr(actxt, ret, node, value, length, type, facetLink->facet, NULL, NULL, NULL); else return (ret); if (error == 0) error = ret; } ret = 0; } pattern_and_enum: found = 0; /* * Process enumerations. Facet values are in the value space * of the defining type's base type. This seems to be a bug in the * XML Schema 1.0 spec. Use the whitespace type of the base type. * Only the first set of enumerations in the ancestor-or-self axis * is used for validation. */ ret = 0; tmpType = type; do { for (facet = tmpType->facets; facet != NULL; facet = facet->next) { if (facet->type != XML_SCHEMA_FACET_ENUMERATION) continue; found = 1; ret = xmlSchemaAreValuesEqual(facet->val, val); if (ret == 1) break; else if (ret < 0) { AERROR_INT("xmlSchemaValidateFacets", "validating against an enumeration facet"); return (-1); } } if (ret != 0) break; /* * Break on the first set of enumerations. Any additional * enumerations which might be existent on the ancestors * of the current type are restricted by this set; thus * *must* *not* be taken into account. */ if (found) break; tmpType = tmpType->baseType; } while ((tmpType != NULL) && (tmpType->type != XML_SCHEMA_TYPE_BASIC)); if (found && (ret == 0)) { ret = XML_SCHEMAV_CVC_ENUMERATION_VALID; if (fireErrors) { xmlSchemaFacetErr(actxt, ret, node, value, 0, type, NULL, NULL, NULL, NULL); } else return (ret); if (error == 0) error = ret; } /* * Process patters. Pattern facets are ORed at type level * and ANDed if derived. Walk the base type axis. */ tmpType = type; facet = NULL; do { found = 0; for (facetLink = tmpType->facetSet; facetLink != NULL; facetLink = facetLink->next) { if (facetLink->facet->type != XML_SCHEMA_FACET_PATTERN) continue; found = 1; /* * NOTE that for patterns, @value needs to be the * normalized value. */ ret = xmlRegexpExec(facetLink->facet->regexp, value); if (ret == 1) break; else if (ret < 0) { AERROR_INT("xmlSchemaValidateFacets", "validating against a pattern facet"); return (-1); } else { /* * Save the last non-validating facet. */ facet = facetLink->facet; } } if (found && (ret != 1)) { ret = XML_SCHEMAV_CVC_PATTERN_VALID; if (fireErrors) { xmlSchemaFacetErr(actxt, ret, node, value, 0, type, facet, NULL, NULL, NULL); } else return (ret); if (error == 0) error = ret; break; } tmpType = tmpType->baseType; } while ((tmpType != NULL) && (tmpType->type != XML_SCHEMA_TYPE_BASIC)); return (error); } static xmlChar * xmlSchemaNormalizeValue(xmlSchemaTypePtr type, const xmlChar *value) { switch (xmlSchemaGetWhiteSpaceFacetValue(type)) { case XML_SCHEMA_WHITESPACE_COLLAPSE: return (xmlSchemaCollapseString(value)); case XML_SCHEMA_WHITESPACE_REPLACE: return (xmlSchemaWhiteSpaceReplace(value)); default: return (NULL); } } static int xmlSchemaValidateQName(xmlSchemaValidCtxtPtr vctxt, const xmlChar *value, xmlSchemaValPtr *val, int valNeeded) { int ret; xmlChar *stripped; const xmlChar *nsName; xmlChar *local, *prefix = NULL; ret = xmlValidateQName(value, 1); if (ret != 0) { if (ret == -1) { VERROR_INT("xmlSchemaValidateQName", "calling xmlValidateQName()"); return (-1); } return( XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1); } /* * NOTE: xmlSplitQName2 will always return a duplicated * strings. */ /* TODO: Export and use xmlSchemaStrip instead */ stripped = xmlSchemaCollapseString(value); local = xmlSplitQName2(stripped ? stripped : value, &prefix); xmlFree(stripped); if (local == NULL) local = xmlStrdup(value); /* * OPTIMIZE TODO: Use flags for: * - is there any namespace binding? * - is there a default namespace? */ nsName = xmlSchemaLookupNamespace(vctxt, prefix); if (prefix != NULL) { xmlFree(prefix); /* * A namespace must be found if the prefix is * NOT NULL. */ if (nsName == NULL) { ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; xmlSchemaCustomErr(ACTXT_CAST vctxt, ret, NULL, WXS_BASIC_CAST xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), "The QName value '%s' has no " "corresponding namespace declaration in " "scope", value, NULL); if (local != NULL) xmlFree(local); return (ret); } } if (valNeeded && val) { if (nsName != NULL) *val = xmlSchemaNewQNameValue( BAD_CAST xmlStrdup(nsName), BAD_CAST local); else *val = xmlSchemaNewQNameValue(NULL, BAD_CAST local); } else xmlFree(local); return (0); } /* * cvc-simple-type */ static int xmlSchemaVCheckCVCSimpleType(xmlSchemaAbstractCtxtPtr actxt, xmlNodePtr node, xmlSchemaTypePtr type, const xmlChar *value, xmlSchemaValPtr *retVal, int fireErrors, int normalize, int isNormalized) { int ret = 0, valNeeded = (retVal) ? 1 : 0; xmlSchemaValPtr val = NULL; /* xmlSchemaWhitespaceValueType ws; */ xmlChar *normValue = NULL; #define NORMALIZE(atype) \ if ((! isNormalized) && \ (normalize || (type->flags & XML_SCHEMAS_TYPE_NORMVALUENEEDED))) { \ normValue = xmlSchemaNormalizeValue(atype, value); \ if (normValue != NULL) \ value = normValue; \ isNormalized = 1; \ } if ((retVal != NULL) && (*retVal != NULL)) { xmlSchemaFreeValue(*retVal); *retVal = NULL; } /* * 3.14.4 Simple Type Definition Validation Rules * Validation Rule: String Valid */ /* * 1 It is schema-valid with respect to that definition as defined * by Datatype Valid in [XML Schemas: Datatypes]. */ /* * 2.1 If The definition is ENTITY or is validly derived from ENTITY given * the empty set, as defined in Type Derivation OK (Simple) ($3.14.6), then * the string must be a `declared entity name`. */ /* * 2.2 If The definition is ENTITIES or is validly derived from ENTITIES * given the empty set, as defined in Type Derivation OK (Simple) ($3.14.6), * then every whitespace-delimited substring of the string must be a `declared * entity name`. */ /* * 2.3 otherwise no further condition applies. */ if ((! valNeeded) && (type->flags & XML_SCHEMAS_TYPE_FACETSNEEDVALUE)) valNeeded = 1; if (value == NULL) value = BAD_CAST ""; if (WXS_IS_ANY_SIMPLE_TYPE(type) || WXS_IS_ATOMIC(type)) { xmlSchemaTypePtr biType; /* The built-in type. */ /* * SPEC (1.2.1) "if {variety} is `atomic` then the string must `match` * a literal in the `lexical space` of {base type definition}" */ /* * Whitespace-normalize. */ NORMALIZE(type); if (type->type != XML_SCHEMA_TYPE_BASIC) { /* * Get the built-in type. */ biType = type->baseType; while ((biType != NULL) && (biType->type != XML_SCHEMA_TYPE_BASIC)) biType = biType->baseType; if (biType == NULL) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "could not get the built-in type"); goto internal_error; } } else biType = type; /* * NOTATIONs need to be processed here, since they need * to lookup in the hashtable of NOTATION declarations of the schema. */ if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR) { switch (biType->builtInType) { case XML_SCHEMAS_NOTATION: ret = xmlSchemaValidateNotation( (xmlSchemaValidCtxtPtr) actxt, ((xmlSchemaValidCtxtPtr) actxt)->schema, NULL, value, &val, valNeeded); break; case XML_SCHEMAS_QNAME: ret = xmlSchemaValidateQName((xmlSchemaValidCtxtPtr) actxt, value, &val, valNeeded); break; default: /* ws = xmlSchemaGetWhiteSpaceFacetValue(type); */ if (valNeeded) ret = xmlSchemaValPredefTypeNodeNoNorm(biType, value, &val, node); else ret = xmlSchemaValPredefTypeNodeNoNorm(biType, value, NULL, node); break; } } else if (actxt->type == XML_SCHEMA_CTXT_PARSER) { switch (biType->builtInType) { case XML_SCHEMAS_NOTATION: ret = xmlSchemaValidateNotation(NULL, ((xmlSchemaParserCtxtPtr) actxt)->schema, node, value, &val, valNeeded); break; default: /* ws = xmlSchemaGetWhiteSpaceFacetValue(type); */ if (valNeeded) ret = xmlSchemaValPredefTypeNodeNoNorm(biType, value, &val, node); else ret = xmlSchemaValPredefTypeNodeNoNorm(biType, value, NULL, node); break; } } else { /* * Validation via a public API is not implemented yet. */ goto internal_error; } if (ret != 0) { if (ret < 0) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "validating against a built-in type"); goto internal_error; } if (WXS_IS_LIST(type)) ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; else ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; } if ((ret == 0) && (type->flags & XML_SCHEMAS_TYPE_HAS_FACETS)) { /* * Check facets. */ ret = xmlSchemaValidateFacets(actxt, node, type, (xmlSchemaValType) biType->builtInType, value, val, 0, fireErrors); if (ret != 0) { if (ret < 0) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "validating facets of atomic simple type"); goto internal_error; } if (WXS_IS_LIST(type)) ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; else ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; } } else if (fireErrors && (ret > 0)) xmlSchemaSimpleTypeErr(actxt, ret, node, value, type, 1); } else if (WXS_IS_LIST(type)) { xmlSchemaTypePtr itemType; const xmlChar *cur, *end; xmlChar *tmpValue = NULL; unsigned long len = 0; xmlSchemaValPtr prevVal = NULL, curVal = NULL; /* 1.2.2 if {variety} is `list` then the string must be a sequence * of white space separated tokens, each of which `match`es a literal * in the `lexical space` of {item type definition} */ /* * Note that XML_SCHEMAS_TYPE_NORMVALUENEEDED will be set if * the list type has an enum or pattern facet. */ NORMALIZE(type); /* * VAL TODO: Optimize validation of empty values. * VAL TODO: We do not have computed values for lists. */ itemType = WXS_LIST_ITEMTYPE(type); cur = value; do { while (IS_BLANK_CH(*cur)) cur++; end = cur; while ((*end != 0) && (!(IS_BLANK_CH(*end)))) end++; if (end == cur) break; tmpValue = xmlStrndup(cur, end - cur); len++; if (valNeeded) ret = xmlSchemaVCheckCVCSimpleType(actxt, node, itemType, tmpValue, &curVal, fireErrors, 0, 1); else ret = xmlSchemaVCheckCVCSimpleType(actxt, node, itemType, tmpValue, NULL, fireErrors, 0, 1); FREE_AND_NULL(tmpValue); if (curVal != NULL) { /* * Add to list of computed values. */ if (val == NULL) val = curVal; else xmlSchemaValueAppend(prevVal, curVal); prevVal = curVal; curVal = NULL; } if (ret != 0) { if (ret < 0) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "validating an item of list simple type"); goto internal_error; } ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; break; } cur = end; } while (*cur != 0); FREE_AND_NULL(tmpValue); if ((ret == 0) && (type->flags & XML_SCHEMAS_TYPE_HAS_FACETS)) { /* * Apply facets (pattern, enumeration). */ ret = xmlSchemaValidateFacets(actxt, node, type, XML_SCHEMAS_UNKNOWN, value, val, len, fireErrors); if (ret != 0) { if (ret < 0) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "validating facets of list simple type"); goto internal_error; } ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; } } if (fireErrors && (ret > 0)) { /* * Report the normalized value. */ normalize = 1; NORMALIZE(type); xmlSchemaSimpleTypeErr(actxt, ret, node, value, type, 1); } } else if (WXS_IS_UNION(type)) { xmlSchemaTypeLinkPtr memberLink; /* * TODO: For all datatypes `derived` by `union` whiteSpace does * not apply directly; however, the normalization behavior of `union` * types is controlled by the value of whiteSpace on that one of the * `memberTypes` against which the `union` is successfully validated. * * This means that the value is normalized by the first validating * member type, then the facets of the union type are applied. This * needs changing of the value! */ /* * 1.2.3 if {variety} is `union` then the string must `match` a * literal in the `lexical space` of at least one member of * {member type definitions} */ memberLink = xmlSchemaGetUnionSimpleTypeMemberTypes(type); if (memberLink == NULL) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "union simple type has no member types"); goto internal_error; } /* * Always normalize union type values, since we currently * cannot store the whitespace information with the value * itself; otherwise a later value-comparison would be * not possible. */ while (memberLink != NULL) { if (valNeeded) ret = xmlSchemaVCheckCVCSimpleType(actxt, node, memberLink->type, value, &val, 0, 1, 0); else ret = xmlSchemaVCheckCVCSimpleType(actxt, node, memberLink->type, value, NULL, 0, 1, 0); if (ret <= 0) break; memberLink = memberLink->next; } if (ret != 0) { if (ret < 0) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "validating members of union simple type"); goto internal_error; } ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_3; } /* * Apply facets (pattern, enumeration). */ if ((ret == 0) && (type->flags & XML_SCHEMAS_TYPE_HAS_FACETS)) { /* * The normalization behavior of `union` types is controlled by * the value of whiteSpace on that one of the `memberTypes` * against which the `union` is successfully validated. */ NORMALIZE(memberLink->type); ret = xmlSchemaValidateFacets(actxt, node, type, XML_SCHEMAS_UNKNOWN, value, val, 0, fireErrors); if (ret != 0) { if (ret < 0) { AERROR_INT("xmlSchemaVCheckCVCSimpleType", "validating facets of union simple type"); goto internal_error; } ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_3; } } if (fireErrors && (ret > 0)) xmlSchemaSimpleTypeErr(actxt, ret, node, value, type, 1); } if (normValue != NULL) xmlFree(normValue); if (ret == 0) { if (retVal != NULL) *retVal = val; else if (val != NULL) xmlSchemaFreeValue(val); } else if (val != NULL) xmlSchemaFreeValue(val); return (ret); internal_error: if (normValue != NULL) xmlFree(normValue); if (val != NULL) xmlSchemaFreeValue(val); return (-1); } static int xmlSchemaVExpandQName(xmlSchemaValidCtxtPtr vctxt, const xmlChar *value, const xmlChar **nsName, const xmlChar **localName) { int ret = 0; if ((nsName == NULL) || (localName == NULL)) return (-1); *nsName = NULL; *localName = NULL; ret = xmlValidateQName(value, 1); if (ret == -1) return (-1); if (ret > 0) { xmlSchemaSimpleTypeErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1, NULL, value, xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), 1); return (1); } { xmlChar *local = NULL; xmlChar *prefix; /* * NOTE: xmlSplitQName2 will return a duplicated * string. */ local = xmlSplitQName2(value, &prefix); if (local == NULL) *localName = xmlDictLookup(vctxt->dict, value, -1); else { *localName = xmlDictLookup(vctxt->dict, local, -1); xmlFree(local); } *nsName = xmlSchemaLookupNamespace(vctxt, prefix); if (prefix != NULL) { xmlFree(prefix); /* * A namespace must be found if the prefix is NOT NULL. */ if (*nsName == NULL) { xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1, NULL, WXS_BASIC_CAST xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), "The QName value '%s' has no " "corresponding namespace declaration in scope", value, NULL); return (2); } } } return (0); } static int xmlSchemaProcessXSIType(xmlSchemaValidCtxtPtr vctxt, xmlSchemaAttrInfoPtr iattr, xmlSchemaTypePtr *localType, xmlSchemaElementPtr elemDecl) { int ret = 0; /* * cvc-elt (3.3.4) : (4) * AND * Schema-Validity Assessment (Element) (cvc-assess-elt) * (1.2.1.2.1) - (1.2.1.2.4) * Handle 'xsi:type'. */ if (localType == NULL) return (-1); *localType = NULL; if (iattr == NULL) return (0); else { const xmlChar *nsName = NULL, *local = NULL; /* * TODO: We should report a *warning* that the type was overridden * by the instance. */ ACTIVATE_ATTRIBUTE(iattr); /* * (cvc-elt) (3.3.4) : (4.1) * (cvc-assess-elt) (1.2.1.2.2) */ ret = xmlSchemaVExpandQName(vctxt, iattr->value, &nsName, &local); if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaValidateElementByDeclaration", "calling xmlSchemaQNameExpand() to validate the " "attribute 'xsi:type'"); goto internal_error; } goto exit; } /* * (cvc-elt) (3.3.4) : (4.2) * (cvc-assess-elt) (1.2.1.2.3) */ *localType = xmlSchemaGetType(vctxt->schema, local, nsName); if (*localType == NULL) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_ELT_4_2, NULL, WXS_BASIC_CAST xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), "The QName value '%s' of the xsi:type attribute does not " "resolve to a type definition", xmlSchemaFormatQName(&str, nsName, local), NULL); FREE_AND_NULL(str); ret = vctxt->err; goto exit; } if (elemDecl != NULL) { int set = 0; /* * SPEC cvc-elt (3.3.4) : (4.3) (Type Derivation OK) * "The `local type definition` must be validly * derived from the {type definition} given the union of * the {disallowed substitutions} and the {type definition}'s * {prohibited substitutions}, as defined in * Type Derivation OK (Complex) ($3.4.6) * (if it is a complex type definition), * or given {disallowed substitutions} as defined in Type * Derivation OK (Simple) ($3.14.6) (if it is a simple type * definition)." * * {disallowed substitutions}: the "block" on the element decl. * {prohibited substitutions}: the "block" on the type def. */ /* * OPTIMIZE TODO: We could map types already evaluated * to be validly derived from other types to avoid checking * this over and over for the same types. */ if ((elemDecl->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) || (elemDecl->subtypes->flags & XML_SCHEMAS_TYPE_BLOCK_EXTENSION)) set |= SUBSET_EXTENSION; if ((elemDecl->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) || (elemDecl->subtypes->flags & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION)) set |= SUBSET_RESTRICTION; /* * REMOVED and CHANGED since this produced a parser context * which adds to the string dict of the schema. So this would * change the schema and we don't want this. We don't need * the parser context anymore. * * if ((vctxt->pctxt == NULL) && * (xmlSchemaCreatePCtxtOnVCtxt(vctxt) == -1)) * return (-1); */ if (xmlSchemaCheckCOSDerivedOK(ACTXT_CAST vctxt, *localType, elemDecl->subtypes, set) != 0) { xmlChar *str = NULL; xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_ELT_4_3, NULL, NULL, "The type definition '%s', specified by xsi:type, is " "blocked or not validly derived from the type definition " "of the element declaration", xmlSchemaFormatQName(&str, (*localType)->targetNamespace, (*localType)->name), NULL); FREE_AND_NULL(str); ret = vctxt->err; *localType = NULL; } } } exit: ACTIVATE_ELEM; return (ret); internal_error: ACTIVATE_ELEM; return (-1); } static int xmlSchemaValidateElemDecl(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaElementPtr elemDecl = vctxt->inode->decl; xmlSchemaTypePtr actualType; /* * cvc-elt (3.3.4) : 1 */ if (elemDecl == NULL) { VERROR(XML_SCHEMAV_CVC_ELT_1, NULL, "No matching declaration available"); return (vctxt->err); } actualType = WXS_ELEM_TYPEDEF(elemDecl); /* * cvc-elt (3.3.4) : 2 */ if (elemDecl->flags & XML_SCHEMAS_ELEM_ABSTRACT) { VERROR(XML_SCHEMAV_CVC_ELT_2, NULL, "The element declaration is abstract"); return (vctxt->err); } if (actualType == NULL) { VERROR(XML_SCHEMAV_CVC_TYPE_1, NULL, "The type definition is absent"); return (XML_SCHEMAV_CVC_TYPE_1); } if (vctxt->nbAttrInfos != 0) { int ret; xmlSchemaAttrInfoPtr iattr; /* * cvc-elt (3.3.4) : 3 * Handle 'xsi:nil'. */ iattr = xmlSchemaGetMetaAttrInfo(vctxt, XML_SCHEMA_ATTR_INFO_META_XSI_NIL); if (iattr) { ACTIVATE_ATTRIBUTE(iattr); /* * Validate the value. */ ret = xmlSchemaVCheckCVCSimpleType( ACTXT_CAST vctxt, NULL, xmlSchemaGetBuiltInType(XML_SCHEMAS_BOOLEAN), iattr->value, &(iattr->val), 1, 0, 0); ACTIVATE_ELEM; if (ret < 0) { VERROR_INT("xmlSchemaValidateElemDecl", "calling xmlSchemaVCheckCVCSimpleType() to " "validate the attribute 'xsi:nil'"); return (-1); } if (ret == 0) { if ((elemDecl->flags & XML_SCHEMAS_ELEM_NILLABLE) == 0) { /* * cvc-elt (3.3.4) : 3.1 */ VERROR(XML_SCHEMAV_CVC_ELT_3_1, NULL, "The element is not 'nillable'"); /* Does not return an error on purpose. */ } else { if (xmlSchemaValueGetAsBoolean(iattr->val)) { /* * cvc-elt (3.3.4) : 3.2.2 */ if ((elemDecl->flags & XML_SCHEMAS_ELEM_FIXED) && (elemDecl->value != NULL)) { VERROR(XML_SCHEMAV_CVC_ELT_3_2_2, NULL, "The element cannot be 'nilled' because " "there is a fixed value constraint defined " "for it"); /* Does not return an error on purpose. */ } else vctxt->inode->flags |= XML_SCHEMA_ELEM_INFO_NILLED; } } } } /* * cvc-elt (3.3.4) : 4 * Handle 'xsi:type'. */ iattr = xmlSchemaGetMetaAttrInfo(vctxt, XML_SCHEMA_ATTR_INFO_META_XSI_TYPE); if (iattr) { xmlSchemaTypePtr localType = NULL; ret = xmlSchemaProcessXSIType(vctxt, iattr, &localType, elemDecl); if (ret != 0) { if (ret == -1) { VERROR_INT("xmlSchemaValidateElemDecl", "calling xmlSchemaProcessXSIType() to " "process the attribute 'xsi:type'"); return (-1); } /* Does not return an error on purpose. */ } if (localType != NULL) { vctxt->inode->flags |= XML_SCHEMA_ELEM_INFO_LOCAL_TYPE; actualType = localType; } } } /* * IDC: Register identity-constraint XPath matchers. */ if ((elemDecl->idcs != NULL) && (xmlSchemaIDCRegisterMatchers(vctxt, elemDecl) == -1)) return (-1); /* * No actual type definition. */ if (actualType == NULL) { VERROR(XML_SCHEMAV_CVC_TYPE_1, NULL, "The type definition is absent"); return (XML_SCHEMAV_CVC_TYPE_1); } /* * Remember the actual type definition. */ vctxt->inode->typeDef = actualType; return (0); } static int xmlSchemaVAttributesSimple(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaAttrInfoPtr iattr; int ret = 0, i; /* * SPEC cvc-type (3.1.1) * "The attributes of must be empty, excepting those whose namespace * name is identical to http://www.w3.org/2001/XMLSchema-instance and * whose local name is one of type, nil, schemaLocation or * noNamespaceSchemaLocation." */ if (vctxt->nbAttrInfos == 0) return (0); for (i = 0; i < vctxt->nbAttrInfos; i++) { iattr = vctxt->attrInfos[i]; if (! iattr->metaType) { ACTIVATE_ATTRIBUTE(iattr) xmlSchemaIllegalAttrErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_TYPE_3_1_1, iattr, NULL); ret = XML_SCHEMAV_CVC_TYPE_3_1_1; } } ACTIVATE_ELEM return (ret); } /* * Cleanup currently used attribute infos. */ static void xmlSchemaClearAttrInfos(xmlSchemaValidCtxtPtr vctxt) { int i; xmlSchemaAttrInfoPtr attr; if (vctxt->nbAttrInfos == 0) return; for (i = 0; i < vctxt->nbAttrInfos; i++) { attr = vctxt->attrInfos[i]; if (attr->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES) { if (attr->localName != NULL) xmlFree((xmlChar *) attr->localName); if (attr->nsName != NULL) xmlFree((xmlChar *) attr->nsName); } if (attr->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES) { if (attr->value != NULL) xmlFree((xmlChar *) attr->value); } if (attr->val != NULL) { xmlSchemaFreeValue(attr->val); attr->val = NULL; } memset(attr, 0, sizeof(xmlSchemaAttrInfo)); } vctxt->nbAttrInfos = 0; } /* * 3.4.4 Complex Type Definition Validation Rules * Element Locally Valid (Complex Type) (cvc-complex-type) * 3.2.4 Attribute Declaration Validation Rules * Validation Rule: Attribute Locally Valid (cvc-attribute) * Attribute Locally Valid (Use) (cvc-au) * * Only "assessed" attribute information items will be visible to * IDCs. I.e. not "lax" (without declaration) and "skip" wild attributes. */ static int xmlSchemaVAttributesComplex(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaTypePtr type = vctxt->inode->typeDef; xmlSchemaItemListPtr attrUseList; xmlSchemaAttributeUsePtr attrUse = NULL; xmlSchemaAttributePtr attrDecl = NULL; xmlSchemaAttrInfoPtr iattr, tmpiattr; int i, j, found, nbAttrs, nbUses; int xpathRes = 0, res, wildIDs = 0, fixed; xmlNodePtr defAttrOwnerElem = NULL; /* * SPEC (cvc-attribute) * (1) "The declaration must not be `absent` (see Missing * Sub-components ($5.3) for how this can fail to be * the case)." * (2) "Its {type definition} must not be absent." * * NOTE (1) + (2): This is not handled here, since we currently do not * allow validation against schemas which have missing sub-components. * * SPEC (cvc-complex-type) * (3) "For each attribute information item in the element information * item's [attributes] excepting those whose [namespace name] is * identical to http://www.w3.org/2001/XMLSchema-instance and whose * [local name] is one of type, nil, schemaLocation or * noNamespaceSchemaLocation, the appropriate case among the following * must be true: * */ attrUseList = (xmlSchemaItemListPtr) type->attrUses; /* * @nbAttrs is the number of attributes present in the instance. */ nbAttrs = vctxt->nbAttrInfos; if (attrUseList != NULL) nbUses = attrUseList->nbItems; else nbUses = 0; for (i = 0; i < nbUses; i++) { found = 0; attrUse = attrUseList->items[i]; attrDecl = WXS_ATTRUSE_DECL(attrUse); for (j = 0; j < nbAttrs; j++) { iattr = vctxt->attrInfos[j]; /* * SPEC (cvc-complex-type) (3) * Skip meta attributes. */ if (iattr->metaType) continue; if (iattr->localName[0] != attrDecl->name[0]) continue; if (!xmlStrEqual(iattr->localName, attrDecl->name)) continue; if (!xmlStrEqual(iattr->nsName, attrDecl->targetNamespace)) continue; found = 1; /* * SPEC (cvc-complex-type) * (3.1) "If there is among the {attribute uses} an attribute * use with an {attribute declaration} whose {name} matches * the attribute information item's [local name] and whose * {target namespace} is identical to the attribute information * item's [namespace name] (where an `absent` {target namespace} * is taken to be identical to a [namespace name] with no value), * then the attribute information must be `valid` with respect * to that attribute use as per Attribute Locally Valid (Use) * ($3.5.4). In this case the {attribute declaration} of that * attribute use is the `context-determined declaration` for the * attribute information item with respect to Schema-Validity * Assessment (Attribute) ($3.2.4) and * Assessment Outcome (Attribute) ($3.2.5). */ iattr->state = XML_SCHEMAS_ATTR_ASSESSED; iattr->use = attrUse; /* * Context-determined declaration. */ iattr->decl = attrDecl; iattr->typeDef = attrDecl->subtypes; break; } if (found) continue; if (attrUse->occurs == XML_SCHEMAS_ATTR_USE_REQUIRED) { /* * Handle non-existent, required attributes. * * SPEC (cvc-complex-type) * (4) "The {attribute declaration} of each attribute use in * the {attribute uses} whose {required} is true matches one * of the attribute information items in the element information * item's [attributes] as per clause 3.1 above." */ tmpiattr = xmlSchemaGetFreshAttrInfo(vctxt); if (tmpiattr == NULL) { VERROR_INT( "xmlSchemaVAttributesComplex", "calling xmlSchemaGetFreshAttrInfo()"); return (-1); } tmpiattr->state = XML_SCHEMAS_ATTR_ERR_MISSING; tmpiattr->use = attrUse; tmpiattr->decl = attrDecl; } else if ((attrUse->occurs == XML_SCHEMAS_ATTR_USE_OPTIONAL) && ((attrUse->defValue != NULL) || (attrDecl->defValue != NULL))) { /* * Handle non-existent, optional, default/fixed attributes. */ tmpiattr = xmlSchemaGetFreshAttrInfo(vctxt); if (tmpiattr == NULL) { VERROR_INT( "xmlSchemaVAttributesComplex", "calling xmlSchemaGetFreshAttrInfo()"); return (-1); } tmpiattr->state = XML_SCHEMAS_ATTR_DEFAULT; tmpiattr->use = attrUse; tmpiattr->decl = attrDecl; tmpiattr->typeDef = attrDecl->subtypes; tmpiattr->localName = attrDecl->name; tmpiattr->nsName = attrDecl->targetNamespace; } } if (vctxt->nbAttrInfos == 0) return (0); /* * Validate against the wildcard. */ if (type->attributeWildcard != NULL) { /* * SPEC (cvc-complex-type) * (3.2.1) "There must be an {attribute wildcard}." */ for (i = 0; i < nbAttrs; i++) { iattr = vctxt->attrInfos[i]; /* * SPEC (cvc-complex-type) (3) * Skip meta attributes. */ if (iattr->state != XML_SCHEMAS_ATTR_UNKNOWN) continue; /* * SPEC (cvc-complex-type) * (3.2.2) "The attribute information item must be `valid` with * respect to it as defined in Item Valid (Wildcard) ($3.10.4)." * * SPEC Item Valid (Wildcard) (cvc-wildcard) * "... its [namespace name] must be `valid` with respect to * the wildcard constraint, as defined in Wildcard allows * Namespace Name ($3.10.4)." */ if (xmlSchemaCheckCVCWildcardNamespace(type->attributeWildcard, iattr->nsName) == 0) { /* * Handle processContents. * * SPEC (cvc-wildcard): * processContents | context-determined declaration: * "strict" "mustFind" * "lax" "none" * "skip" "skip" */ if (type->attributeWildcard->processContents == XML_SCHEMAS_ANY_SKIP) { /* * context-determined declaration = "skip" * * SPEC PSVI Assessment Outcome (Attribute) * [validity] = "notKnown" * [validation attempted] = "none" */ iattr->state = XML_SCHEMAS_ATTR_WILD_SKIP; continue; } /* * Find an attribute declaration. */ iattr->decl = xmlSchemaGetAttributeDecl(vctxt->schema, iattr->localName, iattr->nsName); if (iattr->decl != NULL) { iattr->state = XML_SCHEMAS_ATTR_ASSESSED; /* * SPEC (cvc-complex-type) * (5) "Let [Definition:] the wild IDs be the set of * all attribute information item to which clause 3.2 * applied and whose `validation` resulted in a * `context-determined declaration` of mustFind or no * `context-determined declaration` at all, and whose * [local name] and [namespace name] resolve (as * defined by QName resolution (Instance) ($3.15.4)) to * an attribute declaration whose {type definition} is * or is derived from ID. Then all of the following * must be true:" */ iattr->typeDef = WXS_ATTR_TYPEDEF(iattr->decl); if (xmlSchemaIsDerivedFromBuiltInType( iattr->typeDef, XML_SCHEMAS_ID)) { /* * SPEC (5.1) "There must be no more than one * item in `wild IDs`." */ if (wildIDs != 0) { /* VAL TODO */ iattr->state = XML_SCHEMAS_ATTR_ERR_WILD_DUPLICATE_ID; continue; } wildIDs++; /* * SPEC (cvc-complex-type) * (5.2) "If `wild IDs` is non-empty, there must not * be any attribute uses among the {attribute uses} * whose {attribute declaration}'s {type definition} * is or is derived from ID." */ if (attrUseList != NULL) { for (j = 0; j < attrUseList->nbItems; j++) { if (xmlSchemaIsDerivedFromBuiltInType( WXS_ATTRUSE_TYPEDEF(attrUseList->items[j]), XML_SCHEMAS_ID)) { /* URGENT VAL TODO: implement */ iattr->state = XML_SCHEMAS_ATTR_ERR_WILD_AND_USE_ID; break; } } } } } else if (type->attributeWildcard->processContents == XML_SCHEMAS_ANY_LAX) { iattr->state = XML_SCHEMAS_ATTR_WILD_LAX_NO_DECL; /* * SPEC PSVI Assessment Outcome (Attribute) * [validity] = "notKnown" * [validation attempted] = "none" */ } else { iattr->state = XML_SCHEMAS_ATTR_ERR_WILD_STRICT_NO_DECL; } } } } if (vctxt->nbAttrInfos == 0) return (0); /* * Get the owner element; needed for creation of default attributes. * This fixes bug #341337, reported by David Grohmann. */ if (vctxt->options & XML_SCHEMA_VAL_VC_I_CREATE) { xmlSchemaNodeInfoPtr ielem = vctxt->elemInfos[vctxt->depth]; if (ielem && ielem->node && ielem->node->doc) defAttrOwnerElem = ielem->node; } /* * Validate values, create default attributes, evaluate IDCs. */ for (i = 0; i < vctxt->nbAttrInfos; i++) { iattr = vctxt->attrInfos[i]; /* * VAL TODO: Note that we won't try to resolve IDCs to * "lax" and "skip" validated attributes. Check what to * do in this case. */ if ((iattr->state != XML_SCHEMAS_ATTR_ASSESSED) && (iattr->state != XML_SCHEMAS_ATTR_DEFAULT)) continue; /* * VAL TODO: What to do if the type definition is missing? */ if (iattr->typeDef == NULL) { iattr->state = XML_SCHEMAS_ATTR_ERR_NO_TYPE; continue; } ACTIVATE_ATTRIBUTE(iattr); fixed = 0; xpathRes = 0; if (vctxt->xpathStates != NULL) { /* * Evaluate IDCs. */ xpathRes = xmlSchemaXPathEvaluate(vctxt, XML_ATTRIBUTE_NODE); if (xpathRes == -1) { VERROR_INT("xmlSchemaVAttributesComplex", "calling xmlSchemaXPathEvaluate()"); goto internal_error; } } if (iattr->state == XML_SCHEMAS_ATTR_DEFAULT) { /* * Default/fixed attributes. * We need the value only if we need to resolve IDCs or * will create default attributes. */ if ((xpathRes) || (defAttrOwnerElem)) { if (iattr->use->defValue != NULL) { iattr->value = (xmlChar *) iattr->use->defValue; iattr->val = iattr->use->defVal; } else { iattr->value = (xmlChar *) iattr->decl->defValue; iattr->val = iattr->decl->defVal; } /* * IDCs will consume the precomputed default value, * so we need to clone it. */ if (iattr->val == NULL) { VERROR_INT("xmlSchemaVAttributesComplex", "default/fixed value on an attribute use was " "not precomputed"); goto internal_error; } iattr->val = xmlSchemaCopyValue(iattr->val); if (iattr->val == NULL) { VERROR_INT("xmlSchemaVAttributesComplex", "calling xmlSchemaCopyValue()"); goto internal_error; } } /* * PSVI: Add the default attribute to the current element. * VAL TODO: Should we use the *normalized* value? This currently * uses the *initial* value. */ if (defAttrOwnerElem) { xmlChar *normValue; const xmlChar *value; value = iattr->value; /* * Normalize the value. */ normValue = xmlSchemaNormalizeValue(iattr->typeDef, iattr->value); if (normValue != NULL) value = BAD_CAST normValue; if (iattr->nsName == NULL) { if (xmlNewProp(defAttrOwnerElem, iattr->localName, value) == NULL) { VERROR_INT("xmlSchemaVAttributesComplex", "calling xmlNewProp()"); if (normValue != NULL) xmlFree(normValue); goto internal_error; } } else { xmlNsPtr ns; ns = xmlSearchNsByHref(defAttrOwnerElem->doc, defAttrOwnerElem, iattr->nsName); if (ns == NULL) { xmlChar prefix[13]; int counter = 0; /* * Create a namespace declaration on the validation * root node if no namespace declaration is in scope. */ do { snprintf((char *) prefix, 13, "p%d", counter++); ns = xmlSearchNs(defAttrOwnerElem->doc, defAttrOwnerElem, BAD_CAST prefix); if (counter > 1000) { VERROR_INT( "xmlSchemaVAttributesComplex", "could not compute a ns prefix for a " "default/fixed attribute"); if (normValue != NULL) xmlFree(normValue); goto internal_error; } } while (ns != NULL); ns = xmlNewNs(vctxt->validationRoot, iattr->nsName, BAD_CAST prefix); } /* * TODO: * http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005JulSep/0406.html * If we have QNames: do we need to ensure there's a * prefix defined for the QName? */ xmlNewNsProp(defAttrOwnerElem, ns, iattr->localName, value); } if (normValue != NULL) xmlFree(normValue); } /* * Go directly to IDC evaluation. */ goto eval_idcs; } /* * Validate the value. */ if (vctxt->value != NULL) { /* * Free last computed value; just for safety reasons. */ xmlSchemaFreeValue(vctxt->value); vctxt->value = NULL; } /* * Note that the attribute *use* can be unavailable, if * the attribute was a wild attribute. */ if ((iattr->decl->flags & XML_SCHEMAS_ATTR_FIXED) || ((iattr->use != NULL) && (iattr->use->flags & XML_SCHEMAS_ATTR_FIXED))) fixed = 1; else fixed = 0; /* * SPEC (cvc-attribute) * (3) "The item's `normalized value` must be locally `valid` * with respect to that {type definition} as per * String Valid ($3.14.4)." * * VAL TODO: Do we already have the * "normalized attribute value" here? */ if (xpathRes || fixed) { iattr->flags |= XML_SCHEMA_NODE_INFO_VALUE_NEEDED; /* * Request a computed value. */ res = xmlSchemaVCheckCVCSimpleType( ACTXT_CAST vctxt, iattr->node, iattr->typeDef, iattr->value, &(iattr->val), 1, 1, 0); } else { res = xmlSchemaVCheckCVCSimpleType( ACTXT_CAST vctxt, iattr->node, iattr->typeDef, iattr->value, NULL, 1, 0, 0); } if (res != 0) { if (res == -1) { VERROR_INT("xmlSchemaVAttributesComplex", "calling xmlSchemaStreamValidateSimpleTypeValue()"); goto internal_error; } iattr->state = XML_SCHEMAS_ATTR_INVALID_VALUE; /* * SPEC PSVI Assessment Outcome (Attribute) * [validity] = "invalid" */ goto eval_idcs; } if (fixed) { /* * SPEC Attribute Locally Valid (Use) (cvc-au) * "For an attribute information item to be `valid` * with respect to an attribute use its *normalized* * value must match the *canonical* lexical * representation of the attribute use's {value * constraint}value, if it is present and fixed." * * VAL TODO: The requirement for the *canonical* value * will be removed in XML Schema 1.1. */ /* * SPEC Attribute Locally Valid (cvc-attribute) * (4) "The item's *actual* value must match the *value* of * the {value constraint}, if it is present and fixed." */ if (iattr->val == NULL) { /* VAL TODO: A value was not precomputed. */ goto eval_idcs; } if ((iattr->use != NULL) && (iattr->use->defValue != NULL)) { if (iattr->use->defVal == NULL) { /* VAL TODO: A default value was not precomputed. */ goto eval_idcs; } iattr->vcValue = iattr->use->defValue; /* if (xmlSchemaCompareValuesWhtsp(attr->val, (xmlSchemaWhitespaceValueType) ws, attr->use->defVal, (xmlSchemaWhitespaceValueType) ws) != 0) { */ if (! xmlSchemaAreValuesEqual(iattr->val, iattr->use->defVal)) iattr->state = XML_SCHEMAS_ATTR_ERR_FIXED_VALUE; } else { if (iattr->decl->defVal == NULL) { /* VAL TODO: A default value was not precomputed. */ goto eval_idcs; } iattr->vcValue = iattr->decl->defValue; /* if (xmlSchemaCompareValuesWhtsp(attr->val, (xmlSchemaWhitespaceValueType) ws, attrDecl->defVal, (xmlSchemaWhitespaceValueType) ws) != 0) { */ if (! xmlSchemaAreValuesEqual(iattr->val, iattr->decl->defVal)) iattr->state = XML_SCHEMAS_ATTR_ERR_FIXED_VALUE; } /* * [validity] = "valid" */ } eval_idcs: /* * Evaluate IDCs. */ if (xpathRes) { if (xmlSchemaXPathProcessHistory(vctxt, vctxt->depth +1) == -1) { VERROR_INT("xmlSchemaVAttributesComplex", "calling xmlSchemaXPathEvaluate()"); goto internal_error; } } else if (vctxt->xpathStates != NULL) xmlSchemaXPathPop(vctxt); } /* * Report errors. */ for (i = 0; i < vctxt->nbAttrInfos; i++) { iattr = vctxt->attrInfos[i]; if ((iattr->state == XML_SCHEMAS_ATTR_META) || (iattr->state == XML_SCHEMAS_ATTR_ASSESSED) || (iattr->state == XML_SCHEMAS_ATTR_WILD_SKIP) || (iattr->state == XML_SCHEMAS_ATTR_WILD_LAX_NO_DECL)) continue; ACTIVATE_ATTRIBUTE(iattr); switch (iattr->state) { case XML_SCHEMAS_ATTR_ERR_MISSING: { xmlChar *str = NULL; ACTIVATE_ELEM; xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_COMPLEX_TYPE_4, NULL, NULL, "The attribute '%s' is required but missing", xmlSchemaFormatQName(&str, iattr->decl->targetNamespace, iattr->decl->name), NULL); FREE_AND_NULL(str) break; } case XML_SCHEMAS_ATTR_ERR_NO_TYPE: VERROR(XML_SCHEMAV_CVC_ATTRIBUTE_2, NULL, "The type definition is absent"); break; case XML_SCHEMAS_ATTR_ERR_FIXED_VALUE: xmlSchemaCustomErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_AU, NULL, NULL, "The value '%s' does not match the fixed " "value constraint '%s'", iattr->value, iattr->vcValue); break; case XML_SCHEMAS_ATTR_ERR_WILD_STRICT_NO_DECL: VERROR(XML_SCHEMAV_CVC_WILDCARD, NULL, "No matching global attribute declaration available, but " "demanded by the strict wildcard"); break; case XML_SCHEMAS_ATTR_UNKNOWN: if (iattr->metaType) break; /* * MAYBE VAL TODO: One might report different error messages * for the following errors. */ if (type->attributeWildcard == NULL) { xmlSchemaIllegalAttrErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_1, iattr, NULL); } else { xmlSchemaIllegalAttrErr(ACTXT_CAST vctxt, XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_2, iattr, NULL); } break; default: break; } } ACTIVATE_ELEM; return (0); internal_error: ACTIVATE_ELEM; return (-1); } static int xmlSchemaValidateElemWildcard(xmlSchemaValidCtxtPtr vctxt, int *skip) { xmlSchemaWildcardPtr wild = (xmlSchemaWildcardPtr) vctxt->inode->decl; /* * The namespace of the element was already identified to be * matching the wildcard. */ if ((skip == NULL) || (wild == NULL) || (wild->type != XML_SCHEMA_TYPE_ANY)) { VERROR_INT("xmlSchemaValidateElemWildcard", "bad arguments"); return (-1); } *skip = 0; if (wild->processContents == XML_SCHEMAS_ANY_SKIP) { /* * URGENT VAL TODO: Either we need to position the stream to the * next sibling, or walk the whole subtree. */ *skip = 1; return (0); } { xmlSchemaElementPtr decl = NULL; decl = xmlSchemaGetElem(vctxt->schema, vctxt->inode->localName, vctxt->inode->nsName); if (decl != NULL) { vctxt->inode->decl = decl; return (0); } } if (wild->processContents == XML_SCHEMAS_ANY_STRICT) { /* VAL TODO: Change to proper error code. */ VERROR(XML_SCHEMAV_CVC_ELT_1, NULL, /* WXS_BASIC_CAST wild */ "No matching global element declaration available, but " "demanded by the strict wildcard"); return (vctxt->err); } if (vctxt->nbAttrInfos != 0) { xmlSchemaAttrInfoPtr iattr; /* * SPEC Validation Rule: Schema-Validity Assessment (Element) * (1.2.1.2.1) - (1.2.1.2.3 ) * * Use the xsi:type attribute for the type definition. */ iattr = xmlSchemaGetMetaAttrInfo(vctxt, XML_SCHEMA_ATTR_INFO_META_XSI_TYPE); if (iattr != NULL) { if (xmlSchemaProcessXSIType(vctxt, iattr, &(vctxt->inode->typeDef), NULL) == -1) { VERROR_INT("xmlSchemaValidateElemWildcard", "calling xmlSchemaProcessXSIType() to " "process the attribute 'xsi:nil'"); return (-1); } /* * Don't return an error on purpose. */ return (0); } } /* * SPEC Validation Rule: Schema-Validity Assessment (Element) * * Fallback to "anyType". */ vctxt->inode->typeDef = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); return (0); } /* * xmlSchemaCheckCOSValidDefault: * * This will be called if: not nilled, no content and a default/fixed * value is provided. */ static int xmlSchemaCheckCOSValidDefault(xmlSchemaValidCtxtPtr vctxt, const xmlChar *value, xmlSchemaValPtr *val) { int ret = 0; xmlSchemaNodeInfoPtr inode = vctxt->inode; /* * cos-valid-default: * Schema Component Constraint: Element Default Valid (Immediate) * For a string to be a valid default with respect to a type * definition the appropriate case among the following must be true: */ if WXS_IS_COMPLEX(inode->typeDef) { /* * Complex type. * * SPEC (2.1) "its {content type} must be a simple type definition * or mixed." * SPEC (2.2.2) "If the {content type} is mixed, then the {content * type}'s particle must be `emptiable` as defined by * Particle Emptiable ($3.9.6)." */ if ((! WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) && ((! WXS_HAS_MIXED_CONTENT(inode->typeDef)) || (! WXS_EMPTIABLE(inode->typeDef)))) { ret = XML_SCHEMAP_COS_VALID_DEFAULT_2_1; /* NOTE that this covers (2.2.2) as well. */ VERROR(ret, NULL, "For a string to be a valid default, the type definition " "must be a simple type or a complex type with simple content " "or mixed content and a particle emptiable"); return(ret); } } /* * 1 If the type definition is a simple type definition, then the string * must be `valid` with respect to that definition as defined by String * Valid ($3.14.4). * * AND * * 2.2.1 If the {content type} is a simple type definition, then the * string must be `valid` with respect to that simple type definition * as defined by String Valid ($3.14.4). */ if (WXS_IS_SIMPLE(inode->typeDef)) { ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST vctxt, NULL, inode->typeDef, value, val, 1, 1, 0); } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST vctxt, NULL, inode->typeDef->contentTypeDef, value, val, 1, 1, 0); } if (ret < 0) { VERROR_INT("xmlSchemaCheckCOSValidDefault", "calling xmlSchemaVCheckCVCSimpleType()"); } return (ret); } static void xmlSchemaVContentModelCallback(xmlRegExecCtxtPtr exec ATTRIBUTE_UNUSED, const xmlChar * name ATTRIBUTE_UNUSED, void *transdata, void *inputdata) { xmlSchemaElementPtr item = (xmlSchemaElementPtr) transdata; xmlSchemaNodeInfoPtr inode = (xmlSchemaNodeInfoPtr) inputdata; inode->decl = item; } static int xmlSchemaValidatorPushElem(xmlSchemaValidCtxtPtr vctxt) { vctxt->inode = xmlSchemaGetFreshElemInfo(vctxt); if (vctxt->inode == NULL) { VERROR_INT("xmlSchemaValidatorPushElem", "calling xmlSchemaGetFreshElemInfo()"); return (-1); } vctxt->nbAttrInfos = 0; return (0); } static int xmlSchemaVCheckINodeDataType(xmlSchemaValidCtxtPtr vctxt, xmlSchemaNodeInfoPtr inode, xmlSchemaTypePtr type, const xmlChar *value) { if (inode->flags & XML_SCHEMA_NODE_INFO_VALUE_NEEDED) return (xmlSchemaVCheckCVCSimpleType( ACTXT_CAST vctxt, NULL, type, value, &(inode->val), 1, 1, 0)); else return (xmlSchemaVCheckCVCSimpleType( ACTXT_CAST vctxt, NULL, type, value, NULL, 1, 0, 0)); } /* * Process END of element. */ static int xmlSchemaValidatorPopElem(xmlSchemaValidCtxtPtr vctxt) { int ret = 0; xmlSchemaNodeInfoPtr inode = vctxt->inode; if (vctxt->nbAttrInfos != 0) xmlSchemaClearAttrInfos(vctxt); if (inode->flags & XML_SCHEMA_NODE_INFO_ERR_NOT_EXPECTED) { /* * This element was not expected; * we will not validate child elements of broken parents. * Skip validation of all content of the parent. */ vctxt->skipDepth = vctxt->depth -1; goto end_elem; } if ((inode->typeDef == NULL) || (inode->flags & XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE)) { /* * 1. the type definition might be missing if the element was * error prone * 2. it might be abstract. */ goto end_elem; } /* * Check the content model. */ if ((inode->typeDef->contentType == XML_SCHEMA_CONTENT_MIXED) || (inode->typeDef->contentType == XML_SCHEMA_CONTENT_ELEMENTS)) { /* * Workaround for "anyType". */ if (inode->typeDef->builtInType == XML_SCHEMAS_ANYTYPE) goto character_content; if ((inode->flags & XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT) == 0) { xmlChar *values[10]; int terminal, nbval = 10, nbneg; if (inode->regexCtxt == NULL) { /* * Create the regex context. */ inode->regexCtxt = xmlRegNewExecCtxt(inode->typeDef->contModel, xmlSchemaVContentModelCallback, vctxt); if (inode->regexCtxt == NULL) { VERROR_INT("xmlSchemaValidatorPopElem", "failed to create a regex context"); goto internal_error; } } /* * Do not check further content if the node has been nilled */ if (INODE_NILLED(inode)) { ret = 0; goto skip_nilled; } /* * Get hold of the still expected content, since a further * call to xmlRegExecPushString() will lose this information. */ xmlRegExecNextValues(inode->regexCtxt, &nbval, &nbneg, &values[0], &terminal); ret = xmlRegExecPushString(inode->regexCtxt, NULL, NULL); if ((ret<0) || ((ret==0) && (!INODE_NILLED(inode)))) { /* * Still missing something. */ ret = 1; inode->flags |= XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT; xmlSchemaComplexTypeErr(ACTXT_CAST vctxt, XML_SCHEMAV_ELEMENT_CONTENT, NULL, NULL, "Missing child element(s)", nbval, nbneg, values); } else { /* * Content model is satisfied. */ ret = 0; } } } skip_nilled: if (inode->typeDef->contentType == XML_SCHEMA_CONTENT_ELEMENTS) goto end_elem; character_content: if (vctxt->value != NULL) { xmlSchemaFreeValue(vctxt->value); vctxt->value = NULL; } /* * Check character content. */ if (inode->decl == NULL) { /* * Speedup if no declaration exists. */ if (WXS_IS_SIMPLE(inode->typeDef)) { ret = xmlSchemaVCheckINodeDataType(vctxt, inode, inode->typeDef, inode->value); } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { ret = xmlSchemaVCheckINodeDataType(vctxt, inode, inode->typeDef->contentTypeDef, inode->value); } if (ret < 0) { VERROR_INT("xmlSchemaValidatorPopElem", "calling xmlSchemaVCheckCVCSimpleType()"); goto internal_error; } goto end_elem; } /* * cvc-elt (3.3.4) : 5 * The appropriate case among the following must be true: */ /* * cvc-elt (3.3.4) : 5.1 * If the declaration has a {value constraint}, * the item has neither element nor character [children] and * clause 3.2 has not applied, then all of the following must be true: */ if ((inode->decl->value != NULL) && (inode->flags & XML_SCHEMA_ELEM_INFO_EMPTY) && (! INODE_NILLED(inode))) { /* * cvc-elt (3.3.4) : 5.1.1 * If the `actual type definition` is a `local type definition` * then the canonical lexical representation of the {value constraint} * value must be a valid default for the `actual type definition` as * defined in Element Default Valid (Immediate) ($3.3.6). */ /* * NOTE: 'local' above means types acquired by xsi:type. * NOTE: Although the *canonical* value is stated, it is not * relevant if canonical or not. Additionally XML Schema 1.1 * will removed this requirement as well. */ if (inode->flags & XML_SCHEMA_ELEM_INFO_LOCAL_TYPE) { ret = xmlSchemaCheckCOSValidDefault(vctxt, inode->decl->value, &(inode->val)); if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaValidatorPopElem", "calling xmlSchemaCheckCOSValidDefault()"); goto internal_error; } goto end_elem; } /* * Stop here, to avoid redundant validation of the value * (see following). */ goto default_psvi; } /* * cvc-elt (3.3.4) : 5.1.2 * The element information item with the canonical lexical * representation of the {value constraint} value used as its * `normalized value` must be `valid` with respect to the * `actual type definition` as defined by Element Locally Valid (Type) * ($3.3.4). */ if (WXS_IS_SIMPLE(inode->typeDef)) { ret = xmlSchemaVCheckINodeDataType(vctxt, inode, inode->typeDef, inode->decl->value); } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { ret = xmlSchemaVCheckINodeDataType(vctxt, inode, inode->typeDef->contentTypeDef, inode->decl->value); } if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaValidatorPopElem", "calling xmlSchemaVCheckCVCSimpleType()"); goto internal_error; } goto end_elem; } default_psvi: /* * PSVI: Create a text node on the instance element. */ if ((vctxt->options & XML_SCHEMA_VAL_VC_I_CREATE) && (inode->node != NULL)) { xmlNodePtr textChild; xmlChar *normValue; /* * VAL TODO: Normalize the value. */ normValue = xmlSchemaNormalizeValue(inode->typeDef, inode->decl->value); if (normValue != NULL) { textChild = xmlNewDocText(inode->node->doc, BAD_CAST normValue); xmlFree(normValue); } else textChild = xmlNewDocText(inode->node->doc, inode->decl->value); if (textChild == NULL) { VERROR_INT("xmlSchemaValidatorPopElem", "calling xmlNewDocText()"); goto internal_error; } else xmlAddChild(inode->node, textChild); } } else if (! INODE_NILLED(inode)) { /* * 5.2.1 The element information item must be `valid` with respect * to the `actual type definition` as defined by Element Locally * Valid (Type) ($3.3.4). */ if (WXS_IS_SIMPLE(inode->typeDef)) { /* * SPEC (cvc-type) (3.1) * "If the type definition is a simple type definition, ..." * (3.1.3) "If clause 3.2 of Element Locally Valid * (Element) ($3.3.4) did not apply, then the `normalized value` * must be `valid` with respect to the type definition as defined * by String Valid ($3.14.4). */ ret = xmlSchemaVCheckINodeDataType(vctxt, inode, inode->typeDef, inode->value); } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { /* * SPEC (cvc-type) (3.2) "If the type definition is a complex type * definition, then the element information item must be * `valid` with respect to the type definition as per * Element Locally Valid (Complex Type) ($3.4.4);" * * SPEC (cvc-complex-type) (2.2) * "If the {content type} is a simple type definition, ... * the `normalized value` of the element information item is * `valid` with respect to that simple type definition as * defined by String Valid ($3.14.4)." */ ret = xmlSchemaVCheckINodeDataType(vctxt, inode, inode->typeDef->contentTypeDef, inode->value); } if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaValidatorPopElem", "calling xmlSchemaVCheckCVCSimpleType()"); goto internal_error; } goto end_elem; } /* * 5.2.2 If there is a fixed {value constraint} and clause 3.2 has * not applied, all of the following must be true: */ if ((inode->decl->value != NULL) && (inode->decl->flags & XML_SCHEMAS_ELEM_FIXED)) { /* * TODO: We will need a computed value, when comparison is * done on computed values. */ /* * 5.2.2.1 The element information item must have no element * information item [children]. */ if (inode->flags & XML_SCHEMA_ELEM_INFO_HAS_ELEM_CONTENT) { ret = XML_SCHEMAV_CVC_ELT_5_2_2_1; VERROR(ret, NULL, "The content must not contain element nodes since " "there is a fixed value constraint"); goto end_elem; } else { /* * 5.2.2.2 The appropriate case among the following must * be true: */ if (WXS_HAS_MIXED_CONTENT(inode->typeDef)) { /* * 5.2.2.2.1 If the {content type} of the `actual type * definition` is mixed, then the *initial value* of the * item must match the canonical lexical representation * of the {value constraint} value. * * ... the *initial value* of an element information * item is the string composed of, in order, the * [character code] of each character information item in * the [children] of that element information item. */ if (! xmlStrEqual(inode->value, inode->decl->value)){ /* * VAL TODO: Report invalid & expected values as well. * VAL TODO: Implement the canonical stuff. */ ret = XML_SCHEMAV_CVC_ELT_5_2_2_2_1; xmlSchemaCustomErr(ACTXT_CAST vctxt, ret, NULL, NULL, "The initial value '%s' does not match the fixed " "value constraint '%s'", inode->value, inode->decl->value); goto end_elem; } } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { /* * 5.2.2.2.2 If the {content type} of the `actual type * definition` is a simple type definition, then the * *actual value* of the item must match the canonical * lexical representation of the {value constraint} value. */ /* * VAL TODO: *actual value* is the normalized value, impl. * this. * VAL TODO: Report invalid & expected values as well. * VAL TODO: Implement a comparison with the computed values. */ if (! xmlStrEqual(inode->value, inode->decl->value)) { ret = XML_SCHEMAV_CVC_ELT_5_2_2_2_2; xmlSchemaCustomErr(ACTXT_CAST vctxt, ret, NULL, NULL, "The actual value '%s' does not match the fixed " "value constraint '%s'", inode->value, inode->decl->value); goto end_elem; } } } } } end_elem: if (vctxt->depth < 0) { /* TODO: raise error? */ return (0); } if (vctxt->depth == vctxt->skipDepth) vctxt->skipDepth = -1; /* * Evaluate the history of XPath state objects. */ if (inode->appliedXPath && (xmlSchemaXPathProcessHistory(vctxt, vctxt->depth) == -1)) goto internal_error; /* * MAYBE TODO: * SPEC (6) "The element information item must be `valid` with * respect to each of the {identity-constraint definitions} as per * Identity-constraint Satisfied ($3.11.4)." */ /* * PSVI TODO: If we expose IDC node-tables via PSVI then the tables * need to be built in any case. * We will currently build IDC node-tables and bubble them only if * keyrefs do exist. */ /* * Add the current IDC target-nodes to the IDC node-tables. */ if ((inode->idcMatchers != NULL) && (vctxt->hasKeyrefs || vctxt->createIDCNodeTables)) { if (xmlSchemaIDCFillNodeTables(vctxt, inode) == -1) goto internal_error; } /* * Validate IDC keyrefs. */ if (vctxt->inode->hasKeyrefs) if (xmlSchemaCheckCVCIDCKeyRef(vctxt) == -1) goto internal_error; /* * Merge/free the IDC table. */ if (inode->idcTable != NULL) { if ((vctxt->depth > 0) && (vctxt->hasKeyrefs || vctxt->createIDCNodeTables)) { /* * Merge the IDC node table with the table of the parent node. */ if (xmlSchemaBubbleIDCNodeTables(vctxt) == -1) goto internal_error; } } /* * Clear the current ielem. * VAL TODO: Don't free the PSVI IDC tables if they are * requested for the PSVI. */ xmlSchemaClearElemInfo(vctxt, inode); /* * Skip further processing if we are on the validation root. */ if (vctxt->depth == 0) { vctxt->depth--; vctxt->inode = NULL; return (0); } /* * Reset the keyrefDepth if needed. */ if (vctxt->aidcs != NULL) { xmlSchemaIDCAugPtr aidc = vctxt->aidcs; do { if (aidc->keyrefDepth == vctxt->depth) { /* * A 'keyrefDepth' of a key/unique IDC matches the current * depth, this means that we are leaving the scope of the * top-most keyref IDC which refers to this IDC. */ aidc->keyrefDepth = -1; } aidc = aidc->next; } while (aidc != NULL); } vctxt->depth--; vctxt->inode = vctxt->elemInfos[vctxt->depth]; /* * VAL TODO: 7 If the element information item is the `validation root`, it must be * `valid` per Validation Root Valid (ID/IDREF) ($3.3.4). */ return (ret); internal_error: vctxt->err = -1; return (-1); } /* * 3.4.4 Complex Type Definition Validation Rules * Validation Rule: Element Locally Valid (Complex Type) (cvc-complex-type) */ static int xmlSchemaValidateChildElem(xmlSchemaValidCtxtPtr vctxt) { xmlSchemaNodeInfoPtr pielem; xmlSchemaTypePtr ptype; int ret = 0; if (vctxt->depth <= 0) { VERROR_INT("xmlSchemaValidateChildElem", "not intended for the validation root"); return (-1); } pielem = vctxt->elemInfos[vctxt->depth -1]; if (pielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY) pielem->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; /* * Handle 'nilled' elements. */ if (INODE_NILLED(pielem)) { /* * SPEC (cvc-elt) (3.3.4) : (3.2.1) */ ACTIVATE_PARENT_ELEM; ret = XML_SCHEMAV_CVC_ELT_3_2_1; VERROR(ret, NULL, "Neither character nor element content is allowed, " "because the element was 'nilled'"); ACTIVATE_ELEM; goto unexpected_elem; } ptype = pielem->typeDef; if (ptype->builtInType == XML_SCHEMAS_ANYTYPE) { /* * Workaround for "anyType": we have currently no content model * assigned for "anyType", so handle it explicitly. * "anyType" has an unbounded, lax "any" wildcard. */ vctxt->inode->decl = xmlSchemaGetElem(vctxt->schema, vctxt->inode->localName, vctxt->inode->nsName); if (vctxt->inode->decl == NULL) { xmlSchemaAttrInfoPtr iattr; /* * Process "xsi:type". * SPEC (cvc-assess-elt) (1.2.1.2.1) - (1.2.1.2.3) */ iattr = xmlSchemaGetMetaAttrInfo(vctxt, XML_SCHEMA_ATTR_INFO_META_XSI_TYPE); if (iattr != NULL) { ret = xmlSchemaProcessXSIType(vctxt, iattr, &(vctxt->inode->typeDef), NULL); if (ret != 0) { if (ret == -1) { VERROR_INT("xmlSchemaValidateChildElem", "calling xmlSchemaProcessXSIType() to " "process the attribute 'xsi:nil'"); return (-1); } return (ret); } } else { /* * Fallback to "anyType". * * SPEC (cvc-assess-elt) * "If the item cannot be `strictly assessed`, [...] * an element information item's schema validity may be laxly * assessed if its `context-determined declaration` is not * skip by `validating` with respect to the `ur-type * definition` as per Element Locally Valid (Type) ($3.3.4)." */ vctxt->inode->typeDef = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); } } return (0); } switch (ptype->contentType) { case XML_SCHEMA_CONTENT_EMPTY: /* * SPEC (2.1) "If the {content type} is empty, then the * element information item has no character or element * information item [children]." */ ACTIVATE_PARENT_ELEM ret = XML_SCHEMAV_CVC_COMPLEX_TYPE_2_1; VERROR(ret, NULL, "Element content is not allowed, " "because the content type is empty"); ACTIVATE_ELEM goto unexpected_elem; break; case XML_SCHEMA_CONTENT_MIXED: case XML_SCHEMA_CONTENT_ELEMENTS: { xmlRegExecCtxtPtr regexCtxt; xmlChar *values[10]; int terminal, nbval = 10, nbneg; /* VAL TODO: Optimized "anyType" validation.*/ if (ptype->contModel == NULL) { VERROR_INT("xmlSchemaValidateChildElem", "type has elem content but no content model"); return (-1); } /* * Safety belt for evaluation if the cont. model was already * examined to be invalid. */ if (pielem->flags & XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT) { VERROR_INT("xmlSchemaValidateChildElem", "validating elem, but elem content is already invalid"); return (-1); } regexCtxt = pielem->regexCtxt; if (regexCtxt == NULL) { /* * Create the regex context. */ regexCtxt = xmlRegNewExecCtxt(ptype->contModel, xmlSchemaVContentModelCallback, vctxt); if (regexCtxt == NULL) { VERROR_INT("xmlSchemaValidateChildElem", "failed to create a regex context"); return (-1); } pielem->regexCtxt = regexCtxt; } /* * SPEC (2.4) "If the {content type} is element-only or mixed, * then the sequence of the element information item's * element information item [children], if any, taken in * order, is `valid` with respect to the {content type}'s * particle, as defined in Element Sequence Locally Valid * (Particle) ($3.9.4)." */ ret = xmlRegExecPushString2(regexCtxt, vctxt->inode->localName, vctxt->inode->nsName, vctxt->inode); if (vctxt->err == XML_SCHEMAV_INTERNAL) { VERROR_INT("xmlSchemaValidateChildElem", "calling xmlRegExecPushString2()"); return (-1); } if (ret < 0) { xmlRegExecErrInfo(regexCtxt, NULL, &nbval, &nbneg, &values[0], &terminal); xmlSchemaComplexTypeErr(ACTXT_CAST vctxt, XML_SCHEMAV_ELEMENT_CONTENT, NULL,NULL, "This element is not expected", nbval, nbneg, values); ret = vctxt->err; goto unexpected_elem; } else ret = 0; } break; case XML_SCHEMA_CONTENT_SIMPLE: case XML_SCHEMA_CONTENT_BASIC: ACTIVATE_PARENT_ELEM if (WXS_IS_COMPLEX(ptype)) { /* * SPEC (cvc-complex-type) (2.2) * "If the {content type} is a simple type definition, then * the element information item has no element information * item [children], ..." */ ret = XML_SCHEMAV_CVC_COMPLEX_TYPE_2_2; VERROR(ret, NULL, "Element content is not allowed, " "because the content type is a simple type definition"); } else { /* * SPEC (cvc-type) (3.1.2) "The element information item must * have no element information item [children]." */ ret = XML_SCHEMAV_CVC_TYPE_3_1_2; VERROR(ret, NULL, "Element content is not allowed, " "because the type definition is simple"); } ACTIVATE_ELEM ret = vctxt->err; goto unexpected_elem; break; default: break; } return (ret); unexpected_elem: /* * Pop this element and set the skipDepth to skip * all further content of the parent element. */ vctxt->skipDepth = vctxt->depth; vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_ERR_NOT_EXPECTED; pielem->flags |= XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT; return (ret); } #define XML_SCHEMA_PUSH_TEXT_PERSIST 1 #define XML_SCHEMA_PUSH_TEXT_CREATED 2 #define XML_SCHEMA_PUSH_TEXT_VOLATILE 3 static int xmlSchemaVPushText(xmlSchemaValidCtxtPtr vctxt, int nodeType, const xmlChar *value, int len, int mode, int *consumed) { /* * Unfortunately we have to duplicate the text sometimes. * OPTIMIZE: Maybe we could skip it, if: * 1. content type is simple * 2. whitespace is "collapse" * 3. it consists of whitespace only * * Process character content. */ if (consumed != NULL) *consumed = 0; if (INODE_NILLED(vctxt->inode)) { /* * SPEC cvc-elt (3.3.4 - 3.2.1) * "The element information item must have no character or * element information item [children]." */ VERROR(XML_SCHEMAV_CVC_ELT_3_2_1, NULL, "Neither character nor element content is allowed " "because the element is 'nilled'"); return (vctxt->err); } /* * SPEC (2.1) "If the {content type} is empty, then the * element information item has no character or element * information item [children]." */ if (vctxt->inode->typeDef->contentType == XML_SCHEMA_CONTENT_EMPTY) { VERROR(XML_SCHEMAV_CVC_COMPLEX_TYPE_2_1, NULL, "Character content is not allowed, " "because the content type is empty"); return (vctxt->err); } if (vctxt->inode->typeDef->contentType == XML_SCHEMA_CONTENT_ELEMENTS) { if ((nodeType != XML_TEXT_NODE) || (! xmlSchemaIsBlank((xmlChar *) value, len))) { /* * SPEC cvc-complex-type (2.3) * "If the {content type} is element-only, then the * element information item has no character information * item [children] other than those whose [character * code] is defined as a white space in [XML 1.0 (Second * Edition)]." */ VERROR(XML_SCHEMAV_CVC_COMPLEX_TYPE_2_3, NULL, "Character content other than whitespace is not allowed " "because the content type is 'element-only'"); return (vctxt->err); } return (0); } if ((value == NULL) || (value[0] == 0)) return (0); /* * Save the value. * NOTE that even if the content type is *mixed*, we need the * *initial value* for default/fixed value constraints. */ if ((vctxt->inode->typeDef->contentType == XML_SCHEMA_CONTENT_MIXED) && ((vctxt->inode->decl == NULL) || (vctxt->inode->decl->value == NULL))) return (0); if (vctxt->inode->value == NULL) { /* * Set the value. */ switch (mode) { case XML_SCHEMA_PUSH_TEXT_PERSIST: /* * When working on a tree. */ vctxt->inode->value = value; break; case XML_SCHEMA_PUSH_TEXT_CREATED: /* * When working with the reader. * The value will be freed by the element info. */ vctxt->inode->value = value; if (consumed != NULL) *consumed = 1; vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; break; case XML_SCHEMA_PUSH_TEXT_VOLATILE: /* * When working with SAX. * The value will be freed by the element info. */ if (len != -1) vctxt->inode->value = BAD_CAST xmlStrndup(value, len); else vctxt->inode->value = BAD_CAST xmlStrdup(value); vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; break; default: break; } } else { if (len < 0) len = xmlStrlen(value); /* * Concat the value. */ if (vctxt->inode->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES) { vctxt->inode->value = BAD_CAST xmlStrncat( (xmlChar *) vctxt->inode->value, value, len); } else { vctxt->inode->value = BAD_CAST xmlStrncatNew(vctxt->inode->value, value, len); vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; } } return (0); } static int xmlSchemaValidateElem(xmlSchemaValidCtxtPtr vctxt) { int ret = 0; if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) { VERROR_INT("xmlSchemaValidateElem", "in skip-state"); goto internal_error; } if (vctxt->xsiAssemble) { /* * We will stop validation if there was an error during * dynamic schema construction. * Note that we simply set @skipDepth to 0, this could * mean that a streaming document via SAX would be * still read to the end but it won't be validated any more. * TODO: If we are sure how to stop the validation at once * for all input scenarios, then this should be changed to * instantly stop the validation. */ ret = xmlSchemaAssembleByXSI(vctxt); if (ret != 0) { if (ret == -1) goto internal_error; vctxt->skipDepth = 0; return(ret); } /* * Augment the IDC definitions for the main schema and all imported ones * NOTE: main schema is the first in the imported list */ xmlHashScan(vctxt->schema->schemasImports, xmlSchemaAugmentImportedIDC, vctxt); } if (vctxt->depth > 0) { /* * Validate this element against the content model * of the parent. */ ret = xmlSchemaValidateChildElem(vctxt); if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaValidateElem", "calling xmlSchemaStreamValidateChildElement()"); goto internal_error; } goto exit; } if (vctxt->depth == vctxt->skipDepth) goto exit; if ((vctxt->inode->decl == NULL) && (vctxt->inode->typeDef == NULL)) { VERROR_INT("xmlSchemaValidateElem", "the child element was valid but neither the " "declaration nor the type was set"); goto internal_error; } } else { /* * Get the declaration of the validation root. */ vctxt->inode->decl = xmlSchemaGetElem(vctxt->schema, vctxt->inode->localName, vctxt->inode->nsName); if (vctxt->inode->decl == NULL) { ret = XML_SCHEMAV_CVC_ELT_1; VERROR(ret, NULL, "No matching global declaration available " "for the validation root"); goto exit; } } if (vctxt->inode->decl == NULL) goto type_validation; if (vctxt->inode->decl->type == XML_SCHEMA_TYPE_ANY) { int skip; /* * Wildcards. */ ret = xmlSchemaValidateElemWildcard(vctxt, &skip); if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaValidateElem", "calling xmlSchemaValidateElemWildcard()"); goto internal_error; } goto exit; } if (skip) { vctxt->skipDepth = vctxt->depth; goto exit; } /* * The declaration might be set by the wildcard validation, * when the processContents is "lax" or "strict". */ if (vctxt->inode->decl->type != XML_SCHEMA_TYPE_ELEMENT) { /* * Clear the "decl" field to not confuse further processing. */ vctxt->inode->decl = NULL; goto type_validation; } } /* * Validate against the declaration. */ ret = xmlSchemaValidateElemDecl(vctxt); if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaValidateElem", "calling xmlSchemaValidateElemDecl()"); goto internal_error; } goto exit; } /* * Validate against the type definition. */ type_validation: if (vctxt->inode->typeDef == NULL) { vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE; ret = XML_SCHEMAV_CVC_TYPE_1; VERROR(ret, NULL, "The type definition is absent"); goto exit; } if (vctxt->inode->typeDef->flags & XML_SCHEMAS_TYPE_ABSTRACT) { vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE; ret = XML_SCHEMAV_CVC_TYPE_2; VERROR(ret, NULL, "The type definition is abstract"); goto exit; } /* * Evaluate IDCs. Do it here, since new IDC matchers are registered * during validation against the declaration. This must be done * _before_ attribute validation. */ if (vctxt->xpathStates != NULL) { ret = xmlSchemaXPathEvaluate(vctxt, XML_ELEMENT_NODE); vctxt->inode->appliedXPath = 1; if (ret == -1) { VERROR_INT("xmlSchemaValidateElem", "calling xmlSchemaXPathEvaluate()"); goto internal_error; } } /* * Validate attributes. */ if (WXS_IS_COMPLEX(vctxt->inode->typeDef)) { if ((vctxt->nbAttrInfos != 0) || (vctxt->inode->typeDef->attrUses != NULL)) { ret = xmlSchemaVAttributesComplex(vctxt); } } else if (vctxt->nbAttrInfos != 0) { ret = xmlSchemaVAttributesSimple(vctxt); } /* * Clear registered attributes. */ if (vctxt->nbAttrInfos != 0) xmlSchemaClearAttrInfos(vctxt); if (ret == -1) { VERROR_INT("xmlSchemaValidateElem", "calling attributes validation"); goto internal_error; } /* * Don't return an error if attributes are invalid on purpose. */ ret = 0; exit: if (ret != 0) vctxt->skipDepth = vctxt->depth; return (ret); internal_error: return (-1); } #ifdef XML_SCHEMA_READER_ENABLED static int xmlSchemaVReaderWalk(xmlSchemaValidCtxtPtr vctxt) { const int WHTSP = 13, SIGN_WHTSP = 14, END_ELEM = 15; int depth, nodeType, ret = 0, consumed; xmlSchemaNodeInfoPtr ielem; vctxt->depth = -1; ret = xmlTextReaderRead(vctxt->reader); /* * Move to the document element. */ while (ret == 1) { nodeType = xmlTextReaderNodeType(vctxt->reader); if (nodeType == XML_ELEMENT_NODE) goto root_found; ret = xmlTextReaderRead(vctxt->reader); } goto exit; root_found: do { depth = xmlTextReaderDepth(vctxt->reader); nodeType = xmlTextReaderNodeType(vctxt->reader); if (nodeType == XML_ELEMENT_NODE) { vctxt->depth++; if (xmlSchemaValidatorPushElem(vctxt) == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlSchemaValidatorPushElem()"); goto internal_error; } ielem = vctxt->inode; ielem->localName = xmlTextReaderLocalName(vctxt->reader); ielem->nsName = xmlTextReaderNamespaceUri(vctxt->reader); ielem->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES; /* * Is the element empty? */ ret = xmlTextReaderIsEmptyElement(vctxt->reader); if (ret == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlTextReaderIsEmptyElement()"); goto internal_error; } if (ret) { ielem->flags |= XML_SCHEMA_ELEM_INFO_EMPTY; } /* * Register attributes. */ vctxt->nbAttrInfos = 0; ret = xmlTextReaderMoveToFirstAttribute(vctxt->reader); if (ret == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlTextReaderMoveToFirstAttribute()"); goto internal_error; } if (ret == 1) { do { /* * VAL TODO: How do we know that the reader works on a * node tree, to be able to pass a node here? */ if (xmlSchemaValidatorPushAttribute(vctxt, NULL, (const xmlChar *) xmlTextReaderLocalName(vctxt->reader), xmlTextReaderNamespaceUri(vctxt->reader), 1, xmlTextReaderValue(vctxt->reader), 1) == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlSchemaValidatorPushAttribute()"); goto internal_error; } ret = xmlTextReaderMoveToNextAttribute(vctxt->reader); if (ret == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlTextReaderMoveToFirstAttribute()"); goto internal_error; } } while (ret == 1); /* * Back to element position. */ ret = xmlTextReaderMoveToElement(vctxt->reader); if (ret == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlTextReaderMoveToElement()"); goto internal_error; } } /* * Validate the element. */ ret= xmlSchemaValidateElem(vctxt); if (ret != 0) { if (ret == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlSchemaValidateElem()"); goto internal_error; } goto exit; } if (vctxt->depth == vctxt->skipDepth) { int curDepth; /* * Skip all content. */ if ((ielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY) == 0) { ret = xmlTextReaderRead(vctxt->reader); curDepth = xmlTextReaderDepth(vctxt->reader); while ((ret == 1) && (curDepth != depth)) { ret = xmlTextReaderRead(vctxt->reader); curDepth = xmlTextReaderDepth(vctxt->reader); } if (ret < 0) { /* * VAL TODO: A reader error occurred; what to do here? */ ret = 1; goto exit; } } goto leave_elem; } /* * READER VAL TODO: Is an END_ELEM really never called * if the elem is empty? */ if (ielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY) goto leave_elem; } else if (nodeType == END_ELEM) { /* * Process END of element. */ leave_elem: ret = xmlSchemaValidatorPopElem(vctxt); if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlSchemaValidatorPopElem()"); goto internal_error; } goto exit; } if (vctxt->depth >= 0) ielem = vctxt->inode; else ielem = NULL; } else if ((nodeType == XML_TEXT_NODE) || (nodeType == XML_CDATA_SECTION_NODE) || (nodeType == WHTSP) || (nodeType == SIGN_WHTSP)) { /* * Process character content. */ xmlChar *value; if ((nodeType == WHTSP) || (nodeType == SIGN_WHTSP)) nodeType = XML_TEXT_NODE; value = xmlTextReaderValue(vctxt->reader); ret = xmlSchemaVPushText(vctxt, nodeType, BAD_CAST value, -1, XML_SCHEMA_PUSH_TEXT_CREATED, &consumed); if (! consumed) xmlFree(value); if (ret == -1) { VERROR_INT("xmlSchemaVReaderWalk", "calling xmlSchemaVPushText()"); goto internal_error; } } else if ((nodeType == XML_ENTITY_NODE) || (nodeType == XML_ENTITY_REF_NODE)) { /* * VAL TODO: What to do with entities? */ TODO } /* * Read next node. */ ret = xmlTextReaderRead(vctxt->reader); } while (ret == 1); exit: return (ret); internal_error: return (-1); } #endif /************************************************************************ * * * SAX validation handlers * * * ************************************************************************/ /* * Process text content. */ static void xmlSchemaSAXHandleText(void *ctx, const xmlChar * ch, int len) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; if (vctxt->depth < 0) return; if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) return; if (vctxt->inode->flags & XML_SCHEMA_ELEM_INFO_EMPTY) vctxt->inode->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; if (xmlSchemaVPushText(vctxt, XML_TEXT_NODE, ch, len, XML_SCHEMA_PUSH_TEXT_VOLATILE, NULL) == -1) { VERROR_INT("xmlSchemaSAXHandleCDataSection", "calling xmlSchemaVPushText()"); vctxt->err = -1; xmlStopParser(vctxt->parserCtxt); } } /* * Process CDATA content. */ static void xmlSchemaSAXHandleCDataSection(void *ctx, const xmlChar * ch, int len) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; if (vctxt->depth < 0) return; if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) return; if (vctxt->inode->flags & XML_SCHEMA_ELEM_INFO_EMPTY) vctxt->inode->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; if (xmlSchemaVPushText(vctxt, XML_CDATA_SECTION_NODE, ch, len, XML_SCHEMA_PUSH_TEXT_VOLATILE, NULL) == -1) { VERROR_INT("xmlSchemaSAXHandleCDataSection", "calling xmlSchemaVPushText()"); vctxt->err = -1; xmlStopParser(vctxt->parserCtxt); } } static void xmlSchemaSAXHandleReference(void *ctx ATTRIBUTE_UNUSED, const xmlChar * name ATTRIBUTE_UNUSED) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; if (vctxt->depth < 0) return; if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) return; /* SAX VAL TODO: What to do here? */ } static void xmlSchemaSAXHandleStartElementNs(void *ctx, const xmlChar * localname, const xmlChar * prefix ATTRIBUTE_UNUSED, const xmlChar * URI, int nb_namespaces, const xmlChar ** namespaces, int nb_attributes, int nb_defaulted ATTRIBUTE_UNUSED, const xmlChar ** attributes) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; int ret; xmlSchemaNodeInfoPtr ielem; int i, j; /* * SAX VAL TODO: What to do with nb_defaulted? */ /* * Skip elements if inside a "skip" wildcard or invalid. */ vctxt->depth++; if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) return; /* * Push the element. */ if (xmlSchemaValidatorPushElem(vctxt) == -1) { VERROR_INT("xmlSchemaSAXHandleStartElementNs", "calling xmlSchemaValidatorPushElem()"); goto internal_error; } ielem = vctxt->inode; /* * TODO: Is this OK? */ ielem->nodeLine = xmlSAX2GetLineNumber(vctxt->parserCtxt); ielem->localName = localname; ielem->nsName = URI; ielem->flags |= XML_SCHEMA_ELEM_INFO_EMPTY; /* * Register namespaces on the elem info. */ if (nb_namespaces != 0) { /* * Although the parser builds its own namespace list, * we have no access to it, so we'll use an own one. */ for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) { /* * Store prefix and namespace name. */ if (ielem->nsBindings == NULL) { ielem->nsBindings = (const xmlChar **) xmlMalloc(10 * sizeof(const xmlChar *)); if (ielem->nsBindings == NULL) { xmlSchemaVErrMemory(vctxt); goto internal_error; } ielem->nbNsBindings = 0; ielem->sizeNsBindings = 5; } else if (ielem->sizeNsBindings <= ielem->nbNsBindings) { ielem->sizeNsBindings *= 2; ielem->nsBindings = (const xmlChar **) xmlRealloc( (void *) ielem->nsBindings, ielem->sizeNsBindings * 2 * sizeof(const xmlChar *)); if (ielem->nsBindings == NULL) { xmlSchemaVErrMemory(vctxt); goto internal_error; } } ielem->nsBindings[ielem->nbNsBindings * 2] = namespaces[j]; if (namespaces[j+1][0] == 0) { /* * Handle xmlns="". */ ielem->nsBindings[ielem->nbNsBindings * 2 + 1] = NULL; } else ielem->nsBindings[ielem->nbNsBindings * 2 + 1] = namespaces[j+1]; ielem->nbNsBindings++; } } /* * Register attributes. * SAX VAL TODO: We are not adding namespace declaration * attributes yet. */ if (nb_attributes != 0) { int valueLen, k, l; xmlChar *value; for (j = 0, i = 0; i < nb_attributes; i++, j += 5) { /* * Duplicate the value, changing any & to a literal ampersand. * * libxml2 differs from normal SAX here in that it escapes all ampersands * as & instead of delivering the raw converted string. Changing the * behavior at this point would break applications that use this API, so * we are forced to work around it. */ valueLen = attributes[j+4] - attributes[j+3]; value = xmlMallocAtomic(valueLen + 1); if (value == NULL) { xmlSchemaVErrMemory(vctxt); goto internal_error; } for (k = 0, l = 0; k < valueLen; l++) { if (k < valueLen - 4 && attributes[j+3][k+0] == '&' && attributes[j+3][k+1] == '#' && attributes[j+3][k+2] == '3' && attributes[j+3][k+3] == '8' && attributes[j+3][k+4] == ';') { value[l] = '&'; k += 5; } else { value[l] = attributes[j+3][k]; k++; } } value[l] = '\0'; /* * TODO: Set the node line. */ ret = xmlSchemaValidatorPushAttribute(vctxt, NULL, ielem->nodeLine, attributes[j], attributes[j+2], 0, value, 1); if (ret == -1) { VERROR_INT("xmlSchemaSAXHandleStartElementNs", "calling xmlSchemaValidatorPushAttribute()"); goto internal_error; } } } /* * Validate the element. */ ret = xmlSchemaValidateElem(vctxt); if (ret != 0) { if (ret == -1) { VERROR_INT("xmlSchemaSAXHandleStartElementNs", "calling xmlSchemaValidateElem()"); goto internal_error; } goto exit; } exit: return; internal_error: vctxt->err = -1; xmlStopParser(vctxt->parserCtxt); return; } static void xmlSchemaSAXHandleEndElementNs(void *ctx, const xmlChar * localname ATTRIBUTE_UNUSED, const xmlChar * prefix ATTRIBUTE_UNUSED, const xmlChar * URI ATTRIBUTE_UNUSED) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; int res; /* * Skip elements if inside a "skip" wildcard or if invalid. */ if (vctxt->skipDepth != -1) { if (vctxt->depth > vctxt->skipDepth) { vctxt->depth--; return; } else vctxt->skipDepth = -1; } /* * SAX VAL TODO: Just a temporary check. */ if ((!xmlStrEqual(vctxt->inode->localName, localname)) || (!xmlStrEqual(vctxt->inode->nsName, URI))) { VERROR_INT("xmlSchemaSAXHandleEndElementNs", "elem pop mismatch"); } res = xmlSchemaValidatorPopElem(vctxt); if (res != 0) { if (res < 0) { VERROR_INT("xmlSchemaSAXHandleEndElementNs", "calling xmlSchemaValidatorPopElem()"); goto internal_error; } goto exit; } exit: return; internal_error: vctxt->err = -1; xmlStopParser(vctxt->parserCtxt); return; } /************************************************************************ * * * Validation interfaces * * * ************************************************************************/ /** * xmlSchemaNewValidCtxt: * @schema: a precompiled XML Schemas * * Create an XML Schemas validation context based on the given schema. * * Returns the validation context or NULL in case of error */ xmlSchemaValidCtxtPtr xmlSchemaNewValidCtxt(xmlSchemaPtr schema) { xmlSchemaValidCtxtPtr ret; ret = (xmlSchemaValidCtxtPtr) xmlMalloc(sizeof(xmlSchemaValidCtxt)); if (ret == NULL) { xmlSchemaVErrMemory(NULL); return (NULL); } memset(ret, 0, sizeof(xmlSchemaValidCtxt)); ret->type = XML_SCHEMA_CTXT_VALIDATOR; ret->dict = xmlDictCreate(); ret->nodeQNames = xmlSchemaItemListCreate(); ret->schema = schema; return (ret); } /** * xmlSchemaValidateSetFilename: * @vctxt: the schema validation context * @filename: the file name * * Workaround to provide file error reporting information when this is * not provided by current APIs */ void xmlSchemaValidateSetFilename(xmlSchemaValidCtxtPtr vctxt, const char *filename) { if (vctxt == NULL) return; if (vctxt->filename != NULL) xmlFree(vctxt->filename); if (filename != NULL) vctxt->filename = (char *) xmlStrdup((const xmlChar *) filename); else vctxt->filename = NULL; } /** * xmlSchemaClearValidCtxt: * @vctxt: the schema validation context * * Free the resources associated to the schema validation context; * leaves some fields alive intended for reuse of the context. */ static void xmlSchemaClearValidCtxt(xmlSchemaValidCtxtPtr vctxt) { if (vctxt == NULL) return; /* * TODO: Should we clear the flags? * Might be problematic if one reuses the context * and assumes that the options remain the same. */ vctxt->flags = 0; vctxt->validationRoot = NULL; vctxt->doc = NULL; #ifdef LIBXML_READER_ENABLED vctxt->reader = NULL; #endif vctxt->hasKeyrefs = 0; if (vctxt->value != NULL) { xmlSchemaFreeValue(vctxt->value); vctxt->value = NULL; } /* * Augmented IDC information. */ if (vctxt->aidcs != NULL) { xmlSchemaIDCAugPtr cur = vctxt->aidcs, next; do { next = cur->next; xmlFree(cur); cur = next; } while (cur != NULL); vctxt->aidcs = NULL; } if (vctxt->idcNodes != NULL) { int i; xmlSchemaPSVIIDCNodePtr item; for (i = 0; i < vctxt->nbIdcNodes; i++) { item = vctxt->idcNodes[i]; xmlFree(item->keys); xmlFree(item); } xmlFree(vctxt->idcNodes); vctxt->idcNodes = NULL; vctxt->nbIdcNodes = 0; vctxt->sizeIdcNodes = 0; } if (vctxt->idcKeys != NULL) { int i; for (i = 0; i < vctxt->nbIdcKeys; i++) xmlSchemaIDCFreeKey(vctxt->idcKeys[i]); xmlFree(vctxt->idcKeys); vctxt->idcKeys = NULL; vctxt->nbIdcKeys = 0; vctxt->sizeIdcKeys = 0; } /* * Note that we won't delete the XPath state pool here. */ if (vctxt->xpathStates != NULL) { xmlSchemaFreeIDCStateObjList(vctxt->xpathStates); vctxt->xpathStates = NULL; } /* * Attribute info. */ if (vctxt->nbAttrInfos != 0) { xmlSchemaClearAttrInfos(vctxt); } /* * Element info. */ if (vctxt->elemInfos != NULL) { int i; xmlSchemaNodeInfoPtr ei; for (i = 0; i < vctxt->sizeElemInfos; i++) { ei = vctxt->elemInfos[i]; if (ei == NULL) break; xmlSchemaClearElemInfo(vctxt, ei); } } xmlSchemaItemListClear(vctxt->nodeQNames); /* Recreate the dict. */ xmlDictFree(vctxt->dict); /* * TODO: Is is save to recreate it? Do we have a scenario * where the user provides the dict? */ vctxt->dict = xmlDictCreate(); if (vctxt->filename != NULL) { xmlFree(vctxt->filename); vctxt->filename = NULL; } /* * Note that some cleanup functions can move items to the cache, * so the cache shouldn't be freed too early. */ if (vctxt->idcMatcherCache != NULL) { xmlSchemaIDCMatcherPtr matcher = vctxt->idcMatcherCache, tmp; while (matcher) { tmp = matcher; matcher = matcher->nextCached; xmlSchemaIDCFreeMatcherList(tmp); } vctxt->idcMatcherCache = NULL; } } /** * xmlSchemaFreeValidCtxt: * @ctxt: the schema validation context * * Free the resources associated to the schema validation context */ void xmlSchemaFreeValidCtxt(xmlSchemaValidCtxtPtr ctxt) { if (ctxt == NULL) return; if (ctxt->value != NULL) xmlSchemaFreeValue(ctxt->value); if (ctxt->pctxt != NULL) xmlSchemaFreeParserCtxt(ctxt->pctxt); if (ctxt->idcNodes != NULL) { int i; xmlSchemaPSVIIDCNodePtr item; for (i = 0; i < ctxt->nbIdcNodes; i++) { item = ctxt->idcNodes[i]; xmlFree(item->keys); xmlFree(item); } xmlFree(ctxt->idcNodes); } if (ctxt->idcKeys != NULL) { int i; for (i = 0; i < ctxt->nbIdcKeys; i++) xmlSchemaIDCFreeKey(ctxt->idcKeys[i]); xmlFree(ctxt->idcKeys); } if (ctxt->xpathStates != NULL) { xmlSchemaFreeIDCStateObjList(ctxt->xpathStates); ctxt->xpathStates = NULL; } if (ctxt->xpathStatePool != NULL) { xmlSchemaFreeIDCStateObjList(ctxt->xpathStatePool); ctxt->xpathStatePool = NULL; } /* * Augmented IDC information. */ if (ctxt->aidcs != NULL) { xmlSchemaIDCAugPtr cur = ctxt->aidcs, next; do { next = cur->next; xmlFree(cur); cur = next; } while (cur != NULL); } if (ctxt->attrInfos != NULL) { int i; xmlSchemaAttrInfoPtr attr; /* Just a paranoid call to the cleanup. */ if (ctxt->nbAttrInfos != 0) xmlSchemaClearAttrInfos(ctxt); for (i = 0; i < ctxt->sizeAttrInfos; i++) { attr = ctxt->attrInfos[i]; xmlFree(attr); } xmlFree(ctxt->attrInfos); } if (ctxt->elemInfos != NULL) { int i; xmlSchemaNodeInfoPtr ei; for (i = 0; i < ctxt->sizeElemInfos; i++) { ei = ctxt->elemInfos[i]; if (ei == NULL) break; xmlSchemaClearElemInfo(ctxt, ei); xmlFree(ei); } xmlFree(ctxt->elemInfos); } if (ctxt->nodeQNames != NULL) xmlSchemaItemListFree(ctxt->nodeQNames); if (ctxt->dict != NULL) xmlDictFree(ctxt->dict); if (ctxt->filename != NULL) xmlFree(ctxt->filename); xmlFree(ctxt); } /** * xmlSchemaIsValid: * @ctxt: the schema validation context * * Check if any error was detected during validation. * * Returns 1 if valid so far, 0 if errors were detected, and -1 in case * of internal error. */ int xmlSchemaIsValid(xmlSchemaValidCtxtPtr ctxt) { if (ctxt == NULL) return(-1); return(ctxt->err == 0); } /** * xmlSchemaSetValidErrors: * @ctxt: a schema validation context * @err: the error function * @warn: the warning function * @ctx: the functions context * * DEPRECATED: Use xmlSchemaSetValidStructuredErrors. * * Set the error and warning callback information */ void xmlSchemaSetValidErrors(xmlSchemaValidCtxtPtr ctxt, xmlSchemaValidityErrorFunc err, xmlSchemaValidityWarningFunc warn, void *ctx) { if (ctxt == NULL) return; ctxt->error = err; ctxt->warning = warn; ctxt->errCtxt = ctx; if (ctxt->pctxt != NULL) xmlSchemaSetParserErrors(ctxt->pctxt, err, warn, ctx); } /** * xmlSchemaSetValidStructuredErrors: * @ctxt: a schema validation context * @serror: the structured error function * @ctx: the functions context * * Set the structured error callback */ void xmlSchemaSetValidStructuredErrors(xmlSchemaValidCtxtPtr ctxt, xmlStructuredErrorFunc serror, void *ctx) { if (ctxt == NULL) return; ctxt->serror = serror; ctxt->error = NULL; ctxt->warning = NULL; ctxt->errCtxt = ctx; if (ctxt->pctxt != NULL) xmlSchemaSetParserStructuredErrors(ctxt->pctxt, serror, ctx); } /** * xmlSchemaGetValidErrors: * @ctxt: a XML-Schema validation context * @err: the error function result * @warn: the warning function result * @ctx: the functions context result * * Get the error and warning callback information * * Returns -1 in case of error and 0 otherwise */ int xmlSchemaGetValidErrors(xmlSchemaValidCtxtPtr ctxt, xmlSchemaValidityErrorFunc * err, xmlSchemaValidityWarningFunc * warn, void **ctx) { if (ctxt == NULL) return (-1); if (err != NULL) *err = ctxt->error; if (warn != NULL) *warn = ctxt->warning; if (ctx != NULL) *ctx = ctxt->errCtxt; return (0); } /** * xmlSchemaSetValidOptions: * @ctxt: a schema validation context * @options: a combination of xmlSchemaValidOption * * Sets the options to be used during the validation. * * Returns 0 in case of success, -1 in case of an * API error. */ int xmlSchemaSetValidOptions(xmlSchemaValidCtxtPtr ctxt, int options) { int i; if (ctxt == NULL) return (-1); /* * WARNING: Change the start value if adding to the * xmlSchemaValidOption. * TODO: Is there an other, more easy to maintain, * way? */ for (i = 1; i < (int) sizeof(int) * 8; i++) { if (options & 1<options = options; return (0); } /** * xmlSchemaValidCtxtGetOptions: * @ctxt: a schema validation context * * Get the validation context options. * * Returns the option combination or -1 on error. */ int xmlSchemaValidCtxtGetOptions(xmlSchemaValidCtxtPtr ctxt) { if (ctxt == NULL) return (-1); else return (ctxt->options); } static int xmlSchemaVDocWalk(xmlSchemaValidCtxtPtr vctxt) { xmlAttrPtr attr; int ret = 0; xmlSchemaNodeInfoPtr ielem = NULL; xmlNodePtr node, valRoot; const xmlChar *nsName; /* DOC VAL TODO: Move this to the start function. */ if (vctxt->validationRoot != NULL) valRoot = vctxt->validationRoot; else valRoot = xmlDocGetRootElement(vctxt->doc); if (valRoot == NULL) { /* VAL TODO: Error code? */ VERROR(1, NULL, "The document has no document element"); return (1); } vctxt->depth = -1; vctxt->validationRoot = valRoot; node = valRoot; while (node != NULL) { if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) goto next_sibling; if (node->type == XML_ELEMENT_NODE) { /* * Init the node-info. */ vctxt->depth++; if (xmlSchemaValidatorPushElem(vctxt) == -1) goto internal_error; ielem = vctxt->inode; ielem->node = node; ielem->nodeLine = node->line; ielem->localName = node->name; if (node->ns != NULL) ielem->nsName = node->ns->href; ielem->flags |= XML_SCHEMA_ELEM_INFO_EMPTY; /* * Register attributes. * DOC VAL TODO: We do not register namespace declaration * attributes yet. */ vctxt->nbAttrInfos = 0; if (node->properties != NULL) { attr = node->properties; do { if (attr->ns != NULL) nsName = attr->ns->href; else nsName = NULL; ret = xmlSchemaValidatorPushAttribute(vctxt, (xmlNodePtr) attr, /* * Note that we give it the line number of the * parent element. */ ielem->nodeLine, attr->name, nsName, 0, xmlNodeListGetString(attr->doc, attr->children, 1), 1); if (ret == -1) { VERROR_INT("xmlSchemaDocWalk", "calling xmlSchemaValidatorPushAttribute()"); goto internal_error; } attr = attr->next; } while (attr); } /* * Validate the element. */ ret = xmlSchemaValidateElem(vctxt); if (ret != 0) { if (ret == -1) { VERROR_INT("xmlSchemaDocWalk", "calling xmlSchemaValidateElem()"); goto internal_error; } /* * Don't stop validation; just skip the content * of this element. */ goto leave_node; } if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) goto leave_node; } else if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) { /* * Process character content. */ if ((ielem != NULL) && (ielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY)) ielem->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; ret = xmlSchemaVPushText(vctxt, node->type, node->content, -1, XML_SCHEMA_PUSH_TEXT_PERSIST, NULL); if (ret < 0) { VERROR_INT("xmlSchemaVDocWalk", "calling xmlSchemaVPushText()"); goto internal_error; } /* * DOC VAL TODO: Should we skip further validation of the * element content here? */ } else if ((node->type == XML_ENTITY_NODE) || (node->type == XML_ENTITY_REF_NODE)) { /* * DOC VAL TODO: What to do with entities? */ VERROR_INT("xmlSchemaVDocWalk", "there is at least one entity reference in the node-tree " "currently being validated. Processing of entities with " "this XML Schema processor is not supported (yet). Please " "substitute entities before validation."); goto internal_error; } else { goto leave_node; /* * DOC VAL TODO: XInclude nodes, etc. */ } /* * Walk the doc. */ if (node->children != NULL) { node = node->children; continue; } leave_node: if (node->type == XML_ELEMENT_NODE) { /* * Leaving the scope of an element. */ if (node != vctxt->inode->node) { VERROR_INT("xmlSchemaVDocWalk", "element position mismatch"); goto internal_error; } ret = xmlSchemaValidatorPopElem(vctxt); if (ret != 0) { if (ret < 0) { VERROR_INT("xmlSchemaVDocWalk", "calling xmlSchemaValidatorPopElem()"); goto internal_error; } } if (node == valRoot) goto exit; } next_sibling: if (node->next != NULL) node = node->next; else { node = node->parent; goto leave_node; } } exit: return (ret); internal_error: return (-1); } static int xmlSchemaPreRun(xmlSchemaValidCtxtPtr vctxt) { /* * Some initialization. */ vctxt->err = 0; vctxt->nberrors = 0; vctxt->depth = -1; vctxt->skipDepth = -1; vctxt->hasKeyrefs = 0; #ifdef ENABLE_IDC_NODE_TABLES_TEST vctxt->createIDCNodeTables = 1; #else vctxt->createIDCNodeTables = 0; #endif /* * Create a schema + parser if necessary. */ if (vctxt->schema == NULL) { xmlSchemaParserCtxtPtr pctxt; vctxt->xsiAssemble = 1; /* * If not schema was given then we will create a schema * dynamically using XSI schema locations. * * Create the schema parser context. */ if ((vctxt->pctxt == NULL) && (xmlSchemaCreatePCtxtOnVCtxt(vctxt) == -1)) return (-1); pctxt = vctxt->pctxt; pctxt->xsiAssemble = 1; /* * Create the schema. */ vctxt->schema = xmlSchemaNewSchema(pctxt); if (vctxt->schema == NULL) return (-1); /* * Create the schema construction context. */ pctxt->constructor = xmlSchemaConstructionCtxtCreate(pctxt->dict); if (pctxt->constructor == NULL) return(-1); pctxt->constructor->mainSchema = vctxt->schema; /* * Take ownership of the constructor to be able to free it. */ pctxt->ownsConstructor = 1; } /* * Augment the IDC definitions for the main schema and all imported ones * NOTE: main schema if the first in the imported list */ xmlHashScan(vctxt->schema->schemasImports, xmlSchemaAugmentImportedIDC, vctxt); return(0); } static void xmlSchemaPostRun(xmlSchemaValidCtxtPtr vctxt) { if (vctxt->xsiAssemble) { if (vctxt->schema != NULL) { xmlSchemaFree(vctxt->schema); vctxt->schema = NULL; } } xmlSchemaClearValidCtxt(vctxt); } static int xmlSchemaVStart(xmlSchemaValidCtxtPtr vctxt) { int ret = 0; if (xmlSchemaPreRun(vctxt) < 0) return(-1); if (vctxt->doc != NULL) { /* * Tree validation. */ ret = xmlSchemaVDocWalk(vctxt); #ifdef LIBXML_READER_ENABLED } else if (vctxt->reader != NULL) { /* * XML Reader validation. */ #ifdef XML_SCHEMA_READER_ENABLED ret = xmlSchemaVReaderWalk(vctxt); #endif #endif } else if ((vctxt->sax != NULL) && (vctxt->parserCtxt != NULL)) { /* * SAX validation. */ ret = xmlParseDocument(vctxt->parserCtxt); } else { VERROR_INT("xmlSchemaVStart", "no instance to validate"); ret = -1; } xmlSchemaPostRun(vctxt); if (ret == 0) ret = vctxt->err; return (ret); } /** * xmlSchemaValidateOneElement: * @ctxt: a schema validation context * @elem: an element node * * Validate a branch of a tree, starting with the given @elem. * * Returns 0 if the element and its subtree is valid, a positive error * code number otherwise and -1 in case of an internal or API error. */ int xmlSchemaValidateOneElement(xmlSchemaValidCtxtPtr ctxt, xmlNodePtr elem) { if ((ctxt == NULL) || (elem == NULL) || (elem->type != XML_ELEMENT_NODE)) return (-1); if (ctxt->schema == NULL) return (-1); ctxt->doc = elem->doc; ctxt->node = elem; ctxt->validationRoot = elem; return(xmlSchemaVStart(ctxt)); } /** * xmlSchemaValidateDoc: * @ctxt: a schema validation context * @doc: a parsed document tree * * Validate a document tree in memory. * * Returns 0 if the document is schemas valid, a positive error code * number otherwise and -1 in case of internal or API error. */ int xmlSchemaValidateDoc(xmlSchemaValidCtxtPtr ctxt, xmlDocPtr doc) { if ((ctxt == NULL) || (doc == NULL)) return (-1); ctxt->doc = doc; ctxt->node = xmlDocGetRootElement(doc); if (ctxt->node == NULL) { xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAV_DOCUMENT_ELEMENT_MISSING, (xmlNodePtr) doc, NULL, "The document has no document element", NULL, NULL); return (ctxt->err); } ctxt->validationRoot = ctxt->node; return (xmlSchemaVStart(ctxt)); } /************************************************************************ * * * Function and data for SAX streaming API * * * ************************************************************************/ typedef struct _xmlSchemaSplitSAXData xmlSchemaSplitSAXData; typedef xmlSchemaSplitSAXData *xmlSchemaSplitSAXDataPtr; struct _xmlSchemaSplitSAXData { xmlSAXHandlerPtr user_sax; void *user_data; xmlSchemaValidCtxtPtr ctxt; xmlSAXHandlerPtr schemas_sax; }; #define XML_SAX_PLUG_MAGIC 0xdc43ba21 struct _xmlSchemaSAXPlug { unsigned int magic; /* the original callbacks information */ xmlSAXHandlerPtr *user_sax_ptr; xmlSAXHandlerPtr user_sax; void **user_data_ptr; void *user_data; /* the block plugged back and validation information */ xmlSAXHandler schemas_sax; xmlSchemaValidCtxtPtr ctxt; }; /* All those functions just bounces to the user provided SAX handlers */ static void internalSubsetSplit(void *ctx, const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->internalSubset != NULL)) ctxt->user_sax->internalSubset(ctxt->user_data, name, ExternalID, SystemID); } static int isStandaloneSplit(void *ctx) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->isStandalone != NULL)) return(ctxt->user_sax->isStandalone(ctxt->user_data)); return(0); } static int hasInternalSubsetSplit(void *ctx) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->hasInternalSubset != NULL)) return(ctxt->user_sax->hasInternalSubset(ctxt->user_data)); return(0); } static int hasExternalSubsetSplit(void *ctx) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->hasExternalSubset != NULL)) return(ctxt->user_sax->hasExternalSubset(ctxt->user_data)); return(0); } static void externalSubsetSplit(void *ctx, const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->externalSubset != NULL)) ctxt->user_sax->externalSubset(ctxt->user_data, name, ExternalID, SystemID); } static xmlParserInputPtr resolveEntitySplit(void *ctx, const xmlChar *publicId, const xmlChar *systemId) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->resolveEntity != NULL)) return(ctxt->user_sax->resolveEntity(ctxt->user_data, publicId, systemId)); return(NULL); } static xmlEntityPtr getEntitySplit(void *ctx, const xmlChar *name) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->getEntity != NULL)) return(ctxt->user_sax->getEntity(ctxt->user_data, name)); return(NULL); } static xmlEntityPtr getParameterEntitySplit(void *ctx, const xmlChar *name) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->getParameterEntity != NULL)) return(ctxt->user_sax->getParameterEntity(ctxt->user_data, name)); return(NULL); } static void entityDeclSplit(void *ctx, const xmlChar *name, int type, const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->entityDecl != NULL)) ctxt->user_sax->entityDecl(ctxt->user_data, name, type, publicId, systemId, content); } static void attributeDeclSplit(void *ctx, const xmlChar * elem, const xmlChar * name, int type, int def, const xmlChar * defaultValue, xmlEnumerationPtr tree) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->attributeDecl != NULL)) { ctxt->user_sax->attributeDecl(ctxt->user_data, elem, name, type, def, defaultValue, tree); } else { xmlFreeEnumeration(tree); } } static void elementDeclSplit(void *ctx, const xmlChar *name, int type, xmlElementContentPtr content) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->elementDecl != NULL)) ctxt->user_sax->elementDecl(ctxt->user_data, name, type, content); } static void notationDeclSplit(void *ctx, const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->notationDecl != NULL)) ctxt->user_sax->notationDecl(ctxt->user_data, name, publicId, systemId); } static void unparsedEntityDeclSplit(void *ctx, const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId, const xmlChar *notationName) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->unparsedEntityDecl != NULL)) ctxt->user_sax->unparsedEntityDecl(ctxt->user_data, name, publicId, systemId, notationName); } static void setDocumentLocatorSplit(void *ctx, xmlSAXLocatorPtr loc) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->setDocumentLocator != NULL)) ctxt->user_sax->setDocumentLocator(ctxt->user_data, loc); } static void startDocumentSplit(void *ctx) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->startDocument != NULL)) ctxt->user_sax->startDocument(ctxt->user_data); } static void endDocumentSplit(void *ctx) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->endDocument != NULL)) ctxt->user_sax->endDocument(ctxt->user_data); } static void processingInstructionSplit(void *ctx, const xmlChar *target, const xmlChar *data) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->processingInstruction != NULL)) ctxt->user_sax->processingInstruction(ctxt->user_data, target, data); } static void commentSplit(void *ctx, const xmlChar *value) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->comment != NULL)) ctxt->user_sax->comment(ctxt->user_data, value); } /* * Varargs error callbacks to the user application, harder ... */ static void warningSplit(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->warning != NULL)) { /* TODO */ } } static void errorSplit(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->error != NULL)) { /* TODO */ } } static void fatalErrorSplit(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->fatalError != NULL)) { /* TODO */ } } /* * Those are function where both the user handler and the schemas handler * need to be called. */ static void charactersSplit(void *ctx, const xmlChar *ch, int len) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if (ctxt == NULL) return; if ((ctxt->user_sax != NULL) && (ctxt->user_sax->characters != NULL)) ctxt->user_sax->characters(ctxt->user_data, ch, len); if (ctxt->ctxt != NULL) xmlSchemaSAXHandleText(ctxt->ctxt, ch, len); } static void ignorableWhitespaceSplit(void *ctx, const xmlChar *ch, int len) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if (ctxt == NULL) return; if ((ctxt->user_sax != NULL) && (ctxt->user_sax->ignorableWhitespace != NULL)) ctxt->user_sax->ignorableWhitespace(ctxt->user_data, ch, len); if (ctxt->ctxt != NULL) xmlSchemaSAXHandleText(ctxt->ctxt, ch, len); } static void cdataBlockSplit(void *ctx, const xmlChar *value, int len) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if (ctxt == NULL) return; if ((ctxt->user_sax != NULL) && (ctxt->user_sax->cdataBlock != NULL)) ctxt->user_sax->cdataBlock(ctxt->user_data, value, len); if (ctxt->ctxt != NULL) xmlSchemaSAXHandleCDataSection(ctxt->ctxt, value, len); } static void referenceSplit(void *ctx, const xmlChar *name) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if (ctxt == NULL) return; if ((ctxt != NULL) && (ctxt->user_sax != NULL) && (ctxt->user_sax->reference != NULL)) ctxt->user_sax->reference(ctxt->user_data, name); if (ctxt->ctxt != NULL) xmlSchemaSAXHandleReference(ctxt->user_data, name); } static void startElementNsSplit(void *ctx, const xmlChar * localname, const xmlChar * prefix, const xmlChar * URI, int nb_namespaces, const xmlChar ** namespaces, int nb_attributes, int nb_defaulted, const xmlChar ** attributes) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if (ctxt == NULL) return; if ((ctxt->user_sax != NULL) && (ctxt->user_sax->startElementNs != NULL)) ctxt->user_sax->startElementNs(ctxt->user_data, localname, prefix, URI, nb_namespaces, namespaces, nb_attributes, nb_defaulted, attributes); if (ctxt->ctxt != NULL) xmlSchemaSAXHandleStartElementNs(ctxt->ctxt, localname, prefix, URI, nb_namespaces, namespaces, nb_attributes, nb_defaulted, attributes); } static void endElementNsSplit(void *ctx, const xmlChar * localname, const xmlChar * prefix, const xmlChar * URI) { xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; if (ctxt == NULL) return; if ((ctxt->user_sax != NULL) && (ctxt->user_sax->endElementNs != NULL)) ctxt->user_sax->endElementNs(ctxt->user_data, localname, prefix, URI); if (ctxt->ctxt != NULL) xmlSchemaSAXHandleEndElementNs(ctxt->ctxt, localname, prefix, URI); } /** * xmlSchemaSAXPlug: * @ctxt: a schema validation context * @sax: a pointer to the original xmlSAXHandlerPtr * @user_data: a pointer to the original SAX user data pointer * * Plug a SAX based validation layer in a SAX parsing event flow. * The original @saxptr and @dataptr data are replaced by new pointers * but the calls to the original will be maintained. * * Returns a pointer to a data structure needed to unplug the validation layer * or NULL in case of errors. */ xmlSchemaSAXPlugPtr xmlSchemaSAXPlug(xmlSchemaValidCtxtPtr ctxt, xmlSAXHandlerPtr *sax, void **user_data) { xmlSchemaSAXPlugPtr ret; xmlSAXHandlerPtr old_sax; if ((ctxt == NULL) || (sax == NULL) || (user_data == NULL)) return(NULL); /* * We only allow to plug into SAX2 event streams */ old_sax = *sax; if ((old_sax != NULL) && (old_sax->initialized != XML_SAX2_MAGIC)) return(NULL); if ((old_sax != NULL) && (old_sax->startElementNs == NULL) && (old_sax->endElementNs == NULL) && ((old_sax->startElement != NULL) || (old_sax->endElement != NULL))) return(NULL); /* * everything seems right allocate the local data needed for that layer */ ret = (xmlSchemaSAXPlugPtr) xmlMalloc(sizeof(xmlSchemaSAXPlugStruct)); if (ret == NULL) { return(NULL); } memset(ret, 0, sizeof(xmlSchemaSAXPlugStruct)); ret->magic = XML_SAX_PLUG_MAGIC; ret->schemas_sax.initialized = XML_SAX2_MAGIC; ret->ctxt = ctxt; ret->user_sax_ptr = sax; ret->user_sax = old_sax; if (old_sax == NULL) { /* * go direct, no need for the split block and functions. */ ret->schemas_sax.startElementNs = xmlSchemaSAXHandleStartElementNs; ret->schemas_sax.endElementNs = xmlSchemaSAXHandleEndElementNs; /* * Note that we use the same text-function for both, to prevent * the parser from testing for ignorable whitespace. */ ret->schemas_sax.ignorableWhitespace = xmlSchemaSAXHandleText; ret->schemas_sax.characters = xmlSchemaSAXHandleText; ret->schemas_sax.cdataBlock = xmlSchemaSAXHandleCDataSection; ret->schemas_sax.reference = xmlSchemaSAXHandleReference; ret->user_data = ctxt; *user_data = ctxt; } else { /* * for each callback unused by Schemas initialize it to the Split * routine only if non NULL in the user block, this can speed up * things at the SAX level. */ if (old_sax->internalSubset != NULL) ret->schemas_sax.internalSubset = internalSubsetSplit; if (old_sax->isStandalone != NULL) ret->schemas_sax.isStandalone = isStandaloneSplit; if (old_sax->hasInternalSubset != NULL) ret->schemas_sax.hasInternalSubset = hasInternalSubsetSplit; if (old_sax->hasExternalSubset != NULL) ret->schemas_sax.hasExternalSubset = hasExternalSubsetSplit; if (old_sax->resolveEntity != NULL) ret->schemas_sax.resolveEntity = resolveEntitySplit; if (old_sax->getEntity != NULL) ret->schemas_sax.getEntity = getEntitySplit; if (old_sax->entityDecl != NULL) ret->schemas_sax.entityDecl = entityDeclSplit; if (old_sax->notationDecl != NULL) ret->schemas_sax.notationDecl = notationDeclSplit; if (old_sax->attributeDecl != NULL) ret->schemas_sax.attributeDecl = attributeDeclSplit; if (old_sax->elementDecl != NULL) ret->schemas_sax.elementDecl = elementDeclSplit; if (old_sax->unparsedEntityDecl != NULL) ret->schemas_sax.unparsedEntityDecl = unparsedEntityDeclSplit; if (old_sax->setDocumentLocator != NULL) ret->schemas_sax.setDocumentLocator = setDocumentLocatorSplit; if (old_sax->startDocument != NULL) ret->schemas_sax.startDocument = startDocumentSplit; if (old_sax->endDocument != NULL) ret->schemas_sax.endDocument = endDocumentSplit; if (old_sax->processingInstruction != NULL) ret->schemas_sax.processingInstruction = processingInstructionSplit; if (old_sax->comment != NULL) ret->schemas_sax.comment = commentSplit; if (old_sax->warning != NULL) ret->schemas_sax.warning = warningSplit; if (old_sax->error != NULL) ret->schemas_sax.error = errorSplit; if (old_sax->fatalError != NULL) ret->schemas_sax.fatalError = fatalErrorSplit; if (old_sax->getParameterEntity != NULL) ret->schemas_sax.getParameterEntity = getParameterEntitySplit; if (old_sax->externalSubset != NULL) ret->schemas_sax.externalSubset = externalSubsetSplit; /* * the 6 schemas callback have to go to the splitter functions * Note that we use the same text-function for ignorableWhitespace * if possible, to prevent the parser from testing for ignorable * whitespace. */ ret->schemas_sax.characters = charactersSplit; if ((old_sax->ignorableWhitespace != NULL) && (old_sax->ignorableWhitespace != old_sax->characters)) ret->schemas_sax.ignorableWhitespace = ignorableWhitespaceSplit; else ret->schemas_sax.ignorableWhitespace = charactersSplit; ret->schemas_sax.cdataBlock = cdataBlockSplit; ret->schemas_sax.reference = referenceSplit; ret->schemas_sax.startElementNs = startElementNsSplit; ret->schemas_sax.endElementNs = endElementNsSplit; ret->user_data_ptr = user_data; ret->user_data = *user_data; *user_data = ret; } /* * plug the pointers back. */ *sax = &(ret->schemas_sax); ctxt->sax = *sax; ctxt->flags |= XML_SCHEMA_VALID_CTXT_FLAG_STREAM; xmlSchemaPreRun(ctxt); return(ret); } /** * xmlSchemaSAXUnplug: * @plug: a data structure returned by xmlSchemaSAXPlug * * Unplug a SAX based validation layer in a SAX parsing event flow. * The original pointers used in the call are restored. * * Returns 0 in case of success and -1 in case of failure. */ int xmlSchemaSAXUnplug(xmlSchemaSAXPlugPtr plug) { xmlSAXHandlerPtr *sax; void **user_data; if ((plug == NULL) || (plug->magic != XML_SAX_PLUG_MAGIC)) return(-1); plug->magic = 0; xmlSchemaPostRun(plug->ctxt); /* restore the data */ sax = plug->user_sax_ptr; *sax = plug->user_sax; if (plug->user_sax != NULL) { user_data = plug->user_data_ptr; *user_data = plug->user_data; } /* free and return */ xmlFree(plug); return(0); } /** * xmlSchemaValidateSetLocator: * @vctxt: a schema validation context * @f: the locator function pointer * @ctxt: the locator context * * Allows to set a locator function to the validation context, * which will be used to provide file and line information since * those are not provided as part of the SAX validation flow * Setting @f to NULL disable the locator. */ void xmlSchemaValidateSetLocator(xmlSchemaValidCtxtPtr vctxt, xmlSchemaValidityLocatorFunc f, void *ctxt) { if (vctxt == NULL) return; vctxt->locFunc = f; vctxt->locCtxt = ctxt; } /** * xmlSchemaValidateStreamLocator: * @ctx: the xmlTextReaderPtr used * @file: returned file information * @line: returned line information * * Internal locator function for the readers * * Returns 0 in case the Schema validation could be (de)activated and * -1 in case of error. */ static int xmlSchemaValidateStreamLocator(void *ctx, const char **file, unsigned long *line) { xmlParserCtxtPtr ctxt; if ((ctx == NULL) || ((file == NULL) && (line == NULL))) return(-1); if (file != NULL) *file = NULL; if (line != NULL) *line = 0; ctxt = (xmlParserCtxtPtr) ctx; if (ctxt->input != NULL) { if (file != NULL) *file = ctxt->input->filename; if (line != NULL) *line = ctxt->input->line; return(0); } return(-1); } /** * xmlSchemaValidateStreamInternal: * @ctxt: a schema validation context * @pctxt: a parser context * * Returns 0 if the document is schemas valid, a positive error code * number otherwise and -1 in case of internal or API error. */ static int xmlSchemaValidateStreamInternal(xmlSchemaValidCtxtPtr ctxt, xmlParserCtxtPtr pctxt) { xmlSchemaSAXPlugPtr plug = NULL; int ret; pctxt->linenumbers = 1; xmlSchemaValidateSetLocator(ctxt, xmlSchemaValidateStreamLocator, pctxt); ctxt->parserCtxt = pctxt; ctxt->input = pctxt->input->buf; /* * Plug the validation and launch the parsing */ plug = xmlSchemaSAXPlug(ctxt, &(pctxt->sax), &(pctxt->userData)); if (plug == NULL) { ret = -1; goto done; } ctxt->input = pctxt->input->buf; ctxt->sax = pctxt->sax; ctxt->flags |= XML_SCHEMA_VALID_CTXT_FLAG_STREAM; ret = xmlSchemaVStart(ctxt); if ((ret == 0) && (! ctxt->parserCtxt->wellFormed)) { ret = ctxt->parserCtxt->errNo; if (ret == 0) ret = 1; } done: ctxt->parserCtxt = NULL; ctxt->sax = NULL; ctxt->input = NULL; if (plug != NULL) { xmlSchemaSAXUnplug(plug); } return (ret); } /** * xmlSchemaValidateStream: * @ctxt: a schema validation context * @input: the input to use for reading the data * @enc: an optional encoding information * @sax: a SAX handler for the resulting events * @user_data: the context to provide to the SAX handler. * * Validate an input based on a flow of SAX event from the parser * and forward the events to the @sax handler with the provided @user_data * the user provided @sax handler must be a SAX2 one. * * Returns 0 if the document is schemas valid, a positive error code * number otherwise and -1 in case of internal or API error. */ int xmlSchemaValidateStream(xmlSchemaValidCtxtPtr ctxt, xmlParserInputBufferPtr input, xmlCharEncoding enc, xmlSAXHandlerPtr sax, void *user_data) { xmlParserCtxtPtr pctxt = NULL; xmlParserInputPtr inputStream = NULL; int ret; if ((ctxt == NULL) || (input == NULL)) return (-1); /* * prepare the parser */ if (sax != NULL) { pctxt = xmlNewSAXParserCtxt(sax, user_data); if (pctxt == NULL) return (-1); } else { pctxt = xmlNewParserCtxt(); if (pctxt == NULL) return (-1); /* We really want pctxt->sax to be NULL here. */ xmlFree(pctxt->sax); pctxt->sax = NULL; } #if 0 if (options) xmlCtxtUseOptions(pctxt, options); #endif inputStream = xmlNewIOInputStream(pctxt, input, enc);; if (inputStream == NULL) { ret = -1; goto done; } inputPush(pctxt, inputStream); ctxt->enc = enc; ret = xmlSchemaValidateStreamInternal(ctxt, pctxt); done: /* cleanup */ if (pctxt != NULL) { xmlFreeParserCtxt(pctxt); } return (ret); } /** * xmlSchemaValidateFile: * @ctxt: a schema validation context * @filename: the URI of the instance * @options: a future set of options, currently unused * * Do a schemas validation of the given resource, it will use the * SAX streamable validation internally. * * Returns 0 if the document is valid, a positive error code * number otherwise and -1 in case of an internal or API error. */ int xmlSchemaValidateFile(xmlSchemaValidCtxtPtr ctxt, const char * filename, int options ATTRIBUTE_UNUSED) { int ret; xmlParserCtxtPtr pctxt = NULL; if ((ctxt == NULL) || (filename == NULL)) return (-1); pctxt = xmlCreateURLParserCtxt(filename, 0); if (pctxt == NULL) return (-1); /* We really want pctxt->sax to be NULL here. */ xmlFree(pctxt->sax); pctxt->sax = NULL; ret = xmlSchemaValidateStreamInternal(ctxt, pctxt); xmlFreeParserCtxt(pctxt); return (ret); } /** * xmlSchemaValidCtxtGetParserCtxt: * @ctxt: a schema validation context * * allow access to the parser context of the schema validation context * * Returns the parser context of the schema validation context or NULL * in case of error. */ xmlParserCtxtPtr xmlSchemaValidCtxtGetParserCtxt(xmlSchemaValidCtxtPtr ctxt) { if (ctxt == NULL) return(NULL); return (ctxt->parserCtxt); } #endif /* LIBXML_SCHEMAS_ENABLED */