<
p:declare-step xmlns:p=
"http://www.w3.org/ns/xproc" xmlns:c=
"http://www.w3.org/ns/xproc-step" xmlns:cx=
"http://xmlcalabash.com/ns/extensions" xmlns:xs=
"http://www.w3.org/2001/XMLSchema" xmlns:px=
"http://www.daisy.org/ns/pipeline/xproc" xmlns:pxi=
"http://www.daisy.org/ns/pipeline/xproc/internal/daisy202-validator" xmlns:d=
"http://www.daisy.org/ns/pipeline/data" xmlns:html=
"http://www.w3.org/1999/xhtml" xmlns:l=
"http://xproc.org/library" version=
"1.0" exclude-inline-prefixes=
"#all" type=
"px:daisy202-validator" name=
"main">
<
p:input port=
"fileset.in" primary=
"true"/>
<
p:input port=
"in-memory.in" sequence=
"true"/>
<
p:option name=
"timeToleranceMs" select=
"'500'" cx:as=
"xs:string" cx:type=
"xs:integer"/>
<
p:option name=
"ncc" required=
"true"/>
<
p:output port=
"fileset.out" primary=
"true">
<
p:pipe port=
"fileset.in" step=
"main"/>
</
p:output>
<
p:output port=
"in-memory.out" sequence=
"true">
<
p:pipe port=
"in-memory.in" step=
"main"/>
</
p:output>
<
p:output port=
"xml-report">
<
p:pipe step=
"xml-report" port=
"result"/>
</
p:output>
<
p:output port=
"html-report">
<
p:pipe step=
"html-report" port=
"result"/>
</
p:output>
<
p:output port=
"validation-status" px:media-type=
"application/vnd.pipeline.status+xml">
<
p:pipe step=
"validation-status" port=
"result"/>
</
p:output>
<
p:import href=
"http://www.daisy.org/pipeline/modules/common-utils/library.xpl"/>
<
p:import href=
"http://www.daisy.org/pipeline/modules/fileset-utils/library.xpl"/>
<
p:import href=
"http://www.daisy.org/pipeline/modules/mediatype-utils/library.xpl"/>
<
p:import href=
"http://www.daisy.org/pipeline/modules/validation-utils/library.xpl"/>
<
p:variable name=
"start" select=
"current-dateTime()"/>
<
p:variable name=
"timeToleranceMsValid" cx:as=
"xs:integer" select=
"if (matches($timeToleranceMs,'^\d+')) then $timeToleranceMs else 500"/>
<
px:mediatype-detect name=
"fileset.in">
<
p:input port=
"source">
<
p:pipe port=
"fileset.in" step=
"main"/>
</
p:input>
<
p:input port=
"in-memory">
<
p:pipe port=
"in-memory.in" step=
"main"/>
</
p:input>
</
px:mediatype-detect>
<
px:message message=
"Validating DAISY 2.02 fileset"/>
<
px:message message=
"timeToleranceMs set to $1">
<
p:with-option name=
"param1" select=
"$timeToleranceMsValid"/>
</
px:message>
<
px:fileset-load media-types=
"application/smil+xml">
<
p:input port=
"in-memory">
<
p:pipe port=
"in-memory.in" step=
"main"/>
</
p:input>
</
px:fileset-load>
<
p:identity name=
"smil"/>
<
p:sink/>
<
px:fileset-load href=
"*/ncc.html">
<
p:input port=
"fileset">
<
p:pipe port=
"result" step=
"fileset.in"/>
</
p:input>
<
p:input port=
"in-memory">
<
p:pipe port=
"in-memory.in" step=
"main"/>
</
p:input>
</
px:fileset-load>
<
p:identity name=
"ncc"/>
<
p:sink/>
<
p:delete match=
"/*/d:file[ends-with(lower-case(resolve-uri(@href,base-uri(.))),'/ncc.html')]">
<
p:input port=
"source">
<
p:pipe port=
"result" step=
"fileset.in"/>
</
p:input>
</
p:delete>
<
px:fileset-load media-types=
"application/xhtml+xml text/html">
<
p:input port=
"in-memory">
<
p:pipe port=
"in-memory.in" step=
"main"/>
</
p:input>
</
px:fileset-load>
<
p:identity name=
"content"/>
<
p:sink/>
<
px:fileset-load media-types=
"application/*+xml application/xml text/html">
<
p:input port=
"fileset">
<
p:pipe port=
"result" step=
"fileset.in"/>
</
p:input>
<
p:input port=
"in-memory">
<
p:pipe port=
"in-memory.in" step=
"main"/>
</
p:input>
</
px:fileset-load>
<
p:for-each>
<
p:add-xml-base/>
</
p:for-each>
<
p:identity name=
"interdoc-urichecker.before-wrap"/>
<
p:wrap-sequence wrapper=
"wrapper">
<
p:input port=
"source">
<
p:pipe port=
"result" step=
"fileset.in"/>
<
p:pipe port=
"result" step=
"interdoc-urichecker.before-wrap"/>
</
p:input>
</
p:wrap-sequence>
<
p:xslt>
<
p:input port=
"parameters">
<
p:empty/>
</
p:input>
<
p:input port=
"stylesheet">
<
p:document href=
"validate.check-references.xsl"/>
</
p:input>
</
p:xslt>
<
p:filter select=
"/*/*"/>
<
p:identity name=
"interdoc-urichecker-tests"/>
<
p:sink/>
<
p:for-each name=
"file-types.iterate">
<
p:iteration-source select=
"/*/d:file">
<
p:pipe port=
"result" step=
"fileset.in"/>
</
p:iteration-source>
<
p:variable name=
"base-uri-xpath" select=
"concat('"',replace(resolve-uri(/*/@href,base-uri(/*)),'"','""'),'"')"/>
<
p:choose>
<
p:when test=
"/*/@media-type=('application/xhtml+xml', 'text/html', 'application/smil+xml', 'audio/mpeg3', 'audio/mpeg', 'audio/wav', 'audio/wave', 'audio/x-wav', 'audio/vnd.wave', 'image/jpeg', 'image/gif', 'image/png', 'text/css')">
<
p:output port=
"result" sequence=
"true"/>
<
p:identity>
<
p:input port=
"source">
<
p:empty/>
</
p:input>
</
p:identity>
</
p:when>
<
p:otherwise>
<
p:output port=
"result" sequence=
"true"/>
<
p:identity>
<
p:input port=
"source">
<
p:inline exclude-inline-prefixes=
"#all">
<
d:message severity=
"error">
<
d:desc>DESC</
d:desc>
<
d:file>FILE</
d:file>
</
d:message>
</
p:inline>
</
p:input>
</
p:identity>
<
p:string-replace match=
"//d:desc/text()">
<
p:with-option name=
"replace" select=
"concat('"file type not allowed in DAISY 2.02 fileset: ',/*/@media-type,' (expected a html, smil, mp2, mp3, wav, jpg, gif, png or css file type)"')">
<
p:pipe port=
"current" step=
"file-types.iterate"/>
</
p:with-option>
</
p:string-replace>
<
p:string-replace match=
"//d:file/text()">
<
p:with-option name=
"replace" select=
"$base-uri-xpath"/>
</
p:string-replace>
</
p:otherwise>
</
p:choose>
</
p:for-each>
<
p:identity name=
"filetype-restriction-tests"/>
<
p:sink/>
<
p:for-each name=
"for-each.smil">
<
p:iteration-source>
<
p:pipe port=
"result" step=
"smil"/>
</
p:iteration-source>
<
p:choose>
<
p:when test=
"ends-with(lower-case(base-uri(/*)),'/master.smil')">
<
l:relax-ng-report name=
"for-each.smil.validate-smil-master">
<
p:input port=
"schema">
<
p:document href=
"http://www.daisy.org/pipeline/modules/daisy202-utils/d202msmil.rng"/>
</
p:input>
</
l:relax-ng-report>
<
p:sink/>
<
p:identity>
<
p:input port=
"source">
<
p:pipe port=
"report" step=
"for-each.smil.validate-smil-master"/>
</
p:input>
</
p:identity>
</
p:when>
<
p:otherwise>
<
p:group>
<
p:output port=
"report" sequence=
"true">
<
p:pipe step=
"for-each.smil.validate-smil-other.rng" port=
"report"/>
<
p:pipe step=
"for-each.smil.validate-smil-other.sch" port=
"report"/>
</
p:output>
<
l:relax-ng-report name=
"for-each.smil.validate-smil-other.rng">
<
p:input port=
"schema">
<
p:document href=
"http://www.daisy.org/pipeline/modules/daisy202-utils/d202smil.rng"/>
</
p:input>
</
l:relax-ng-report>
<
p:sink/>
<
px:relax-ng-to-schematron>
<
p:input port=
"source">
<
p:document href=
"http://www.daisy.org/pipeline/modules/daisy202-utils/d202smil.rng"/>
</
p:input>
</
px:relax-ng-to-schematron>
<
p:for-each name=
"for-each.smil.validate-smil-other.sch">
<
p:output port=
"report" sequence=
"true">
<
p:pipe step=
"validate-with-schematron" port=
"report"/>
</
p:output>
<
p:validate-with-schematron assert-valid=
"false" name=
"validate-with-schematron">
<
p:input port=
"source">
<
p:pipe step=
"for-each.smil" port=
"current"/>
</
p:input>
<
p:input port=
"schema">
<
p:pipe step=
"for-each.smil.validate-smil-other.sch" port=
"current"/>
</
p:input>
<
p:input port=
"parameters">
<
p:empty/>
</
p:input>
</
p:validate-with-schematron>
<
p:sink/>
</
p:for-each>
<
p:sink/>
</
p:group>
</
p:otherwise>
</
p:choose>
</
p:for-each>
<
p:identity name=
"smil-schema-tests"/>
<
p:sink/>
<
p:group name=
"ncc.validate-ncc">
<
p:output port=
"report" sequence=
"true">
<
p:pipe step=
"ncc.validate-ncc.rng" port=
"report"/>
<
p:pipe step=
"ncc.validate-ncc.sch" port=
"report"/>
</
p:output>
<
l:relax-ng-report name=
"ncc.validate-ncc.rng">
<
p:input port=
"source">
<
p:pipe port=
"result" step=
"ncc"/>
</
p:input>
<
p:input port=
"schema">
<
p:document href=
"http://www.daisy.org/pipeline/modules/daisy202-utils/d202ncc.rng"/>
</
p:input>
</
l:relax-ng-report>
<
p:sink/>
<
px:relax-ng-to-schematron>
<
p:input port=
"source">
<
p:document href=
"http://www.daisy.org/pipeline/modules/daisy202-utils/d202ncc.rng"/>
</
p:input>
</
px:relax-ng-to-schematron>
<
p:for-each name=
"ncc.validate-ncc.sch">
<
p:output port=
"report" sequence=
"true">
<
p:pipe step=
"validate-with-schematron" port=
"report"/>
</
p:output>
<
p:validate-with-schematron assert-valid=
"false" name=
"validate-with-schematron">
<
p:input port=
"source">
<
p:pipe step=
"ncc" port=
"result"/>
</
p:input>
<
p:input port=
"schema">
<
p:pipe step=
"ncc.validate-ncc.sch" port=
"current"/>
</
p:input>
<
p:input port=
"parameters">
<
p:empty/>
</
p:input>
</
p:validate-with-schematron>
<
p:sink/>
</
p:for-each>
<
p:sink/>
</
p:group>
<
p:identity name=
"ncc-schema-tests"/>
<
p:sink/>
<
p:for-each>
<
p:iteration-source>
<
p:pipe port=
"result" step=
"ncc"/>
<
p:pipe port=
"result" step=
"content"/>
</
p:iteration-source>
<
px:message message=
"validating heading hierarchy for $1">
<
p:with-option name=
"param1" select=
"replace(base-uri(/*),'.*/','')"/>
</
px:message>
<
p:xslt>
<
p:input port=
"parameters">
<
p:empty/>
</
p:input>
<
p:input port=
"stylesheet">
<
p:document href=
"validate.check-heading-hierarchy.xsl"/>
</
p:input>
</
p:xslt>
<
p:filter select=
"/*/*"/>
</
p:for-each>
<
p:identity name=
"html-heading-hierarchy-tests"/>
<
p:sink/>
<
p:identity name=
"audio-duration-and-id3-tests">
<
p:input port=
"source">
<
p:empty/>
</
p:input>
</
p:identity>
<
px:message message=
"MP3 file duration and ID3 tag validation is not implemented yet" severity=
"WARN"/>
<
p:sink/>
<
p:for-each>
<
p:iteration-source>
<
p:pipe port=
"result" step=
"smil"/>
</
p:iteration-source>
<
p:add-xml-base/>
</
p:for-each>
<
p:wrap-sequence wrapper=
"c:result"/>
<
p:xslt>
<
p:input port=
"parameters">
<
p:empty/>
</
p:input>
<
p:input port=
"stylesheet">
<
p:document href=
"validate.smil-times-1.xsl"/>
</
p:input>
</
p:xslt>
<
p:xslt>
<
p:with-param name=
"ncc-totalTime" select=
"/*/*[local-name()='head']/*[local-name()='meta' and @name='ncc:totalTime']/@content">
<
p:pipe port=
"result" step=
"ncc"/>
</
p:with-param>
<
p:input port=
"stylesheet">
<
p:document href=
"validate.smil-times-2.xsl"/>
</
p:input>
</
p:xslt>
<
p:identity name=
"smil-times"/>
<
p:for-each name=
"smil-times.iterate">
<
p:iteration-source select=
"/*/*"/>
<
p:output port=
"result" sequence=
"true">
<
p:pipe port=
"result" step=
"smil-times.iterate.totalTime"/>
<
p:pipe port=
"result" step=
"smil-times.iterate.duration"/>
</
p:output>
<
p:variable name=
"base-uri-xpath" select=
"concat('"',replace(base-uri(/*),'"','""'),'"')"/>
<
p:choose name=
"smil-times.iterate.totalTime">
<
p:xpath-context>
<
p:pipe port=
"current" step=
"smil-times.iterate"/>
</
p:xpath-context>
<
p:when test=
"abs(/*/number(@calculated-totalTime) - /*/number(@meta-totalTime)) > $timeToleranceMsValid div 1000">
<
p:output port=
"result" sequence=
"true"/>
<
p:identity>
<
p:input port=
"source">
<
p:inline exclude-inline-prefixes=
"#all">
<
d:message severity=
"error">
<
d:desc>DESC</
d:desc>
<
d:file>FILE</
d:file>
<
d:location>/smil/head/meta[@name='ncc:totalElapsedTime']</
d:location>
</
d:message>
</
p:inline>
</
p:input>
</
p:identity>
<
p:string-replace match=
"//d:desc/text()">
<
p:with-option name=
"replace" select=
"concat('"expected total elapsed time ',/*/@calculated-totalTime,' but found ',/*/@meta-totalTime,'"')">
<
p:pipe port=
"current" step=
"smil-times.iterate"/>
</
p:with-option>
</
p:string-replace>
<
p:string-replace match=
"//d:file/text()">
<
p:with-option name=
"replace" select=
"$base-uri-xpath"/>
</
p:string-replace>
</
p:when>
<
p:otherwise>
<
p:output port=
"result" sequence=
"true"/>
<
p:identity>
<
p:input port=
"source">
<
p:inline exclude-inline-prefixes=
"#all">
<
d:message severity=
"info">
<
d:desc>DESC</
d:desc>
<
d:file>FILE</
d:file>
<
d:location>/smil/head/meta[@name='ncc:totalElapsedTime']</
d:location>
</
d:message>
</
p:inline>
</
p:input>
</
p:identity>
<
p:string-replace match=
"//d:desc/text()">
<
p:with-option name=
"replace" select=
"concat('"total elapsed time ',/*/@calculated-totalTime,' is close enough to the declared ',/*/@meta-totalTime,'"')">
<
p:pipe port=
"current" step=
"smil-times.iterate"/>
</
p:with-option>
</
p:string-replace>
<
p:string-replace match=
"//d:file/text()">
<
p:with-option name=
"replace" select=
"$base-uri-xpath"/>
</
p:string-replace>
</
p:otherwise>
</
p:choose>
<
p:choose name=
"smil-times.iterate.duration">
<
p:xpath-context>
<
p:pipe port=
"current" step=
"smil-times.iterate"/>
</
p:xpath-context>
<
p:when test=
"abs(/*/number(@calculated-duration) - /*/number(@meta-duration)) > $timeToleranceMsValid div 1000">
<
p:output port=
"result" sequence=
"true"/>
<
p:identity>
<
p:input port=
"source">
<
p:inline exclude-inline-prefixes=
"#all">
<
d:message severity=
"error">
<
d:desc>DESC</
d:desc>
<
d:file>FILE</
d:file>
<
d:location>/smil/head/meta[@name='ncc:timeInThisSmil']</
d:location>
</
d:message>
</
p:inline>
</
p:input>
</
p:identity>
<
p:string-replace match=
"//d:desc/text()">
<
p:with-option name=
"replace" select=
"concat('"expected duration ',/*/@calculated-duration,' but found ',/*/@meta-duration,'"')">
<
p:pipe port=
"current" step=
"smil-times.iterate"/>
</
p:with-option>
</
p:string-replace>
<
p:string-replace match=
"//d:file/text()">
<
p:with-option name=
"replace" select=
"$base-uri-xpath"/>
</
p:string-replace>
</
p:when>
<
p:otherwise>
<
p:output port=
"result" sequence=
"true"/>
<
p:identity>
<
p:input port=
"source">
<
p:inline exclude-inline-prefixes=
"#all">
<
d:message severity=
"info">
<
d:desc>DESC</
d:desc>
<
d:file>FILE</
d:file>
<
d:location>/smil/head/meta[@name='ncc:timeInThisSmil']</
d:location>
</
d:message>
</
p:inline>
</
p:input>
</
p:identity>
<
p:string-replace match=
"//d:desc/text()">
<
p:with-option name=
"replace" select=
"concat('"duration ',/*/@calculated-duration,' is close enough to the declared ',/*/@meta-duration,'"')">
<
p:pipe port=
"current" step=
"smil-times.iterate"/>
</
p:with-option>
</
p:string-replace>
<
p:string-replace match=
"//d:file/text()">
<
p:with-option name=
"replace" select=
"$base-uri-xpath"/>
</
p:string-replace>
</
p:otherwise>
</
p:choose>
</
p:for-each>
<
p:identity name=
"smil-time-tests"/>
<
p:sink/>
<
p:group>
<
p:variable name=
"base-uri-xpath" select=
"concat('"',replace(base-uri(/*),'"','""'),'"')">
<
p:pipe port=
"result" step=
"ncc"/>
</
p:variable>
<
p:choose>
<
p:xpath-context>
<
p:pipe port=
"result" step=
"smil-times"/>
</
p:xpath-context>
<
p:when test=
"abs(/*/number(@calculated-totalTime) - /*/number(@ncc-meta-totalTime)) > $timeToleranceMsValid div 1000">
<
p:identity>
<
p:input port=
"source">
<
p:inline exclude-inline-prefixes=
"#all">
<
d:message severity=
"error">
<
d:desc>DESC</
d:desc>
<
d:file>FILE</
d:file>
<
d:location>/html/head/meta[@name='ncc:totalTime']</
d:location>
</
d:message>
</
p:inline>
</
p:input>
</
p:identity>
<
p:string-replace match=
"//d:desc/text()">
<
p:with-option name=
"replace" select=
"concat('"expected total time ',/*/@calculated-totalTime,' but found ',/*/@ncc-meta-totalTime,'"')">
<
p:pipe port=
"result" step=
"smil-times"/>
</
p:with-option>
</
p:string-replace>
<
p:string-replace match=
"//d:file/text()">
<
p:with-option name=
"replace" select=
"$base-uri-xpath"/>
</
p:string-replace>
</
p:when>
<
p:otherwise>
<
p:identity>
<
p:input port=
"source">
<
p:inline exclude-inline-prefixes=
"#all">
<
d:message severity=
"info">
<
d:desc>DESC</
d:desc>
<
d:file>FILE</
d:file>
<
d:location>/html/head/meta[@name='ncc:totalTime']</
d:location>
</
d:message>
</
p:inline>
</
p:input>
</
p:identity>
<
p:string-replace match=
"//d:desc/text()">
<
p:with-option name=
"replace" select=
"concat('"total time ',/*/@calculated-totalTime,' is close enough to the declared ',/*/@ncc-meta-totalTime,'"')">
<
p:pipe port=
"result" step=
"smil-times"/>
</
p:with-option>
</
p:string-replace>
<
p:string-replace match=
"//d:file/text()">
<
p:with-option name=
"replace" select=
"$base-uri-xpath"/>
</
p:string-replace>
</
p:otherwise>
</
p:choose>
</
p:group>
<
p:identity name=
"ncc-time-test"/>
<
p:sink/>
<
p:identity>
<
p:input port=
"source">
<
p:pipe step=
"interdoc-urichecker-tests" port=
"result"/>
<
p:pipe step=
"filetype-restriction-tests" port=
"result"/>
<
p:pipe step=
"smil-schema-tests" port=
"result"/>
<
p:pipe step=
"ncc-schema-tests" port=
"result"/>
<
p:pipe step=
"html-heading-hierarchy-tests" port=
"result"/>
<
p:pipe step=
"audio-duration-and-id3-tests" port=
"result"/>
<
p:pipe step=
"smil-time-tests" port=
"result"/>
<
p:pipe step=
"ncc-time-test" port=
"result"/>
</
p:input>
</
p:identity>
<
p:group>
<
p:variable name=
"duration" cx:as=
"xs:string" select=
"current-dateTime() - $start">
<
p:empty/>
</
p:variable>
<
px:message message=
"Validation completed in $1">
<
p:with-option name=
"param1" select=
"concat(if (contains($duration,'D')) then concat(days-from-duration(xs:duration($duration)), ' days, ') else '', if (contains($duration,'H')) then concat(hours-from-duration(xs:duration($duration)),':') else '', if (contains($duration,'H') and minutes-from-duration(xs:duration($duration)) < 10) then '0' else '', minutes-from-duration(xs:duration($duration)), ':' , if (seconds-from-duration(xs:duration($duration)) < 10) then '0' else '', seconds-from-duration(xs:duration($duration)) )">
<
p:empty/>
</
p:with-option>
</
px:message>
</
p:group>
<
p:identity name=
"report"/>
<
px:combine-validation-reports>
<
p:with-option name=
"document-name" select=
"replace($ncc,'.*/','')">
<
p:empty/>
</
p:with-option>
<
p:with-option name=
"document-type" select=
"'DAISY 2.02'">
<
p:empty/>
</
p:with-option>
<
p:with-option name=
"document-path" select=
"$ncc">
<
p:empty/>
</
p:with-option>
</
px:combine-validation-reports>
<
p:identity name=
"xml-report"/>
<
px:validation-report-to-html toc=
"false" name=
"html-report"/>
<
p:sink/>
<
px:validation-status name=
"validation-status">
<
p:input port=
"source">
<
p:pipe step=
"xml-report" port=
"result"/>
</
p:input>
</
px:validation-status>
<
p:sink/>
</
p:declare-step>