-- ------------------------------------------------------------{- |
Module : Text.XML.HXT.DTDValidation.TypeDefs
Copyright : Copyright (C) 2008 Uwe Schmidt
License : MIT
Maintainer : Uwe Schmidt (uwe@fh-wedel.de)
Stability : experimental
Portability: portable
This module provides functions for validating the DTD of XML documents
represented as XmlTree.
Unlike other popular XML validation tools the validation process returns
a list of errors instead of aborting after the first error was found.
Unlike validation of the document, the DTD branch is traversed four times:
- Validation of Notations
- Validation of Unparsed Entities
- Validation of Element declarations
- Validation of Attribute declarations
-}-- ------------------------------------------------------------moduleText.XML.HXT.DTDValidation.DTDValidation(removeDoublicateDefs,validateDTD)whereimportText.XML.HXT.DTDValidation.TypeDefsimportText.XML.HXT.DTDValidation.AttributeValueValidation-- |-- Validate a DTD.---- - returns : a functions which takes the DTD subset of the XmlTree, checks-- if the DTD is valid and returns a list of errorsvalidateDTD::XmlArrowvalidateDTD-- dtdPart=isDTDDoctype`guards`(listAgetChildren>>>(validateParts$<<(getNotationNames&&&getElemNames)))wherevalidatePartsnotationNameselemNames=validateNotations<+>validateEntitiesnotationNames<+>validateElementselemNames<+>validateAttributeselemNamesnotationNamesgetNotationNames::LA[XmlTree][String]getNotationNames=listA$unlistA>>>isDTDNotation>>>getDTDAttrValuea_namegetElemNames::LA[XmlTree][String]getElemNames=listA$unlistA>>>isDTDElement>>>getDTDAttrValuea_name-- ------------------------------------------------------------checkName::String->SLA[String]XmlTreeXmlTree->SLA[String]XmlTreeXmlTreecheckNamenamemsg=ifA(getState>>>isA(name`elem`))msg(nextState(name:)>>>none)-- -------------------------------------------------------------- |-- Validation of Notations, checks if all notation names are unique.-- Validity constraint: Unique Notation Name (4.7 \/ p.44 in Spec)---- * 1.parameter dtdPart : the children of the @DOCTYPE@ node---- - returns : a list of errorsvalidateNotations::LAXmlTreesXmlTreevalidateNotations=fromSLA[](unlistA>>>isDTDNotation>>>(checkForUniqueNotation$<getDTDAttrl))wherecheckForUniqueNotation::Attributes->SLA[String]XmlTreeXmlTreecheckForUniqueNotational=checkNamename$err("Notation "++showname++" was already specified.")wherename=dtd_nameal-- |-- Validation of Entities.---- 1. Issues a warning if entities are declared multiple times.---- Optional warning: (4.2 \/ p.35 in Spec)------ 2. Validates that a notation is declared for an unparsed entity.---- Validity constraint: Notation Declared (4.2.2 \/ p.36 in Spec)---- * 1.parameter dtdPart : the children of the @DOCTYPE@ node---- - 2.parameter notationNames : list of all notation names declared in the DTD---- - returns : a list of errorsvalidateEntities::[String]->LAXmlTreesXmlTreevalidateEntitiesnotationNames=(fromSLA[](unlistA>>>isDTDEntity>>>(checkForUniqueEntity$<getDTDAttrl)))<+>(unlistA>>>isUnparsedEntity>>>(checkNotationDecl$<getDTDAttrl))where-- Check if entities are declared multiple timescheckForUniqueEntity::Attributes->SLA[String]XmlTreeXmlTreecheckForUniqueEntityal=checkNamename$warn("Entity "++showname++" was already specified. "++"First declaration will be used.")wherename=dtd_nameal-- Find unparsed entities for which no notation is specifiedcheckNotationDecl::Attributes->XmlArrowcheckNotationDeclal|notationName`elem`notationNames=none|otherwise=err("The notation "++shownotationName++" must be declared "++"when referenced in the unparsed entity declaration for "++showupEntityName++".")wherenotationName=lookup1k_ndataalupEntityName=dtd_nameal-- |-- Validation of Element declarations.---- 1. Validates that an element is not declared multiple times.---- Validity constraint: Unique Element Type Declaration (3.2 \/ p.21 in Spec)------ 2. Validates that an element name only appears once in a mixed-content declaration.---- Validity constraint: No Duplicate Types (3.2 \/ p.21 in Spec)------ 3. Issues a warning if an element mentioned in a content model is not declared in the-- DTD.---- Optional warning: (3.2 \/ p.21 in Spec)---- * 1.parameter dtdPart : the children of the @DOCTYPE@ node---- - 2.parameter elemNames : list of all element names declared in the DTD---- - returns : a list of errorsvalidateElements::[String]->LAXmlTreesXmlTreevalidateElementselemNames-- dtdPart=(fromSLA[](unlistA>>>isDTDElement>>>(checkForUniqueElement$<getDTDAttrl)))<+>(unlistA>>>isMixedContentElement>>>(checkMixedContent$<getDTDAttrl))<+>(unlistA>>>isDTDElement>>>(checkContentModelelemNames$<getDTDAttrl))where-- Validates that an element is not declared multiple timescheckForUniqueElement::Attributes->SLA[String]XmlTreeXmlTreecheckForUniqueElemental=checkNamename$err("Element type "++showname++" must not be declared more than once.")wherename=dtd_nameal-- Validates that an element name only appears once in a mixed-content declarationcheckMixedContent::Attributes->XmlArrowcheckMixedContental=fromSLA[](getChildren>>>getChildren>>>isDTDName>>>(check$<getDTDAttrl))whereelemName=dtd_namealcheckal'=checkNamename$err("The element type "++showname++" was already specified in the mixed-content model of the element declaration "++showelemName++".")wherename=dtd_nameal'-- Issues a warning if an element mentioned in a content model is not-- declared in the DTD.checkContentModel::[String]->Attributes->XmlArrowcheckContentModelnamesal|cm`elem`[v_children,v_mixed]=getChildren>>>checkContent|otherwise=nonewhereelemName=dtd_namealcm=dtd_typealcheckContent::XmlArrowcheckContent=choiceA[isDTDName:->(checkName'$<getDTDAttrl),isDTDContent:->(getChildren>>>checkContent),this:->none]wherecheckName'al'|childElemName`elem`names=none|otherwise=warn("The element type "++showchildElemName++", used in content model of element "++showelemName++", is not declared.")wherechildElemName=dtd_nameal'-- |-- Validation of Attribute declarations.---- (1) Issues a warning if an attribute is declared for an element type not itself-- decared.---- Optinal warning: (3.3 \/ p. 24 in Spec)------ 2. Issues a warning if more than one definition is provided for the same-- attribute of a given element type. Fist declaration is binding, later-- definitions are ignored.---- Optional warning: (3.3 \/ p.24 in Spec)------ 3. Issues a warning if the same Nmtoken occures more than once in enumerated-- attribute types of a single element type.---- Optional warning: (3.3.1 \/ p.27 in Spec)------ 4. Validates that an element type has not more than one ID attribute defined.---- Validity constraint: One ID per Element Type (3.3.1 \/ p.26 in Spec)------ 5. Validates that an element type has not more than one NOTATION attribute defined.---- Validity constraint: One Notation per Element Type (3.3.1 \/ p.27 in Spec)------ 6. Validates that an ID attributes has the type #IMPLIED or #REQUIRED.---- Validity constraint: ID Attribute Default (3.3.1 \/ p.26 in Spec)------ 7. Validates that all referenced notations are declared.---- Validity constraint: Notation Attributes (3.3.1 \/ p.27 in Spec)------ 8. Validates that notations are not declared for EMPTY elements.---- Validity constraint: No Notation on Empty Element (3.3.1 \/p.27 in Spec)------ 9. Validates that the default value matches the lexical constraints of it's type.---- Validity constraint: Attribute default legal (3.3.2 \/ p.28 in Spec)------ * 1.parameter dtdPart : the children of the @DOCTYPE@ node---- - 2.parameter elemNames : list of all element names declared in the DTD---- - 3.parameter notationNames : list of all notation names declared in the DTD---- - returns : a list of errorsvalidateAttributes::[String]->[String]->LAXmlTreesXmlTreevalidateAttributeselemNamesnotationNames=-- 1. Find attributes for which no elements are declared(runCheckthis(checkDeclaredElementselemNames))<+>-- 2. Find attributes which are declared more than once(runNameCheckthischeckForUniqueAttributeDeclaration)<+>-- 3. Find enumerated attribute types which nmtokens are declared more than once(runCheck(isEnumAttrType`orElse`isNotationAttrType)checkEnumeratedTypes)<+>-- 4. Validate that there exists only one ID attribute for an element(runNameCheckisIdAttrTypecheckForUniqueId)<+>-- 5. Validate that there exists only one NOTATION attribute for an element(runNameCheckisNotationAttrTypecheckForUniqueNotation)<+>-- 6. Validate that ID attributes have the type #IMPLIED or #REQUIRED(runCheckisIdAttrTypecheckIdKindConstraint)<+>-- 7. Validate that all referenced notations are declared(runCheckisNotationAttrType(checkNotationDeclarationnotationNames))<+>-- 8. Validate that notations are not declared for EMPTY elements(checkNoNotationForEmptyElements$<listA(unlistA>>>isEmptyElement>>>getDTDAttrValuea_name))<+>-- 9. Validate that the default value matches the lexical constraints of it's type(checkDefaultValueTypes$<this)where-- -------------------------------------------------------------- control structuresrunCheckselectcheck=unlistA>>>isDTDAttlist>>>select>>>(check$<getDTDAttrl)runNameCheckselectcheck=fromSLA[]$runCheckselectcheck---------------------------------------------------------------------------- 1. Find attributes for which no elements are declaredcheckDeclaredElements::[String]->Attributes->XmlArrowcheckDeclaredElementselemNames'al|en`elem`elemNames'=none|otherwise=warn("The element type \""++en++"\" used in dclaration "++"of attribute \""++an++"\" is not declared.")whereen=dtd_namealan=dtd_valueal---------------------------------------------------------------------------- 2. Find attributes which are declared more than oncecheckForUniqueAttributeDeclaration::Attributes->SLA[String]XmlTreeXmlTreecheckForUniqueAttributeDeclarational=checkNamename$warn("Attribute \""++aname++"\" for element type \""++ename++"\" is already declared. First "++"declaration will be used.")whereename=dtd_namealaname=dtd_valuealname=ename++"|"++aname---------------------------------------------------------------------------- 3. Find enumerated attribute types which nmtokens are declared more than oncecheckEnumeratedTypes::Attributes->XmlArrowcheckEnumeratedTypesal=fromSLA[](getChildren>>>isDTDName>>>(checkForUniqueType$<getDTDAttrl))wherecheckForUniqueType::Attributes->SLA[String]XmlTreeXmlTreecheckForUniqueTypeal'=checkNamenmtoken$warn("Nmtoken \""++nmtoken++"\" should not "++"occur more than once in attribute \""++dtd_valueal++"\" for element \""++dtd_nameal++"\".")wherenmtoken=dtd_nameal'---------------------------------------------------------------------------- 4. Validate that there exists only one ID attribute for an elementcheckForUniqueId::Attributes->SLA[String]XmlTreeXmlTreecheckForUniqueIdal=checkNameename$err("Element \""++ename++"\" already has attribute of type "++"ID, another attribute \""++dtd_valueal++"\" of type ID is "++"not permitted.")whereename=dtd_nameal---------------------------------------------------------------------------- 5. Validate that there exists only one NOTATION attribute for an elementcheckForUniqueNotation::Attributes->SLA[String]XmlTreeXmlTreecheckForUniqueNotational=checkNameename$err("Element \""++ename++"\" already has attribute of type "++"NOTATION, another attribute \""++dtd_valueal++"\" of type NOTATION "++"is not permitted.")whereename=dtd_nameal---------------------------------------------------------------------------- 6. Validate that ID attributes have the type #IMPLIED or #REQUIREDcheckIdKindConstraint::Attributes->XmlArrowcheckIdKindConstraintal|attKind`elem`[k_implied,k_required]=none|otherwise=err("ID attribute \""++dtd_valueal++"\" must have a declared default "++"of \"#IMPLIED\" or \"REQUIRED\"")whereattKind=dtd_kindal---------------------------------------------------------------------------- 7. Validate that all referenced notations are declaredcheckNotationDeclaration::[String]->Attributes->XmlArrowcheckNotationDeclarationnotationsal=getChildren>>>isDTDName>>>(checkNotations$<getDTDAttrl)wherecheckNotations::Attributes->XmlArrowcheckNotationsal'|notation`elem`notations=none|otherwise=err("The notation \""++notation++"\" must be declared when "++"referenced in the notation type list for attribute \""++dtd_valueal++"\" of element \""++dtd_nameal++"\".")wherenotation=dtd_nameal'---------------------------------------------------------------------------- 8. Validate that notations are not declared for EMPTY elementscheckNoNotationForEmptyElements::[String]->LAXmlTreesXmlTreecheckNoNotationForEmptyElementsemptyElems=unlistA>>>isDTDAttlist>>>isNotationAttrType>>>(checkNoNotationForEmptyElement$<getDTDAttrl)wherecheckNoNotationForEmptyElement::Attributes->XmlArrowcheckNoNotationForEmptyElemental|ename`elem`emptyElems=err("Attribute \""++dtd_valueal++"\" of type NOTATION must not be "++"declared on the element \""++ename++"\" declared EMPTY.")|otherwise=nonewhereename=dtd_nameal---------------------------------------------------------------------------- 9. Validate that default values meet the lexical constraints of the attribute typescheckDefaultValueTypes::XmlTrees->LAXmlTreesXmlTreecheckDefaultValueTypesdtdPart'=unlistA>>>isDTDAttlist>>>isDefaultAttrKind>>>(checkAttributeValuedtdPart'$<this)-- -------------------------------------------------------------- |-- Removes doublicate declarations from the DTD, which first declaration is-- binding. This is the case for ATTLIST and ENTITY declarations.---- - returns : A function that replaces the children of DOCTYPE nodes by a list-- where all multiple declarations are removed.removeDoublicateDefs::XmlArrowremoveDoublicateDefs=replaceChildren(fromSLA[](getChildren>>>choiceA[isDTDAttlist:->(removeDoubleAttlist$<getDTDAttrl),isDTDEntity:->(removeDoubleEntity$<getDTDAttrl),this:->this]))`when`isDTDDoctypewherecheckName'n'=ifA(getState>>>isA(n'`elem`))none(this>>>perform(nextState(n':)))removeDoubleAttlist::Attributes->SLA[String]XmlTreeXmlTreeremoveDoubleAttlistal=checkName'elemAttrwhereelemAttr=elemName++"|"++attrNameattrName=dtd_valuealelemName=dtd_namealremoveDoubleEntity::Attributes->SLA[String]XmlTreeXmlTreeremoveDoubleEntityal=checkName'(dtd_nameal)-- ------------------------------------------------------------