/* * 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: * - Elimated 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. */ #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 /* #define DEBUG 1 */ /* #define DEBUG_CONTENT 1 */ /* #define DEBUG_TYPE 1 */ /* #define DEBUG_CONTENT_REGEXP 1 */ /* #define DEBUG_AUTOMATA 1 */ /* #define DEBUG_IDC */ /* #define DEBUG_IDC_NODE_TABLE */ /* #define WXS_ELEM_DECL_CONS_ENABLED */ #ifdef DEBUG_IDC #ifndef DEBUG_IDC_NODE_TABLE #define DEBUG_IDC_NODE_TABLE #endif #endif /* #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 TODO \ xmlGenericError(xmlGenericErrorContext, \ "Unimplemented block at %s:%d\n", \ __FILE__, __LINE__); #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_CAST (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) \ xmlSchemaAddItemSize(&(WXS_BUCKET(ctx)->locals), 10, item) #define WXS_ADD_GLOBAL(ctx, item) \ xmlSchemaAddItemSize(&(WXS_BUCKET(ctx)->globals), 5, item) #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 */ }; 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; }; /** * 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; /* dictionnary 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 */ }; /* * 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 informations */ int sizeElemInfos; xmlSchemaNodeInfoPtr inode; /* the current element information */ xmlSchemaIDCAugPtr aidcs; /* a list of augmented IDC informations */ 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; }; /************************************************************************ * * * 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); 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) { 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) { 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 * * 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(xmlSchemaValPtr val, xmlSchemaWhitespaceValueType ws, xmlChar **retValue) { 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; } 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); } /** * 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); } 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 (*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 * * * ************************************************************************/ #if 0 static void xmlSchemaErrMemory(const char *msg) { __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, NULL, NULL, msg); } #endif static void xmlSchemaPSimpleErr(const char *msg) { __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, NULL, NULL, msg); } /** * xmlSchemaPErrMemory: * @node: a context node * @extra: extra informations * * Handle an out of memory condition */ static void xmlSchemaPErrMemory(xmlSchemaParserCtxtPtr ctxt, const char *extra, xmlNodePtr node) { if (ctxt != NULL) ctxt->nberrors++; __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL, extra); } /** * 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 xmlSchemaPErr(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int error, const char *msg, const xmlChar * str1, const xmlChar * str2) { xmlGenericErrorFunc channel = NULL; xmlStructuredErrorFunc schannel = NULL; void *data = NULL; if (ctxt != NULL) { ctxt->nberrors++; ctxt->err = error; channel = ctxt->error; data = ctxt->errCtxt; schannel = ctxt->serror; } __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP, error, XML_ERR_ERROR, NULL, 0, (const char *) str1, (const char *) str2, NULL, 0, 0, msg, str1, str2); } /** * 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 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 xmlSchemaPErrExt(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int error, 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) { xmlGenericErrorFunc channel = NULL; xmlStructuredErrorFunc schannel = NULL; void *data = NULL; if (ctxt != NULL) { ctxt->nberrors++; ctxt->err = error; channel = ctxt->error; data = ctxt->errCtxt; schannel = ctxt->serror; } __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP, error, XML_ERR_ERROR, NULL, 0, (const char *) strData1, (const char *) strData2, (const char *) strData3, 0, 0, msg, str1, str2, str3, str4, str5); } /************************************************************************ * * * Allround error functions * * * ************************************************************************/ /** * xmlSchemaVTypeErrMemory: * @node: a context node * @extra: extra informations * * Handle an out of memory condition */ static void xmlSchemaVErrMemory(xmlSchemaValidCtxtPtr ctxt, const char *extra, xmlNodePtr node) { if (ctxt != NULL) { ctxt->nberrors++; ctxt->err = XML_SCHEMAV_INTERNAL; } __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL, extra); } static void xmlSchemaPSimpleInternalErr(xmlNodePtr node, const char *msg, const xmlChar *str) { __xmlSimpleError(XML_FROM_SCHEMASP, XML_SCHEMAP_INTERNAL, node, msg, (const char *) str); } #define WXS_ERROR_TYPE_ERROR 1 #define WXS_ERROR_TYPE_WARNING 2 /** * 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 xmlSchemaErr4Line(xmlSchemaAbstractCtxtPtr ctxt, xmlErrorLevel errorLevel, int error, xmlNodePtr node, int line, const char *msg, const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, const xmlChar *str4) { xmlStructuredErrorFunc schannel = NULL; xmlGenericErrorFunc channel = NULL; void *data = NULL; if (ctxt != NULL) { if (ctxt->type == XML_SCHEMA_CTXT_VALIDATOR) { xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctxt; const char *file = NULL; int col = 0; if (errorLevel != XML_ERR_WARNING) { vctxt->nberrors++; vctxt->err = error; channel = vctxt->error; } else { channel = vctxt->warning; } schannel = vctxt->serror; data = vctxt->errCtxt; /* * 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; 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; __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASV, error, errorLevel, file, line, (const char *) str1, (const char *) str2, (const char *) str3, 0, col, msg, str1, str2, str3, str4); } else if (ctxt->type == XML_SCHEMA_CTXT_PARSER) { xmlSchemaParserCtxtPtr pctxt = (xmlSchemaParserCtxtPtr) ctxt; if (errorLevel != XML_ERR_WARNING) { pctxt->nberrors++; pctxt->err = error; channel = pctxt->error; } else { channel = pctxt->warning; } schannel = pctxt->serror; data = pctxt->errCtxt; __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP, error, errorLevel, NULL, 0, (const char *) str1, (const char *) str2, (const char *) str3, 0, 0, msg, str1, str2, str3, str4); } else { TODO } } } /** * 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 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 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 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 save 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); } /* * 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 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: "); msg = xmlStrcat(msg, BAD_CAST funcName); msg = xmlStrcat(msg, BAD_CAST ", "); msg = xmlStrcat(msg, BAD_CAST message); msg = xmlStrcat(msg, BAD_CAST ".\n"); if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR) xmlSchemaErr(actxt, XML_SCHEMAV_INTERNAL, NULL, (const char *) msg, str1, str2); else if (actxt->type == XML_SCHEMA_CTXT_PARSER) xmlSchemaErr(actxt, XML_SCHEMAP_INTERNAL, NULL, (const char *) msg, str1, str2); FREE_AND_NULL(msg) } static void xmlSchemaInternalErr(xmlSchemaAbstractCtxtPtr actxt, const char *funcName, const char *message) { xmlSchemaInternalErr2(actxt, funcName, message, NULL, NULL); } #if 0 static void 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 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 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 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 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:"); msg = xmlStrcat(msg, type->name); } else msg = xmlStrcat(msg, xmlSchemaFormatQName(&str, type->targetNamespace, type->name)); 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 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, BAD_CAST 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 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 * @ownerDes: the designation of the owner * @ownerName: the name of the owner * @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 * @ownerDes: the designation of the owner * @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 * @ownerDes: the designation of the attribute's owner * @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 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 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 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 * @ownerDes: the designation of the owner * @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 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:"); msg = xmlStrcat(msg, type->name); } else msg = xmlStrcat(msg, xmlSchemaFormatQName(&str, type->targetNamespace, type->name)); 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) { msg = xmlStrcat(msg, BAD_CAST " Expected is '"); msg = xmlStrcat(msg, BAD_CAST expected); 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 * @onwerDes: the designation of the holder of the content * @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, "allocating schema", NULL); 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, "allocating annotation", node); 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, "allocating an item list structure", 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->items == NULL) { list->items = (void **) xmlMalloc( 20 * sizeof(void *)); if (list->items == NULL) { xmlSchemaPErrMemory(NULL, "allocating new item list", NULL); return(-1); } list->sizeItems = 20; } 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, "growing item list", NULL); list->sizeItems = 0; return(-1); } } 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, "allocating new item list", 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, "growing item list", NULL); list->sizeItems = 0; return(-1); } } list->items[list->nbItems++] = item; return(0); } static int xmlSchemaItemListInsert(xmlSchemaItemListPtr list, void *item, int idx) { if (list->items == NULL) { list->items = (void **) xmlMalloc( 20 * sizeof(void *)); if (list->items == NULL) { xmlSchemaPErrMemory(NULL, "allocating new item list", NULL); return(-1); } list->sizeItems = 20; } 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, "growing item list", 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); } #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, "allocating new item list", 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, "growing item list", 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)) { xmlSchemaPSimpleErr("Internal error: xmlSchemaItemListRemove, " "index error.\n"); 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 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, "allocating schema bucket", NULL); return(NULL); } memset(ret, 0, size); ret->targetNamespace = targetNamespace; ret->type = type; ret->globals = xmlSchemaItemListCreate(); if (ret->globals == NULL) { xmlFree(ret); return(NULL); } ret->locals = xmlSchemaItemListCreate(); if (ret->locals == NULL) { xmlFree(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); } } xmlSchemaItemListAdd(mainSchema->includes, ret); } /* * 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); } xmlSchemaItemListAddSize(*list, initialSize, item); return(0); } /** * 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; 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. */ xmlSchemaPSimpleInternalErr(NULL, "Internal error: xmlSchemaComponentListFree, " "unexpected component type '%s'\n", (const xmlChar *) WXS_ITEM_TYPE_NAME(item)); } break; } } list->nbItems = 0; } } /** * xmlSchemaFree: * @schema: a schema structure * * Deallocate a Schema structure. */ void xmlSchemaFree(xmlSchemaPtr schema) { if (schema == NULL) return; /* @volatiles is not used anymore :-/ */ if (schema->volatiles != NULL) TODO /* * 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, (xmlHashDeallocator) xmlSchemaBucketFree); 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(xmlSchemaElementPtr elem, FILE * output, const xmlChar * name ATTRIBUTE_UNUSED, const xmlChar * namespace ATTRIBUTE_UNUSED, const xmlChar * context ATTRIBUTE_UNUSED) { 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 intentation * * 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 } /** * 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, (xmlHashScanner) xmlSchemaTypeDump, output); xmlHashScanFull(schema->elemDecl, (xmlHashScannerFull) xmlSchemaElementDump, output); } #ifdef DEBUG_IDC_NODE_TABLE /** * xmlSchemaDebugDumpIDCTable: * @vctxt: the WXS validation context * * Displays the current IDC table for debug purposes. */ static void xmlSchemaDebugDumpIDCTable(FILE * output, const xmlChar *namespaceName, const xmlChar *localName, xmlSchemaPSVIIDCBindingPtr bind) { xmlChar *str = NULL; const xmlChar *value; xmlSchemaPSVIIDCNodePtr tab; xmlSchemaPSVIIDCKeyPtr key; int i, j, res; fprintf(output, "IDC: TABLES on '%s'\n", xmlSchemaFormatQName(&str, namespaceName, localName)); FREE_AND_NULL(str) if (bind == NULL) return; do { fprintf(output, "IDC: BINDING '%s' (%d)\n", xmlSchemaGetComponentQName(&str, bind->definition), bind->nbNodes); FREE_AND_NULL(str) for (i = 0; i < bind->nbNodes; i++) { tab = bind->nodeTable[i]; fprintf(output, " ( "); for (j = 0; j < bind->definition->nbFields; j++) { key = tab->keys[j]; if ((key != NULL) && (key->val != NULL)) { res = xmlSchemaGetCanonValue(key->val, &value); if (res >= 0) fprintf(output, "'%s' ", value); else fprintf(output, "CANON-VALUE-FAILED "); if (res == 0) FREE_AND_NULL(value) } else if (key != NULL) fprintf(output, "(no val), "); else fprintf(output, "(key missing), "); } fprintf(output, ")\n"); } if (bind->dupls && bind->dupls->nbItems) { fprintf(output, "IDC: dupls (%d):\n", bind->dupls->nbItems); for (i = 0; i < bind->dupls->nbItems; i++) { tab = bind->dupls->items[i]; fprintf(output, " ( "); for (j = 0; j < bind->definition->nbFields; j++) { key = tab->keys[j]; if ((key != NULL) && (key->val != NULL)) { res = xmlSchemaGetCanonValue(key->val, &value); if (res >= 0) fprintf(output, "'%s' ", value); else fprintf(output, "CANON-VALUE-FAILED "); if (res == 0) FREE_AND_NULL(value) } else if (key != NULL) fprintf(output, "(no val), "); else fprintf(output, "(key missing), "); } fprintf(output, ")\n"); } } bind = bind->next; } while (bind != NULL); } #endif /* DEBUG_IDC */ #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); 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: #ifdef DEBUG if (ret == NULL) { if (nsName == NULL) fprintf(stderr, "Unable to lookup element decl. %s", name); else fprintf(stderr, "Unable to lookup element decl. %s:%s", name, nsName); } #endif 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: #ifdef DEBUG if (ret == NULL) { if (nsName == NULL) fprintf(stderr, "Unable to lookup type %s", name); else fprintf(stderr, "Unable to lookup type %s:%s", name, nsName); } #endif 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: #ifdef DEBUG if (ret == NULL) { if (nsName == NULL) fprintf(stderr, "Unable to lookup attribute %s", name); else fprintf(stderr, "Unable to lookup attribute %s:%s", name, nsName); } #endif 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; } */ #ifdef DEBUG if (ret == NULL) { if (nsName == NULL) fprintf(stderr, "Unable to lookup attribute group %s", name); else fprintf(stderr, "Unable to lookup attribute group %s:%s", name, nsName); } #endif 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: #ifdef DEBUG if (ret == NULL) { if (nsName == NULL) fprintf(stderr, "Unable to lookup group %s", name); else fprintf(stderr, "Unable to lookup group %s:%s", name, nsName); } #endif 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 struture 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, "add annotation", NULL); 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 Attrribute declaration * *WARNING* this interface is highly subject to change * * Returns the new struture 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, "allocating attribute", NULL); 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 Attrribute declaration * *WARNING* this interface is highly subject to change * * Returns the new struture 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, "allocating attribute", NULL); 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, "allocating redefinition info", NULL); 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 Attrribute Group definition. * * Returns the new struture 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, "allocating attribute group", NULL); 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 struture 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, "allocating element", NULL); 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 struture 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, "allocating type", NULL); 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, "allocating QName reference item", NULL); 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, "allocating attribute use prohibition", NULL); 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 struture 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, "allocating model group component", NULL); 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 struture 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); #ifdef DEBUG #ifndef VBOX fprintf(stderr, "Adding particle component\n"); #endif /* !VBOX */ #endif ret = (xmlSchemaParticlePtr) xmlMalloc(sizeof(xmlSchemaParticle)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt, "allocating particle component", NULL); 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 struture 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, "adding group", NULL); 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 struture or NULL in case of error */ static xmlSchemaWildcardNsPtr xmlSchemaNewWildcardNsConstraint(xmlSchemaParserCtxtPtr ctxt) { xmlSchemaWildcardNsPtr ret; ret = (xmlSchemaWildcardNsPtr) xmlMalloc(sizeof(xmlSchemaWildcardNs)); if (ret == NULL) { xmlSchemaPErrMemory(ctxt, "creating wildcard namespace constraint", NULL); 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, "allocating an identity-constraint definition", NULL); 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 struture 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, "adding wildcard", NULL); 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 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, "allocating a substitution group container", 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 * @ownerDes: the designation of the parent element * @ownerItem: the parent as a schema object * @value: the QName value * @local: the resulting local part if found, the attribute value otherwise * @uri: the resulting namespace URI if found * * 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) *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 * @ownerDes: the designation of the owner element * @ownerItem: the owner as a schema object * @attr: the attribute node * @local: the resulting local part if found, the attribute value otherwise * @uri: the resulting namespace URI if found * * 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 * @ownerDes: the designation of the parent element * @ownerItem: the owner as a schema object * @ownerElem: the parent node of the attribute * @name: the name of the attribute * @local: the resulting local part if found, the attribute value otherwise * @uri: the resulting namespace URI if found * * 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 * @schema: the schema context * @ownerDes: the designation of the parent element * @ownerItem: the owner as a schema object * @ownerElem: the parent node of the attribute * @name: the name of the attribute * * 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) { xmlIDPtr res; xmlChar *strip; /* * TODO: Use xmlSchemaStrip here; it's not exported at this * moment. */ strip = xmlSchemaCollapseString(value); if (strip != NULL) { xmlFree((xmlChar *) value); value = strip; } res = xmlAddID(NULL, attr->doc, value, attr); if (res == NULL) { 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 attr->atype = XML_ATTRIBUTE_ID; } } 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 informations * * 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 (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')) { ret = ret * 10 + (*cur - '0'); 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 informations * * 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); 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')) { ret = ret * 10 + (*cur - '0'); 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 * @ownerDes: owner designation * @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 informations * @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); } /************************************************************************ * * * Shema 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: * * @ctxt: a schema parser context * @ownerDes: the designation of the parent element * @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 * @ownerDes: the designation of the parent element * @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 * @ownerDes: the designation of the parent element * @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 informations * @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 informations * * parse a XML schema Attrribute 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 informations * * 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, "allocating facet", node); 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 informations * * 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 ((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 dublicate 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 informations * * 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 informations * * 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 informations * * parse a XML schema AnyAttrribute 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 informations * * parse a XML schema Attrribute 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 informations * * 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 informations * * 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 dublicate 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, "allocating a namespace array", NULL); 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; \ if (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 annotaion. */ 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 informations * * Parses a XML Schema identity-contraint 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, "allocating a 'selector' of an identity-constraint definition", NULL); 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 informations * * Parses a XML Schema identity-contraint 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 informations * @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 informations * * 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); 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 (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, "xmlSchemaParseUnion, " "allocating a type link", NULL); 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 informations * * 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 informations * * 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 informations * * Parses a XML schema model group definition. * * Note that the contraint 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, "allocating schema relation", 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, (xmlHashDeallocator) xmlSchemaSubstGroupFree); 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, "allocating schema construction context", NULL); return (NULL); } memset(ret, 0, sizeof(xmlSchemaConstructionCtxt)); ret->buckets = xmlSchemaItemListCreate(); if (ret->buckets == NULL) { xmlSchemaPErrMemory(NULL, "allocating list of schema buckets", NULL); xmlFree(ret); return (NULL); } ret->pending = xmlSchemaItemListCreate(); if (ret->pending == NULL) { xmlSchemaPErrMemory(NULL, "allocating list of pending global components", 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, "allocating schema parser context", 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 obove 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 absolue 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 informations * * 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 absulute 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 * aleady 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 destinct 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, "xmlSchemaGetDoc, " "allocating a parser context", NULL); goto exit_failure; } 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) { xmlErrorPtr 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 explicitely; 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 informations * * 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; if (bucket->parsed && bucket->origTargetNamespace != NULL) { xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_SRC_INCLUDE, node, NULL, "The target namespace of the included/redefined schema " "'%s' has to be absent or the same as the " "including/redefining schema's target namespace", schemaLocation, NULL); goto exit_error; } 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 informations * @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 informations * * 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, "allocating a facet link", NULL); 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 informations * * 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 informations * * 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 informations * * 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 informations * * 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 occurances 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 transtions * 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; #ifdef DEBUG_CONTENT xmlGenericError(xmlGenericErrorContext, "Building content model for %s\n", name); #endif ctxt->am = NULL; ctxt->am = xmlNewAutomata(); if (ctxt->am == NULL) { xmlGenericError(xmlGenericErrorContext, "Cannot create automata for complex type %s\n", type->name); 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 { #ifdef DEBUG_CONTENT_REGEXP xmlGenericError(xmlGenericErrorContext, "Content model of %s:\n", type->name); xmlRegexpPrint(stderr, type->contModel); #endif } 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) 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, "allocating a type link", NULL); 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 assignes 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 wilcard 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 wilcard 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: Experimantally 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"); } 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"); } } } /* * 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 containt 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); } /** * xmlSchemaGetParticleTotalRangeMin: * @particle: the particle * * Schema Component Constraint: Effective Total Range * (all and sequence) + (choice) * * Returns the minimun 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); } } #if 0 /** * 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 /** * 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)) { if (xmlSchemaGetParticleTotalRangeMin(particle) == 0) return (1); } return (0); } /** * xmlSchemaCheckCOSSTDerivedOK: * @actxt: a context * @type: the derived simple type definition * @baseType: the base type definition * @subset: the subset of ('restriction', ect.) * * Schema Component Constraint: * Type Derivation OK (Simple) (cos-st-derived-OK) * * Checks wheter @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 identy 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 inifinite 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 * * Resolvese 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 occured. */ 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 wheter @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 occured. */ 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->attributeWilcard == 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 occured. */ 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 implicitely * 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 occured. */ 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 occured. */ 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 occured. */ 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 occured. */ 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 occured. */ 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 occured. */ 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 occured. */ 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 occured. */ 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 occured. */ 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 occured. */ 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 * happed 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, "deriving facets, creating a facet link", NULL); 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 occured"); 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, "allocating a type link", NULL); 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); } #ifdef DEBUG_TYPE static void xmlSchemaDebugFixedType(xmlSchemaParserCtxtPtr pctxt, xmlSchemaTypePtr type) { if (type->node != NULL) { xmlGenericError(xmlGenericErrorContext, "Type of %s : %s:%d :", name, type->node->doc->URL, xmlGetLineNo(type->node)); } else { xmlGenericError(xmlGenericErrorContext, "Type of %s :", name); } if ((WXS_IS_SIMPLE(type)) || (WXS_IS_COMPLEX(type))) { switch (type->contentType) { case XML_SCHEMA_CONTENT_SIMPLE: xmlGenericError(xmlGenericErrorContext, "simple\n"); break; case XML_SCHEMA_CONTENT_ELEMENTS: xmlGenericError(xmlGenericErrorContext, "elements\n"); break; case XML_SCHEMA_CONTENT_UNKNOWN: xmlGenericError(xmlGenericErrorContext, "unknown !!!\n"); break; case XML_SCHEMA_CONTENT_EMPTY: xmlGenericError(xmlGenericErrorContext, "empty\n"); break; case XML_SCHEMA_CONTENT_MIXED: if (xmlSchemaIsParticleEmptiable((xmlSchemaParticlePtr) type->subtypes)) xmlGenericError(xmlGenericErrorContext, "mixed as emptiable particle\n"); else xmlGenericError(xmlGenericErrorContext, "mixed\n"); break; /* Removed, since not used. */ /* case XML_SCHEMA_CONTENT_MIXED_OR_ELEMENTS: xmlGenericError(xmlGenericErrorContext, "mixed or elems\n"); break; */ case XML_SCHEMA_CONTENT_BASIC: xmlGenericError(xmlGenericErrorContext, "basic\n"); break; default: xmlGenericError(xmlGenericErrorContext, "not registered !!!\n"); break; } } } #endif /* * 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: #ifdef DEBUG_TYPE xmlSchemaDebugFixedType(pctxt, type); #endif if (olderrs != pctxt->nberrors) return(pctxt->err); return(0); exit_failure: #ifdef DEBUG_TYPE xmlSchemaDebugFixedType(pctxt, type); #endif 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 catched " "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 catched 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) { 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 contraints. */ 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 #ifdef DEBUG_TYPE xmlSchemaDebugFixedType(pctxt, type); #endif if (olderrs != pctxt->nberrors) return(pctxt->err); else return(0); exit_error: type->flags |= XML_SCHEMAS_TYPE_INTERNAL_INVALID; #ifdef DEBUG_TYPE xmlSchemaDebugFixedType(pctxt, type); #endif return(pctxt->err); exit_failure: type->flags |= XML_SCHEMAS_TYPE_INTERNAL_INVALID; #ifdef DEBUG_TYPE xmlSchemaDebugFixedType(pctxt, type); #endif 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 * defintions 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 grou 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. Wilcards 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. Wilcards 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. Wilcards 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 simle type definitions to be already * builded 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)) { 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. Currenlty we 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 beed expanded and simple types have * beed 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 attribue 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 circual 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 seperate 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 contraints 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 builded 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, (xmlHashDeallocator) xmlSchemaSubstGroupFree); 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 Shema struture 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. */ xmlSchemaInitTypes(); 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) return(NULL); /* 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 communitated. */ if (mainSchema) { xmlSchemaFree(mainSchema); mainSchema = NULL; } if (ctxt->constructor) { xmlSchemaConstructionCtxtFree(ctxt->constructor); ctxt->constructor = NULL; ctxt->ownsConstructor = 0; } PERROR_INT2("xmlSchemaParse", "An internal error occured"); 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 * * 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 count = 0; 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; count++; /* 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; } count++; /* 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 avaliable"); 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(xmlSchemaIDCPtr idcDef, xmlSchemaValidCtxtPtr vctxt) { xmlSchemaIDCAugPtr aidc; aidc = (xmlSchemaIDCAugPtr) xmlMalloc(sizeof(xmlSchemaIDCAug)); if (aidc == NULL) { xmlSchemaVErrMemory(vctxt, "xmlSchemaAugmentIDC: allocating an augmented IDC definition", NULL); 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(xmlSchemaImportPtr imported, xmlSchemaValidCtxtPtr vctxt) { if (imported->schema->idcDef != NULL) { xmlHashScan(imported->schema->idcDef , (xmlHashScanner) 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, "allocating a PSVI IDC binding item", 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 gobal list. */ if (vctxt->idcNodes == NULL) { vctxt->idcNodes = (xmlSchemaPSVIIDCNodePtr *) xmlMalloc(20 * sizeof(xmlSchemaPSVIIDCNodePtr)); if (vctxt->idcNodes == NULL) { xmlSchemaVErrMemory(vctxt, "allocating the IDC node table item list", NULL); 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, "re-allocating the IDC node table item list", NULL); 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 gobal list. */ if (vctxt->idcKeys == NULL) { vctxt->idcKeys = (xmlSchemaPSVIIDCKeyPtr *) xmlMalloc(40 * sizeof(xmlSchemaPSVIIDCKeyPtr)); if (vctxt->idcKeys == NULL) { xmlSchemaVErrMemory(vctxt, "allocating the IDC key storage list", NULL); 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, "re-allocating the IDC key storage list", NULL); 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, "allocating an array of IDC node-table items", 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, "re-allocating an array of IDC node-table items", 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); } } /** * 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); } 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; } 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, "allocating an IDC state object", 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; #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: STO push '%s'\n", sto->sel->xpath); #endif 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++; #ifdef DEBUG_IDC { xmlChar *str = NULL; xmlGenericError(xmlGenericErrorContext, "IDC: EVAL on %s, depth %d, type %d\n", xmlSchemaFormatQName(&str, vctxt->inode->nsName, vctxt->inode->localName), depth, nodeType); FREE_AND_NULL(str) } #endif /* * Process all active XPath state objects. */ first = vctxt->xpathStates; sto = first; while (sto != head) { #ifdef DEBUG_IDC if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) xmlGenericError(xmlGenericErrorContext, "IDC: ['%s'] selector '%s'\n", sto->matcher->aidc->def->name, sto->sel->xpath); else xmlGenericError(xmlGenericErrorContext, "IDC: ['%s'] field '%s'\n", sto->matcher->aidc->def->name, sto->sel->xpath); #endif 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. */ #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: " "MATCH\n"); #endif /* * 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, "allocating the state object history", 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, "re-allocating the state object history", NULL); return(-1); } } sto->history[sto->nbHistory++] = depth; #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: push match '%d'\n", vctxt->depth); #endif if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) { xmlSchemaIDCSelectPtr sel; /* * Activate state objects for the IDC fields of * the IDC selector. */ #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: " "activating field states\n"); #endif 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. */ #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: key found\n"); #endif /* * 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(xmlSchemaValidCtxtPtr vctxt, xmlChar **buf, xmlSchemaPSVIIDCKeyPtr *seq, int count) { int i, res; xmlChar *value = NULL; *buf = xmlStrdup(BAD_CAST "["); for (i = 0; i < count; i++) { *buf = xmlStrcat(*buf, BAD_CAST "'"); res = xmlSchemaGetCanonValueWhtspExt(seq[i]->val, xmlSchemaGetWhiteSpaceFacetValue(seq[i]->type), &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); } /** * 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; #ifdef DEBUG_IDC { xmlChar *str = NULL; xmlGenericError(xmlGenericErrorContext, "IDC: BACK on %s, depth %d\n", xmlSchemaFormatQName(&str, vctxt->inode->nsName, vctxt->inode->localName), vctxt->depth); FREE_AND_NULL(str) } #endif /* * Evaluate the state objects. */ while (sto != NULL) { res = xmlStreamPop((xmlStreamCtxtPtr) sto->xpathCtxt); if (res == -1) { VERROR_INT("xmlSchemaXPathProcessHistory", "calling xmlStreamPop()"); return (-1); } #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: stream pop '%s'\n", sto->sel->xpath); #endif 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 happend"); 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 dependant 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, "allocating an array of key-sequences", NULL); return(-1); } memset(matcher->keySeqs, 0, matcher->sizeKeySeqs * sizeof(xmlSchemaPSVIIDCKeyPtr *)); } else if (pos >= matcher->sizeKeySeqs) { int i = matcher->sizeKeySeqs; matcher->sizeKeySeqs *= 2; matcher->keySeqs = (xmlSchemaPSVIIDCKeyPtr **) xmlRealloc(matcher->keySeqs, matcher->sizeKeySeqs * sizeof(xmlSchemaPSVIIDCKeyPtr *)); if (matcher->keySeqs == NULL) { xmlSchemaVErrMemory(NULL, "reallocating an array of key-sequences", 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, "allocating an IDC key-sequence", 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, "allocating a IDC key", 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; i = 0; res = 0; /* * Compare the key-sequences, key by key. */ do { bkeySeq = ((xmlSchemaPSVIIDCNodePtr) targets->items[i])->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; } i++; } while (i < targets->nbItems); if (i != targets->nbItems) { 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, "allocating an IDC node-table item", 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); } 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)) { #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: STO pop '%s'\n", sto->sel->xpath); #endif 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); #ifdef DEBUG_IDC { xmlChar *str = NULL; xmlGenericError(xmlGenericErrorContext, "IDC: REGISTER on %s, depth %d\n", (char *) xmlSchemaFormatQName(&str, vctxt->inode->nsName, vctxt->inode->localName), vctxt->depth); FREE_AND_NULL(str) } #endif 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, "allocating an IDC matcher", NULL); 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; #ifdef DEBUG_IDC xmlGenericError(xmlGenericErrorContext, "IDC: register matcher\n"); #endif /* * 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 (! 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; } 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, "allocating IDC list of node-table items", 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, "re-allocating IDC list of node-table items", 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, "allocating an array of IDC node-table " "items", 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; 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. */ for (i = 0; i < matcher->targets->nbItems; i++) { res = 0; refNode = matcher->targets->items[i]; if (bind != NULL) { refKeys = refNode->keys; for (j = 0; j < bind->nbNodes; j++) { keys = bind->nodeTable[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 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); } } } 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, "allocating attribute info list", NULL); 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, "re-allocating attribute info list", NULL); 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, "creating new attribute info", NULL); 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 currect 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, "allocating the element info array", NULL); 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, "re-allocating the element info array", NULL); 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, "allocating an element info", NULL); 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; 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: if (error >= 0) { int 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; } } if (error >= 0) { int found; /* * 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 vaule. */ 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; 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. */ local = xmlSplitQName2(value, &prefix); 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. */ TODO 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; } } 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 overriden * 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; TODO 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; TODO 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", "callling xmlNewProp()"); if (normValue != NULL) xmlFree(normValue); goto internal_error; } } else { xmlNsPtr ns; ns = xmlSearchNsByHref(defAttrOwnerElem->doc, defAttrOwnerElem, iattr->nsName); if (ns == NULL) { xmlChar prefix[12]; int counter = 0; /* * Create a namespace declaration on the validation * root node if no namespace declaration is in scope. */ do { snprintf((char *) prefix, 12, "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. */ TODO goto eval_idcs; } if ((iattr->use != NULL) && (iattr->use->defValue != NULL)) { if (iattr->use->defVal == NULL) { /* VAL TODO: A default value was not precomputed. */ TODO 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. */ TODO 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(xmlSchemaValidCtxtPtr vctxt ATTRIBUTE_UNUSED, const xmlChar * name ATTRIBUTE_UNUSED, xmlSchemaElementPtr item, xmlSchemaNodeInfoPtr inode) { inode->decl = item; #ifdef DEBUG_CONTENT { xmlChar *str = NULL; if (item->type == XML_SCHEMA_TYPE_ELEMENT) { xmlGenericError(xmlGenericErrorContext, "AUTOMATON callback for '%s' [declaration]\n", xmlSchemaFormatQName(&str, inode->localName, inode->nsName)); } else { xmlGenericError(xmlGenericErrorContext, "AUTOMATON callback for '%s' [wildcard]\n", xmlSchemaFormatQName(&str, inode->localName, inode->nsName)); } FREE_AND_NULL(str) } #endif } 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, (xmlRegExecCallbacks) xmlSchemaVContentModelCallback, vctxt); if (inode->regexCtxt == NULL) { VERROR_INT("xmlSchemaValidatorPopElem", "failed to create a regex context"); goto internal_error; } #ifdef DEBUG_AUTOMATA xmlGenericError(xmlGenericErrorContext, "AUTOMATON create on '%s'\n", inode->localName); #endif } /* * Do not check further content if the node has been nilled */ if (INODE_NILLED(inode)) { ret = 0; #ifdef DEBUG_AUTOMATA xmlGenericError(xmlGenericErrorContext, "AUTOMATON succeeded on nilled '%s'\n", inode->localName); #endif goto skip_nilled; } /* * Get hold of the still expected content, since a further * call to xmlRegExecPushString() will loose 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); #ifdef DEBUG_AUTOMATA xmlGenericError(xmlGenericErrorContext, "AUTOMATON missing ERROR on '%s'\n", inode->localName); #endif } else { /* * Content model is satisfied. */ ret = 0; #ifdef DEBUG_AUTOMATA xmlGenericError(xmlGenericErrorContext, "AUTOMATON succeeded on '%s'\n", inode->localName); #endif } } } 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 = xmlNewText(BAD_CAST normValue); xmlFree(normValue); } else textChild = xmlNewText(inode->decl->value); if (textChild == NULL) { VERROR_INT("xmlSchemaValidatorPopElem", "calling xmlNewText()"); 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 containt 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) { #ifdef DEBUG_IDC_NODE_TABLE xmlSchemaDebugDumpIDCTable(stdout, inode->nsName, inode->localName, inode->idcTable); #endif 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 explicitely. * "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 belf 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, (xmlRegExecCallbacks) xmlSchemaVContentModelCallback, vctxt); if (regexCtxt == NULL) { VERROR_INT("xmlSchemaValidateChildElem", "failed to create a regex context"); return (-1); } pielem->regexCtxt = regexCtxt; #ifdef DEBUG_AUTOMATA xmlGenericError(xmlGenericErrorContext, "AUTOMATA create on '%s'\n", pielem->localName); #endif } /* * 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); #ifdef DEBUG_AUTOMATA if (ret < 0) xmlGenericError(xmlGenericErrorContext, "AUTOMATON push ERROR for '%s' on '%s'\n", vctxt->inode->localName, pielem->localName); else xmlGenericError(xmlGenericErrorContext, "AUTOMATON push OK for '%s' on '%s'\n", vctxt->inode->localName, pielem->localName); #endif 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,(xmlHashScanner)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 occured; 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? */ TODO } 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, "allocating namespace bindings for SAX validation", NULL); 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, "re-allocating namespace bindings for SAX validation", NULL); 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) { xmlChar *value; for (j = 0, i = 0; i < nb_attributes; i++, j += 5) { /* * Duplicate the value. */ value = xmlStrndup(attributes[j+3], attributes[j+4] - attributes[j+3]); /* * 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, "allocating validation context", 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->idcMatcherCache != NULL) { xmlSchemaIDCMatcherPtr matcher = vctxt->idcMatcherCache, tmp; while (matcher) { tmp = matcher; matcher = matcher->nextCached; xmlSchemaIDCFreeMatcherList(tmp); } vctxt->idcMatcherCache = 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; } /* * 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; } } /** * 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 * * Set the error and warning callback informations */ 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 informations * * 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->xsiAssemble = 0; 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,(xmlHashScanner)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 informations */ xmlSAXHandlerPtr *user_sax_ptr; xmlSAXHandlerPtr user_sax; void **user_data_ptr; void *user_data; /* the block plugged back and validation informations */ 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 XMLCDECL 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 XMLCDECL 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 XMLCDECL 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 (des)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); } /** * 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) { xmlSchemaSAXPlugPtr plug = NULL; xmlSAXHandlerPtr old_sax = NULL; xmlParserCtxtPtr pctxt = NULL; xmlParserInputPtr inputStream = NULL; int ret; if ((ctxt == NULL) || (input == NULL)) return (-1); /* * prepare the parser */ pctxt = xmlNewParserCtxt(); if (pctxt == NULL) return (-1); old_sax = pctxt->sax; pctxt->sax = sax; pctxt->userData = user_data; #if 0 if (options) xmlCtxtUseOptions(pctxt, options); #endif pctxt->linenumbers = 1; xmlSchemaValidateSetLocator(ctxt, xmlSchemaValidateStreamLocator, pctxt); inputStream = xmlNewIOInputStream(pctxt, input, enc);; if (inputStream == NULL) { ret = -1; goto done; } inputPush(pctxt, inputStream); ctxt->parserCtxt = pctxt; ctxt->input = input; /* * Plug the validation and launch the parsing */ plug = xmlSchemaSAXPlug(ctxt, &(pctxt->sax), &(pctxt->userData)); if (plug == NULL) { ret = -1; goto done; } ctxt->input = input; ctxt->enc = enc; 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); } /* cleanup */ if (pctxt != NULL) { pctxt->sax = old_sax; 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; xmlParserInputBufferPtr input; if ((ctxt == NULL) || (filename == NULL)) return (-1); input = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); if (input == NULL) return (-1); ret = xmlSchemaValidateStream(ctxt, input, XML_CHAR_ENCODING_NONE, NULL, NULL); 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); } #define bottom_xmlschemas #include "elfgcchack.h" #endif /* LIBXML_SCHEMAS_ENABLED */