diff --git a/__data__/gml_id.xsd b/__data__/gml_id.xsd new file mode 100644 index 0000000..4f4c625 --- /dev/null +++ b/__data__/gml_id.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/__tests__/XMLValidatior.js b/__tests__/XMLValidatior.js index 6b02187..6dd4a28 100644 --- a/__tests__/XMLValidatior.js +++ b/__tests__/XMLValidatior.js @@ -569,5 +569,44 @@ describe ('sequence', () => { }) +}) + +describe ('gml:id attribute ref', () => { + + const xs = new XMLSchemata (Path.join (__dirname, '..', '__data__', 'gml_id.xsd')) + + test ('gml:id accepted on FunctionalZone', () => { + + new XMLParser ({xs}).process ([ + ``, + `8b2457c9-85c4-466a-a14b-b20856527718`, + ``, + ].join ('')) + + }) + + test ('bare id rejected (namespace required)', () => { + + expect (() => new XMLParser ({xs}).process ([ + ``, + `8b2457c9-85c4-466a-a14b-b20856527718`, + ``, + ].join (''))).toThrow ('Unknown attribute') + + }) + + test ('wrong namespace rejected', () => { + + expect (() => new XMLParser ({xs}).process ([ + ``, + `8b2457c9-85c4-466a-a14b-b20856527718`, + ``, + ].join (''))).toThrow ('Unknown attribute') + + }) }) diff --git a/lib/validation/XMLValidationState.js b/lib/validation/XMLValidationState.js index a85cfb4..706cbdf 100644 --- a/lib/validation/XMLValidationState.js +++ b/lib/validation/XMLValidationState.js @@ -116,13 +116,39 @@ class XMLValidationState { } + resolveAttribute (name, attributesMap) { + + const {attributes} = this.anyType + + if (attributes.has (name)) { + + const def = attributes.get (name) + + if (!def.targetNamespace || def.targetNamespace === attributesMap.getNamespaceURI (name)) return def + + } + + const localName = attributesMap.getLocalName (name); if (localName === name) return null + + const def = attributes.get (localName); if (!def) return null + + if (def.targetNamespace !== attributesMap.getNamespaceURI (name)) return null + + return def + + } + validateAttributes (attributesMap) { const {attributes, isAnyAttributeAllowed} = this.anyType + const matched = new Set () + for (const [name, value] of attributesMap) { - if (!attributes.has (name)) { + const def = this.resolveAttribute (name, attributesMap) + + if (!def) { if (isAnyAttributeAllowed) continue @@ -131,8 +157,10 @@ class XMLValidationState { throw Error (`Unknown attribute: "${name}"`) } - - const def = attributes.get (name), {attributes: {fixed, type}} = def + + matched.add (def) + + const {attributes: {fixed, type}} = def if (typeof fixed === 'string' && value !== fixed) throw Error (`The attribute "${name}" must have the value "${fixed}", not "${value}"`) @@ -164,7 +192,7 @@ class XMLValidationState { } - for (const [name, def] of attributes) if (!attributesMap.has (name)) { + for (const [name, def] of attributes) if (!matched.has (def)) { if (def.attributes.use === 'required') throw Error (`Missing required attribute: "${name}"`)