diff --git a/build.sbt b/build.sbt
index b565316..8e3304d 100644
--- a/build.sbt
+++ b/build.sbt
@@ -2,8 +2,8 @@ import java.io.{BufferedReader, InputStreamReader}
enablePlugins(JavaAppPackaging)
-lazy val xmlCalabashVersion = "2.99.8"
-lazy val jafplVersion = "0.3.75"
+lazy val xmlCalabashVersion = "2.99.9"
+lazy val jafplVersion = "0.3.83"
lazy val saxonVersion = "10.6"
lazy val useSaxonEE = Option(System.getProperty("saxonEdition")).getOrElse("HE") == "EE"
diff --git a/src/main/resources/com.xmlcalabash.library.xpl b/src/main/resources/com.xmlcalabash.library.xpl
index 60dd8db..9a3abe3 100644
--- a/src/main/resources/com.xmlcalabash.library.xpl
+++ b/src/main/resources/com.xmlcalabash.library.xpl
@@ -3,10 +3,6 @@
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="3.0">
-
-
-
-
@@ -152,7 +148,7 @@
-
+
@@ -178,7 +174,7 @@
-
@@ -191,7 +187,7 @@
-
@@ -209,7 +205,7 @@
-
+
@@ -222,9 +218,9 @@
-
-
-
+
+
@@ -328,9 +324,9 @@
-
-
-
+
+
+
@@ -339,7 +335,7 @@
-
+
@@ -429,8 +425,8 @@
-
-
+
+
@@ -445,7 +441,7 @@
-
+
@@ -455,7 +451,7 @@
-
+
@@ -536,11 +532,19 @@
+
+
+
+
+
+
+
+
@@ -549,4 +553,9 @@
+
+
+
+
+
diff --git a/src/main/resources/com.xmlcalabash.properties b/src/main/resources/com.xmlcalabash.properties
deleted file mode 100644
index a29f2a2..0000000
--- a/src/main/resources/com.xmlcalabash.properties
+++ /dev/null
@@ -1,93 +0,0 @@
-cx = namespace http://xmlcalabash.com/ns/extensions
-p = namespace http://www.w3.org/ns/xproc
-exf = namespace http://exproc.org/standard/functions
-
-com.xmlcalabash.functions.Cwd = function exf:cwd
-com.xmlcalabash.functions.DocumentProperties = function p:document-properties
-com.xmlcalabash.functions.DocumentProperty = function p:document-property
-com.xmlcalabash.functions.ForceQNameKeys = function p:force-qname-keys
-com.xmlcalabash.functions.InjElapsed = function cx:step-elapsed
-com.xmlcalabash.functions.InjId = function cx:injectable-id
-com.xmlcalabash.functions.InjName = function cx:step-name
-com.xmlcalabash.functions.InjType = function cx:step-type
-com.xmlcalabash.functions.SystemProperty = function p:system-property
-com.xmlcalabash.functions.StepAvailable = function p:step-available
-com.xmlcalabash.functions.IterationPosition = function p:iteration-position
-com.xmlcalabash.functions.IterationSize = function p:iteration-size
-com.xmlcalabash.functions.UrifyFunction = function p:urify
-
-com.xmlcalabash.steps.AddAttribute = step p:add-attribute
-com.xmlcalabash.steps.Archive = step p:archive
-com.xmlcalabash.steps.ArchiveManifest = step p:archive-manifest
-com.xmlcalabash.steps.CastContentType = step p:cast-content-type
-com.xmlcalabash.steps.Count = step p:count
-com.xmlcalabash.steps.Compress = step p:compress
-com.xmlcalabash.steps.Delete = step p:delete
-com.xmlcalabash.steps.Error = step p:error
-com.xmlcalabash.steps.EscapeMarkup = step p:escape-markup
-com.xmlcalabash.steps.file.DirectoryList = step p:directory-list
-com.xmlcalabash.steps.file.FileCopy = step p:file-copy
-com.xmlcalabash.steps.file.FileDelete = step p:file-delete
-com.xmlcalabash.steps.file.FileInfo = step p:file-info
-com.xmlcalabash.steps.file.FileMkdir = step p:file-mkdir
-com.xmlcalabash.steps.file.FileMove = step p:file-move
-com.xmlcalabash.steps.file.FileCreateTempFile = step p:file-create-tempfile
-com.xmlcalabash.steps.file.FileTouch = step p:file-touch
-com.xmlcalabash.steps.Filter = step p:filter
-com.xmlcalabash.steps.Hash = step p:hash
-com.xmlcalabash.steps.HttpRequest = step p:http-request
-com.xmlcalabash.steps.Identity = step p:identity
-com.xmlcalabash.steps.Insert = step p:insert
-com.xmlcalabash.steps.json.Join = step p:json-join
-com.xmlcalabash.steps.json.Merge = step p:json-merge
-com.xmlcalabash.steps.LabelElements = step p:label-elements
-com.xmlcalabash.steps.Load = step p:load
-com.xmlcalabash.steps.NamespaceDelete = step p:namespace-delete
-com.xmlcalabash.steps.NamespaceRename = step p:namespace-rename
-com.xmlcalabash.steps.os.OsInfo = step p:os-info
-com.xmlcalabash.steps.os.OsExec = step p:os-exec
-com.xmlcalabash.steps.Pack = step p:pack
-com.xmlcalabash.steps.Parameters = step p:parameters
-com.xmlcalabash.steps.Rename = step p:rename
-com.xmlcalabash.steps.Replace = step p:replace
-com.xmlcalabash.steps.SetAttributes = step p:set-attributes
-com.xmlcalabash.steps.SetProperties = step p:set-properties
-com.xmlcalabash.steps.Sink = step p:sink
-com.xmlcalabash.steps.SplitSequence = step p:split-sequence
-com.xmlcalabash.steps.Store = step p:store
-com.xmlcalabash.steps.StringReplace = step p:string-replace
-com.xmlcalabash.steps.text.Count = step p:text-count
-com.xmlcalabash.steps.text.Head = step p:text-head
-com.xmlcalabash.steps.text.Join = step p:text-join
-com.xmlcalabash.steps.text.Replace = step p:text-replace
-com.xmlcalabash.steps.text.Sort = step p:text-sort
-com.xmlcalabash.steps.text.Tail = step p:text-tail
-com.xmlcalabash.steps.Unarchive = step p:unarchive
-com.xmlcalabash.steps.Uncompress = step p:uncompress
-com.xmlcalabash.steps.UnescapeMarkup = step p:unescape-markup
-com.xmlcalabash.steps.Unwrap = step p:unwrap
-com.xmlcalabash.steps.WwwFormUrlDecode = step p:www-form-urldecode
-com.xmlcalabash.steps.WwwFormUrlEncode = step p:www-form-urlencode
-com.xmlcalabash.steps.Uuid = step p:uuid
-com.xmlcalabash.steps.ValidateWithRNG = step p:validate-with-relax-ng
-com.xmlcalabash.steps.ValidateWithSCH = step p:validate-with-schematron
-com.xmlcalabash.steps.ValidateWithXSD = step p:validate-with-xml-schema
-com.xmlcalabash.steps.Wrap = step p:wrap
-com.xmlcalabash.steps.WrapSequence = step p:wrap-sequence
-com.xmlcalabash.steps.XInclude = step p:xinclude
-com.xmlcalabash.steps.XQuery = step p:xquery
-com.xmlcalabash.steps.Xslt = step p:xslt
-com.xmlcalabash.steps.B64Decode = step cx:base64-decode
-com.xmlcalabash.steps.B64Encode = step cx:base64-encode
-com.xmlcalabash.steps.ExceptionTranslator = step cx:exception-translator
-com.xmlcalabash.steps.JavaScript = step cx:javascript
-com.xmlcalabash.steps.Markdown = step cx:markdown
-com.xmlcalabash.steps.OptionValue = step cx:option-value
-com.xmlcalabash.steps.PropertyExtract = step cx:property-extract
-com.xmlcalabash.steps.PropertyMerge = step cx:property-merge
-com.xmlcalabash.steps.internal.ContentTypeChecker = step cx:content-type-checker
-com.xmlcalabash.steps.internal.SelectFilter = step cx:select-filter
-com.xmlcalabash.steps.internal.DocumentLoader = step cx:document-loader
-com.xmlcalabash.steps.internal.InlineLoader = step cx:inline-loader
-com.xmlcalabash.steps.internal.EmptyLoader = step cx:empty-loader
-
diff --git a/src/main/resources/com.xmlcalabash.settings b/src/main/resources/com.xmlcalabash.settings
new file mode 100644
index 0000000..ba8ee4d
--- /dev/null
+++ b/src/main/resources/com.xmlcalabash.settings
@@ -0,0 +1,95 @@
+namespace http://xmlcalabash.com/ns/extensions = cx
+namespace http://www.w3.org/ns/xproc = p
+namespace http://exproc.org/standard/functions = exf
+
+function exf:cwd = com.xmlcalabash.functions.Cwd
+function p:document-properties = com.xmlcalabash.functions.DocumentProperties
+function p:document-property = com.xmlcalabash.functions.DocumentProperty
+function p:force-qname-keys = com.xmlcalabash.functions.ForceQNameKeys
+function cx:step-elapsed = com.xmlcalabash.functions.InjElapsed
+function cx:injectable-id = com.xmlcalabash.functions.InjId
+function cx:step-name = com.xmlcalabash.functions.InjName
+function cx:step-type = com.xmlcalabash.functions.InjType
+function p:system-property = com.xmlcalabash.functions.SystemProperty
+function p:step-available = com.xmlcalabash.functions.StepAvailable
+function p:iteration-position = com.xmlcalabash.functions.IterationPosition
+function p:iteration-size = com.xmlcalabash.functions.IterationSize
+function p:urify = com.xmlcalabash.functions.UrifyFunction
+
+step p:add-attribute = com.xmlcalabash.steps.AddAttribute
+step p:archive = com.xmlcalabash.steps.Archive
+step p:archive-manifest = com.xmlcalabash.steps.ArchiveManifest
+step p:cast-content-type = com.xmlcalabash.steps.CastContentType
+step p:count = com.xmlcalabash.steps.Count
+step p:compress = com.xmlcalabash.steps.Compress
+step p:delete = com.xmlcalabash.steps.Delete
+step p:error = com.xmlcalabash.steps.Error
+step p:escape-markup = com.xmlcalabash.steps.EscapeMarkup
+step p:directory-list = com.xmlcalabash.steps.file.DirectoryList
+step p:file-copy = com.xmlcalabash.steps.file.FileCopy
+step p:file-delete = com.xmlcalabash.steps.file.FileDelete
+step p:file-info = com.xmlcalabash.steps.file.FileInfo
+step p:file-mkdir = com.xmlcalabash.steps.file.FileMkdir
+step p:file-move = com.xmlcalabash.steps.file.FileMove
+step p:file-create-tempfile = com.xmlcalabash.steps.file.FileCreateTempFile
+step p:file-touch = com.xmlcalabash.steps.file.FileTouch
+step p:filter = com.xmlcalabash.steps.Filter
+step p:hash = com.xmlcalabash.steps.Hash
+step p:http-request = com.xmlcalabash.steps.HttpRequest
+step p:identity = com.xmlcalabash.steps.Identity
+step p:insert = com.xmlcalabash.steps.Insert
+step p:json-join = com.xmlcalabash.steps.json.Join
+step p:json-merge = com.xmlcalabash.steps.json.Merge
+step p:label-elements = com.xmlcalabash.steps.LabelElements
+step p:load = com.xmlcalabash.steps.Load
+step p:namespace-delete = com.xmlcalabash.steps.NamespaceDelete
+step p:namespace-rename = com.xmlcalabash.steps.NamespaceRename
+step p:os-info = com.xmlcalabash.steps.os.OsInfo
+step p:os-exec = com.xmlcalabash.steps.os.OsExec
+step p:pack = com.xmlcalabash.steps.Pack
+step p:parameters = com.xmlcalabash.steps.Parameters
+step p:rename = com.xmlcalabash.steps.Rename
+step p:replace = com.xmlcalabash.steps.Replace
+step p:set-attributes = com.xmlcalabash.steps.SetAttributes
+step p:set-properties = com.xmlcalabash.steps.SetProperties
+step p:sink = com.xmlcalabash.steps.Sink
+step p:split-sequence = com.xmlcalabash.steps.SplitSequence
+step p:store = com.xmlcalabash.steps.Store
+step p:string-replace = com.xmlcalabash.steps.StringReplace
+step p:text-count = com.xmlcalabash.steps.text.Count
+step p:text-head = com.xmlcalabash.steps.text.Head
+step p:text-join = com.xmlcalabash.steps.text.Join
+step p:text-replace = com.xmlcalabash.steps.text.Replace
+step p:text-sort = com.xmlcalabash.steps.text.Sort
+step p:text-tail = com.xmlcalabash.steps.text.Tail
+step p:unarchive = com.xmlcalabash.steps.Unarchive
+step p:uncompress = com.xmlcalabash.steps.Uncompress
+step p:unescape-markup = com.xmlcalabash.steps.UnescapeMarkup
+step p:unwrap = com.xmlcalabash.steps.Unwrap
+step p:www-form-urldecode = com.xmlcalabash.steps.WwwFormUrlDecode
+step p:www-form-urlencode = com.xmlcalabash.steps.WwwFormUrlEncode
+step p:uuid = com.xmlcalabash.steps.Uuid
+step p:validate-with-relax-ng = com.xmlcalabash.steps.ValidateWithRNG
+step p:validate-with-schematron = com.xmlcalabash.steps.ValidateWithSCH
+step p:validate-with-xml-schema = com.xmlcalabash.steps.ValidateWithXSD
+step p:wrap = com.xmlcalabash.steps.Wrap
+step p:wrap-sequence = com.xmlcalabash.steps.WrapSequence
+step p:xinclude = com.xmlcalabash.steps.XInclude
+step p:xquery = com.xmlcalabash.steps.XQuery
+step p:xslt = com.xmlcalabash.steps.Xslt
+step cx:base64-decode = com.xmlcalabash.steps.B64Decode
+step cx:base64-encode = com.xmlcalabash.steps.B64Encode
+step cx:exception-translator = com.xmlcalabash.steps.ExceptionTranslator
+step cx:javascript = com.xmlcalabash.steps.JavaScript
+step cx:markdown = com.xmlcalabash.steps.Markdown
+step cx:option-value = com.xmlcalabash.steps.OptionValue
+step cx:property-extract = com.xmlcalabash.steps.PropertyExtract
+step cx:property-merge = com.xmlcalabash.steps.PropertyMerge
+step cx:content-type-checker = com.xmlcalabash.steps.internal.ContentTypeChecker
+step cx:select-filter = com.xmlcalabash.steps.internal.SelectFilter
+step cx:document-loader = com.xmlcalabash.steps.internal.DocumentLoader
+step cx:document-loader-vt = com.xmlcalabash.steps.internal.DocumentLoader
+step cx:inline-loader = com.xmlcalabash.steps.internal.InlineLoader
+step cx:inline-loader-vt = com.xmlcalabash.steps.internal.InlineLoader
+step cx:empty-loader = com.xmlcalabash.steps.internal.EmptyLoader
+step cx:value-computation = com.xmlcalabash.steps.internal.ValueComputation
diff --git a/src/main/resources/com/xmlcalabash/stylesheets/pl2dot.xsl b/src/main/resources/com/xmlcalabash/stylesheets/pl2dot.xsl
index 60d4298..c48fca7 100644
--- a/src/main/resources/com/xmlcalabash/stylesheets/pl2dot.xsl
+++ b/src/main/resources/com/xmlcalabash/stylesheets/pl2dot.xsl
@@ -14,7 +14,8 @@
-
+
@@ -52,33 +53,51 @@
-
+ select="'$' || @name || ' ' || local-name(.)"/>
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select="concat(if (starts-with(@name, '!syn'))
+ then '' else concat(@name, ' '),
+ $step-label)"/>
@@ -102,7 +121,7 @@
-
@@ -111,11 +130,11 @@
-
-
diff --git a/src/main/resources/explain-errors.txt b/src/main/resources/explain-errors.txt
index 48819e7..6126988 100644
--- a/src/main/resources/explain-errors.txt
+++ b/src/main/resources/explain-errors.txt
@@ -62,16 +62,19 @@ Validity error in “$1”: $2
XD0026
The context item is empty in expression: “$1” (Saxon: $2)
+XD0028
+Value does not satisfy type: “$1”
+
XD0030
Error: $1
XD0034
$1
-XD0036
+XD0036/1
Computed result “$1” does not match specified type “$2”.
-XD0036
+XD0036/2
Computed result “$2” does not match specified type “$3” for “$1”.
XD0038
@@ -182,6 +185,9 @@ There is no port named “$2” on “$1” steps.
XS0011
Duplicate port name: ‘$1’
+XS0014
+Attempt to make output port ‘$1’ primary when ‘$2’ is already primary.
+
XS0017
Option is both required and has default value: $1.
@@ -200,11 +206,17 @@ There’s no primary output port on the step named “$1”.
XS0022/4
The port “$2” on “$1” is not readable from here.
+XS0025/1
+Step types must explicitly be in a namespace.
+
+XS0025/2
+Step types may not be declared in the “$1” namespace.
+
XS0028
Name cannot be in the XProc namespace: “$1”.
XS0030
-Attempt to make port ‘$1’ primary when ‘$2’ is already primary.
+Attempt to make input port ‘$1’ primary when ‘$2’ is already primary.
XS0031
The option named ‘$2’ is not allowed on a ‘$1’ step.
@@ -236,6 +248,12 @@ Error p:import cannot import a “$1”.
XS0053
An imported pipeline must have a type.
+XS0059/1
+The pipeline document has no root element.
+
+XS0059/2
+The pipeline document has an invalid root element: “$1”.
+
XS0064/1
Only the last p:catch may omit the code.
@@ -276,20 +294,23 @@ Invalid output port: “$1”. The outputs of p:finally must be different from t
XS0073
There is no in-scope step named “$1”.
+XS0074
+A p:choose must have at least one p:when or p:otherwise.
+
XS0075
Invalid p:try: $1.
-XS0077
+XS0077/1
Invalid value specified: “$1”. Value must be $2.
-XS0079/1
-Comment not allowed here. Found “$1”.
+XS0077/2
+Invalid value, an empty string is not allowed here.
-XS0079/2
-Processing instruction not allowed here. Found “$1”.
+XS0077/3
+Invalid value “$1”, visibility must be “public” or “private”.
-XS0079/3
-Text not allowed here. Found “$1”.
+XS0079/1
+Can’t mix comments, processing-instructions, or (non-ws) text here.
XS0080
Duplicated option name: “$1”.
@@ -312,27 +333,51 @@ More than one p:with-input for the same port: “$1”
XS0087
There’s no in-scope namespace binding for the name “$1”.
+XS0088
+“$1” shadows static option.
+
XS0089
If a p:empty binding is used, it must be the only binding.
XS0090
Invalid pipe attribute value: “$1”; must be port, port@step, or @step.
+XS0092
+The value of a static option cannot be changed: “$1”.
+
+XS0095
+An option may not be both required and static: “$1”.
+
XS0096
Invalid sequence type: “$1”. $2.
+XS0097
+An attribute in the XProc namespace (“$1”) is not allowed on an element in the XProc namespace: “$2”
+
XS0100/1
Invalid pipeline: $1.
XS0100/2
XProc element not allowed here: “$1”
+XS0100/3
+Cannot mix implicit inlines with other connections: “$1”.
+
XS0102/1
-Mismatched primary outputs in p:choose, “$1” != “$2”
+Mismatched primary outputs in p:choose, “$1” != unnamed port
XS0102/2
+Mismatched primary outputs in p:choose, “$1” != “$2”
+
+XS0102/3
+Mismatched primary outputs in p:try, “$1” != unnamed
+
+XS0102/4
Mismatched primary outputs in p:try, “$1” != “$2”
+XS0103/1
+Mismatched primary outputs in p:choose, “$1” != “$2”
+
XS0107/1
There is no variable or reference named “$1” in scope.
@@ -345,12 +390,33 @@ Static expression references context item.
XS0107/4
Static expression references non-static variable “$1”
+XS0107/5
+Output port “$1” must not have bindings on atomic step type “$2”.
+
+XS0107/6
+Output port “$1” must not have bindings on atomic step.
+
XS0108
It is a static error if a p:if does not specify a primary output port.
+XS0111
+Unrecognized content type shortcut value: “$1”.
+
XS0112
No output on p:finally may be primary: “$1”
+XS0113
+Value of the $1 attribute must be ‘true’ or ‘false’.
+
+XS0114
+The port “$1” is not allowed on “$2” steps.
+
+XS0115/1
+Unresolvable deadlock in use-when: “$1”.
+
+XS0115/2
+Unresolvable deadlock in use-when: “$1”.
+
XC0001
The content type is not a valid text content type: “$1”.
@@ -528,6 +594,9 @@ Delete not allowed: $1
XC0151
Schema is not a valid schematron document.
+XC0153
+Schema is not a valid RELAX NG grammar.
+
XC0155
Validity error in “$1”: $2.
@@ -690,9 +759,15 @@ The format of the -D value must be “name=value”.
xi:XI0071
$1
+xi:XI0072
+$1
+
xi:XI0073
Inputs must be created with the same Processor that XML Calabash is using
+xi:XI0998
+$1
+
{http://xmlcalabash.com/ns/extensions}XI0999
It must be time for breakfast at Milliways, something impossible has happened: $1
diff --git a/src/main/scala/com/xmlcalabash/XMLCalabash.scala b/src/main/scala/com/xmlcalabash/XMLCalabash.scala
index ad92fe5..7015815 100644
--- a/src/main/scala/com/xmlcalabash/XMLCalabash.scala
+++ b/src/main/scala/com/xmlcalabash/XMLCalabash.scala
@@ -4,7 +4,6 @@ import com.jafpl.exceptions.{JafplException, JafplLoopDetected}
import com.jafpl.graph.{Binding, Node}
import com.jafpl.messages.Message
import com.jafpl.runtime.RuntimeConfiguration
-import com.jafpl.steps.DataConsumer
import com.jafpl.util.{ErrorListener, TraceEventManager}
import com.xmlcalabash.XMLCalabash.loggedProcessorDetail
import com.xmlcalabash.config.{DocumentManager, DocumentRequest, ErrorExplanation, XMLCalabashDebugOptions, XProcConfigurer}
@@ -12,17 +11,18 @@ import com.xmlcalabash.exceptions.{ConfigurationException, ExceptionCode, ModelE
import com.xmlcalabash.functions.FunctionImpl
import com.xmlcalabash.messages.XdmValueItemMessage
import com.xmlcalabash.model.util.{ExpressionParser, XProcConstants}
-import com.xmlcalabash.model.xml.{DeclContainer, DeclareStep, Library, Parser, XMLContext}
+import com.xmlcalabash.model.xxml.{XDeclareStep, XNameBinding, XParser, XStaticContext}
import com.xmlcalabash.parsers.XPathParser
-import com.xmlcalabash.runtime.{PrintingConsumer, SaxonExpressionEvaluator, StaticContext, XMLCalabashRuntime, XProcMetadata, XProcXPathExpression}
+import com.xmlcalabash.runtime.params.XPathBindingParams
+import com.xmlcalabash.runtime.{PrintingConsumer, SaxonExpressionEvaluator, StaticContext, XMLCalabashProcessor, XMLCalabashRuntime, XProcMetadata, XProcXPathExpression}
import com.xmlcalabash.sbt.BuildInfo
import com.xmlcalabash.util.{ArgBundle, DefaultErrorExplanation, DefaultXProcConfigurer, MediaType, PipelineBooleanOption, PipelineDocument, PipelineDocumentOption, PipelineDoubleOption, PipelineEnvironmentOption, PipelineEnvironmentOptionMap, PipelineEnvironmentOptionSerialization, PipelineExpressionOption, PipelineFileDocument, PipelineFilenameDocument, PipelineFunctionImplementation, PipelineInputDocument, PipelineInputFile, PipelineInputFilename, PipelineInputText, PipelineInputURI, PipelineInputXdm, PipelineNamespace, PipelineOption, PipelineOptionValue, PipelineOutputConsumer, PipelineOutputDocument, PipelineOutputFilename, PipelineOutputURI, PipelineParameter, PipelineStepImplementation, PipelineStringOption, PipelineSystemProperty, PipelineTextDocument, PipelineURIDocument, PipelineUntypedOption, PipelineUriOption, PipelineXdmDocument, PipelineXdmValueOption, URIUtils}
import net.sf.saxon.lib.{ModuleURIResolver, UnparsedTextURIResolver}
-import net.sf.saxon.s9api.{ItemType, Processor, QName, XdmAtomicValue, XdmNode, XdmValue}
+import net.sf.saxon.s9api.{ItemType, ItemTypeFactory, Processor, QName, XdmAtomicValue, XdmNode, XdmValue}
import org.slf4j.{Logger, LoggerFactory}
import org.xml.sax.{EntityResolver, InputSource}
-import java.io.{BufferedReader, ByteArrayInputStream, FileInputStream, FileReader}
+import java.io.{ByteArrayInputStream, FileInputStream}
import java.net.URI
import java.nio.charset.StandardCharsets
import javax.xml.transform.URIResolver
@@ -64,10 +64,11 @@ object XMLCalabash {
}
}
-class XMLCalabash private(userProcessor: Option[Processor], val configurer: XProcConfigurer) extends RuntimeConfiguration {
+class XMLCalabash private(userProcessor: Option[Processor], val configurer: XProcConfigurer) extends XMLCalabashProcessor with RuntimeConfiguration {
protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
private var _processor: Processor = _
+ private var _itemTypeFactory: ItemTypeFactory = _
private var _expressionEvaluator: SaxonExpressionEvaluator = _
private val _collections = mutable.HashMap.empty[String, List[XdmNode]]
private var _debugOptions: XMLCalabashDebugOptions = new XMLCalabashDebugOptions(this)
@@ -87,18 +88,23 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
private var _staticBaseURI = URIUtils.cwdAsURI
private var _locale = defaultLocale
private var _episode = computeEpisode
- private val _builtinSteps = ListBuffer.empty[Library]
- private val _importedURIs = mutable.HashMap.empty[URI, DeclContainer]
- // Do not allow the order to be random
- private val _imports = ListBuffer.empty[URI]
- private var _declaration = Option.empty[DeclareStep]
+ private var _declaration = Option.empty[XDeclareStep]
private var _runtime: XMLCalabashRuntime = _
private var _pipeline = Option.empty[PipelineDocument]
private val _inputs = mutable.HashMap.empty[String,ListBuffer[PipelineInputDocument]]
private val _outputs = mutable.HashMap.empty[String,ListBuffer[PipelineOutputDocument]]
private val _options = mutable.HashMap.empty[QName,PipelineOptionValue]
+ private val optionBindings = mutable.HashMap.empty[String,Message]
+ private val _staticOptions = mutable.HashMap.empty[QName, XdmValueItemMessage]
+
+ private var _standardLibraryParser = false
+ private val _funcImplClasses = mutable.HashMap.empty[QName,String]
+ private val _stepImplClasses = mutable.HashMap.empty[QName,String]
+ // Only used during static analysis and not valid at runtime
+ private val _staticStepsAvailable = mutable.HashSet.empty[QName]
+ private var _staticStepsIndeterminate = true
private var _except: Option[Exception] = None
private var _longError = ""
@@ -106,6 +112,37 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
val args = new ArgBundle()
+ protected[xmlcalabash] def standardLibraryParser: Boolean = _standardLibraryParser
+ protected[xmlcalabash] def standardLibraryParser_=(std: Boolean): Unit = {
+ _standardLibraryParser = std
+ }
+
+ def externalSteps: Map[QName,String] = _stepImplClasses.toMap
+
+ // Only used during static analysis and not valid at runtime
+ protected[xmlcalabash] def staticStepsIndeterminate: Boolean = _staticStepsIndeterminate
+ protected[xmlcalabash] def staticStepsIndeterminate_=(det: Boolean): Unit = {
+ _staticStepsIndeterminate = det
+ }
+
+ // Only used during static analysis and not valid at runtime
+ protected[xmlcalabash] def staticStepsAvailable: Set[QName] = _staticStepsAvailable.toSet
+ protected[xmlcalabash] def staticStepsAvailable_=(aset: Set[QName]): Unit = {
+ _staticStepsAvailable.clear()
+ _staticStepsAvailable ++= aset
+ }
+
+ protected[xmlcalabash] def staticStepAvailable(stepType: QName): Boolean = {
+ if (_stepImplClasses.contains(stepType) || _staticStepsAvailable.contains(stepType)) {
+ true
+ } else {
+ if (staticStepsIndeterminate) {
+ throw XProcException.xiIndeterminateSteps()
+ }
+ false
+ }
+ }
+
def inputs: Map[String,List[PipelineInputDocument]] = {
val map = mutable.HashMap.empty[String,List[PipelineInputDocument]]
for ((port,list) <- _inputs) {
@@ -123,6 +160,13 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
}
def options: Map[QName,PipelineOptionValue] = _options.toMap
+ def staticOptions: Map[QName, XdmValueItemMessage] = _staticOptions.toMap
+ def addStatic(name: QName, value: XdmValueItemMessage): Unit = {
+ if (_staticOptions.contains(name)) {
+ throw XProcException.xsShadowsStatic(name, None)
+ }
+ _staticOptions.put(name, value)
+ }
def environmentOptions(name: QName): List[PipelineEnvironmentOption] = {
parameters collect { case p: PipelineEnvironmentOption => p } filter { _.eqname == name.getEQName }
@@ -163,8 +207,9 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
}
}
- val context = new XMLContext(this)
- val map = mutable.HashMap.empty[QName,String]
+ _itemTypeFactory = new ItemTypeFactory(processor)
+
+ val context = new XStaticContext()
for (funcEnv <- parameters collect { case p: PipelineFunctionImplementation => p }) {
val name = context.parseQName(funcEnv.eqname)
try {
@@ -175,7 +220,7 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
logger.warn(s"Failed to register ${name} with implementation ${funcEnv.className}; class implements ${func.getFunctionQName}")
} else {
_processor.registerExtensionFunction(func)
- map.put(context.parseQName(funcEnv.eqname), funcEnv.className)
+ _funcImplClasses.put(context.parseQName(funcEnv.eqname), funcEnv.className)
logger.debug(s"Registered ${name} with implementation ${funcEnv.className}")
}
} catch {
@@ -187,7 +232,10 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
logger.warn(s"Failed to register ${name} with implementation ${funcEnv.className}: ${ex.getMessage}")
}
}
- _funcImplClasses = Some(map.toMap)
+
+ for (stepEnv <- parameters collect { case p: PipelineStepImplementation => p }) {
+ _stepImplClasses.put(context.parseQName(stepEnv.eqname), stepEnv.className)
+ }
_expressionEvaluator = new SaxonExpressionEvaluator(this)
configurer.xmlCalabashConfigurer.update(this)
@@ -225,12 +273,27 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
}
}
+ // Build the options before compiling in case some of them are statics...
+ _options.clear()
+ val nsmap = mutable.HashMap.empty[String,String]
+ for (param <- args.parameters) {
+ param match {
+ case ns: PipelineNamespace =>
+ nsmap.put(ns.prefix, ns.namespace)
+ case opt: PipelineOption =>
+ updateOptions(_options, nsmap.toMap, opt)
+ case _ =>
+ () // Just ignore it?
+ }
+ }
+
_pipeline = args.pipeline
if (_pipeline.isDefined && _declaration.isEmpty) {
- val parser = new Parser(this)
- val pipeline = try {
- _pipeline.get match {
+ val parser = new XParser(this)
+ var pipeline: XDeclareStep = null
+ try {
+ pipeline = _pipeline.get match {
case uri: PipelineURIDocument =>
parser.loadDeclareStep(uri.value)
case str: PipelineFilenameDocument =>
@@ -251,24 +314,33 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
case _ =>
throw new RuntimeException("Unexpected pipeline input")
}
+ if (parser.exceptions.nonEmpty) {
+ throw parser.exceptions.head
+ }
} catch {
case ex: XProcException =>
handleException(ex)
ex.code match {
case XProcException.err_xd0036 =>
- val value = ex.details.head.asInstanceOf[String]
- val seqtype = ex.details(1).asInstanceOf[String]
- throw XProcException.xsBadTypeValue(value, seqtype, ex.location)
+ // Weirdo remapping to satisfy the test suite
+ if (ex.variant == 1) {
+ val value = ex.details.head.asInstanceOf[String]
+ val seqtype = ex.details(1).asInstanceOf[String]
+ throw XProcException.xsBadTypeValue(value, seqtype, ex.location)
+ }
case _ =>
- throw ex
+ ()
}
- case ex: Throwable =>
+ throw ex
+ case ex: Exception =>
throw ex
}
_declaration = Some(pipeline)
close()
+ //println(_declaration.get.dump)
+
debugOptions.dumpTree(pipeline)
debugOptions.dumpPipeline(pipeline)
}
@@ -301,7 +373,7 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
_inputs(in.port) += in
- case out: PipelineOutputDocument =>
+ case _: PipelineOutputDocument =>
() // See below
case opt: PipelineOption =>
@@ -366,7 +438,7 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
}
private def updateOptions(options: mutable.HashMap[QName,PipelineOptionValue], nsmap: Map[String,String], opt: PipelineOption): Unit = {
- val context = new XMLContext(this, Some(URIUtils.cwdAsURI), nsmap, None)
+ val context = new XStaticContext(URIUtils.cwdAsURI, nsmap)
val name = context.parseQName(opt.eqname)
val value: XdmValue = opt match {
case v: PipelineBooleanOption => new XdmAtomicValue(v.value)
@@ -494,8 +566,23 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
runtime.output(port, pc)
}
- for ((name, value) <- _options) {
- runtime.option(name, value.value, value.context)
+ for (option <- _declaration.get.options) {
+ if (_options.contains(option.name)) {
+ setOption(option, _options(option.name))
+ } else {
+ if (option.required) {
+ throw XProcException.xsMissingRequiredOption(option.name, None)
+ }
+ if (option.usedByPipeline) {
+ setOption(option)
+ }
+ }
+ }
+
+ for (name <- _options.keySet) {
+ if (!optionBindings.contains(name.getClarkName)) {
+ logger.info(s"Ignoring option '${name}'; it is not used by the pipeline")
+ }
}
runtime.run()
@@ -519,6 +606,23 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
}
}
+ private def setOption(option: XNameBinding): Unit = {
+ val eval = expressionEvaluator.newInstance()
+ val expr = new XProcXPathExpression(option.staticContext, option.select.getOrElse("()"))
+ val value = eval.compute(expr, List(), optionBindings.toMap, Map(), XPathBindingParams.EMPTY)
+ runtime.option(option.name, value, option.staticContext)
+ val msg = new XdmValueItemMessage(value, XProcMetadata.ANY, option.staticContext)
+ optionBindings.put(option.name.getClarkName, msg)
+ // FIXME: does the option value satisfy the type constraints?
+ }
+
+ private def setOption(option: XNameBinding, value: PipelineOptionValue): Unit = {
+ runtime.option(option.name, value.value, value.context)
+ val msg = new XdmValueItemMessage(value.value, XProcMetadata.ANY, value.context)
+ optionBindings.put(option.name.getClarkName, msg)
+ // FIXME: does the option value satisfy the type constraints?
+ }
+
private def handleException(exception: Exception): Unit = {
_except = Some(exception)
@@ -608,7 +712,8 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
def runtime: XMLCalabashRuntime = _runtime
def processor: Processor = _processor
- def step: DeclareStep = _declaration.orNull
+ def itemTypeFactory: ItemTypeFactory = _itemTypeFactory
+ def step: XDeclareStep = _declaration.orNull
def pipeline: PipelineDocument = _pipeline.orNull
def errorMessage: String = _shortError
@@ -684,30 +789,6 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
}
}
- protected[xmlcalabash] def builtinSteps: List[Library] = _builtinSteps.toList
- protected[xmlcalabash] def builtinSteps_=(libs: List[Library]): Unit = {
- if (_builtinSteps.nonEmpty) {
- throw XProcException.xiThisCantHappen("Attempt to redefine builtin steps", None)
- }
- _builtinSteps ++= libs
- }
-
- protected[xmlcalabash] def importedURIs: List[URI] = _imports.toList
- protected[xmlcalabash] def importedURI(href: URI): Option[DeclContainer] = {
- _importedURIs.get(href)
- }
- protected[xmlcalabash] def addImportedURI(href: URI, container: DeclContainer): Unit = {
- if (_importedURIs.contains(href)) {
- throw new RuntimeException(s"Attempt to redefine imported uri: $href")
- }
- _importedURIs.put(href, container)
- _imports += href
- }
- protected[xmlcalabash] def clearImportedURIs(): Unit = {
- _importedURIs.clear()
- _imports.clear()
- }
-
def debugOptions: XMLCalabashDebugOptions = _debugOptions
def debugOptions_=(options: XMLCalabashDebugOptions): Unit = {
_debugOptions = options
@@ -841,7 +922,7 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
def defaultSerializationOptions(contentType: String): Map[QName,String] = {
if (_defaultSerializationOptions.isEmpty) {
checkClosed()
- val context = new XMLContext(this)
+ val context = new XStaticContext()
val map = mutable.HashMap.empty[String,Map[QName,String]]
for (ser <- parameters collect { case p: PipelineEnvironmentOptionSerialization => p }) {
val smap = mutable.HashMap.empty[QName, String]
@@ -871,28 +952,6 @@ class XMLCalabash private(userProcessor: Option[Processor], val configurer: XPro
_episode = episode
}
- // ==============================================================================================
- private var _stepImplClasses: Option[Map[QName,String]] = None
- private var _funcImplClasses: Option[Map[QName,String]] = None
-
- def atomicStepImplementation(stepType: QName): Option[String] = {
- if (_stepImplClasses.isEmpty) {
- checkClosed()
- val context = new XMLContext(this)
- val map = mutable.HashMap.empty[QName,String]
- for (step <- parameters collect { case p: PipelineStepImplementation => p }) {
- map.put(context.parseQName(step.eqname), step.className)
- }
- _stepImplClasses = Some(map.toMap)
- }
- _stepImplClasses.get.get(stepType)
- }
-
- def functionImplementation(funcName: QName): Option[String] = {
- // Never empty because it's initialized when the configuration is closed.
- _funcImplClasses.get.get(funcName)
- }
-
// FIXME: Should this be a factory, or should XPathParser be reusable?
def expressionParser: ExpressionParser = {
new XPathParser(this)
diff --git a/src/main/scala/com/xmlcalabash/config/OptionSignature.scala b/src/main/scala/com/xmlcalabash/config/OptionSignature.scala
index 87647f4..1888e91 100644
--- a/src/main/scala/com/xmlcalabash/config/OptionSignature.scala
+++ b/src/main/scala/com/xmlcalabash/config/OptionSignature.scala
@@ -1,32 +1,33 @@
package com.xmlcalabash.config
+import com.xmlcalabash.model.xxml.XOption
import net.sf.saxon.s9api.{QName, SequenceType, XdmAtomicValue}
import scala.collection.mutable.ListBuffer
-class OptionSignature(val name: QName) {
+class OptionSignature private(val name: QName) {
private var _required = true
+ private var _static = false
+ private var _as = "xs:untypedAtomic"
private var _declaredType = Option.empty[SequenceType]
private var _occurrence = Option.empty[String]
private var _tokenList: Option[ListBuffer[XdmAtomicValue]] = None
private var _defaultValue = Option.empty[String]
private var _forceQNameKeys = false
- def this(name: QName, optType: SequenceType, required: Boolean) = {
- this(name)
- _declaredType = Some(optType)
- _required = required
+ def this(option: XOption) = {
+ this(option.name)
+ _as = option.as.getOrElse("xs:untypedAtomic")
+ _declaredType = option.declaredType
+ _required = option.required
+ _static = option.static
}
def required: Boolean = _required
- def required_=(req: Boolean): Unit = {
- _required = req
- }
+ def static: Boolean = _static
+ def as: String = _as
def declaredType: Option[SequenceType] = _declaredType
- def declaredType_=(value: SequenceType): Unit = {
- _declaredType = Some(value)
- }
def occurrence: Option[String] = _occurrence
def occurrence_=(value: String): Unit = {
diff --git a/src/main/scala/com/xmlcalabash/config/PortSignature.scala b/src/main/scala/com/xmlcalabash/config/PortSignature.scala
index b8babe5..07557e6 100644
--- a/src/main/scala/com/xmlcalabash/config/PortSignature.scala
+++ b/src/main/scala/com/xmlcalabash/config/PortSignature.scala
@@ -1,6 +1,6 @@
package com.xmlcalabash.config
-import com.xmlcalabash.model.xml.DataSource
+import com.xmlcalabash.model.xxml.XDataSource
import com.xmlcalabash.util.MediaType
import scala.collection.mutable.ListBuffer
@@ -9,7 +9,7 @@ class PortSignature(val port: String) {
private var _cardinality = "1"
private var _primary = Option.empty[Boolean]
private val _contentTypes = ListBuffer.empty[MediaType]
- private val _defaultBindings = ListBuffer.empty[DataSource]
+ private val _defaultBindings = ListBuffer.empty[XDataSource]
def this(port: String, primary: Boolean, sequence: Boolean) = {
this(port)
@@ -19,7 +19,7 @@ class PortSignature(val port: String) {
}
}
- def this(port: String, primary: Boolean, sequence: Boolean, bindings: List[DataSource]) = {
+ def this(port: String, primary: Boolean, sequence: Boolean, bindings: List[XDataSource]) = {
this(port, primary, sequence)
_defaultBindings ++= bindings
}
@@ -42,5 +42,5 @@ class PortSignature(val port: String) {
_contentTypes ++= types
}
- def defaultBindings: List[DataSource] = _defaultBindings.toList
+ def defaultBindings: List[XDataSource] = _defaultBindings.toList
}
diff --git a/src/main/scala/com/xmlcalabash/config/StepSignature.scala b/src/main/scala/com/xmlcalabash/config/StepSignature.scala
index 07b6ad7..123fc95 100644
--- a/src/main/scala/com/xmlcalabash/config/StepSignature.scala
+++ b/src/main/scala/com/xmlcalabash/config/StepSignature.scala
@@ -1,8 +1,8 @@
package com.xmlcalabash.config
import com.jafpl.graph.Location
-import com.xmlcalabash.exceptions.{ExceptionCode, ModelException}
-import com.xmlcalabash.model.xml.DeclareStep
+import com.xmlcalabash.exceptions.{ExceptionCode, ModelException, XProcException}
+import com.xmlcalabash.model.xxml.XDeclareStep
import net.sf.saxon.s9api.QName
import scala.collection.mutable
@@ -13,7 +13,7 @@ class StepSignature(val stepType: Option[QName]) {
private val _outputPorts = mutable.HashMap.empty[String, PortSignature]
private val _options = ListBuffer.empty[OptionSignature]
private val _implementation = ListBuffer.empty[String]
- private var _declaration = Option.empty[DeclareStep]
+ private var _declaration = Option.empty[XDeclareStep]
def addInput(port: PortSignature, location: Location): Unit = {
if (_inputPorts.contains(port.port)) {
@@ -47,8 +47,8 @@ class StepSignature(val stepType: Option[QName]) {
_implementation += className
}
- def declaration: Option[DeclareStep] = _declaration
- def declaration_=(decl: DeclareStep): Unit = {
+ def declaration: Option[XDeclareStep] = _declaration
+ def declaration_=(decl: XDeclareStep): Unit = {
if (_implementation.nonEmpty) {
throw new RuntimeException("Cannot have an atomic step with the same name as a non-atomic step")
}
@@ -91,13 +91,13 @@ class StepSignature(val stepType: Option[QName]) {
}
}
- def option(name: QName, location: Option[Location]): OptionSignature = {
+ def option(name: QName): Option[OptionSignature] = {
for (opt <- _options) {
if (opt.name == name) {
- return opt
+ return Some(opt)
}
}
- throw new ModelException(ExceptionCode.BADOPTSIG, List(stepType.toString, name.toString), location)
+ None
}
def primaryInput: Option[PortSignature] = {
@@ -119,11 +119,7 @@ class StepSignature(val stepType: Option[QName]) {
}
override def toString: String = {
- if (stepType.isDefined) {
- stepType.get.toString
- } else {
- "anonymous StepSignature"
- }
+ stepType.toString
}
}
diff --git a/src/main/scala/com/xmlcalabash/config/XMLCalabashDebugOptions.scala b/src/main/scala/com/xmlcalabash/config/XMLCalabashDebugOptions.scala
index 6a2122e..0543e93 100644
--- a/src/main/scala/com/xmlcalabash/config/XMLCalabashDebugOptions.scala
+++ b/src/main/scala/com/xmlcalabash/config/XMLCalabashDebugOptions.scala
@@ -1,18 +1,18 @@
package com.xmlcalabash.config
-import java.io.{ByteArrayInputStream, ByteArrayOutputStream, File, FileOutputStream, PrintStream, PrintWriter}
import com.jafpl.graph.Graph
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.config.XMLCalabashDebugOptions.{GRAPH, OPENGRAPH, PIPELINE, TREE}
-import com.xmlcalabash.model.xml.DeclareStep
import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.model.xxml.XDeclareStep
import com.xmlcalabash.util.PipelineEnvironmentOption
-
-import javax.xml.transform.sax.SAXSource
import net.sf.saxon.s9api.{QName, XdmDestination, XdmNode}
import org.slf4j.{Logger, LoggerFactory}
import org.xml.sax.InputSource
+import java.io.{ByteArrayInputStream, ByteArrayOutputStream, File, FileOutputStream, PrintWriter}
+import java.nio.charset.StandardCharsets
+import javax.xml.transform.sax.SAXSource
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
@@ -28,8 +28,8 @@ class XMLCalabashDebugOptions(config: XMLCalabash) {
private val _injectables = ListBuffer.empty[String]
- private val dumped = mutable.HashMap.empty[DeclareStep, mutable.HashSet[String]]
- private val dumpCount = mutable.HashMap.empty[DeclareStep, mutable.HashMap[String, Long]]
+ private val dumped = mutable.HashMap.empty[XDeclareStep, mutable.HashSet[String]]
+ private val dumpCount = mutable.HashMap.empty[XDeclareStep, mutable.HashMap[String, Long]]
def injectables: List[String] = _injectables.toList
@@ -43,12 +43,18 @@ class XMLCalabashDebugOptions(config: XMLCalabash) {
}
def graphviz_dot: Option[String] = {
- val envOpt = environmentOptions(XProcConstants.cc_graphviz).headOption
- if (envOpt.isDefined && envOpt.head.getString.isDefined) {
- envOpt.head.getString
- } else {
- None
+ var dot = Option.empty[String]
+ for (option <- environmentOptions(XProcConstants.cc_graphviz)) {
+ if (dot.isEmpty && option.getString.isDefined) {
+ for (path <- option.getString.get.split("\\s+")) {
+ val exec = new File(path)
+ if (exec.exists() && exec.canExecute) {
+ dot = Some(path)
+ }
+ }
+ }
}
+ dot
}
def run: Boolean = {
@@ -107,27 +113,27 @@ class XMLCalabashDebugOptions(config: XMLCalabash) {
// ===========================================================================================
- def dumpTree(decl: DeclareStep): Unit = {
+ def dumpTree(decl: XDeclareStep): Unit = {
dump(decl, TREE)
}
- def dumpPipeline(decl: DeclareStep): Unit = {
+ def dumpPipeline(decl: XDeclareStep): Unit = {
dump(decl, PIPELINE)
}
- def dumpGraph(decl: DeclareStep, graph: Graph): Unit = {
+ def dumpGraph(decl: XDeclareStep, graph: Graph): Unit = {
dump(decl, GRAPH, Some(graph))
}
- def dumpOpenGraph(decl: DeclareStep, graph: Graph): Unit = {
+ def dumpOpenGraph(decl: XDeclareStep, graph: Graph): Unit = {
dump(decl, OPENGRAPH, Some(graph))
}
- private def dump(decl: DeclareStep, opt: String): Unit = {
+ private def dump(decl: XDeclareStep, opt: String): Unit = {
dump(decl, opt, None)
}
- private def dump(decl: DeclareStep, opt: String, graph: Option[Graph]): Unit = {
+ private def dump(decl: XDeclareStep, opt: String, graph: Option[Graph]): Unit = {
if (!graph_option(opt)) {
return
}
@@ -161,7 +167,7 @@ class XMLCalabashDebugOptions(config: XMLCalabash) {
val fn = s"$outputDirectory/$basefn$ext.xml"
val fos = new FileOutputStream(new File(fn))
val pw = new PrintWriter(fos)
- pw.write(decl.xdump.toString)
+ pw.write(decl.dump.toString)
pw.close()
fos.close()
case PIPELINE =>
@@ -169,14 +175,18 @@ class XMLCalabashDebugOptions(config: XMLCalabash) {
val fn = s"$outputDirectory/$basefn$ext.svg"
val baos = new ByteArrayOutputStream()
val pw = new PrintWriter(baos)
- pw.write(decl.xdump.toString)
+ pw.write(decl.dump.toString)
pw.close()
+ //println("********* PIPELINE ************")
+ //println(baos.toString(StandardCharsets.UTF_8))
svgPipeline(fn, baos)
case GRAPH =>
val basefn = s"${name}_graph"
val fn = s"$outputDirectory/$basefn$ext.svg"
val baos = new ByteArrayOutputStream()
val pw = new PrintWriter(baos)
+ //println("********* GRAPH ************")
+ //println(graph.get.asXML.toString)
pw.write(graph.get.asXML.toString)
pw.close()
svgGraph(fn, baos, "pgx2dot.xsl")
@@ -188,6 +198,8 @@ class XMLCalabashDebugOptions(config: XMLCalabash) {
val pw = new PrintWriter(baos)
pw.write(graph.get.asXML.toString)
pw.close()
+ //println("********* OPEN GRAPH ************")
+ //println(baos.toString(StandardCharsets.UTF_8))
svgGraph(fn, baos, "pg2dot.xsl")
}
}
diff --git a/src/main/scala/com/xmlcalabash/exceptions/XProcException.scala b/src/main/scala/com/xmlcalabash/exceptions/XProcException.scala
index e7b90ee..7583acc 100644
--- a/src/main/scala/com/xmlcalabash/exceptions/XProcException.scala
+++ b/src/main/scala/com/xmlcalabash/exceptions/XProcException.scala
@@ -4,7 +4,7 @@ import com.jafpl.exceptions.{JafplException, JafplExceptionCode}
import com.jafpl.graph.Location
import com.jafpl.messages.{Message, Metadata}
import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.model.xml.Artifact
+import com.xmlcalabash.model.xxml.XArtifact
import com.xmlcalabash.runtime.{StaticContext, XProcExpression}
import com.xmlcalabash.util.MediaType
import net.sf.saxon.om.StructuredQName
@@ -14,11 +14,14 @@ import java.net.URI
import scala.collection.mutable.ListBuffer
object XProcException {
+ val err_stepAvailableIndeterminate = new QName("cx", XProcConstants.ns_cx, "XI0100")
+
val err_xd0011 = new QName("err", XProcConstants.ns_err, "XD0011")
val err_xd0015 = new QName("err", XProcConstants.ns_err, "XD0015")
val err_xd0030 = new QName("err", XProcConstants.ns_err, "XD0030")
val err_xd0036 = new QName("err", XProcConstants.ns_err, "XD0036")
val err_xd0038 = new QName("err", XProcConstants.ns_err, "XD0038")
+ val err_XD0045 = new QName("err", XProcConstants.ns_err, "XD0045")
val err_xd0057 = new QName("err", XProcConstants.ns_err, "XD0057")
val err_xd0059 = new QName("err", XProcConstants.ns_err, "XD0059")
val err_xd0072 = new QName("err", XProcConstants.ns_err, "XD0072")
@@ -28,6 +31,18 @@ object XProcException {
var cx_XI0073 = new QName("cx", XProcConstants.ns_cx, "XI0073")
+ def errxs(code: Int): QName = {
+ new QName("err", XProcConstants.ns_err, f"XS$code%04d")
+ }
+
+ def errxd(code: Int): QName = {
+ new QName("err", XProcConstants.ns_err, f"XD$code%04d")
+ }
+
+ def errxi(code: Int): QName = {
+ new QName("cx", XProcConstants.ns_cx, f"XI$code%04d")
+ }
+
def xtde(errNo: Int): StructuredQName = {
new StructuredQName("err", XProcConstants.ns_xqt_errors, f"XTDE$errNo%04d")
}
@@ -55,8 +70,8 @@ object XProcException {
def xiInjectMessageNodes(location: Option[Location]): XProcException = internalError(17, location)
def xiInjectRedefPort(location: Option[Location]): XProcException = internalError(18, location)
def xiChildNotFound(location: Option[Location]): XProcException = internalError(19, location)
- def xiBadPatch(node: Artifact, location: Option[Location]): XProcException = internalError(20, location, node)
- def xiBadPatchChild(node: Artifact, location: Option[Location]): XProcException = internalError(21, location, node)
+ def xiBadPatch(node: XArtifact, location: Option[Location]): XProcException = internalError(20, location, node)
+ def xiBadPatchChild(node: XArtifact, location: Option[Location]): XProcException = internalError(21, location, node)
def xiBadMessage(message: Message, location: Option[Location]): XProcException = internalError(22, location, message)
def xiInvalidPort(port: String, location: Option[Location]): XProcException = internalError(23, location, port)
def xiInvalidPropertyValue(value: Any, location: Option[Location]): XProcException = internalError(24, location, value)
@@ -112,7 +127,12 @@ object XProcException {
def xiWrongProcessor(message: String, location: Option[Location]): XProcException = internalError(73, location, message)
def xiBadMediaType(message: String, location: Option[Location]): XProcException = internalError(74, location, message)
def xiBadConsumers(message: String, location: Option[Location]): XProcException = internalError(75, location, message)
+ def xiImpossibleNamespaceConfiguration(prefix: String, uri: String, location: Option[Location]): XProcException = internalError(76, location, List(prefix,uri))
+ def xiIndeterminateSteps(): XProcException = internalError(100, None)
+ def xiStepAvailableForbidden(): XProcException = internalError(101, None)
+
+ def xiUserError(msg: String): XProcException = internalError(998, None, msg)
def xiThisCantHappen(msg: String): XProcException = internalError(999, None, msg)
def xiThisCantHappen(msg: String, location: Option[Location]): XProcException = internalError(999, location, msg)
@@ -130,7 +150,6 @@ object XProcException {
def xdContextItemAbsent(expr: String, msg: String, location: Option[Location]): XProcException = dynamicError(1, List(expr, msg), location)
// FIXME: subtypes
- def xdBadValue(value: String, vtype: String, location: Option[Location]): XProcException = dynamicError((19, 1), List(value,vtype), location)
def xdBadMatchPattern(pattern: String, message: String, location: Option[Location]): XProcException = dynamicError((19, 2), List(pattern, message), location)
def xdBadVisibility(visibility: String, location: Option[Location]): XProcException = dynamicError((19, 3), visibility, location)
def xdBadValue(value: String, location: Option[Location]): XProcException = dynamicError((19, 4), value, location)
@@ -145,8 +164,10 @@ object XProcException {
def xdStepFailed(msg: String, location: Option[Location]): XProcException = dynamicError(30, msg, location)
def xdConflictingNamespaceDeclarations(msg: String, location: Option[Location]): XProcException = dynamicError(34, msg, location)
- def xdBadType(value: String, as: String, location: Option[Location]): XProcException = dynamicError(36, List(value, as), location)
- def xdBadType(name: QName, value: String, as: String, location: Option[Location]): XProcException = dynamicError(36, List(name, value, as), location)
+
+ def xdBadType(value: String, as: String, location: Option[Location]): XProcException = dynamicError((36,1), List(value, as), location)
+ def xdBadType(name: QName, value: String, as: String, location: Option[Location]): XProcException = dynamicError((36,2), List(name, value, as), location)
+
def xdBadInputMediaType(ctype: MediaType, allowed: List[MediaType], location: Option[Location]): XProcException = dynamicError(38, List(ctype, allowed), location)
def xdUnsupportedCharset(charset: String, location: Option[Location]): XProcException = dynamicError(39, charset, location)
def xdIncorrectEncoding(encoding: String, location: Option[Location]): XProcException = dynamicError(40, encoding, location)
@@ -188,17 +209,20 @@ object XProcException {
def xsBadAttribute(name: QName, location: Option[Location]): XProcException = staticError(8, name, location)
def xsBadPortName(stepType: QName, port: String, location: Option[Location]): XProcException = staticError(10, List(stepType, port), location)
def xsDupPortName(port: String, location: Option[Location]): XProcException = staticError(11, port, location)
+ def xsDupPrimaryOutputPort(port: String, primaryPort: String, location: Option[Location]): XProcException = staticError(14, List(port, primaryPort), location)
def xsRequiredAndDefaulted(name: QName, location: Option[Location]): XProcException = staticError(17, name, location)
def xsMissingRequiredOption(optName: QName, location: Option[Location]): XProcException = staticError(18, optName, location)
- def xsPortNotReadableNoStep(step: String, location: Option[Location]): XProcException = staticError((22,1), step, location)
+ def xsPortNotReadableNoStep(port: String, location: Option[Location]): XProcException = staticError((22,1), port, location)
def xsPortNotReadableNoPrimaryInput(step: String, location: Option[Location]): XProcException = staticError((22,2), step, location)
def xsPortNotReadableNoPrimaryOutput(step: String, location: Option[Location]): XProcException = staticError((22,3), step, location)
def xsPortNotReadable(step: String, port: String, location: Option[Location]): XProcException = staticError((22,4), List(step, port), location)
+ def xsBadStepTypeNamespace(location: Option[Location]): XProcException = staticError((25,1), location)
+ def xsBadStepTypeNamespace(uri: String, location: Option[Location]): XProcException = staticError((25,2), uri, location)
def xsOptionInXProcNamespace(name: QName, location: Option[Location]): XProcException = staticError(28, name, location)
- def xsDupPrimaryPort(port: String, primaryPort: String, location: Option[Location]): XProcException = staticError(30, List(port, primaryPort), location)
+ def xsDupPrimaryInputPort(port: String, primaryPort: String, location: Option[Location]): XProcException = staticError(30, List(port, primaryPort), location)
def xsUndeclaredOption(stepType: QName, optName: QName, location: Option[Location]): XProcException = staticError(31, List(stepType,optName), location)
def xsUnconnectedPrimaryInputPort(step: String, port: String, location: Option[Location]): XProcException = staticError(32, List(step,port), location)
def xsDupStepType(stepType: QName, location: Option[Location]): XProcException = staticError(36, stepType, location)
@@ -210,6 +234,11 @@ object XProcException {
def xsImportFailed(href: URI, location: Option[Location]): XProcException = staticError((52,1), href, location)
def xsBadImport(name: QName, location: Option[Location]): XProcException = staticError((52,2), name, location)
def xsStepTypeRequired(location: Option[Location]): XProcException = staticError(53, List(), location)
+ def xsBadExcludeInlinePrefixes(): XProcException = staticError((57,1), List(), None)
+ def xsBadExcludeInlinePrefixes(prefix: String): XProcException = staticError((57,2), prefix, None)
+ def xsBadExcludeInlinePrefixesDefault(): XProcException = staticError(58, List(), None)
+ def xsNotAPipeline(): XProcException = staticError((59,1), None)
+ def xsNotAPipeline(name: QName): XProcException = staticError((59,2), name, None)
def xsInvalidVersion(version: Double, location: Option[Location]): XProcException = staticError(60, version, location)
def xsVersionRequired(location: Option[Location]): XProcException = staticError(62, location)
def xsBadVersion(version: String, location: Option[Location]): XProcException = staticError(63, version, location)
@@ -227,11 +256,11 @@ object XProcException {
def xsMissingWhen(location: Option[Location]): XProcException = staticError(74, location)
def xsInvalidTryCatch(msg: String, location: Option[Location]): XProcException = staticError(75, msg, location)
- def xsBadTypeValue(value: String, reqdType: String, location: Option[Location]): XProcException = staticError(77, List(value, reqdType), location)
+ def xsBadTypeValue(value: String, reqdType: String, location: Option[Location]): XProcException = staticError((77,1), List(value, reqdType), location)
+ def xsBadTypeEmpty(value: String, location: Option[Location]): XProcException = staticError((77,2), value, location)
+ def xsBadVisibility(value: String, location: Option[Location]): XProcException = staticError((77,3), value, location)
- def xsInlineCommentNotAllowed(comment: String, location: Option[Location]): XProcException = staticError((79,1), comment, location)
- def xsInlinePiNotAllowed(pi: String, location: Option[Location]): XProcException = staticError((79,2), pi, location)
- def xsInlineTextNotAllowed(text: String, location: Option[Location]): XProcException = staticError((79,3), text, location)
+ def xsInlineNotAllowed(location: Option[Location]): XProcException = staticError(79, location)
def xsDupWithOptionName(optName: QName, location: Option[Location]): XProcException = staticError(80, optName, location)
@@ -242,36 +271,44 @@ object XProcException {
def xsPipeAndHref(location: Option[Location]): XProcException = staticError(85, location)
def xsDupWithInputPort(port: String, location: Option[Location]): XProcException = staticError(86, port, location)
def xsOptionUndeclaredNamespace(name: String, location: Option[Location]): XProcException = staticError(87, name, location)
- def xsTvtForbidden(location: Option[Location]): XProcException = staticError(88, location)
+ def xsShadowsStatic(name: QName, location: Option[Location]): XProcException = staticError(88, name, location)
def xsNoSiblingsOnEmpty(location: Option[Location]): XProcException = staticError(89, None, location)
def xsInvalidPipeToken(token: String, location: Option[Location]): XProcException = staticError(90, token, location)
+ def xsRedeclareStatic(name: QName, location: Option[Location]): XProcException = staticError(92, name, location)
+
def xsNoSelectOnStaticOption(location: Option[Location]): XProcException = staticError(93, None, location)
def xsNoSelectOnVariable(location: Option[Location]): XProcException = staticError(94, None, location)
+ def xsRequiredAndStatic(name: QName, location: Option[Location]): XProcException = staticError(95, name, location)
def xsInvalidSequenceType(seqType: String, errMsg: String, location: Option[Location]): XProcException = staticError(96, List(seqType, errMsg), location)
- def xsXProcNamespaceError(attrName: QName, location: Option[Location]): XProcException = staticError(97, attrName, location)
+ def xsXProcNamespaceError(attrName: QName, elemName: QName, location: Option[Location]): XProcException = staticError(97, List(attrName, elemName), location)
//def xsElementNotAllowed(element: QName, message: String, location: Option[Location]): XProcException = staticError(100, List(element, message), location)
def xsInvalidPipeline(message: String, location: Option[Location]): XProcException = staticError((100,1), message, location)
def xsElementNotAllowed(element: QName, location: Option[Location]): XProcException = staticError((100,2), List(element, "element is not allowed here"), location)
+ def xsCantMixConnectionsWithImplicitInlines(name: QName, location: Option[Location]): XProcException = staticError((100,3), name, location)
def xsInvalidValues(values: String, location: Option[Location]): XProcException = staticError(101, values, location)
- def xsBadChooseOutputs(primary: String, alsoPrimary: String, location: Option[Location]): XProcException = staticError((102,1), List(primary,alsoPrimary), location)
- def xsBadTryOutputs(primary: String, alsoPrimary: String, location: Option[Location]): XProcException = staticError((102,2), List(primary,alsoPrimary), location)
-
- def xsMissingRequiredInput(port: String, location: Option[Location]): XProcException = staticError(998, port, location)
-
- def xsXProcElementNotAllowed(name: String, location: Option[Location]): XProcException = staticError(100, name, location)
+ def xsBadChooseOutputs(primary: String, location: Option[Location]): XProcException = staticError((102,1), primary, location)
+ def xsBadChooseOutputs(primary: String, alsoPrimary: String, location: Option[Location]): XProcException = staticError((102,2), List(primary,alsoPrimary), location)
+ def xsBadTryOutputs(primary: String, location: Option[Location]): XProcException = staticError((102,3), primary, location)
+ def xsBadTryOutputs(primary: String, alsoPrimary: String, location: Option[Location]): XProcException = staticError((102,4), List(primary,alsoPrimary), location)
def xsNoBindingInExpression(name: QName, location: Option[Location]): XProcException = staticError((107,1), name, location)
def xsStaticErrorInExpression(expr: String, msg: String, location: Option[Location]): XProcException = staticError((107,2), List(expr, msg), location)
def xsStaticRefsContext(msg: String, location: Option[Location]): XProcException = staticError((107,3), msg, location)
def xsStaticRefsNonStatic(name: QName, location: Option[Location]): XProcException = staticError((107,4), name, location)
def xsStaticRefsNonStaticStr(name: String, location: Option[Location]): XProcException = staticError((107,4), name, location)
+ def xsAtomicOutputWithBinding(port: String, stepType: QName, location: Option[Location]): XProcException = staticError((107,5), List(port, stepType), location)
+ def xsAtomicOutputWithBinding(port: String, location: Option[Location]): XProcException = staticError((107,6), port, location)
def xsPrimaryOutputRequired(location: Option[Location]): XProcException = staticError(108, location)
+ def xsOptionMustBeStatic(name: QName, location: Option[Location]): XProcException = staticError(109, name, location)
def xsUnrecognizedContentTypeShortcut(ctype: String, location: Option[Location]): XProcException = staticError(111, ctype, location)
def xsPrimaryOutputOnFinally(port: String, location: Option[Location]): XProcException = staticError(112, port, location)
+ def xsInvalidExpandText(name: QName, value: String, location: Option[Location]): XProcException = staticError((113,1), List(name, value), location)
+ def xsNoSuchPort(port: String, stepType: QName, location: Option[Location]): XProcException = staticError(114, List(port, stepType), location)
+ def xsUseWhenDeadlock(expr: String, location: Option[Location]): XProcException = staticError((115,1), expr, location)
def xcGeneralException(code: QName, errors: Option[XdmNode], location: Option[Location]): XProcException = {
val except = new XProcException(code, 1, None, location, List())
@@ -387,6 +424,7 @@ object XProcException {
def xcOverrideContentTypesBadRegex(regex: String, location: Option[Location]): XProcException = stepError(147, regex, location)
def xcFileMoveBadScheme(uri: URI, location: Option[Location]): XProcException = stepError(148, uri, location)
def xcNotASchematronDocument(): XProcException = stepError(151, None)
+ def xcNotRelaxNG(href: String, message: String, location: Option[Location]): XProcException = stepError(153, List(href, message), location)
def xcNotSchemaValidRelaxNG(href: String, message: String, location: Option[Location]): XProcException = stepError(155, List(href, message), location)
def xcNotSchemaValidXmlSchema(href: String, message: String, location: Option[Location]): XProcException = stepError((156,1), List(href, message), location)
def xcNotSchemaValidXmlSchema(href: String, line: Long, col: Long, message: String, location: Option[Location]): XProcException = stepError((156,2), List(href, line, col, message), location)
diff --git a/src/main/scala/com/xmlcalabash/functions/DocumentProperties.scala b/src/main/scala/com/xmlcalabash/functions/DocumentProperties.scala
index 51fb322..dae6715 100644
--- a/src/main/scala/com/xmlcalabash/functions/DocumentProperties.scala
+++ b/src/main/scala/com/xmlcalabash/functions/DocumentProperties.scala
@@ -68,11 +68,14 @@ class DocumentProperties(runtime: XMLCalabash) extends FunctionImpl {
map = map.put(new XdmAtomicValue(XProcConstants._base_uri), value)
}
case _ =>
+ map = map.put(new XdmAtomicValue(key), value)
+/*
if (key.getNamespaceURI == "") {
map = map.put(new XdmAtomicValue(key.getLocalName), value)
} else {
map = map.put(new XdmAtomicValue(key), value)
}
+ */
}
}
diff --git a/src/main/scala/com/xmlcalabash/functions/StepAvailable.scala b/src/main/scala/com/xmlcalabash/functions/StepAvailable.scala
index ca9fb19..549f599 100644
--- a/src/main/scala/com/xmlcalabash/functions/StepAvailable.scala
+++ b/src/main/scala/com/xmlcalabash/functions/StepAvailable.scala
@@ -3,13 +3,13 @@ package com.xmlcalabash.functions
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.model.xml.DeclareStep
+import com.xmlcalabash.model.xxml.{XArtifact, XDeclareStep}
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.expr.{Expression, StaticContext, XPathContext}
import net.sf.saxon.lib.{ExtensionFunctionCall, ExtensionFunctionDefinition}
import net.sf.saxon.om.{Item, Sequence, StructuredQName}
import net.sf.saxon.s9api.QName
-import net.sf.saxon.value.{BooleanValue, Int64Value, SequenceType}
+import net.sf.saxon.value.{BooleanValue, SequenceType}
class StepAvailable(runtime: XMLCalabash) extends FunctionImpl() {
private val funcname = new StructuredQName("p", XProcConstants.ns_p, "step-available")
@@ -34,41 +34,41 @@ class StepAvailable(runtime: XMLCalabash) extends FunctionImpl() {
override def call(context: XPathContext, arguments: Array[Sequence]): Sequence = {
val exprEval = runtime.expressionEvaluator
- if (exprEval.dynContext == null) {
+ if (exprEval.dynContext.isEmpty) {
throw XProcException.xiExtFunctionNotAllowed()
}
- val lexicalQName = arguments(0).head().asInstanceOf[Item].getStringValue
+ val lexicalQName = arguments(0).head().getStringValue
val propertyName = if (lexicalQName.trim.startsWith("Q{")) {
StructuredQName.fromClarkName(lexicalQName)
} else {
StructuredQName.fromLexicalQName(lexicalQName, false, false, staticContext.getNamespaceResolver)
}
- var available = false
-
- // This is a bit of a hack; it relies on artifact having been passed to the
- // dynamic context and I'm not confident that's always going to have happened.
- // But it appears to have happened in the common case.
- var art = exprEval.dynContext.get.artifact
- while (art.isDefined && !art.get.isInstanceOf[DeclareStep]) {
- art = art.get.parent
+ val stepType = new QName(propertyName.getPrefix, propertyName.getURI, propertyName.getLocalPart)
+ if (runtime.externalSteps.contains(stepType)) {
+ return new BooleanValue(true, BuiltInAtomicType.BOOLEAN)
}
- if (art.isDefined) {
- val decl = art.get.asInstanceOf[DeclareStep]
- val qname = new QName(propertyName.getPrefix, propertyName.getURI, propertyName.getLocalPart)
- val sig = decl.declaration(qname)
- if (sig.isDefined) {
- val impl = sig.get.implementation
- if (impl.isEmpty) {
- available = !sig.get.declaration.get.atomic
- } else {
- available = true
+
+ val dc = exprEval.dynContext.get
+ if (dc.artifact.isEmpty) {
+ new BooleanValue(runtime.staticStepAvailable(stepType), BuiltInAtomicType.BOOLEAN)
+ } else {
+ var p: Option[XArtifact] = dc.artifact
+ while (p.isDefined) {
+ p.get match {
+ case decl: XDeclareStep =>
+ for (step <- decl.inScopeSteps) {
+ if (step.stepType.isDefined && step.stepType.get == stepType) {
+ return new BooleanValue(!step.atomic || step.implementationClass.isDefined, BuiltInAtomicType.BOOLEAN)
+ }
+ }
+ case _ => ()
}
+ p = p.get.parent
}
+ new BooleanValue(false, BuiltInAtomicType.BOOLEAN)
}
-
- new BooleanValue(available, BuiltInAtomicType.BOOLEAN)
}
}
}
diff --git a/src/main/scala/com/xmlcalabash/messages/AnyItemMessage.scala b/src/main/scala/com/xmlcalabash/messages/AnyItemMessage.scala
index 6db9bbf..2e8e3af 100644
--- a/src/main/scala/com/xmlcalabash/messages/AnyItemMessage.scala
+++ b/src/main/scala/com/xmlcalabash/messages/AnyItemMessage.scala
@@ -1,13 +1,14 @@
package com.xmlcalabash.messages
-import com.xmlcalabash.runtime.{BinaryNode, StaticContext, XProcMetadata}
+import com.xmlcalabash.runtime.{BinaryNode, XProcMetadata}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.XdmNode
import org.slf4j.{Logger, LoggerFactory}
class AnyItemMessage(override val item: XdmNode,
private val binary: BinaryNode,
override val metadata: XProcMetadata,
- override val context: StaticContext) extends XProcItemMessage(item, metadata, context) {
+ override val context: MinimalStaticContext) extends XProcItemMessage(item, metadata, context) {
protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
def shadow: BinaryNode = {
diff --git a/src/main/scala/com/xmlcalabash/messages/XProcItemMessage.scala b/src/main/scala/com/xmlcalabash/messages/XProcItemMessage.scala
index bc52025..9e91908 100644
--- a/src/main/scala/com/xmlcalabash/messages/XProcItemMessage.scala
+++ b/src/main/scala/com/xmlcalabash/messages/XProcItemMessage.scala
@@ -1,10 +1,11 @@
package com.xmlcalabash.messages
import com.jafpl.messages.ItemMessage
-import com.xmlcalabash.runtime.{StaticContext, XProcMetadata}
+import com.xmlcalabash.runtime.XProcMetadata
+import com.xmlcalabash.util.MinimalStaticContext
class XProcItemMessage(override val item: Any,
override val metadata: XProcMetadata,
- val context: StaticContext)
+ val context: MinimalStaticContext)
extends ItemMessage(item, metadata) {
}
diff --git a/src/main/scala/com/xmlcalabash/messages/XdmNodeItemMessage.scala b/src/main/scala/com/xmlcalabash/messages/XdmNodeItemMessage.scala
index d454ac6..a0103ff 100644
--- a/src/main/scala/com/xmlcalabash/messages/XdmNodeItemMessage.scala
+++ b/src/main/scala/com/xmlcalabash/messages/XdmNodeItemMessage.scala
@@ -1,10 +1,12 @@
package com.xmlcalabash.messages
-import com.xmlcalabash.runtime.{StaticContext, XProcMetadata}
+import com.xmlcalabash.model.xxml.XStaticContext
+import com.xmlcalabash.runtime.XProcMetadata
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.XdmNode
class XdmNodeItemMessage(override val item: XdmNode,
override val metadata: XProcMetadata,
- override val context: StaticContext)
+ override val context: MinimalStaticContext)
extends XdmValueItemMessage(item, metadata, context) {
}
diff --git a/src/main/scala/com/xmlcalabash/messages/XdmValueItemMessage.scala b/src/main/scala/com/xmlcalabash/messages/XdmValueItemMessage.scala
index beaaceb..a3f56bf 100644
--- a/src/main/scala/com/xmlcalabash/messages/XdmValueItemMessage.scala
+++ b/src/main/scala/com/xmlcalabash/messages/XdmValueItemMessage.scala
@@ -1,10 +1,12 @@
package com.xmlcalabash.messages
-import com.xmlcalabash.runtime.{StaticContext, XProcMetadata}
+import com.xmlcalabash.model.xxml.XStaticContext
+import com.xmlcalabash.runtime.XProcMetadata
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.XdmValue
class XdmValueItemMessage(override val item: XdmValue,
override val metadata: XProcMetadata,
- override val context: StaticContext)
+ override val context: MinimalStaticContext)
extends XProcItemMessage(item, metadata, context) {
}
diff --git a/src/main/scala/com/xmlcalabash/model/util/SaxonTreeBuilder.scala b/src/main/scala/com/xmlcalabash/model/util/SaxonTreeBuilder.scala
index 4e27f56..aa1f7fc 100644
--- a/src/main/scala/com/xmlcalabash/model/util/SaxonTreeBuilder.scala
+++ b/src/main/scala/com/xmlcalabash/model/util/SaxonTreeBuilder.scala
@@ -2,7 +2,7 @@ package com.xmlcalabash.model.util
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.{ExceptionCode, ModelException, XProcException}
-import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.runtime.{XMLCalabashProcessor, XMLCalabashRuntime}
import com.xmlcalabash.util.{DefaultLocation, S9Api, SysIdLocation, VoidLocation}
import net.sf.saxon.`type`.{SchemaType, Untyped}
import net.sf.saxon.event.{NamespaceReducer, Receiver}
@@ -61,14 +61,10 @@ class SaxonTreeBuilder(processor: Processor) {
private val emptyAttributeMap = EmptyAttributeMap.getInstance()
private var _location = Option.empty[Location]
- def this(config: XMLCalabash) = {
+ def this(config: XMLCalabashProcessor) = {
this(config.processor)
}
- def this(runtime: XMLCalabashRuntime) = {
- this(runtime.config)
- }
-
def location: Option[Location] = _location
def location_=(loc: Location): Unit = {
_location = Some(loc)
@@ -144,8 +140,7 @@ class SaxonTreeBuilder(processor: Processor) {
throw new ModelException(ExceptionCode.BADTREENODE, List(node.getNodeKind.toString, node.getNodeName.toString), node)
}
case xpe: XPathException =>
- // FIXME: wrap in xprocexception
- throw new RuntimeException(xpe)
+ throw xpe
}
}
@@ -256,7 +251,6 @@ class SaxonTreeBuilder(processor: Processor) {
def addStartElement(elemName: NodeName, attrs: AttributeMap, typeCode: SchemaType, nsmap: NamespaceMap): Unit = {
// Sort out the namespaces...
var newmap = updateMap(nsmap, elemName.getPrefix, elemName.getURI)
- // FIXME: this should be iterable?
for (attr <- attrs.asList.asScala) {
if (attr.getNodeName.getURI != null && attr.getNodeName.getURI != "") {
newmap = updateMap(newmap, attr.getNodeName.getPrefix, attr.getNodeName.getURI)
@@ -276,8 +270,7 @@ class SaxonTreeBuilder(processor: Processor) {
_location = None
} catch {
case e: XPathException =>
- // FIXME: some sort of XProcException
- throw new RuntimeException(e)
+ throw e
}
}
@@ -299,8 +292,7 @@ class SaxonTreeBuilder(processor: Processor) {
return nsmap
}
- // FIXME: runtime exception should be some form of xproc exception?
- throw new RuntimeException("Cannot add " + prefix + " to namespace map with URI " + uri)
+ throw XProcException.xiImpossibleNamespaceConfiguration(prefix, uri, None)
}
def addEndElement(): Unit = {
diff --git a/src/main/scala/com/xmlcalabash/model/util/ValueParser.scala b/src/main/scala/com/xmlcalabash/model/util/ValueParser.scala
index fec6bc9..ebe1066 100644
--- a/src/main/scala/com/xmlcalabash/model/util/ValueParser.scala
+++ b/src/main/scala/com/xmlcalabash/model/util/ValueParser.scala
@@ -5,11 +5,10 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.{ExceptionCode, ModelException, XProcException}
import com.xmlcalabash.model.util.XProcConstants.ValueTemplate
import com.xmlcalabash.runtime.{StaticContext, XMLCalabashRuntime, XProcExpression, XProcVtExpression, XProcXPathExpression}
-import com.xmlcalabash.util.{TypeUtils, ValueTemplateParser}
+import com.xmlcalabash.util.{MinimalStaticContext, TypeUtils, ValueTemplateParser}
import net.sf.saxon.s9api.{Axis, ItemType, QName, XdmAtomicValue, XdmItem, XdmMap, XdmNode, XdmNodeKind, XdmValue}
import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
object ValueParser {
def parseAvt(value: String): Option[ValueTemplate] = {
@@ -97,7 +96,7 @@ object ValueParser {
if (static) {
throw XProcException.xsBadTypeValue(value.get, "boolean", location)
} else {
- throw XProcException.xdBadValue(value.get, "boolean", location)
+ throw XProcException.xdBadType(value.get, "boolean", location)
}
}
} else {
@@ -106,7 +105,7 @@ object ValueParser {
}
- def parseParameters(value: XdmValue, context: StaticContext): Map[QName, XdmValue] = {
+ def parseParameters(value: XdmValue, context: MinimalStaticContext): Map[QName, XdmValue] = {
val params = mutable.HashMap.empty[QName, XdmValue]
value match {
@@ -117,7 +116,7 @@ object ValueParser {
val key = iter.next()
val value = map.get(key)
- val qname = ValueParser.parseQName(key.getStringValue, context)
+ val qname = context.parseQName(key.getStringValue)
params.put(qname, value)
}
case _ =>
@@ -127,7 +126,7 @@ object ValueParser {
params.toMap
}
- def parseDocumentProperties(value: XdmItem, context: StaticContext, location: Option[Location]): Map[QName, XdmValue] = {
+ def parseDocumentProperties(value: XdmItem, context: MinimalStaticContext, location: Option[Location]): Map[QName, XdmValue] = {
val params = mutable.HashMap.empty[QName, XdmValue]
value match {
@@ -143,7 +142,7 @@ object ValueParser {
case XProcConstants.xs_QName =>
key.getQNameValue
case XProcConstants.xs_string =>
- ValueParser.parseQName(key.getStringValue, context)
+ context.parseQName(key.getStringValue)
case _ =>
throw XProcException.xdBadMapKey(key.getStringValue, location)
}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/XMLViewportComposer.scala b/src/main/scala/com/xmlcalabash/model/util/XMLViewportComposer.scala
similarity index 89%
rename from src/main/scala/com/xmlcalabash/model/xml/XMLViewportComposer.scala
rename to src/main/scala/com/xmlcalabash/model/util/XMLViewportComposer.scala
index a33fd4e..af30129 100644
--- a/src/main/scala/com/xmlcalabash/model/xml/XMLViewportComposer.scala
+++ b/src/main/scala/com/xmlcalabash/model/util/XMLViewportComposer.scala
@@ -1,20 +1,20 @@
-package com.xmlcalabash.model.xml
+package com.xmlcalabash.model.util
import com.jafpl.messages.{Message, Metadata}
import com.jafpl.steps.{ViewportComposer, ViewportItem}
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{AnyItemMessage, XdmNodeItemMessage, XdmValueItemMessage}
-import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
-import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XProcVtExpression, XProcXPathExpression}
-import com.xmlcalabash.util.{TypeUtils, VoidLocation}
+import com.xmlcalabash.model.xxml.XStaticContext
+import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, XProcMetadata}
+import com.xmlcalabash.util.TypeUtils
import net.sf.saxon.om.{AttributeMap, SingletonAttributeMap}
-import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmNode, XdmValue}
+import net.sf.saxon.s9api.{QName, XdmNode}
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
-class XMLViewportComposer(config: XMLCalabash, context: StaticContext, patternString: String) extends ViewportComposer {
+class XMLViewportComposer(config: XMLCalabash, context: XStaticContext, matchExpr: String) extends ViewportComposer {
private val cx_viewport = new QName("cx", XProcConstants.ns_cx,"viewport")
private val _index = new QName("index")
private var matcher: ProcessMatch = _
@@ -22,7 +22,7 @@ class XMLViewportComposer(config: XMLCalabash, context: StaticContext, patternSt
private var metadata: XProcMetadata = _
private var items = ListBuffer.empty[XMLViewportItem]
private var itemIndex = 0
- private var dynBindings = Map.empty[String, Message]
+ private var dynBindings: Map[String, Message] = _
override def runtimeBindings(bindings: Map[String, Message]): Unit = {
dynBindings = bindings
@@ -41,7 +41,7 @@ class XMLViewportComposer(config: XMLCalabash, context: StaticContext, patternSt
throw XProcException.xiThisCantHappen("Viewport message without metadata?", context.location)
}
- val bindings = mutable.HashMap.empty[String,Message] ++ context.statics
+ val bindings = mutable.HashMap.empty[String,Message] ++ context.inscopeConstantBindings
for ((name, message) <- dynBindings) {
bindings.put(name,message)
}
@@ -54,9 +54,10 @@ class XMLViewportComposer(config: XMLCalabash, context: StaticContext, patternSt
*/
matcher = new ProcessMatch(config, new Decomposer(), context, bindings.toMap)
- matcher.process(source, patternString)
+ matcher.process(source, matchExpr)
decomposed = matcher.result
items.toList
+
}
override def recompose(): Message = {
@@ -94,7 +95,7 @@ class XMLViewportComposer(config: XMLCalabash, context: StaticContext, patternSt
}
override def attributes(node: XdmNode, matchingAttributes: AttributeMap, nonMatchingAttributes: AttributeMap): Option[AttributeMap] = {
- throw XProcException.xdViewportOnAttribute(patternString, context.location)
+ throw XProcException.xdViewportOnAttribute(matchExpr, context.location)
}
override def text(node: XdmNode): Unit = {
@@ -148,7 +149,7 @@ class XMLViewportComposer(config: XMLCalabash, context: StaticContext, patternSt
}
override def attributes(node: XdmNode, matchingAttributes: AttributeMap, nonMatchingAttributes: AttributeMap): Option[AttributeMap] = {
- throw XProcException.xcInvalidSelection(patternString, "attribute", None)
+ throw XProcException.xcInvalidSelection(matchExpr, "attribute", None)
}
override def text(node: XdmNode): Unit = {
@@ -203,4 +204,5 @@ class XMLViewportComposer(config: XMLCalabash, context: StaticContext, patternSt
}
}
}
+
}
diff --git a/src/main/scala/com/xmlcalabash/model/util/XProcConstants.scala b/src/main/scala/com/xmlcalabash/model/util/XProcConstants.scala
index e9d969b..92ab7cb 100644
--- a/src/main/scala/com/xmlcalabash/model/util/XProcConstants.scala
+++ b/src/main/scala/com/xmlcalabash/model/util/XProcConstants.scala
@@ -1,6 +1,7 @@
package com.xmlcalabash.model.util
import net.sf.saxon.lib.SaxonOutputKeys
+import net.sf.saxon.om.StructuredQName
import net.sf.saxon.s9api.QName
import net.sf.saxon.serialize.SerializationProperties
@@ -26,6 +27,8 @@ object XProcConstants {
val UNKNOWN = new QName("", "UNKNOWN")
+ val structured_xs_QName = new StructuredQName("xs", XProcConstants.ns_xs, "QName")
+
val xml_base = new QName("xml", ns_xml, "base")
val xml_id = new QName("xml", ns_xml, "id")
val xml_lang = new QName("xml", ns_xml, "lang")
@@ -34,6 +37,16 @@ object XProcConstants {
val fn_collection = new QName("fn", ns_fn, "collection")
val fn_map = new QName("fn", ns_fn, "map")
+ val fn_position = new QName("fn", ns_fn, "position")
+ val fn_last = new QName("fn", ns_fn, "last")
+ val fn_current_dateTime = new QName("fn", ns_fn, "current-dateTime")
+ val fn_current_date = new QName("fn", ns_fn, "current-date")
+ val fn_current_time = new QName("fn", ns_fn, "current-time")
+ val fn_implicit_timezone = new QName("fn", ns_fn, "implicit-timezone")
+ val fn_default_collation = new QName("fn", ns_fn, "default-collation")
+ val fn_default_language = new QName("fn", ns_fn, "default-language")
+ val fn_static_base_uri = new QName("fn", ns_fn, "static-base-uri")
+
val p_catch = new QName("p", ns_p, "catch")
val p_choose = new QName("p", ns_p, "choose")
val p_data = new QName("p", ns_p, "data")
@@ -70,6 +83,7 @@ object XProcConstants {
val p_serialization = new QName("p", ns_p, "serialization")
val p_sink = new QName("p", ns_p, "sink")
val p_start = new QName("p", ns_p, "start")
+ val p_system_property = new QName("p", ns_p, "system-property")
val p_try = new QName("p", ns_p, "try")
val p_variable = new QName("p", ns_p, "variable")
val p_viewport = new QName("p", ns_p, "viewport")
@@ -84,12 +98,14 @@ object XProcConstants {
val cx_creation_time = new QName("cx", ns_cx, "creation-time")
val cx_document = new QName("cx", ns_cx, "document")
val cx_document_loader = new QName("cx", ns_cx, "document-loader")
+ val cx_document_loader_vt = new QName("cx", ns_cx, "document-loader-vt")
val cx_dtd_validate = new QName("cx", ns_cx,"dtd-validate")
val cx_empty_loader = new QName("cx", ns_cx, "empty-loader")
val cx_encoding = new QName("cx", ns_cx, "encoding")
val cx_exception_translator = new QName("cx", ns_cx, "exception-translator")
val cx_filter = new QName("cx", ns_cx, "filter")
val cx_inline_loader = new QName("cx", ns_cx, "inline-loader")
+ val cx_inline_loader_vt = new QName("cx", ns_cx, "inline-loader-vt")
val cx_last_accessed = new QName("cx", ns_cx, "last-accessed")
val cx_loop = new QName("cx", ns_cx, "loop")
val cx_multipart = new QName("cx", ns_cx, "multipart")
@@ -98,6 +114,7 @@ object XProcConstants {
val cx_unknown = new QName("cx", ns_cx, "unknown")
val cx_until = new QName("cx", ns_cx, "until")
val cx_use_default_input = new QName("cx", ns_cx, "use-default-input")
+ val cx_value_computation = new QName("cx", ns_cx, "value-computation")
val cx_while = new QName("cx", ns_cx, "while")
val cc_debug_graph = new QName("cc", ns_cc, "debug-graph")
@@ -290,11 +307,13 @@ object XProcConstants {
val _suffix = new QName("", "suffix")
val _suppress_cookies = new QName("", "suppress-cookies")
val _suppress_indentation = new QName("", "suppress-indentation")
+ val _synthetic = new QName("", "synthetic")
val _target = new QName("", "target")
val _test = new QName("", "test")
val _timeout = new QName("", "timeout")
val _timestamp = new QName("", "timestamp")
val _transfer_encoding = new QName("", "transfer-encoding")
+ val _tumble_id = new QName("", "tumble-id")
val _type = new QName("", "type")
val _undeclare_prefixes = new QName("", "undeclare-prefixes")
val _use_when = new QName("", "use-when")
@@ -335,11 +354,12 @@ object XProcConstants {
val p_expand_text = new QName("p", ns_p, "expand-text")
val p_inline_expand_text = new QName("p", ns_p, "inline-expand-text")
val p_message = new QName("p", ns_p, "message")
+ val p_timeout = new QName("p", ns_p, "timeout")
val p_use_when = new QName("p", ns_p, "use-when")
val err_XC0095 = new QName(ns_err, "XC0095")
val err_XTMM9000 = new QName(ns_xqt_errors, "XTMM9000")
val err_XTDE0040 = new QName(ns_xqt_errors, "XTDE0040")
- val BUILD_TREE = ValueParser.parseClarkName(SaxonOutputKeys.BUILD_TREE)
+ val BUILD_TREE: QName = ValueParser.parseClarkName(SaxonOutputKeys.BUILD_TREE)
}
diff --git a/src/main/scala/com/xmlcalabash/model/util/XValueParser.scala b/src/main/scala/com/xmlcalabash/model/util/XValueParser.scala
new file mode 100644
index 0000000..4b96d21
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/util/XValueParser.scala
@@ -0,0 +1,130 @@
+package com.xmlcalabash.model.util
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants.ValueTemplate
+import com.xmlcalabash.util.{MinimalStaticContext, ValueTemplateParser}
+import net.sf.saxon.expr.parser.ExpressionTool
+import net.sf.saxon.s9api.{QName, SaxonApiException}
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+object XValueParser {
+ private val contextFunctions =
+ List(XProcConstants.p_iteration_size, XProcConstants.p_iteration_position, XProcConstants.fn_collection)
+ private val nonStaticFunctions =
+ List(XProcConstants.fn_current_dateTime, XProcConstants.fn_current_date,
+ XProcConstants.fn_current_time, XProcConstants.fn_implicit_timezone,
+ XProcConstants.fn_default_collation, XProcConstants.fn_default_language,
+ XProcConstants.fn_static_base_uri, XProcConstants.p_system_property
+ // I don't think position() and last() are non-static from XProc's perspective...
+ )
+
+ def parseAvt(value: String): ValueTemplate = {
+ val parser = new ValueTemplateParser(value)
+ parser.template()
+ }
+}
+
+class XValueParser private(config: XMLCalabash, context: MinimalStaticContext) {
+ private var _contextDependent = false
+ private var _static = true
+ private val _variableRefs = mutable.HashSet.empty[QName]
+ private val _functionRefs = mutable.HashSet.empty[QName]
+ private val _expressions = ListBuffer.empty[String]
+ private val _segments = ListBuffer.empty[ExpressionParser]
+
+ def contextDependent: Boolean = _contextDependent
+ def static: Boolean = _static && !_contextDependent
+ def variables: Set[QName] = _variableRefs.toSet
+ def functions: Set[QName] = _functionRefs.toSet
+
+ def this(config: XMLCalabash, context: MinimalStaticContext, avt: ValueTemplate) = {
+ this(config, context)
+ var inexpr = false
+ for (substr <- avt) {
+ if (inexpr) {
+ _expressions += substr
+
+ val parser = config.expressionParser
+ parser.parse(substr)
+ _segments += parser
+ }
+ inexpr = !inexpr
+ }
+
+ findVariableRefs()
+ findFunctionRefs()
+ dependsOnContext()
+ }
+
+ def this(config: XMLCalabash, context: MinimalStaticContext, select: String) = {
+ this(config, context)
+ _expressions += select
+
+ val parser = config.expressionParser
+ parser.parse(select)
+ _segments += parser
+
+ findVariableRefs()
+ findFunctionRefs()
+ dependsOnContext()
+ }
+
+ private def findVariableRefs(): Unit = {
+ for (segment <- _segments) {
+ for (ref <- segment.variableRefs) {
+ _variableRefs += context.parseClarkName(ref)
+ }
+ }
+ }
+
+ private def findFunctionRefs(): Unit = {
+ for (segment <- _segments) {
+ for (ref <- segment.functionRefs) {
+ val qname = context.parseQName(ref)
+ if (Option(qname.getNamespaceURI).isEmpty || qname.getNamespaceURI == "") {
+ _functionRefs += new QName("fn", XProcConstants.ns_fn, qname.getLocalName)
+ } else {
+ _functionRefs += qname
+ }
+ }
+ }
+ }
+
+ private def dependsOnContext(): Unit = {
+ // Make sure functions and variables have been computed first!
+ for (func <- _functionRefs) {
+ _contextDependent = _contextDependent || XValueParser.contextFunctions.contains(func)
+ if (XValueParser.nonStaticFunctions.contains(func)) {
+ _static = false
+ }
+ }
+
+ if (!_contextDependent) {
+ for (expr <- _expressions) {
+ val xcomp = config.processor.newXPathCompiler()
+ for ((prefix, uri) <- context.inscopeNamespaces) {
+ xcomp.declareNamespace(prefix, uri)
+ }
+ for (name <- _variableRefs) {
+ xcomp.declareVariable(name)
+ }
+ try {
+ val xexec = xcomp.compile(expr)
+ val xexpr = xexec.getUnderlyingExpression.getInternalExpression
+ _contextDependent = _contextDependent || ExpressionTool.dependsOnFocus(xexpr)
+ } catch {
+ /*
+ case ex: SaxonApiException =>
+ throw XProcException.xsStaticErrorInExpression(expr, ex.getMessage, None)
+ case ex: Exception =>
+ throw ex
+ */
+ case ex: Exception => _static = false
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Artifact.scala b/src/main/scala/com/xmlcalabash/model/xml/Artifact.scala
deleted file mode 100644
index 970e032..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Artifact.scala
+++ /dev/null
@@ -1,503 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import java.net.URI
-import com.jafpl.graph.{Location, Node}
-import com.jafpl.messages.Message
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.StepSignature
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.messages.{XdmNodeItemMessage, XdmValueItemMessage}
-import com.xmlcalabash.model.util.{UniqueId, ValueParser, XProcConstants}
-import com.xmlcalabash.runtime.params.{StepParams, XPathBindingParams}
-import com.xmlcalabash.runtime.{StaticContext, XMLCalabashRuntime, XProcLocation, XProcXPathExpression}
-import com.xmlcalabash.util.S9Api
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{Axis, QName, SaxonApiException, XdmNode, XdmNodeKind, XdmValue}
-import org.slf4j.{Logger, LoggerFactory}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-import scala.reflect.ClassTag
-
-class Artifact(val config: XMLCalabash) {
- protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
- protected[model] var _readablePorts = List.empty[Port]
- protected[model] var _graphNode: Option[Node] = None
- private var _parent: Option[Artifact] = None
- private val _children: ListBuffer[Artifact] = ListBuffer.empty[Artifact]
- protected[model] var _staticContext: XMLContext = new XMLContext(config, this)
- private var _xmlId = Option.empty[String]
- protected var _synthetic = true
- private val _uid = UniqueId.nextId
- private var _tumbleId = s"!syn_$uid"
- private var _expand_text = Option.empty[Boolean]
- protected[model] var _inScopeStatics = mutable.HashMap.empty[String, NameBinding]
- protected[model] var _inScopeDynamics = mutable.HashMap.empty[QName, NameBinding]
-
- protected[model] val attributes = mutable.HashMap.empty[QName, String]
- protected[model] val extensionAttributes = mutable.HashMap.empty[QName, String]
-
- def graphNode: Option[Node] = _graphNode
-
- protected[model] def expand_text: Boolean = {
- if (_expand_text.isDefined) {
- _expand_text.get
- } else {
- if (parent.isDefined) {
- parent.get.expand_text
- } else {
- true
- }
- }
- }
-
- protected[model] def attr(name: QName): Option[String] = {
- if (attributes.contains(name)) {
- val str = attributes(name)
- attributes.remove(name)
- Some(str)
- } else {
- None
- }
- }
- protected[xmlcalabash] def extensionAttr(name: QName): Option[String] = {
- extensionAttributes.get(name)
- }
-
- protected[xmlcalabash] def parent: Option[Artifact] = _parent
- protected[model] def parent_=(art: Artifact): Unit = {
- _parent = Some(art)
- }
- protected[model] def rawChildren: List[Artifact] = _children.toList
-
- protected[model] def allChildren: List[Artifact] = {
- _children.toList.flatMap {
- case _: Documentation => None
- case _: PipeInfo => None
- case art:Artifact => Some(art)
- }
- }
-
- protected[model] def children[T <: Artifact](implicit tag: ClassTag[T]): List[T] = {
- allChildren.flatMap {
- case art: T => Some(art)
- case _ => None
- }
- }
-
- protected[model] def findAll[T <: Artifact](implicit tag: ClassTag[T]): List[T] = {
- root.findDescendants(tag)
- }
-
- protected[model] def findDescendants[T <: Artifact](implicit tag: ClassTag[T]): List[T] = {
- val arts = ListBuffer.empty[T]
- for (child <- allChildren) {
- child match {
- case t: T =>
- arts += t
- arts ++= t.findDescendants(tag)
- case _ =>
- arts ++= child.findDescendants(tag)
- }
- }
- arts.toList
- }
-
- protected[model] def root: Artifact = {
- var root: Artifact = this
- while (root.parent.isDefined) {
- root = root.parent.get
- }
- root
- }
-
- protected[model] def findInScopeOption(name: QName): Option[NameBinding] = {
- findInScopeOption(this, name, this)
- }
-
- protected[model] def findInScopeOption(art: Artifact, name: QName, self: Artifact): Option[NameBinding] = {
- var found = Option.empty[NameBinding]
-
- art match {
- case _: AtomicStep =>
- () // No one can see the options of atomic steps
- case _ =>
- for (nb <- art.children[NameBinding]) {
- if ((nb ne self) && nb.name == name) {
- found = Some(nb)
- }
- }
- }
-
- if (found.isEmpty && art.parent.isDefined) {
- findInScopeOption(art.parent.get, name, self)
- } else {
- found
- }
- }
-
- protected[model] def ancestor(art: Artifact): Boolean = {
- var p: Option[Artifact] = parent
- while (p.isDefined) {
- if (p.get == art) {
- return true
- }
- p = p.get.parent
- }
- false
- }
-
- protected[model] def containingStep: Option[Step] = {
- var p: Option[Artifact] = Some(this)
- while (p.isDefined) {
- p.get match {
- case step: Step =>
- return Some(step)
- case _ =>
- p = p.get.parent
- }
- }
- None
- }
-
- protected[xmlcalabash] def location: Option[Location] = _staticContext.location
- protected[model] def staticContext: XMLContext = _staticContext
- protected[model] def staticContext_=(context: StaticContext): Unit = {
- _staticContext = new XMLContext(config, this, context.baseURI, context.nsBindings, context.location)
- }
- protected[model] def uid: Long = _uid
- protected[model] def xml_id: Option[String] = _xmlId
- protected[model] def tumble_id: String = _tumbleId
- protected[model] def synthetic: Boolean = _synthetic
-
- protected[model] def inScopeStatics: Map[String, Message] = {
- val statics = mutable.HashMap.empty[String,Message]
- for ((name,binding) <- _inScopeStatics) {
- if (binding.staticValue.isDefined) {
- statics.put(name, binding.staticValue.get)
- }
- }
- statics.toMap
- }
-
- protected[model] def addChild(art: Artifact): Unit = {
- _children += art
- art.parent = this
- }
-
- protected[model] def addChild(art: Artifact, before: Option[Artifact]): Unit = {
- if (before.isDefined) {
- addChild(art, before.get)
- } else {
- addChild(art)
- }
- }
-
- protected[model] def addChild(art: Artifact, before: Artifact): Unit = {
- var pos = 0
- var found = false
- for (child <- allChildren) {
- found = found || child == before
- if (!found) {
- pos += 1
- }
- }
- if (!found) {
- throw new RuntimeException("Insert before is not a child of the container")
- }
-
- _children.insert(pos, art)
- art.parent = this
- }
-
- protected[model] def replaceChild(art: Artifact, target: Artifact): Unit = {
- var pos = 0
- var found = false
- for (child <- rawChildren) {
- found = found || child == target
- if (!found) {
- pos += 1
- }
- }
- if (!found) {
- throw new RuntimeException("Replace target is not a child of the container")
- }
-
- _children.insert(pos, art)
- _children.remove(pos+1)
- art.parent = this
- }
-
- protected[model] def removeChild(target: Artifact): Unit = {
- var pos = 0
- var childPos = -1
- for (child <- _children) {
- if (child == target) {
- childPos = pos
- }
- pos += 1
- }
- if (childPos >= 0) {
- _children.remove(childPos)
- }
- }
-
- protected[model] def removeChildren(): Unit = {
- _children.clear()
- }
-
- protected[model] def firstChild: Option[Artifact] = allChildren.headOption
-
- protected[model] def firstWithInput: Option[WithInput] = {
- children[WithInput].headOption
- }
-
- protected[model] def firstStepChild: Option[Step] = {
- children[Step].headOption
- }
-
- private def tumbleId(node: XdmNode): String = {
- var count = 1
- val iter = node.axisIterator(Axis.PRECEDING_SIBLING)
- while (iter.hasNext) {
- val child = iter.next()
- if (child.getNodeKind == XdmNodeKind.ELEMENT) {
- count += 1
- }
- }
- val parent = Option(node.getParent)
- if (parent.isEmpty) {
- "!"
- } else {
- val pid = tumbleId(parent.get)
- if (pid == "!") {
- s"$pid$count"
- } else {
- s"$pid.$count"
- }
- }
- }
-
- def inScopeExpandText(node: XdmNode): Boolean = {
- if (node.getNodeKind == XdmNodeKind.ELEMENT) {
- val attr = if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- XProcConstants._expand_text
- } else {
- XProcConstants.p_expand_text
- }
- val expand = Option(node.getAttributeValue(attr))
- if (expand.isDefined) {
- // FIXME: what about AVT? what about invalid values?
- return expand.get == "true"
- }
- }
-
- if (Option(node.getParent).isDefined) {
- inScopeExpandText(node.getParent)
- } else {
- false
- }
- }
-
- protected[model] def parse(node: XdmNode): Unit = {
- _synthetic = false
- _tumbleId = tumbleId(node)
- _staticContext.baseURI = node.getBaseURI
- _staticContext.location = new XProcLocation(node)
- _staticContext.nsBindings = S9Api.inScopeNamespaces(node)
-
- // Parse attributes
- val aiter = node.axisIterator(Axis.ATTRIBUTE)
- while (aiter.hasNext) {
- val attr = aiter.next()
- attr.getNodeName match {
- case XProcConstants.xml_base => staticContext.baseURI = new URI(attr.getStringValue)
- case XProcConstants.xml_id => _xmlId = Some(attr.getStringValue)
- case _ =>
- if (attr.getNodeName.getNamespaceURI == "" || attr.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- attributes.put(attr.getNodeName, attr.getStringValue)
- } else {
- extensionAttributes.put(attr.getNodeName, attr.getStringValue)
- }
- }
- }
-
- val ns = S9Api.inScopeNamespaces(node)
- var same = parent.isDefined
- if (parent.isDefined) {
- val inScopeNS = parent.get.staticContext.nsBindings
- for (prefix <- inScopeNS.keySet) {
- same = same && (ns.contains(prefix) && (ns(prefix) == inScopeNS(prefix)))
- }
- for (prefix <- ns.keySet) {
- same = same && (inScopeNS.contains(prefix) && (ns(prefix) == inScopeNS(prefix)))
- }
- }
-
- if (same) {
- staticContext.nsBindings = parent.get.staticContext.nsBindings
- } else {
- staticContext.nsBindings = ns
- }
-
- if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- _expand_text = staticContext.parseBoolean(attr(XProcConstants._expand_text))
- } else {
- _expand_text = staticContext.parseBoolean(attr(XProcConstants.p_expand_text))
- }
- }
-
-
- protected[model] def declaration(stepType: QName): Option[StepSignature] = {
- // First find the decl container
- var container: Option[Artifact] = Some(this)
- while (container.isDefined) {
- container.get match {
- case decl: DeclContainer =>
- return declaration(stepType, decl)
- case _ =>
- container = container.get.parent
- }
- }
- None
- }
-
- protected[model] def declaration(stepType: QName, container: DeclContainer): Option[StepSignature] = {
- if (parent.isDefined) {
- parent.get.declaration(stepType, container)
- } else {
- None
- }
- }
-
- protected[model] def environment(): Environment = {
- // N.B. Do not cache this; the structure changes when filters are added.
- Environment.newEnvironment(this)
- }
-
- protected[model] def makeStructureExplicit(): Unit = {
- for (child <- allChildren) {
- child.makeStructureExplicit()
- }
- }
-
- protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child.validateStructure()
- }
- }
-
- protected[model] def makeBindingsExplicit(): Unit = {
- val env = environment()
-
- for (sbinding <- env.staticVariables) {
- _inScopeStatics.put(sbinding.name.getClarkName, sbinding)
- }
-
- for (dbinding <- env.variables) {
- _inScopeDynamics.put(dbinding.name, dbinding)
- }
-
- for (child <- allChildren) {
- child.makeBindingsExplicit()
- }
- }
-
- protected[model] def normalizeToPipes(): Unit = {
- for (child <- allChildren) {
- child.normalizeToPipes()
- }
- }
-
- protected[model] def addFilters(): Unit = {
- for (child <- allChildren) {
- child.addFilters()
- }
- }
-
- protected[model] def insertPipe(source: Port, pipe: Pipe): Unit = {
- for (child <- allChildren) {
- child.insertPipe(source, pipe)
- }
- }
-
- protected[model] def replumb(oldSource: Port, newSource: Port): Unit = {
- for (child <- allChildren) {
- child.replumb(oldSource, newSource)
- }
- }
-
- protected[model] def xpathBindingParams(): XPathBindingParams = {
- val statics = mutable.HashMap.empty[QName, XdmValue]
- for ((name,binding) <- _inScopeStatics) {
- val qname = ValueParser.parseClarkName(name)
- val smsg = binding.staticValue.get
- smsg match {
- case msg: XdmNodeItemMessage =>
- statics.put(qname, msg.item)
- case msg: XdmValueItemMessage =>
- statics.put(qname, msg.item)
- case _ =>
- throw new RuntimeException("Unexpected message type")
- }
- }
- statics.toMap
- new XPathBindingParams(statics.toMap)
- }
-
- protected[model] def stepParams(): StepParams = {
- new StepParams(inScopeStatics)
- }
-
- def computeStatically(xpathExpression: String): XdmValueItemMessage = {
- // Evaluate it; no reference to context or non-statics is allowed.
- val exprContext = staticContext.withStatics(inScopeStatics)
- val expr = new XProcXPathExpression(staticContext, xpathExpression)
- try {
- config.expressionEvaluator.newInstance().value(expr, List(), exprContext.statics, None)
- } catch {
- case sae: SaxonApiException =>
- throw XProcException.xsStaticErrorInExpression(xpathExpression, sae.getMessage, exprContext.location)
- }
- }
-
- def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- if (allChildren.nonEmpty) {
- if (_graphNode.isDefined) {
- for (child <- allChildren) {
- child.graphNodes(runtime, _graphNode.get)
- }
- } else {
- println("cannot graphNodes for children of " + this)
- }
- }
- }
-
- def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- println(s"ERROR: $this doesn't override graphEdges")
- }
-
- def dump(): Unit = {
- dump("", Set.empty[Artifact])
- }
-
- private def dump(indent: String, dumped: Set[Artifact]): Unit = {
- println(s"$indent$this")
- for (child <- allChildren) {
- child match {
- case _: Step =>
- println("")
- case _ => ()
- }
- if (dumped.contains(child)) {
- println(s"$indent$this...")
- } else {
- child.dump(s"$indent ", dumped + child)
- }
- }
- }
-
- def xdump(xml: ElaboratedPipeline): Unit = {
- throw new RuntimeException(s"Don't know how to xdump $this")
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/AtomicLoader.scala b/src/main/scala/com/xmlcalabash/model/xml/AtomicLoader.scala
deleted file mode 100644
index c2e495a..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/AtomicLoader.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.runtime.{ImplParams, XMLCalabashRuntime}
-
-class AtomicLoader(override val config: XMLCalabash, params: Option[ImplParams]) extends AtomicStep(config, params) {
-
- def this(config: XMLCalabash, params: ImplParams, context: Artifact) = {
- this(config, Some(params))
- _inScopeStatics = context._inScopeStatics
- _inScopeDynamics = context._inScopeDynamics
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- for ((name, np) <- _inScopeDynamics) {
- val binding = findInScopeOption(name)
- if (binding.isDefined) {
- // Can be undefined if it's on an atomic step
- val pipe = new NamePipe(config, name, this.tumble_id, binding.get)
- pipe.graphEdges(runtime, _graphNode.get)
- }
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/AtomicStep.scala b/src/main/scala/com/xmlcalabash/model/xml/AtomicStep.scala
deleted file mode 100644
index a6d27fd..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/AtomicStep.scala
+++ /dev/null
@@ -1,368 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.jafpl.messages.Message
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.{ExceptionCode, ModelException, XProcException}
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.params.StepParams
-import com.xmlcalabash.runtime.{ImplParams, StepExecutable, StepProxy, StepRunner, StepWrapper, XMLCalabashRuntime, XmlStep}
-import com.xmlcalabash.steps.internal.{DocumentLoader, InlineLoader}
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.ma.arrays.ArrayItemType
-import net.sf.saxon.ma.map.MapType
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-import java.lang.reflect.InvocationTargetException
-import scala.collection.mutable
-
-class AtomicStep(override val config: XMLCalabash, params: Option[ImplParams]) extends Step(config) with NamedArtifact {
- def this(config: XMLCalabash) = {
- this(config, None)
- }
-
- def this(config: XMLCalabash, params: ImplParams) = {
- this(config, Some(params))
- }
-
- def this(config: XMLCalabash, params: ImplParams, context: Artifact) = {
- this(config, Some(params))
- _inScopeStatics = context._inScopeStatics
- _inScopeDynamics = context._inScopeDynamics
- }
-
- private var _stepType: QName = _
- private var _stepImplementation: StepExecutable = _
-
- def stepType: QName = _stepType
- protected[model] def stepType_=(stype: QName): Unit = {
- _stepType = stype
- }
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
- _stepType = node.getNodeName
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- if (declaration(stepType).isEmpty) {
- throw XProcException.xsMissingDeclaration(stepType, location)
- }
-
- val decl = declaration(stepType).get
-
- for (item <- allChildren) {
- item match {
- case winput: WithInput =>
- winput.makeStructureExplicit()
- if (winput.port == "") {
- if (decl.primaryInput.isDefined) {
- winput.port = decl.primaryInput.get.port
- }
- }
- case _ =>
- item.makeStructureExplicit()
- }
- }
-
- // Make sure there are with-{input/output/option} elements
- for (dinput <- decl.inputs) {
- var found = Option.empty[WithInput]
- for (winput <- children[WithInput]) {
- if (dinput.port == winput.port) {
- found = Some(winput)
- }
- }
- if (found.isEmpty) {
- val newwi = new WithInput(config)
- newwi.port = dinput.port
- newwi.primary = dinput.primary
- newwi.sequence = dinput.sequence
- addChild(newwi)
- }
- }
-
- for (doutput <- decl.outputs) {
- val newwo = new WithOutput(config)
- newwo.port = doutput.port
- newwo.primary = doutput.primary
- newwo.sequence = doutput.sequence
- addChild(newwo)
- }
-
- for (doption <- decl.options) {
- var found = Option.empty[WithOption]
- for (woption <- children[WithOption]) {
- if (woption.name == doption.name) {
- found = Some(woption)
- }
- }
-
- if (found.isEmpty) {
- if (attributes.contains(doption.name)) {
- val woption = new WithOption(config, doption.name)
- woption.staticContext = staticContext
-
- if (doption.declaredType.isDefined) {
- val dtype = doption.declaredType.get
- woption.as = dtype
- woption.qnameKeys = doption.forceQNameKeys
-
- dtype.getItemType.getUnderlyingItemType match {
- case _: MapType =>
- woption.select = attr(doption.name).get
- case _: ArrayItemType =>
- woption.select = attr(doption.name).get
- case _ =>
- woption.avt = attr(doption.name).get
- }
- } else {
- woption.avt = attr(doption.name).get
- }
-
- found = Some(woption)
- addChild(woption)
- } else {
- if (doption.required) {
- throw XProcException.xsMissingRequiredOption(doption.name, location)
- } else {
- val woption = new WithOption(config, doption.name)
- woption.staticContext = staticContext
- woption.select = doption.defaultSelect.getOrElse("()")
- found = Some(woption)
- addChild(woption)
- }
- }
- } else {
- if (attributes.contains(doption.name)) {
- throw XProcException.xsDupWithOptionName(doption.name, location)
- }
- }
-
- // Work out any preceding options that might not be defined
- var prec = true
- for (precopt <- decl.options) {
- prec = prec && precopt.name != found.get.name
- if (prec) {
- found.get.precedingOption(precopt)
- }
- }
-
- if (doption.declaredType.isDefined) {
- found.get.declaredType = doption.declaredType.get
- }
- }
-
- // Make sure there are no extra options
- val seenOption = mutable.HashSet.empty[QName]
- for (woption <- children[WithOption]) {
- if (!decl.optionNames.contains(woption.name)) {
- throw XProcException.xsUndeclaredOption(stepType, woption.name, location)
- }
- if (seenOption.contains(woption.name)) {
- throw XProcException.xsDupWithOptionName(woption.name, location)
- }
- seenOption += woption.name
- }
-
- // Now manufacture "with-option"s for extension attributes
- for (attr <- extensionAttributes.keySet) {
- val woption = new WithOption(config, attr)
- woption.staticContext = staticContext
- woption.qnameKeys = false
- woption.avt = extensionAttributes(attr)
-
- if (seenOption.contains(woption.name)) {
- throw XProcException.xsDupWithOptionName(woption.name, location)
- }
- seenOption += woption.name
-
- addChild(woption)
- }
-
- // Now make sure there are no extra inputs
- val seenInput = mutable.HashSet.empty[String]
- for (winput <- children[WithInput]) {
- if (decl.inputPorts.isEmpty && winput.port == "") {
- throw XProcException.xsNoPrimaryInputPort(stepType, location)
- }
- if (!decl.inputPorts.contains(winput.port)) {
- throw XProcException.xsBadPortName(stepType, winput.port, location)
- }
- if (seenInput.contains(winput.port)) {
- throw XProcException.xsDupWithInputPort(winput.port, location)
- }
- seenInput += winput.port
- }
-
- // Now that we've checked the options, we can add an extra one.
- // If @p:message is given, treat it like a with-option (even though
- // it cannot be specified in that form).
- var msgName = if (stepType.getNamespaceURI == XProcConstants.ns_p) {
- XProcConstants._message
- } else {
- XProcConstants.p_message
- }
-
- if (attributes.contains(msgName)) {
- val woption = new WithOption(config, msgName)
- woption.staticContext = staticContext
- woption.avt = attr(msgName).get
- addChild(woption)
- }
-
- if (attributes.nonEmpty) {
- // On an atomic step, any left over attributes are presumably attempts
- // to use shortcuts for options that don't exist.
- throw XProcException.xsUndeclaredOption(stepType, attributes.keySet.head, location)
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- val iport = mutable.HashSet.empty[String]
-
- for (child <- allChildren) {
- child.validateStructure()
- child match {
- case art: WithInput =>
- if (iport.contains(art.port)) {
- throw XProcException.xsDupWithInputPort(art.port, location)
- }
- iport += art.port
- case _: WithOutput => ()
- case _: WithOption => ()
- case _ =>
- throw new RuntimeException(s"Invalid content in atomic $this")
- }
- }
- }
-
- def stepImplementation(staticContext: XMLContext): StepExecutable = {
- stepImplementation(staticContext, None)
- }
-
- def stepImplementation(staticContext: XMLContext, implParams: Option[ImplParams]): StepExecutable = {
- val location = staticContext.location
-
- val sig = declaration(stepType).get
- val implClass = sig.implementation
- if (implClass.isEmpty) {
- val declStep = sig.declaration
- if (declStep.isDefined) {
- new StepRunner(config, declStep.get, sig)
- } else {
- throw XProcException.xiStepImplementationError(s"No implementation for ${stepType.toString}", location)
- }
- } else {
- try {
- val klass = Class.forName(implClass.head).getDeclaredConstructor().newInstance()
- klass match {
- case step: XmlStep =>
- new StepWrapper(step, sig)
- case _ =>
- throw XProcException.xiStepImplementationError(s"Class does not implement an XmlStep: ${stepType.toString}", location)
- }
- } catch {
- case ex: XProcException =>
- throw ex
- case _: ClassNotFoundException =>
- throw XProcException.xiStepImplementationError(s"Class not found: ${implClass.head}", location)
- case _: IllegalAccessException =>
- throw XProcException.xiStepImplementationError(s"Constructor inaccessible: ${implClass.head}", location)
- case ex: InstantiationException =>
- throw XProcException.xiStepImplementationError(s"Cannot instantiate ${implClass.head}: ${ex.getMessage}", location)
- case ex: InvocationTargetException =>
- throw XProcException.xiStepImplementationError(s"Constructor threw exception ${implClass.head}: ${ex.getMessage}", location)
- case ex: ExceptionInInitializerError =>
- throw XProcException.xiStepImplementationError(s"Constructor failed for ${implClass.head}: ${ex.getMessage}", location)
- case ex: Throwable =>
- throw XProcException.xiStepImplementationError(s"Failed to instantiate ${implClass.head}: ${ex.getMessage}", location)
- }
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[ContainerStart]
- _stepImplementation = stepImplementation(staticContext)
- _stepImplementation.configure(config, stepType, _name, params)
-
- _stepImplementation match {
- // If we're running an instance of a pipeline, work out which
- // ports actually received inputs (even p:empty) so that
- // we can tell the runtime for the pipeline. We need to do this
- // so that p:empty isn't misinterpreted as an unbound port.
- // Unbound ports will get default inputs if they're provided.
- case runner: StepRunner =>
- val usedPorts = mutable.HashSet.empty[String]
- for (input <- children[WithInput]) {
- val bound = input.children[DataSource].nonEmpty
- if (bound) {
- usedPorts += input.port
- }
- }
- runner.usedPorts(usedPorts.toSet)
- case _ => ()
- }
-
- var proxyParams: Option[ImplParams] = params
-
- // Handle any with-option values for this step that have been computed statically
- val staticallyComputed = mutable.HashMap.empty[String, Message]
- for (woption <- children[WithOption]) {
- if (woption.staticValue.isDefined) {
- staticallyComputed.put(woption.name.getClarkName, woption.staticValue.get)
- }
- }
- if (staticallyComputed.nonEmpty) {
- if (proxyParams.isDefined) {
- throw new RuntimeException("static options and constructed params?")
- }
- proxyParams = Some(new StepParams(staticallyComputed.toMap))
- }
-
- val pcontext = staticContext.withStatics(inScopeStatics)
- val proxy = new StepProxy(runtime, stepType, _stepImplementation, proxyParams, pcontext)
- val node = start.addAtomic(proxy, s"$stepType $stepName")
- _graphNode = Some(node)
-
- for (child <- children[WithOption]) {
- child.graphNodes(runtime, _graphNode.get)
- }
-
- // If there are any with-options that have name-bindings to preceding
- // options, patch those bindings to refer to the preceding with-options
- // See test ab-option-050
- val declOptions = mutable.HashMap.empty[QName, Node]
- for (child <- children[WithOption]) {
- if (!child.static) {
- if (child._graphNode.isDefined) {
- declOptions.put(child.name, child._graphNode.get)
- }
- }
- }
- for (nb <- findDescendants[NamePipe]) {
- if (declOptions.contains(nb.link.name)) {
- nb.patchNode(declOptions(nb.link.name))
- }
- }
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
- for (child <- allChildren) {
- child.graphEdges(runtime, _graphNode.get)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startAtomic(tumble_id, stepName, stepType)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endAtomic()
- }
-
- override def toString: String = {
- s"$stepType $stepName"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Catch.scala b/src/main/scala/com/xmlcalabash/model/xml/Catch.scala
deleted file mode 100644
index c734ab8..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Catch.scala
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{Node, TryCatchStart}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-import scala.collection.mutable
-
-class Catch(override val config: XMLCalabash) extends Container(config) with NamedArtifact {
- private val _codes = mutable.HashSet.empty[QName]
-
- def codes: Set[QName] = _codes.toSet
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._code)) {
- val str = attr(XProcConstants._code).get
- var repeated = Option.empty[QName]
- for (code <- str.split("\\s+")) {
- try {
- val qname = staticContext.parseQName(code)
- if (repeated.isEmpty && _codes.contains(qname)) {
- repeated = Some(qname)
- }
- _codes += qname
- } catch {
- case _: Exception =>
- throw XProcException.xsCatchInvalidCode(code, location)
- }
- }
- if (repeated.isDefined) {
- throw XProcException.xsCatchRepeatedCode(repeated.get, location)
- }
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- val input = new DeclareInput(config)
- input.port = "error"
- input.sequence = true
- input.primary = true
- addChild(input, firstChild)
-
- makeContainerStructureExplicit()
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[TryCatchStart]
- val node = start.addCatch(stepName, codes.toList, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- for (child <- allChildren) {
- child.graphEdges(runtime, _graphNode.get)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- if (_codes.nonEmpty) {
- var codes = ""
- for (code <- _codes) {
- if (codes != "") {
- codes += ", "
- }
- codes += code.toString
- }
-
- xml.startCatch(tumble_id, stepName, Some(codes))
- } else {
- xml.startCatch(tumble_id, stepName, None)
- }
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endCatch()
- }
-
- override def toString: String = {
- if (codes.isEmpty) {
- s"p:catch $stepName"
- } else {
- val codestr = codes.mkString(" ")
- s"p:catch $codestr $stepName"
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Choose.scala b/src/main/scala/com/xmlcalabash/model/xml/Choose.scala
deleted file mode 100644
index 2159a8b..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Choose.scala
+++ /dev/null
@@ -1,211 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ChooseStart, ContainerStart, Node, WhenStart}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.{XMLCalabashRuntime, XProcXPathExpression}
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-import scala.collection.mutable
-
-class Choose(override val config: XMLCalabash) extends Container(config) {
- private var hasWhen = false
- private var hasOtherwise = false
- protected var ifexpr: Option[String] = None
- protected var ifcoll: Option[String] = None
- protected[xml] var p_if = false
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (node.getNodeName == XProcConstants.p_if) {
- if (attributes.contains(XProcConstants._test)) {
- ifexpr = attr(XProcConstants._test)
- } else {
- throw XProcException.xsMissingRequiredAttribute(XProcConstants._test, location)
- }
- if (attributes.contains(XProcConstants._collection)) {
- ifcoll = attr(XProcConstants._collection)
- }
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- var firstChild = Option.empty[Artifact]
- var input = Option.empty[WithInput]
- for (child <- allChildren) {
- if (firstChild.isEmpty) {
- firstChild = Some(child)
- }
- child match {
- case winput: WithInput =>
- if (input.isDefined) {
- throw new RuntimeException("Only one with-input is allowed")
- }
- input = Some(winput)
- winput.makeStructureExplicit()
- case when: When =>
- hasWhen = true
- when.makeStructureExplicit()
- case otherwise: Otherwise =>
- hasOtherwise = true
- otherwise.makeStructureExplicit()
- }
- }
-
- if (!hasWhen && !hasOtherwise) {
- throw XProcException.xsMissingWhen(location)
- }
-
- if (input.isEmpty) {
- val winput = new WithInput(config)
- winput.port = "source"
- winput.primary = true
- if (firstChild.isDefined) {
- addChild(winput, firstChild.get)
- } else {
- addChild(winput)
- }
- }
-
- val outputSet = mutable.HashSet.empty[String]
- var primaryOutput = Option.empty[String]
-
- for (branch <- children[Container]) {
- for (child <- branch.children[DeclareOutput]) {
- outputSet += child.port
- if (child.primary) {
- primaryOutput = Some(child.port)
- }
- }
- }
-
- val first = firstChild
- for (port <- outputSet) {
- val woutput = new WithOutput(config)
- woutput.port = port
- woutput.primary = (primaryOutput.isDefined && primaryOutput.get == port)
- addChild(woutput, first)
- }
-
- if (!hasOtherwise) {
- val other = new Otherwise(config)
- other.test = "true()"
-
- val identity = new AtomicStep(config)
- identity.stepType = XProcConstants.p_identity
- /* ??? tangled up in the collectin/expression debacle and unclear to me
- val idin = new WithInput(config)
- idin.port = "source"
- identity.addChild(idin)
- */
- other.addChild(identity)
-
- // If there isn't a primary output, make sure we sink the output of
- // the identity step we just added so that one doesn't get added
- // automatically
- if (primaryOutput.isEmpty) {
- val sink = new AtomicStep(config)
- sink.stepType = XProcConstants.p_sink
- other.addChild(sink)
- }
-
- addChild(other)
- other.makeStructureExplicit()
- }
-
- if (p_if) {
- var primary = false
- for (child <- children[WithOutput]) {
- primary = primary || child.primary
- }
-
- if (!primary) {
- throw XProcException.xsPrimaryOutputRequired(location)
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- super.validateStructure()
-
- var first = true
- var primaryOutput = Option.empty[DeclareOutput]
- for (child <- allChildren) {
- child match {
- case _: WithInput => ()
- case _: WithOutput => ()
- case when: ChooseBranch => // When or Otherwise
- if (first) {
- for (child <- when.children[DeclareOutput]) {
- if (child.primary) {
- primaryOutput = Some(child)
- }
- }
- first = false
- }
-
- var foundPrimary = false
- for (child <- when.children[DeclareOutput]) {
- if (child.primary) {
- foundPrimary = true
- if (primaryOutput.isEmpty) {
- throw XProcException.xsBadChooseOutputs("#NONE", child.port, location)
- }
- if (primaryOutput.isDefined && primaryOutput.get.port != child.port) {
- throw XProcException.xsBadChooseOutputs(primaryOutput.get.port, child.port, location)
- }
- }
- }
-
- if (!foundPrimary && primaryOutput.isDefined) {
- throw XProcException.xsBadChooseOutputs(primaryOutput.get.port, "#NONE", location)
- }
- case _ =>
- throw new RuntimeException(s"Invalid content in $this")
- }
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[ContainerStart]
- val node = start.addChoose(stepName)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- for (child <- children[Container]) {
- child.graphEdges(runtime, _graphNode.get)
- }
-
- val chooseNode = _graphNode.get.asInstanceOf[ChooseStart]
- for (branch <- children[ChooseBranch]) {
- val branchNode = branch._graphNode.get
- for (output <- branch.children[DeclareOutput]) {
- runtime.graph.addEdge(branchNode, output.port, chooseNode, output.port)
- }
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startChoose(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endChoose()
- }
-
- override def toString: String = {
- s"p:choose $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/ChooseBranch.scala b/src/main/scala/com/xmlcalabash/model/xml/ChooseBranch.scala
deleted file mode 100644
index f53c633..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/ChooseBranch.scala
+++ /dev/null
@@ -1,163 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ChooseStart, Node}
-import com.jafpl.steps.Manifold
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.params.XPathBindingParams
-import com.xmlcalabash.runtime.{StaticContext, XMLCalabashRuntime, XProcXPathExpression}
-import com.xmlcalabash.util.ValueUtils
-import net.sf.saxon.s9api.QName
-
-import scala.collection.mutable
-
-class ChooseBranch(override val config: XMLCalabash) extends Container(config) with NamedArtifact {
- protected var _test = ""
- protected var _collection = false
- protected var testExpr: XProcXPathExpression = _
-
- def test: String = _test
- protected[model] def test_=(expr: String): Unit = {
- _test = expr
- setTestExpr()
- }
-
- def collection: Boolean = _collection
- protected[model] def collection_=(coll: String): Unit = {
- if (List("true", "1", "yes").contains(coll)) {
- _collection = true
- } else if (List("false", "0", "no").contains(coll)) {
- _collection = false
- } else {
- throw XProcException.xsBadTypeValue(coll, "xs:boolean", location)
- }
- setTestExpr()
- }
-
- private def setTestExpr(): Unit = {
- val context = new StaticContext(staticContext, this)
- val params = new XPathBindingParams(collection)
- testExpr = new XProcXPathExpression(context, _test, None, None, Some(params))
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- // We don't make the with-input structure explicit here because we want
- // to do it after any potential inputs have been turned into pipes.
- makeContainerStructureExplicit()
- }
-
- override protected def syntheticDeclaredOutput(): DeclareOutput = {
- var portName = "#result"
-
- if (synthetic) { // This must be an otherwise
- // If we've created a synthetic otherwise that has a primary output
- // port, make sure the port name we give it is consistent with the
- // primary output port of the when's, if they have one.
- val choose = parent.get
- val when = choose.children[When].head
- for (child <- when.children[DeclareOutput]) {
- if (child.primary) {
- portName = child.port
- }
- }
- }
-
- val output = new DeclareOutput(config)
- output.port = portName
- output.primary = true
- output.sequence = true
- output
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- var winput = firstWithInput
- if (winput.isEmpty) {
- winput = Some(new WithInput(config))
- winput.get.port = "source"
- winput.get.primary = true
- addChild(winput.get, firstChild)
- }
-
- val bindings = mutable.HashSet.empty[QName]
- bindings ++= staticContext.findVariableRefsInString(_test)
- if (bindings.isEmpty) {
- val depends = staticContext.dependsOnContextString(_test)
- if (!depends) {
- //FIXME: WHEN/OTHERWISE CAN BE RESOLVED STATICALLY
- }
- } else {
- val env = environment()
- for (ref <- bindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw new RuntimeException("Reference to undefined variable")
- }
- if (!binding.get.static) {
- val pipe = new NamePipe(config, ref, binding.get.tumble_id, binding.get)
- addChild(pipe)
- }
- }
- }
- }
-
- override protected[model] def normalizeToPipes(): Unit = {
- for (child <- allChildren) {
- child.normalizeToPipes()
- }
-
- var copyPipes = true
- var winput = firstWithInput
- if (winput.isEmpty) {
- winput = Some(new WithInput(config))
- winput.get.port = "source"
- winput.get.primary = true
- addChild(winput.get, firstChild)
- } else {
- copyPipes = winput.get.children[Pipe].isEmpty
- }
-
- if (copyPipes) {
- // A bit hacky, but here's where we need to inject the with-input
- val parentWithInput = parent.get.firstWithInput
- for (child <- parentWithInput.get.allChildren) {
- child match {
- case pipe: Pipe =>
- winput.get.addChild(new Pipe(pipe))
- case _ =>
- throw new RuntimeException("with input hasn't been normalized to pipes?")
- }
- }
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[ChooseStart]
- val node = start.addWhen(testExpr, stepName, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- for (child <- allChildren) {
- child match {
- case winput: WithInput =>
- for (child <- winput.allChildren) {
- child match {
- case pipe: Pipe =>
- runtime.graph.addEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, "condition")
- case pipe: NamePipe =>
- pipe.graphEdges(runtime, _graphNode.get)
- case _ =>
- throw new RuntimeException("non-pipe children in p:with-input?")
- }
- }
- case _ =>
- child.graphEdges(runtime, _graphNode.get)
- }
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Container.scala b/src/main/scala/com/xmlcalabash/model/xml/Container.scala
deleted file mode 100644
index 4019518..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Container.scala
+++ /dev/null
@@ -1,367 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.jafpl.steps.{Manifold, PortCardinality, PortSpecification}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.{XMLCalabashRuntime, XmlPortSpecification}
-import com.xmlcalabash.runtime.params.{ContentTypeCheckerParams, SelectFilterParams}
-import com.xmlcalabash.util.MediaType
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class Container(override val config: XMLCalabash) extends Step(config) with NamedArtifact {
- protected var _outputs = mutable.HashMap.empty[String, DeclareOutput]
-
- def atomic: Boolean = {
- for (child <- allChildren) {
- child match {
- case _: AtomicStep =>
- // Synthetic children (inline-loader, etc.) don't count
- if (!child.synthetic) {
- return false
- }
- case _: Container => return false
- case _ => ()
- }
- }
- true
- }
-
- protected[model] def makeContainerStructureExplicit(): Unit = {
- var firstChild = Option.empty[Artifact]
- var withInput = Option.empty[WithInput]
- var lastOutput = Option.empty[DeclareOutput]
- var primaryOutput = Option.empty[DeclareOutput]
- var lastStep = Option.empty[Step]
-
- for (child <- allChildren) {
- child.makeStructureExplicit()
-
- if (firstChild.isEmpty) {
- firstChild = Some(child)
- }
-
- child match {
- case input: WithInput =>
- if (withInput.isDefined) {
- throw new RuntimeException("Only one with-input is allowed")
- }
- withInput = Some(input)
- case output: DeclareOutput =>
- if (_outputs.contains(output.port)) {
- throw new RuntimeException("duplicate output port")
- }
- _outputs.put(output.port, output)
-
- lastOutput = Some(output)
- if (output.primary) {
- if (primaryOutput.isDefined) {
- throw XProcException.xsDupPrimaryPort(output.port, primaryOutput.get.port, staticContext.location)
- }
- primaryOutput = Some(output)
- }
- case atomic: AtomicStep =>
- lastStep = Some(atomic)
- case compound: Container =>
- lastStep = Some(compound)
- case variable: Variable =>
- environment().addVariable(variable)
- case _ =>
- ()
- }
- }
-
- if (_outputs.isEmpty && lastStep.isDefined && lastStep.get.primaryOutput.isDefined) {
- val output = syntheticDeclaredOutput()
-
- val pipe = new Pipe(config)
- pipe.step = lastStep.get.stepName
- pipe.port = lastStep.get.primaryOutput.get.port
- pipe.link = lastStep.get.primaryOutput.get
- output.addChild(pipe)
- _outputs.put(output.port, output)
- lastOutput = Some(output)
-
- if (firstChild.isDefined) {
- addChild(output, firstChild.get)
- } else {
- addChild(output)
- }
- }
-
- if (_outputs.size == 1 && lastOutput.get._primary.isEmpty) {
- lastOutput.get.primary = true
- }
- }
-
- protected def syntheticDeclaredOutput(): DeclareOutput = {
- val output = new DeclareOutput(config)
- output.port = "#result"
- output.primary = true
- output.sequence = true
- output
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- val env = environment()
-
- // Be careful here, we don't call super.makeBindingsExplicit() so this method
- // has to be kept up-to-date with respect to changes there!
-
- // Make the bindings for this step's inputs explicit
- for (input <- children[WithInput]) {
- input.makeBindingsExplicit()
- }
-
- for (sbinding <- env.staticVariables) {
- _inScopeStatics.put(sbinding.name.getClarkName, sbinding)
- }
-
- for (dbinding <- env.variables) {
- _inScopeDynamics.put(dbinding.name, dbinding)
- }
-
- if (atomic) {
- return
- }
-
- var poutput = Option.empty[DeclareOutput]
- var lastStep = Option.empty[Step]
- for (child <- allChildren) {
- child match {
- case output: DeclareOutput =>
- if (output.primary) {
- poutput = Some(output)
- }
- case step: Step =>
- lastStep = Some(step)
- case _ => ()
- }
- }
-
- if (lastStep.isDefined && poutput.isDefined && poutput.get.bindings.isEmpty) {
- val lpo = lastStep.get.primaryOutput
- if (lpo.isDefined) {
- val pipe = new Pipe(config)
- pipe.port = lpo.get.port
- pipe.step = lastStep.get.stepName
- pipe.link = lpo.get
- poutput.get.addChild(pipe)
- }
- }
-
- for (child <- allChildren) {
- child match {
- case option: DeclareOption =>
- option.makeBindingsExplicit()
- if (option.select.isDefined && option.static) {
- option.staticValue = computeStatically(option.select.get)
- }
- case variable: Variable =>
- variable.makeBindingsExplicit()
- if (variable.select.isDefined && variable.static) {
- variable.staticValue = computeStatically(variable.select.get)
- }
- case _: WithInput =>
- () // we did this above
- case _ =>
- child.makeBindingsExplicit()
- }
- }
-
- // Add ContentTypeCheckers and select filters if we need them
- if (lastStep.isDefined) {
- val innerEnv = lastStep.get.environment()
- for (input <- children[DeclareInput]) {
- if (input.select.isDefined) {
- logger.debug(s"Adding select filter for container input ${stepName}/${input.port}: ${input.select.get}")
- val context = staticContext.withStatics(inScopeStatics)
-
- val ispec = if (input.sequence) {
- XmlPortSpecification.ANYSOURCESEQ
- } else {
- XmlPortSpecification.ANYSOURCE
- }
-
- val params = new SelectFilterParams(context, input.select.get, input.port, ispec)
- val filter = new AtomicStep(config, params)
- filter.stepType = XProcConstants.cx_select_filter
-
- addChild(filter)
-
- val finput = new WithInput(config)
- finput.port = "source"
- finput.primary = true
- filter.addChild(finput)
-
- val foutput = new WithOutput(config)
- foutput.port = "result"
- foutput.primary = true
- filter.addChild(foutput)
-
- for (name <- staticContext.findVariableRefsInString(input.select.get)) {
- var binding = _inScopeDynamics.get(name)
- if (binding.isDefined) {
- val npipe = new NamePipe(config, name, binding.get.tumble_id, binding.get)
- filter.addChild(npipe)
- } else {
- binding = _inScopeStatics.get(name.getClarkName)
- if (binding.isEmpty) {
- throw new RuntimeException(s"Reference to variable not in scope: $name")
- }
- }
- }
-
- replumb(input, foutput)
-
- val newpipe = new Pipe(config)
- newpipe.step = stepName
- newpipe.port = input.port
- newpipe.link = input
- finput.addChild(newpipe)
- }
-
- if (!MediaType.OCTET_STREAM.allowed(input.contentTypes)) {
- logger.debug(s"Adding content-type-checker for container input ${stepName}/${input.port}")
- val params = new ContentTypeCheckerParams(input.port, input.contentTypes, staticContext, None, inputPort = true, true)
- val atomic = new AtomicStep(config, params)
- atomic.stepType = XProcConstants.cx_content_type_checker
- addChild(atomic)
-
- val winput = new WithInput(config)
- winput.port = "source"
- atomic.addChild(winput)
-
- val woutput = new WithOutput(config)
- woutput.port = "result"
- atomic.addChild(woutput)
-
- replumb(input, woutput)
-
- val newpipe = new Pipe(config)
- newpipe.step = stepName
- newpipe.port = input.port
- newpipe.link = input
- winput.addChild(newpipe)
- }
- }
-
- for (output <- children[DeclareOutput]) {
- var check = false
- //println(s"${stepName}, ${output.port}, ${output.contentTypes}")
- for (binding <- output.children[Pipe]) {
- val port = binding.port
- val step = innerEnv.step(binding.step).get
- val outputContentTypes = step match {
- case atom: AtomicStep =>
- val decl = atom.declaration(atom.stepType).get
- decl.output(port, None).contentTypes
- case cont: Container =>
- if (cont._outputs.contains(port)) {
- cont._outputs(port).contentTypes
- } else {
- // See if we can find a with-output for this port
- var woutput = Option.empty[WithOutput]
- for (wo <- cont.children[WithOutput]) {
- if (wo.port == port) {
- woutput = Some(wo)
- }
- }
- if (woutput.isEmpty) {
- // It must be implicit, so no checking is required
- List(MediaType.OCTET_STREAM)
- } else {
- woutput.get.contentTypes
- }
- }
- }
-
- //println(s" ${step} / ${outputContentTypes}")
- for (pct <- outputContentTypes) {
- check = check || !pct.allowed(output.contentTypes)
- }
- }
-
- if (check) {
- logger.debug(s"Adding content-type-checker for container output ${stepName}/${output.port}")
- val params = new ContentTypeCheckerParams(output.port, output.contentTypes, staticContext, None, inputPort = false, true)
- val atomic = new AtomicStep(config, params)
- atomic.stepType = XProcConstants.cx_content_type_checker
- addChild(atomic)
-
- val winput = new WithInput(config)
- winput.port = "source"
- atomic.addChild(winput)
-
- val pipes = ListBuffer.empty[Pipe] ++ output.children[Pipe]
- for (oldpipe <- pipes) {
- output.removeChild(oldpipe)
- winput.addChild(oldpipe)
- }
-
- val woutput = new WithOutput(config)
- woutput.port = "result"
- atomic.addChild(woutput)
-
- val newpipe = new Pipe(config)
- newpipe.step = atomic.stepName
- newpipe.port = "result"
- newpipe.link = woutput
- output.addChild(newpipe)
- }
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child.validateStructure()
- child match {
- case _: WithInput => ()
- case _: WithOutput => ()
- case _: DeclareInput => ()
- case _: DeclareOutput => ()
- case _: Step => ()
- case _: NamePipe => () // For Viweport
- case _: Variable => ()
- case _ =>
- throw new RuntimeException(s"Unexpected content in $this: $child")
- }
- }
- }
-
- def containerManifold: Manifold = {
- val spec = mutable.HashMap.empty[String, PortCardinality]
- for (output <- _outputs.values) {
- if (output.sequence) {
- spec.put(output.port, PortCardinality.ZERO_OR_MORE)
- } else {
- spec.put(output.port, PortCardinality.EXACTLY_ONE)
- }
- }
- new Manifold(Manifold.WILD, new PortSpecification(spec.toMap))
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- if (allChildren.nonEmpty) {
- if (_graphNode.isDefined) {
- for (child <- allChildren) {
- child match {
- case step: Step =>
- step.graphNodes(runtime, _graphNode.get)
- case option: DeclareOption =>
- option.graphNodes(runtime, _graphNode.get)
- case variable: Variable =>
- variable.graphNodes(runtime, _graphNode.get)
- case _ => ()
- }
- }
- } else {
- println("cannot graphNodes for children of " + this)
- }
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/DataSource.scala b/src/main/scala/com/xmlcalabash/model/xml/DataSource.scala
deleted file mode 100644
index 38cc554..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/DataSource.scala
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.ImplParams
-import net.sf.saxon.s9api.QName
-
-import scala.collection.mutable.ListBuffer
-
-abstract class DataSource(override val config: XMLCalabash) extends Artifact(config) {
- protected val depends: ListBuffer[String] = ListBuffer.empty[String]
-
- override protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child match {
- case _: Pipe => ()
- case _: NamePipe => ()
- case _ =>
- throw new RuntimeException(s"Invalid content in $this")
- }
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- // If the ancestor of a data source has a dependency, so does the data source
- var p = parent
- while (p.isDefined) {
- p.get match {
- case step: Step =>
- for (name <- step.depends) {
- if (!depends.contains(name)) {
- depends += name
- }
- }
- case _ =>
- ()
- }
- p = p.get.parent
- }
- }
-
- protected[model] def normalizeDataSourceToPipes(stepType: QName, params: ImplParams): Unit = {
- if (parent.isDefined && parent.get.parent.isDefined) {
- parent.get.parent.get match {
- case binding: NameBinding =>
- if (binding.static) {
- return // this all has to be resolved statically
- }
- case _ => ()
- }
- }
-
- val loader = new AtomicLoader(config, params, this)
- loader.stepType = stepType
-
- for (depend <- depends) {
- loader._depends += depend
- }
-
- if (allChildren.nonEmpty) {
- val winput = new WithInput(config)
- winput.port = "source"
- loader.addChild(winput)
- for (child <- allChildren) {
- winput.addChild(child)
- }
- removeChildren()
- }
-
- val woutput = new WithOutput(config)
- woutput.port = "result"
- loader.addChild(woutput)
-
- val step = if (parent.get.isInstanceOf[WithInput]) {
- val p = parent.get.parent.get
- // Hack for inline inside with-option
- if (p.isInstanceOf[WithOption]) {
- p.parent.get
- } else {
- p
- }
- } else {
- parent.get
- }
-
- var stepTarget: Artifact = step
- var container = step.parent.get.asInstanceOf[Container]
- if (container.isInstanceOf[Choose]) {
- stepTarget = container
- container = container.parent.get.asInstanceOf[Container]
- }
-
- container.addChild(loader, stepTarget)
-
- val pipe = new Pipe(config)
- pipe.step = loader.stepName
- pipe.port = "result"
- pipe.link = woutput
-
- parent.get.replaceChild(pipe, this)
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/DeclContainer.scala b/src/main/scala/com/xmlcalabash/model/xml/DeclContainer.scala
deleted file mode 100644
index b26312f..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/DeclContainer.scala
+++ /dev/null
@@ -1,135 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.StepSignature
-import com.xmlcalabash.exceptions.XProcException
-import net.sf.saxon.s9api.QName
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class DeclContainer(override val config: XMLCalabash) extends Container(config) {
- protected var _psvi_required = Option.empty[Boolean]
- protected var _xpath_version = Option.empty[Double]
- protected var _exclude_inline_prefixes = Option.empty[String]
- protected var _version = Option.empty[Double]
-
- protected var _signatures: ListBuffer[StepSignature] = ListBuffer.empty[StepSignature]
- private val processed = mutable.HashSet.empty[DeclContainer]
-
- def inScopeDeclarations: List[StepSignature] = _signatures.toList
-
- def addSignature(sig: StepSignature): Unit = {
- if (sig.stepType.isEmpty) {
- return
- }
-
- if (sig.declaration.isDefined) {
- // If it's not defined, we're processing a signature within the library that contains it, visibility is irrelevant
- val sigcontainer = sig.declaration.get.parent
- if (sig.declaration.get.visiblity != "public"
- && (sigcontainer.isEmpty || sigcontainer.get != this)) {
- // not visible here
- return
- }
- }
-
- val dsig = declaration(sig.stepType.get)
- if (dsig.isDefined) {
- if (dsig.get eq sig) {
- return // they're the same signature
- } else {
- throw XProcException.xsDupStepType(sig.stepType.get, location)
- }
- }
- _signatures += sig
- }
-
- def addSignatures(sigs: List[StepSignature]): Unit = {
- for (sig <- sigs) {
- addSignature(sig)
- }
- }
-
- protected[model] def loadImports(): Unit = {
- val imap = collection.mutable.HashMap.empty[Artifact,Artifact]
-
- for (child <- allChildren) {
- child match {
- case decl: DeclareStep =>
- decl.parseDeclarationSignature()
- decl.loadImports()
- case pimport: Import =>
- val icont = pimport.loadImports()
- imap.put(pimport, icont)
- case _ => ()
- }
- }
-
- for ((pimport, icont) <- imap) {
- replaceChild(icont, pimport)
- }
- }
-
- protected[model] def updateInScopeSteps(): Unit = {
- val buf = ListBuffer.empty[DeclContainer]
-
- for (child <- allChildren) {
- child match {
- case decl: DeclareStep =>
- if (!processed.contains(decl)) {
- processed += decl
- decl.updateInScopeSteps()
- }
- if (decl.signature.isEmpty) {
- decl.parseDeclarationSignature()
- }
- addSignature(decl.signature.get)
- case library: Library =>
- if (!processed.contains(library)) {
- processed += library
- library.updateInScopeSteps()
- }
- addSignatures(library._signatures.toList)
- buf += library
- case _ => ()
- }
- }
-
- for (library <- buf) {
- removeChild(library)
- }
- }
-
- override def declaration(stepType: QName): Option[StepSignature] = {
- declaration(stepType, this)
- }
-
- override def declaration(stepType: QName, container: DeclContainer): Option[StepSignature] = {
- var found = Option.empty[StepSignature]
-
- for (sig <- _signatures) {
- if (sig.stepType.isDefined && sig.stepType.get == stepType) {
- found = Some(sig)
- }
- }
-
- if (found.isEmpty) {
- if (parent.isDefined) {
- found = parent.get.declaration(stepType)
- } else {
- for (lib <- config.builtinSteps) {
- for (sig <- lib.inScopeDeclarations) {
- if (found.isEmpty) {
- if (sig.stepType.isDefined && sig.stepType.get == stepType) {
- found = Some(sig)
- }
- }
- }
- }
- }
- }
-
- found
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/DeclareFunction.scala b/src/main/scala/com/xmlcalabash/model/xml/DeclareFunction.scala
deleted file mode 100644
index bd4da0f..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/DeclareFunction.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-
-class DeclareFunction(override val config: XMLCalabash) extends Artifact(config) {
- override protected[model] def makeStructureExplicit(): Unit = {
- for (child <- allChildren) {
- child.makeStructureExplicit()
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child.validateStructure()
- }
- }
-
- override def toString: String = {
- s"p:declare-function $tumble_id"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/DeclareInput.scala b/src/main/scala/com/xmlcalabash/model/xml/DeclareInput.scala
deleted file mode 100644
index a6f31fe..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/DeclareInput.scala
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.runtime.params.SelectFilterParams
-import com.xmlcalabash.util.MediaType
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-import scala.collection.mutable.ListBuffer
-
-class DeclareInput(override val config: XMLCalabash) extends Port(config) {
- private val _exclude_result_prefixes = List.empty[String]
- protected[xmlcalabash] val defaultInputs: ListBuffer[DataSource] = ListBuffer.empty[DataSource]
-
- def exclude_result_prefixes: List[String] = _exclude_result_prefixes
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._port)) {
- _port = staticContext.parseNCName(attr(XProcConstants._port)).get
- } else {
- throw XProcException.xsMissingRequiredAttribute(XProcConstants._port, location)
- }
-
- _sequence = staticContext.parseBoolean(attr(XProcConstants._sequence))
- _primary = staticContext.parseBoolean(attr(XProcConstants._primary))
- _select = attr(XProcConstants._select)
-
- _content_types = staticContext.parseContentTypes(attr(XProcConstants._content_types))
- if (_content_types.isEmpty) {
- _content_types = List(MediaType.OCTET_STREAM)
- }
-
- _href = attr(XProcConstants._href)
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- for (child <- allChildren) {
- child.graphEdges(runtime, parent)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startInput(tumble_id, tumble_id, port)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endInput()
- }
-
- override def toString: String = {
- s"p:input $port $tumble_id"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/DeclareOption.scala b/src/main/scala/com/xmlcalabash/model/xml/DeclareOption.scala
deleted file mode 100644
index 8ea6721..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/DeclareOption.scala
+++ /dev/null
@@ -1,176 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.{ExceptionCode, ModelException, XProcException}
-import com.xmlcalabash.messages.XdmValueItemMessage
-import com.xmlcalabash.runtime.params.XPathBindingParams
-import com.xmlcalabash.runtime.{ExprParams, XMLCalabashRuntime, XProcMetadata, XProcXPathExpression, XProcXPathValue}
-import com.xmlcalabash.util.{S9Api, XProcVarValue}
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, SaxonApiException, SequenceType, XdmMap, XdmValue}
-import net.sf.saxon.trans.XPathException
-
-import java.net.URI
-import scala.collection.mutable
-
-class DeclareOption(override val config: XMLCalabash) extends NameBinding(config) {
- private var _runtimeBindings = Map.empty[QName,XProcVarValue]
-
- override def toString: String = {
- s"p:option $name $tumble_id"
- }
-
- override def declaredType: SequenceType = {
- if (_as.isEmpty) {
- _declaredType = staticContext.parseSequenceType(Some("Q{http://www.w3.org/2001/XMLSchema}string"))
- } else {
- _declaredType = _as
- }
- _declaredType.get
- }
-
- override protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child match {
- case _: WithInput => ()
- case _: NamePipe => ()
- case _ =>
- throw new RuntimeException(s"Invalid content in $this")
- }
- }
- }
-
- def runtimeBindings(bindings: Map[QName, XProcVarValue]): Unit = {
- _runtimeBindings = bindings
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- val env = environment()
-
- val bindings = mutable.HashSet.empty[QName]
- if (_select.isDefined) {
- bindings ++= staticContext.findVariableRefsInString(_select.get)
- }
-
- var nonStaticBindings = false
- for (ref <- bindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw new RuntimeException("Reference to undefined variable")
- }
- nonStaticBindings = nonStaticBindings || !binding.get.static
- }
-
- if (nonStaticBindings) {
- var winput = firstWithInput
- if (winput.isEmpty) {
- val input = new WithInput(config)
- input.port = "source"
- addChild(input)
- winput = Some(input)
- }
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- if (static) {
- // Statics have already been evaluated, they don't appear in the graph
- return
- }
-
- val container = this.parent.get
- val cnode = container._graphNode.get.asInstanceOf[ContainerStart]
- if (cnode.parent.nonEmpty) {
- throw new ModelException(ExceptionCode.INTERNAL, "Don't know what to do about opts here", location)
- }
-
- val params = new XPathBindingParams(collection)
- val init = if (_runtimeBindings.contains(name)) {
- new XProcXPathValue(staticContext, _runtimeBindings(name), as, _allowedValues, params)
- } else {
- val bindings = mutable.HashSet.empty[QName]
- if (_select.isDefined) {
- bindings ++= staticContext.findVariableRefsInString(_select.get)
- }
-
- val extext = _select.getOrElse("()")
- val expr = new XProcXPathExpression(staticContext, extext, as, _allowedValues, params)
-
- // Let's see if we think this is a syntactically valid expression (see bug #506 and test ab-option-024)
- val xcomp = config.processor.newXPathCompiler()
- xcomp.setBaseURI(URI.create("http://example.com/"))
- for (varname <- bindings) {
- xcomp.declareVariable(varname)
- }
- for ((prefix, uri) <- staticContext.nsBindings) {
- xcomp.declareNamespace(prefix, uri)
- }
- try {
- xcomp.compile(extext)
- } catch {
- case sae: SaxonApiException =>
- sae.getCause match {
- case _: XPathException =>
- throw XProcException.xsStaticErrorInExpression(extext, sae.getMessage, location)
- case _ => throw sae
- }
- case other: Throwable =>
- throw other
- }
-
- expr
- }
-
- val node = parent.asInstanceOf[ContainerStart].addOption(_name.getClarkName, init, xpathBindingParams(), topLevel = true)
-
- for (np <- _dependentNameBindings) {
- val binding = findInScopeOption(np.name)
- if (binding.isEmpty) {
- throw XProcException.xsNoBindingInExpression(np.name, location)
- }
- np.patchNode(binding.get.graphNode.get)
- np.graphEdges(runtime, node)
- }
-
- _graphNode = Some(node)
- }
-
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startOption(tumble_id, tumble_id, name)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endOption()
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parNode: Node): Unit = {
- if (static) {
- return
- }
-
- val env = environment()
- for (stepName <- depends) {
- val step = env.step(stepName)
- if (step.isEmpty) {
- throw XProcException.xsNotAStep(stepName, location)
- } else {
- _graphNode.get.dependsOn(step.get._graphNode.get)
- }
- }
-
- val toNode = parNode
- val fromPort = "result"
- val fromNode = _graphNode.get
- val toPort = "#bindings"
- runtime.graph.addEdge(fromNode, fromPort, toNode, toPort)
-
- for (child <- allChildren) {
- child.graphEdges(runtime, _graphNode.get)
- }
- }
-}
-
diff --git a/src/main/scala/com/xmlcalabash/model/xml/DeclareOutput.scala b/src/main/scala/com/xmlcalabash/model/xml/DeclareOutput.scala
deleted file mode 100644
index 8a6b2d1..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/DeclareOutput.scala
+++ /dev/null
@@ -1,89 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.{MediaType, S9Api}
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmMap, XdmNode}
-
-import scala.collection.mutable
-import scala.jdk.CollectionConverters.MapHasAsScala
-
-class DeclareOutput(override val config: XMLCalabash) extends Port(config) {
- private var mapexpr = Option.empty[String]
- private val _serialization = mutable.Map.empty[QName,String]
-
- def serialization: Map[QName,String] = _serialization.toMap
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._port)) {
- _port = staticContext.parseNCName(attr(XProcConstants._port)).get
- } else {
- throw new RuntimeException("Port is required")
- }
- _sequence = staticContext.parseBoolean(attr(XProcConstants._sequence))
- _primary = staticContext.parseBoolean(attr(XProcConstants._primary))
-
- _content_types = staticContext.parseContentTypes(attr(XProcConstants._content_types))
- if (_content_types.isEmpty) {
- _content_types = List(MediaType.OCTET_STREAM)
- }
-
- if (attributes.contains(XProcConstants._serialization)) {
- mapexpr = attr(XProcConstants._serialization)
- }
-
- _href = attr(XProcConstants._href)
- _pipe = attr(XProcConstants._pipe)
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- super.makeStructureExplicit()
- examineBindings()
- }
-
- override def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
- if (mapexpr.isDefined) {
- val msg = computeStatically(mapexpr.get)
- msg.item match {
- case map: XdmMap =>
- val sermap = S9Api.forceQNameKeys(map.getUnderlyingValue, staticContext)
- for ((key,value) <- sermap.asImmutableMap().asScala) {
- val name = key.getQNameValue
- _serialization(name) = value.getUnderlyingValue.getStringValue
- }
- case _ =>
- throw new RuntimeException("Serialization options must be a map")
- }
- }
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- for (child <- allChildren) {
- child.graphEdges(runtime, parent)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startOutput(tumble_id, tumble_id, port)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endOutput()
- }
-
- override def toString: String = {
- s"p:output $port${if (sequence) "*" else ""} $tumble_id"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/DeclareStep.scala b/src/main/scala/com/xmlcalabash/model/xml/DeclareStep.scala
deleted file mode 100644
index 1a30692..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/DeclareStep.scala
+++ /dev/null
@@ -1,384 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.exceptions.JafplLoopDetected
-import com.jafpl.graph.Node
-import com.jafpl.steps.{Manifold, PortCardinality, PortSpecification}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.{OptionSignature, PortSignature, StepSignature}
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.runtime.params.ContentTypeCheckerParams
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import com.xmlcalabash.util.{S9Api, TypeUtils, XProcVarValue}
-import net.sf.saxon.s9api.{ItemType, QName, SaxonApiException, XdmAtomicValue, XdmNode, XdmNodeKind}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class DeclareStep(override val config: XMLCalabash) extends DeclContainer(config) {
- private var _type = Option.empty[QName]
- private var _visibility = Option.empty[String]
- private var _signature = Option.empty[StepSignature]
- private val _inputs = mutable.HashMap.empty[String, DeclareInput]
- private val _bindings = mutable.HashMap.empty[QName, DeclareOption]
- private var _excludeUriBindings = Set.empty[String]
-
- // These are a bit of a hack. It's very hard to tell the difference
- // between an inline declare-step and an imported declare-step.
- // We have to process them, but we have to not process them twice.
- private var madeStructureExplicit = false
- private var madeBindingsExplicit = false
- private var didValidateStructure = false
-
- def stepType: Option[QName] = _type
- def psvi_required: Option[Boolean] = _psvi_required
- def xpath_version: Option[Double] = _xpath_version
- def exclude_inline_prefixes: Option[String] = _exclude_inline_prefixes
- def exclude_uri_bindings: Set[String] = _excludeUriBindings
- def version: Double = _version.getOrElse(3.0)
- def visiblity: String = _visibility.getOrElse("public")
- def signature: Option[StepSignature] = _signature
-
- def inputPorts: List[String] = _inputs.keySet.toList
- def outputPorts: List[String] = _outputs.keySet.toList
-
- def input(port: String): DeclareInput = _inputs(port)
- def output(port: String): DeclareOutput = _outputs(port)
-
- def inputs: List[DeclareInput] = _inputs.values.toList
- def outputs: List[DeclareOutput] = _outputs.values.toList
-
- def bindings: Map[QName,DeclareOption] = _bindings.toMap
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- val aname = attr(XProcConstants._name)
- if (aname.isDefined) {
- try {
- val ncname = TypeUtils.castAtomicAs(XdmAtomicValue.makeAtomicValue(aname.get), ItemType.NCNAME, staticContext)
- _name = Some(ncname.getStringValue)
- } catch {
- case _: SaxonApiException =>
- throw XProcException.xsBadTypeValue(aname.get, "NCName", location)
- }
- }
-
- _type = staticContext.parseQName(attr(XProcConstants._type))
- _psvi_required = staticContext.parseBoolean(attr(XProcConstants._psvi_required))
- if (attributes.contains(XProcConstants._xpath_version)) {
- val vstr = attr(XProcConstants._xpath_version).get
- _xpath_version = Some(vstr.toDouble)
- }
- if (attributes.contains(XProcConstants._version)) {
- val vstr = attr(XProcConstants._version).get
- try {
- _version = Some(vstr.toDouble)
- } catch {
- case _: NumberFormatException =>
- throw XProcException.xsBadVersion(vstr, location)
- }
- if (_version.get != 3.0) {
- throw XProcException.xsInvalidVersion(_version.get, location)
- }
- }
- if (_version.isEmpty) {
- if (node.getParent.getNodeKind == XdmNodeKind.DOCUMENT) {
- throw XProcException.xsVersionRequired(location)
- }
- }
-
- _exclude_inline_prefixes = attr(XProcConstants._exclude_inline_prefixes)
- if (_exclude_inline_prefixes.isDefined) {
- val prefixes = _exclude_inline_prefixes.get.split("\\s+").toList
- _excludeUriBindings = S9Api.urisForPrefixes(node, prefixes.toSet)
- }
-
- _visibility = attr(XProcConstants._visibility)
- if (_visibility.isDefined) {
- if (_visibility.get != "public" && _visibility.get != "private") {
- throw XProcException.xdBadVisibility(_visibility.get, location)
- }
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- protected[xml] def parseDeclarationSignature(): Unit = {
- // If there's only one input and it doesn't have a declared primary status, make it primary
- val pinput = children[DeclareInput].headOption
- if (children[DeclareInput].length == 1 && pinput.get._primary.isEmpty) {
- pinput.get.primary = true
- }
-
- // If any of the inputs have defaults, record them
- for (declinput <- children[DeclareInput]) {
- declinput.examineBindings()
- if (declinput.children[DataSource].nonEmpty) {
- declinput.defaultInputs ++= declinput.children[DataSource]
- // then remove them
- declinput.removeChildren()
- }
- }
-
- // If there's only one output and it doesn't have a declared primary status, make it primary
- val poutput = children[DeclareOutput].headOption
- if (children[DeclareOutput].length == 1 && poutput.get._primary.isEmpty) {
- poutput.get.primary = true
- }
-
- var lastInput = Option.empty[DeclareInput]
- var lastOutput = Option.empty[DeclareOutput]
- var primaryInput = Option.empty[DeclareInput]
- var primaryOutput = Option.empty[DeclareOutput]
-
- for (child <- allChildren) {
- child match {
- case input: DeclareInput =>
- if (_inputs.contains(input.port) || _outputs.contains(input.port)) {
- throw XProcException.xsDupPortName(input.port, location)
- }
- _inputs.put(input.port, input)
-
- lastInput = Some(input)
- if (input.primary) {
- if (primaryInput.isDefined) {
- throw XProcException.xsDupPrimaryPort(input.port, primaryInput.get.port, staticContext.location)
- }
- primaryInput = Some(input)
- }
- case output: DeclareOutput =>
- if (_outputs.contains(output.port) || _inputs.contains(output.port)) {
- throw XProcException.xsDupPortName(output.port, location)
- }
- _outputs.put(output.port, output)
-
- lastOutput = Some(output)
- if (output.primary) {
- if (primaryOutput.isDefined) {
- throw XProcException.xsDupPrimaryPort(output.port, primaryOutput.get.port, staticContext.location)
- }
- primaryOutput = Some(output)
- }
- case option: DeclareOption =>
- if (_bindings.contains(option.name)) {
- throw XProcException.xsDuplicateOptionName(option.name, location)
- }
- _bindings.put(option.name, option)
-
- case _ => ()
- }
- }
-
- if (_inputs.size == 1 && lastInput.get._primary.isEmpty) {
- lastInput.get.primary = true
- }
-
- if (_outputs.size == 1 && lastOutput.get._primary.isEmpty) {
- lastOutput.get.primary = true
- }
-
- val stepSig = new StepSignature(stepType)
- if (stepType.isDefined) {
- if (config.atomicStepImplementation(stepType.get).isDefined) {
- stepSig.implementation = config.atomicStepImplementation(stepType.get).get
- } else {
- stepSig.declaration = this
- if (atomic) {
- logger.info(s"No implementation for declared ${stepType.get} step")
- }
- }
- }
-
- for (child <- allChildren) {
- child match {
- case input: DeclareInput =>
- val portSig = new PortSignature(input.port, input.primary, input.sequence, input.defaultInputs.toList)
- portSig.contentTypes = input.contentTypes
- stepSig.addInput(portSig, input.location.get)
- case output: DeclareOutput =>
- val portSig = new PortSignature(output.port, output.primary, output.sequence)
- portSig.contentTypes = output.contentTypes
- stepSig.addOutput(portSig, output.location.get)
- case option: DeclareOption =>
- val optSig = new OptionSignature(option.name, option.declaredType, option.required)
- if (option.allowedValues.isDefined) {
- optSig.tokenList = option.allowedValues.get
- }
- if (option.select.isDefined) {
- optSig.defaultSelect = option.select.get
- }
- optSig.forceQNameKeys = option.qnameKeys
- stepSig.addOption(optSig, option.location.get)
- case _ =>
- ()
- }
- }
-
- _signature = Some(stepSig)
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- if (madeStructureExplicit) {
- return
- }
- madeStructureExplicit = true
-
- super.makeStructureExplicit()
-
- // If any of the inputs have defaults, make sure we make the structure explicit
- for (declinput <- children[DeclareInput]) {
- for (source <- declinput.defaultInputs) {
- source.makeStructureExplicit()
- }
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- if (madeBindingsExplicit) {
- return
- }
- madeBindingsExplicit = true
-
- super.makeBindingsExplicit()
-
- // If any of the inputs have defaults, make sure we make the bindings explicit
- for (declinput <- children[DeclareInput]) {
- for (source <- declinput.defaultInputs) {
- source.makeBindingsExplicit()
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- if (didValidateStructure) {
- return
- }
- didValidateStructure = true
-
- val buf = ListBuffer.empty[Artifact]
-
- for (child <- allChildren) {
- child.validateStructure()
- child match {
- case decl: DeclareStep =>
- buf += decl
- case _ => ()
- }
- }
-
- for (decl <- buf) {
- removeChild(decl)
- }
-
- if (!atomic) {
- normalizeToPipes()
- addFilters()
- }
- }
-
- def runtime(): XMLCalabashRuntime = {
- val runtime = new XMLCalabashRuntime(this)
- val pipeline = runtime.graph.addPipeline(stepName, manifold)
-
- for (port <- inputPorts) {
- runtime.graph.addInput(pipeline, port)
- }
-
- for (port <- outputPorts) {
- runtime.graph.addOutput(pipeline, port)
- }
-
- _graphNode = Some(pipeline)
-
- graphNodes(runtime, pipeline)
-
- // Look for name bindings that have to be patched
- val declOptions = mutable.HashMap.empty[NameBinding, Node]
- for (child <- children[DeclareOption]) {
- if (!child.static) {
- declOptions.put(child, child._graphNode.get)
- }
- }
- for (nb <- findDescendants[NamePipe]) {
- if (declOptions.contains(nb.link)) {
- nb.patchNode(declOptions(nb.link))
- }
- }
-
- for (child <- allChildren) {
- child match {
- case _: Documentation => ()
- case _: PipeInfo => ()
- case _ =>
- child.graphEdges(runtime, pipeline)
- }
- }
-
- try {
- runtime.init(this)
- } catch {
- case _: JafplLoopDetected =>
- throw XProcException.xsLoop("???", "???", location)
- }
- runtime
- }
-
- private def manifold: Manifold = {
- val inputMap = mutable.HashMap.empty[String,PortCardinality]
- for (input <- inputs) {
- if (input.sequence) {
- inputMap.put(input.port, PortCardinality.ZERO_OR_MORE)
- } else {
- if (input.defaultInputs.nonEmpty) {
- // Allow it to be empty, the default will provide something
- inputMap.put(input.port, new PortCardinality(0, 1))
- } else {
- inputMap.put(input.port, PortCardinality.EXACTLY_ONE)
- }
- }
- }
- val outputMap = mutable.HashMap.empty[String,PortCardinality]
- for (output <- outputs) {
- if (output.sequence) {
- outputMap.put(output.port, PortCardinality.ZERO_OR_MORE)
- } else {
- outputMap.put(output.port, PortCardinality.EXACTLY_ONE)
- }
- }
- new Manifold(new PortSpecification(inputMap.toMap), new PortSpecification(outputMap.toMap))
- }
-
- def patchOptions(bindings: Map[QName,XProcVarValue]): Unit = {
- for (child <- children[DeclareOption]) {
- child.runtimeBindings(bindings)
- }
- }
-
- def xdump: XdmNode = {
- val xml = new ElaboratedPipeline(config)
- xdump(xml)
- val doc = xml.endPipeline()
- doc.get
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startPipeline(tumble_id, stepName, stepType, version, psvi_required, xpath_version, exclude_inline_prefixes, _visibility)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- }
-
- override def toString: String = {
- if (stepType.isDefined) {
- s"p:declare-step ${stepType.get} $stepName $uid"
- } else {
- s"p:declare-step {anon} $stepName $uid"
- }
- }
-
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Document.scala b/src/main/scala/com/xmlcalabash/model/xml/Document.scala
deleted file mode 100644
index 7ffc478..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Document.scala
+++ /dev/null
@@ -1,128 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.{DocumentRequest, DocumentResponse}
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XProcVtExpression
-import com.xmlcalabash.runtime.params.DocumentLoaderParams
-import com.xmlcalabash.util.MediaType
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-import java.net.URI
-import scala.collection.mutable
-
-class Document(override val config: XMLCalabash) extends DataSource(config) {
- private var _href = "UNINITIALIZED"
- private var _hrefAvt = List.empty[String]
- private var _contentType = Option.empty[MediaType]
- private var _documentProperties = Option.empty[String]
- private var _parameters = Option.empty[String]
- private var _context_provided = false
-
- def this(copy: Document) = {
- this(copy.config)
- depends ++= copy.depends
- _href = copy._href
- _hrefAvt = copy._hrefAvt
- _contentType = copy._contentType
- _documentProperties = copy._documentProperties
- _parameters = copy._parameters
- _context_provided = copy._context_provided
- _staticContext = copy._staticContext
- }
-
- def href: String = _href
- protected[model] def hrefAvt: List[String] = _hrefAvt
- protected[model] def href_=(href: String): Unit = {
- _href = href
- _hrefAvt = staticContext.parseAvt(_href)
- }
- def content_type: Option[MediaType] = _contentType
- def document_properties: Option[String] = _documentProperties
- def parameters: Option[String] = _parameters
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (!attributes.contains(XProcConstants._href)) {
- throw XProcException.xsMissingRequiredAttribute(XProcConstants._href, location)
- }
-
- _href = attr(XProcConstants._href).get
- _hrefAvt = staticContext.parseAvt(_href)
- _contentType = MediaType.parse(attr(XProcConstants._content_type))
- _documentProperties = attr(XProcConstants._document_properties)
- _parameters = attr(XProcConstants._parameters)
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- // nop
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- val env = environment()
- if (allChildren.isEmpty && env.defaultReadablePort.isDefined && !parent.get.isInstanceOf[DeclareInput]) {
- _context_provided = true
- val pipe = new Pipe(config)
- pipe.link = env.defaultReadablePort.get
- pipe.port = env.defaultReadablePort.get.port
- pipe.step = env.defaultReadablePort.get.step.stepName
- addChild(pipe)
- }
-
- val bindings = mutable.HashSet.empty[QName]
- bindings ++= staticContext.findVariableRefsInAvt(_hrefAvt)
- bindings ++= staticContext.findVariableRefsInString(_documentProperties)
- bindings ++= staticContext.findVariableRefsInString(_parameters)
-
- for (ref <- bindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw new RuntimeException("Reference to undefined variable")
- }
- if (!binding.get.static) {
- val pipe = new NamePipe(config, ref, binding.get.tumble_id, binding.get)
- addChild(pipe)
- }
- }
- }
-
- override protected[model] def normalizeToPipes(): Unit = {
- val context = staticContext.withStatics(inScopeStatics)
- val params = new DocumentLoaderParams(_hrefAvt, _contentType, _parameters, _documentProperties, _context_provided, context)
- normalizeDataSourceToPipes(XProcConstants.cx_document_loader, params)
- }
-
- def loadDocument(): DocumentRequest = {
- val context = staticContext.withStatics(inScopeStatics)
- val expr = new XProcVtExpression(context, _hrefAvt, true)
- val msg = config.expressionEvaluator.value(expr, List(), inScopeStatics, None)
- val href = if (context.baseURI.isDefined) {
- context.baseURI.get.resolve(msg.item.toString)
- } else {
- new URI(msg.item.toString)
- }
- new DocumentRequest(href, _contentType, location)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startDocument(tumble_id, _href)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endDocument()
- }
-
- override def toString: String = {
- s"p:document $href"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Documentation.scala b/src/main/scala/com/xmlcalabash/model/xml/Documentation.scala
deleted file mode 100644
index c6ba5ff..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Documentation.scala
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{Axis, QName, XdmNode, XdmNodeKind}
-
-class Documentation(override val config: XMLCalabash, val docs: XdmNode) extends Artifact(config) {
- override protected[model] def validateStructure(): Unit = {
- // nop
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- var root = Option.empty[QName]
- val iter = docs.axisIterator(Axis.CHILD)
- while (root.isEmpty && iter.hasNext) {
- val item = iter.next()
- if (item.getNodeKind == XdmNodeKind.ELEMENT) {
- root = Some(item.getNodeName)
- }
- }
-
- xml.startDocumentation(tumble_id, root)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endDocumentation()
- }
-
- override def toString: String = {
- s"p:documentation $tumble_id"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Empty.scala b/src/main/scala/com/xmlcalabash/model/xml/Empty.scala
deleted file mode 100644
index 2da70c2..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Empty.scala
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.runtime.params.EmptyLoaderParams
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-
-class Empty(override val config: XMLCalabash) extends DataSource(config) {
-
- def this(copy: Empty) = {
- this(copy.config)
- depends ++= copy.depends
- }
-
- override protected[model] def normalizeToPipes(): Unit = {
- val params = new EmptyLoaderParams(staticContext)
- normalizeDataSourceToPipes(XProcConstants.cx_empty_loader, params)
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- // nop
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- // nop
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startEmpty(tumble_id)
- xml.endEmpty()
- }
-
- override def toString: String = {
- if (tumble_id.startsWith("!syn")) {
- s"p:empty"
- } else {
- s"p:empty $tumble_id"
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Environment.scala b/src/main/scala/com/xmlcalabash/model/xml/Environment.scala
deleted file mode 100644
index 4ca6830..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Environment.scala
+++ /dev/null
@@ -1,439 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.sun.org.apache.xpath.internal.XPathProcessorException
-import com.xmlcalabash.exceptions.XProcException
-import net.sf.saxon.s9api.QName
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-// You'd think that the environment just inherited down the tree as you went,
-// but it's more complicated than that. What's in and out of the environment
-// is quite different inside a compound step than it is outside. So instead of
-// trying to finesse it as we walk down the tree, this object computes the
-// environment for the step identified in its constructor.
-//
-// N.B. Do not cache this; you'd think it would be a performance enhancement,
-// but dealing with select expressions sometimes adds filters and that
-// changes the default readable port.
-
-object Environment {
- def newEnvironment(step: Artifact): Environment = {
- // Walk "up" the tree until we find a valid starting point
- step match {
- case _: Step => ()
- case _: DeclareInput => ()
- case _: DeclareOutput => ()
- case _: Variable => ()
- case _: DeclareOption => ()
- case _: WithOption => ()
- case _ =>
- return Environment.newEnvironment(step.parent.get)
- }
-
- val env = new Environment()
-
- var ancestors = ListBuffer.empty[Artifact] // includes self for compound steps
-
- // If we start at a DeclareOption, then we're really in a pipeline that we're
- // running for a step. Stop at the first atomic step ancestor...
- val stopAtStep = step.isInstanceOf[DeclareOption]
- var scopeRoot = Option.empty[Artifact]
-
- var pptr: Option[Artifact] = Some(step)
- while (pptr.isDefined) {
- var next = pptr.get.parent
- pptr.get match {
- case variable: Variable =>
- ancestors.insert(0, variable)
- case option: DeclareOption =>
- ancestors.insert(0, option)
- case input: DeclareInput =>
- ancestors.insert(0, input)
- case output: DeclareOutput =>
- ancestors.insert(0, output)
- case library: Library =>
- ancestors.insert(0, library)
- case option: WithOption =>
- ancestors.insert(0, option)
- case step: Step =>
- if (stopAtStep && scopeRoot.isEmpty) {
- scopeRoot = Some(step)
- }
- ancestors.insert(0, step)
- case _ => ()
- }
-
- pptr = next
- }
-
- if (scopeRoot.isDefined) {
- // The scopeRoot is really the top of the ancestor list, *except* that any static
- // variables declared "above" this root are in scope.
- val senv = Environment.newEnvironment(scopeRoot.get)
- for (opt <- senv.staticVariables) {
- if (opt ne step) {
- env.addVariable(opt)
- }
- }
-
- while (ancestors.nonEmpty && (ancestors.head ne scopeRoot.get)) {
- ancestors = ancestors.drop(1)
- }
- if (ancestors.isEmpty) {
- throw XProcException.xiThisCantHappen("Failed to find scopeRoot in ancestors when creating new environment", None)
- }
- ancestors = ancestors.drop(1)
- }
-
- walk(env, ancestors.toList)
- }
-
- private def walk(env: Environment, ancestors: List[Artifact]): Environment = {
- if (ancestors.isEmpty) {
- return env
- }
-
- val head = ancestors.head
- val next = ancestors.tail.headOption
-
- if (head.isInstanceOf[DeclareStep]) {
- // The only thing that survives passing into a declare step are statics
- env.clearSteps()
- env.clearPorts()
- env.clearDynamicVariables()
- }
-
- head match {
- case wi: WithOption =>
- if (wi.parent.isDefined) {
- wi.parent.get match {
- case atom: AtomicStep =>
- val stepsig = atom.declaration(atom.stepType)
- // This appears not to be defined for built-in steps. I'm not sure why, but none
- // of them have options that depend on preceding options, so I'm not worrying
- // about it today.
- if (stepsig.get.declaration.isDefined) {
- val decl = stepsig.get.declaration.get
- var found = false
- for (opt <- decl.children[DeclareOption]) {
- found = found || opt.name == wi.name
- if (!found) {
- env.addVariable(opt)
- }
- }
- }
- env
- case _ =>
- throw XProcException.xiThisCantHappen("Parent of p:with-option is not an atomic step?", None)
- }
- } else {
- throw XProcException.xiThisCantHappen("Parent of p:with-option is undefined?", None)
- }
- case lib: Library =>
- env.defaultReadablePort = None
-
- // Libraries are a special case, they aren't in the children of the container
- if (next.get.isInstanceOf[Library]) {
- return walk(env, ancestors.tail)
- }
-
- // Now walk down to the next ancestor
- for (child <- lib.allChildren) {
- if (next.get == child) {
- return walk(env, ancestors.tail)
- }
-
- child match {
- case variable: Variable =>
- env.addVariable(variable)
- case option: DeclareOption =>
- env.addVariable(option)
- case childstep: Step =>
- if (next.isDefined && next.get == childstep) {
- return walk(env, ancestors.tail)
- }
- case _ => ()
- }
- }
-
- // If we fell off the bottom of this loop, something has gone terribly wrong
- throw new RuntimeException("Fell off ancestor list in computing environment")
-
- case xstep: Container =>
- // DeclareStep is special; if it's the last ancestor, then it's the root of
- // the pipeline and that doesn't get to read its own inputs. If it's not
- // the root, then we're setting up the environment for one of its contained
- // steps.
-
- // This step is in the environment
- env.addStep(xstep)
-
- // Its options are in-scope
- for (option <- xstep.children[DeclareOption]) {
- env.addVariable(option)
- }
-
- if (next.isDefined) {
- // Its inputs are readable
- for (port <- xstep.children[DeclareInput]) {
- if (port.primary) {
- env.defaultReadablePort = port
- }
- env.addPort(port)
- }
-
- xstep match {
- // Choose, when, etc., aren't ordinary container steps
- case container: Choose => ()
- //case container: When => ()
- //case container: Otherwise => ()
- case container: Try => ()
- //case container: Catch => ()
- //case container: Finally => ()
- case _ =>
- // Entering a declare-step resets the default readable port
- if (xstep.isInstanceOf[DeclareStep]) {
- env.defaultReadablePort = xstep.primaryInput
- }
-
- // The outputs of all contained steps are mutually readable
- for (child <- xstep.allChildren) {
- child match {
- case decl: DeclareStep => () // these don't count
- case childstep: Container =>
- if (next.isDefined && next.get == childstep) {
- // ignore this one, we'll be diving down into it
- } else {
- env.addStep(childstep)
- for (port <- childstep.children[WithOutput]) {
- env.addPort(port)
- }
- }
- case childstep: Step =>
- // Yes, this can add the output of the step who's environment
- // we're computing to the list of readable ports. Doing so
- // is a loop, it'll be caught elsewhere.
- env.addStep(childstep)
- for (port <- childstep.children[WithOutput]) {
- env.addPort(port)
- }
- case _ => ()
- }
- }
- }
- }
-
- if (next.isEmpty) {
- return env
- }
-
- // Libraries are a special case, they aren't in the children of the container
- if (next.get.isInstanceOf[Library]) {
- return walk(env, ancestors.tail)
- }
-
- // Now walk down to the next ancestor, calculating the drp
- for (child <- xstep.allChildren) {
- if (next.get eq child) {
- return walk(env, ancestors.tail)
- }
- xstep match {
- // The children of choose and try aren't ordinary children
- case _: Choose => ()
- case _: Try => ()
- case _ =>
- child match {
- case option: DeclareOption =>
- env.addVariable(option)
- case variable: Variable =>
- env.addVariable(variable)
- case _: DeclareStep =>
- ()
- case childstep: Step =>
- env.defaultReadablePort = childstep.primaryOutput
- case _ =>
- ()
- }
- }
- }
-
- next.get match {
- case _: DeclareStep =>
- return walk(env, ancestors.tail)
- case _ =>
- // If we fell off the bottom of this loop, something has gone terribly wrong
- throw new RuntimeException("Fell off ancestor list in container")
- }
-
- case step: AtomicStep =>
- if (next.isEmpty) {
- // This is us.
- return env
- } else {
- return walk(env, ancestors.tail)
- }
-
- case option: DeclareOption =>
- if (next.isEmpty) {
- // This is us. Any preceding options are in-scope
- val parent = option.parent.get
- var found = false
- for (child <- parent.allChildren) {
- child match {
- case opt: DeclareOption =>
- found = found || (opt eq option)
- if (!found) {
- env.addVariable(opt)
- }
- case _ => ()
- }
- }
- return env
- }
-
- // If we got here, something has gone terribly wrong
- throw new RuntimeException("Option with children?")
-
- case variable: Variable =>
- if (next.isEmpty) {
- // This is us.
- return env
- }
-
- // If we got here, something has gone terribly wrong
- throw new RuntimeException("Variable with children?")
-
- case input: DeclareInput =>
- if (next.isEmpty) {
- // This is us.
- return env
- }
-
- // If we got here, something has gone terribly wrong
- throw new RuntimeException("Input with children?")
-
- case output: DeclareOutput =>
- if (next.isEmpty) {
- // This is us.
- // The default readable port from here is the primary output
- // of the last step in the pipeline, if the last step has
- // a primary output.
- var lastchild = Option.empty[Step]
- for (child <- output.parent.get.children[Step]) {
- lastchild = Some(child)
- }
- if (lastchild.isDefined) {
- env.defaultReadablePort = lastchild.get.primaryOutput
- }
-
- return env
- }
-
- // If we got here, something has gone terribly wrong
- throw new RuntimeException("Output with children?")
-
- case _ => throw new RuntimeException(s"Unexpected in list of ancestors: $head")
- }
- }
-}
-
-class Environment private() {
- private val _inScopeSteps = mutable.HashMap.empty[String, Step]
- private val _inScopePorts = mutable.HashMap.empty[String, Port]
- private val _inScopeVariables = mutable.HashMap.empty[QName,NameBinding]
- private var _defaultReadablePort = Option.empty[Port]
-
- def defaultReadablePort: Option[Port] = _defaultReadablePort
- protected[xml] def defaultReadablePort_=(port: Port): Unit = {
- defaultReadablePort = Some(port)
- }
- protected[xml] def defaultReadablePort_=(port: Option[Port]): Unit = {
- _defaultReadablePort = port
- }
- private def clearSteps(): Unit = {
- _inScopeSteps.clear()
- }
-
- def addStep(step: Step): Unit = {
- addName(step.stepName, step)
- }
-
- private def clearPorts(): Unit = {
- _inScopePorts.clear()
- }
-
- def addPort(port: Port): Unit = {
- val name = port.parent.get match {
- case step: Step => step.stepName
- case _ =>
- throw new RuntimeException("Parent of port isn't a step?")
- }
- if (!name.startsWith("!")) {
- _inScopePorts.put(s"$name/${port.port}", port)
- }
- }
-
- private def addName(name: String, step: Step): Unit = {
- if (_inScopeSteps.contains(name)) {
- throw XProcException.xsDuplicateStepName(name, None)
- }
- _inScopeSteps.put(name, step)
- }
-
- def addVariable(binding: NameBinding): Unit = {
- _inScopeVariables.put(binding.name, binding)
- }
-
- private def clearVariables(): Unit = {
- _inScopeVariables.clear()
- }
-
- private def clearDynamicVariables(): Unit = {
- val statics = mutable.HashMap.empty[QName,NameBinding]
- for (static <- staticVariables) {
- statics.put(static.name, static)
- }
- _inScopeVariables.clear()
- _inScopeVariables ++= statics
- }
-
-
- def step(name: String): Option[Step] = _inScopeSteps.get(name)
-
- def port(stepName: String, portName: String): Option[Port] = {
- if (step(stepName).isDefined) {
- _inScopePorts.get(s"$stepName/$portName")
- } else {
- None
- }
- }
-
- def variable(name: QName): Option[NameBinding] = {
- if (_inScopeVariables.contains(name)) {
- _inScopeVariables.get(name)
- } else {
- None
- }
- }
-
- def variables: List[NameBinding] = {
- val lb = ListBuffer.empty[NameBinding]
- for ((name, variable) <- _inScopeVariables) {
- if (!variable.static) {
- lb += variable
- }
- }
- lb.toList
- }
-
- def staticVariables: List[NameBinding] = {
- val lb = ListBuffer.empty[NameBinding]
- for ((name, variable) <- _inScopeVariables) {
- if (variable.static) {
- lb += variable
- }
- }
- lb.toList
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Finally.scala b/src/main/scala/com/xmlcalabash/model/xml/Finally.scala
deleted file mode 100644
index 8ec2ee7..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Finally.scala
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{Node, TryCatchStart}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-class Finally(override val config: XMLCalabash) extends Container(config) with NamedArtifact {
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- val input = new DeclareInput(config)
- input.port = "error"
- input.sequence = true
- input.primary = true
- addChild(input, firstChild)
-
- makeContainerStructureExplicit()
-
- for (output <- children[DeclareOutput]) {
- if (output.primary) {
- throw XProcException.xsPrimaryOutputOnFinally(output.port, location)
- }
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[TryCatchStart]
- val node = start.addFinally(stepName, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- for (child <- allChildren) {
- child.graphEdges(runtime, _graphNode.get)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startFinally(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endFinally()
- }
-
- override def toString: String = {
- s"p:finally $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/ForContainer.scala b/src/main/scala/com/xmlcalabash/model/xml/ForContainer.scala
deleted file mode 100644
index f0bc3a7..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/ForContainer.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-
-abstract class ForContainer(override val config: XMLCalabash) extends Container(config) {
-
- protected def setupLoopInputs(primary: Option[Boolean]): Unit = {
- val first = firstChild
-
- if (primary.isDefined) {
- if (firstWithInput.isEmpty) {
- val input = new WithInput(config)
- input.port = "source"
- input.primary = primary.get
- addChild(input, first)
- }
- }
-
- val current = new DeclareInput(config)
- current.port = "current"
- current.primary = true
- addChild(current, first)
-
- makeContainerStructureExplicit()
- }
-
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/ForEach.scala b/src/main/scala/com/xmlcalabash/model/xml/ForEach.scala
deleted file mode 100644
index 2d3e065..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/ForEach.scala
+++ /dev/null
@@ -1,84 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ChooseStart, ContainerStart, Node}
-import com.jafpl.steps.Manifold
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.MediaType
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-class ForEach(override val config: XMLCalabash) extends Container(config) with NamedArtifact {
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- val fc = firstChild
- if (firstWithInput.isEmpty) {
- val winput = new WithInput(config)
- winput.port = "source"
- winput.primary = true
- winput.sequence = true
- addChild(winput, fc)
- } else {
- val wi = firstWithInput.get
- wi.primary = true
- wi.sequence = true
- if (wi.port == "") {
- wi.port = "source"
- }
- }
-
- val input = new DeclareInput(config)
- input.port = "current"
- input.primary = true
- input.contentTypes = List(MediaType.OCTET_STREAM)
- addChild(input, fc)
-
- makeContainerStructureExplicit()
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[ContainerStart]
- val node = start.addForEach(stepName, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- val winput = firstWithInput
- if (winput.isDefined) {
- for (pipe <- winput.get.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, "source")
- }
- }
-
- for (output <- children[DeclareOutput]) {
- for (pipe <- output.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, output.port)
- }
- }
-
- graphChildEdges(runtime)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startForEach(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endForEach()
- }
-
- override def toString: String = {
- s"p:for-each $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/ForLoop.scala b/src/main/scala/com/xmlcalabash/model/xml/ForLoop.scala
deleted file mode 100644
index 4913379..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/ForLoop.scala
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.jafpl.steps.Manifold
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-class ForLoop(override val config: XMLCalabash) extends ForContainer(config) with NamedArtifact {
- private val _from = new QName("from")
- private val _to = new QName("to")
- private val _by = new QName("by")
- private var countFrom = 1L
- private var countTo = 0L
- private var countBy = 1L
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(_from)) {
- countFrom = attr(_from).get.toLong
- }
-
- if (attributes.contains(_to)) {
- countTo = attr(_to).get.toLong
- } else {
- throw new RuntimeException("to is required")
- }
-
- if (attributes.contains(_by)) {
- countBy = attr(_by).get.toLong
- }
-
- if (countBy == 0) {
- throw XProcException.xiAttempToCountByZero(location)
- }
-
- if ((countFrom > countTo && countBy > 0)
- || (countFrom < countTo && countBy < 0)) {
- logger.debug(s"Counting from $countFrom to $countTo by $countBy will never execute")
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- setupLoopInputs(None)
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[ContainerStart]
- val node = start.addFor(stepName, countFrom, countTo, countBy, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- val winput = firstWithInput
- if (winput.isDefined) {
- for (pipe <- winput.get.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, "source")
- }
- }
-
- for (output <- children[DeclareOutput]) {
- for (pipe <- output.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, output.port)
- }
- }
-
- graphChildEdges(runtime)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startForLoop(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endForLoop()
- }
-
- override def toString: String = {
- s"cx:until $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/ForUntil.scala b/src/main/scala/com/xmlcalabash/model/xml/ForUntil.scala
deleted file mode 100644
index 01dd7f8..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/ForUntil.scala
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.jafpl.steps.Manifold
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.XmlItemComparator
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-class ForUntil(override val config: XMLCalabash) extends ForContainer(config) with NamedArtifact {
- private val _max_iterations = new QName("max-iterations")
- private val _comparator = new QName("comparator")
- private val _return = new QName("return")
- private var maxIterations: Long = -1
- private var comparator: String = "deep-equal($a,$b)"
- private var returnSet: String = "last"
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(_max_iterations)) {
- maxIterations = attr(_max_iterations).get.toString.toInt
- }
-
- if (attributes.contains(_comparator)) {
- comparator = attr(_comparator).get.toString
- }
-
- if (attributes.contains(_return)) {
- returnSet = attr(_return).get.toString
- if (returnSet != "last" && returnSet != "all") {
- throw new RuntimeException("return must be last or all")
- }
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- setupLoopInputs(Some(true))
-
- // Now let's consider what making the container structure explicit has done.
- // If either the primary output or the test output haven't been specified,
- // make sure it defaults to the primary output of the last step.
- // Note: there will always be at least one!
- var testOutput = Option.empty[DeclareOutput]
- var resultOutput = Option.empty[DeclareOutput]
- for (child <- children[DeclareOutput]) {
- if (child.port == "test") {
- testOutput = Some(child)
- child.primary = false
- } else {
- if (child.primary) {
- resultOutput = Some(child)
- }
- }
- }
-
- if (resultOutput.isEmpty) {
- setDefaultOutput("#result", true)
- }
-
- if (testOutput.isEmpty) {
- setDefaultOutput("test", false)
- }
- }
-
- private def setDefaultOutput(port: String, primary: Boolean): Unit = {
- var lastStep = Option.empty[Step]
-
- for (child <- allChildren) {
- child match {
- case atomic: AtomicStep => lastStep = Some(atomic)
- case compound: Container => lastStep = Some(compound)
- case _ => ()
- }
- }
-
- val output = new DeclareOutput(config)
- output.port = port
- output.primary = primary
- output.sequence = true
-
- val pipe = new Pipe(config)
- pipe.step = lastStep.get.stepName
- pipe.port = lastStep.get.primaryOutput.get.port
- pipe.link = lastStep.get.primaryOutput.get
- output.addChild(pipe)
-
- if (firstChild.isDefined) {
- addChild(output, firstChild.get)
- } else {
- addChild(output)
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val compare = new XmlItemComparator(config, comparator, maxIterations, this)
- val start = parent.asInstanceOf[ContainerStart]
- val node = start.addUntil(compare, returnSet=="all", stepName, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- val winput = firstWithInput
- if (winput.isDefined) {
- for (pipe <- winput.get.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, "source")
- }
- }
-
- for (output <- children[DeclareOutput]) {
- for (pipe <- output.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, output.port)
- }
- }
-
- graphChildEdges(runtime)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startForUntil(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endForUntil()
- }
-
- override def toString: String = {
- s"cx:until $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/ForWhile.scala b/src/main/scala/com/xmlcalabash/model/xml/ForWhile.scala
deleted file mode 100644
index 15ffd9f..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/ForWhile.scala
+++ /dev/null
@@ -1,142 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.jafpl.steps.Manifold
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.XmlItemTester
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-class ForWhile(override val config: XMLCalabash) extends ForContainer(config) with NamedArtifact {
- private val _max_iterations = new QName("max-iterations")
- private val _return = new QName("return")
- private var maxIterations: Long = -1
- private var test: String = ""
- private var returnSet: String = "last"
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(_max_iterations)) {
- maxIterations = attr(_max_iterations).get.toInt
- }
-
- if (attributes.contains(XProcConstants._test)) {
- test = attr(XProcConstants._test).get
- } else {
- throw new RuntimeException("test is required")
- }
-
- if (attributes.contains(_return)) {
- returnSet = attr(_return).get
- if (returnSet != "last" && returnSet != "all") {
- throw new RuntimeException("return must be last or all")
- }
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- setupLoopInputs(Some(true))
-
- // Now let's consider what making the container structure explicit has done.
- // If either the primary output or the test output haven't been specified,
- // make sure it defaults to the primary output of the last step.
- // Note: there will always be at least one!
- var testOutput = Option.empty[DeclareOutput]
- var resultOutput = Option.empty[DeclareOutput]
- for (child <- children[DeclareOutput]) {
- if (child.port == "test") {
- testOutput = Some(child)
- child.primary = false
- } else {
- if (child.primary) {
- resultOutput = Some(child)
- }
- }
- }
-
- if (resultOutput.isEmpty) {
- setDefaultOutput("#result", true)
- }
-
- if (testOutput.isEmpty) {
- setDefaultOutput("test", false)
- }
- }
-
- private def setDefaultOutput(port: String, primary: Boolean): Unit = {
- var lastStep = Option.empty[Step]
-
- for (child <- allChildren) {
- child match {
- case atomic: AtomicStep => lastStep = Some(atomic)
- case compound: Container => lastStep = Some(compound)
- case _ => ()
- }
- }
-
- val output = new DeclareOutput(config)
- output.port = port
- output.primary = primary
- output.sequence = true
-
- val pipe = new Pipe(config)
- pipe.step = lastStep.get.stepName
- pipe.port = lastStep.get.primaryOutput.get.port
- pipe.link = lastStep.get.primaryOutput.get
- output.addChild(pipe)
-
- if (firstChild.isDefined) {
- addChild(output, firstChild.get)
- } else {
- addChild(output)
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val tester = new XmlItemTester(config, test, maxIterations, this)
- val start = parent.asInstanceOf[ContainerStart]
- val node = start.addWhile(tester, returnSet=="all", stepName, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- val winput = firstWithInput
- if (winput.isDefined) {
- for (pipe <- winput.get.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, "source")
- }
- }
-
- for (output <- children[DeclareOutput]) {
- for (pipe <- output.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, output.port)
- }
- }
-
- graphChildEdges(runtime)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startForWhile(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endForWhile()
- }
-
- override def toString: String = {
- s"cx:while $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Group.scala b/src/main/scala/com/xmlcalabash/model/xml/Group.scala
deleted file mode 100644
index d1fd8bd..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Group.scala
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node, TryCatchStart}
-import com.jafpl.steps.Manifold
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-class Group(override val config: XMLCalabash) extends Container(config) with NamedArtifact {
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- makeContainerStructureExplicit()
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parNode: Node): Unit = {
- if (parent.get.isInstanceOf[Try]) {
- val start = parNode.asInstanceOf[TryCatchStart]
- val node = start.addTry(stepName, containerManifold)
- _graphNode = Some(node)
- } else {
- val start = parNode.asInstanceOf[ContainerStart]
-
- val node = start.addGroup(stepName, containerManifold)
- _graphNode = Some(node)
- }
-
- super.graphNodes(runtime, parNode)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- for (output <- children[DeclareOutput]) {
- for (pipe <- output.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, output.port)
- }
- }
-
- graphChildEdges(runtime)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startGroup(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endGroup()
- }
-
- override def toString: String = {
- s"p:group $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/If.scala b/src/main/scala/com/xmlcalabash/model/xml/If.scala
deleted file mode 100644
index b1661e3..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/If.scala
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ChooseStart, ContainerStart, Node}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import net.sf.saxon.s9api.XdmNode
-
-import scala.collection.mutable
-
-class If(override val config: XMLCalabash) extends Choose(config) {
- override protected[model] def makeStructureExplicit(): Unit = {
- // p:if is purely syntactic sugar for a p:choose...so let's implement it that way
-
- val choose = new Choose(config)
- choose._name = Some(stepName)
- choose._depends = _depends
- choose.p_if = true
- for (child <- children[WithInput]) {
- choose.addChild(child)
- }
- val when = new When(config)
- when.test = ifexpr.get
- if (ifcoll.isDefined) {
- when.collection = ifcoll.get
- }
- for (child <- allChildren) {
- child match {
- case _: WithInput => ()
- case _ =>
- when.addChild(child)
- }
- }
- choose.addChild(when)
- parent.get.replaceChild(choose, this)
-
- choose.makeStructureExplicit()
- }
-
- override def toString: String = {
- s"p:choose $stepName (was p:if)"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Import.scala b/src/main/scala/com/xmlcalabash/model/xml/Import.scala
deleted file mode 100644
index 7c6c48b..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Import.scala
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-
-import java.net.URI
-import com.xmlcalabash.config.DocumentRequest
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.util.{MediaType, S9Api}
-import net.sf.saxon.s9api.XdmNode
-
-class Import(override val config: XMLCalabash) extends Artifact(config) {
- private var _href: URI = _
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._href)) {
- _href = node.getBaseURI.resolve(attr(XProcConstants._href).get)
- } else {
- throw XProcException.xsMissingRequiredAttribute(XProcConstants._href, location)
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- protected[model] def loadImports(): DeclContainer = {
- if (config.importedURI(_href).isEmpty) {
- var root: XdmNode = null
- try {
- val request = new DocumentRequest(_href, MediaType.XML)
- val response = config.documentManager.parse(request)
- root = S9Api.documentElement(response.value.asInstanceOf[XdmNode]).get
- } catch {
- case ex: XProcException =>
- if (ex.code == XProcException.err_xd0011) {
- throw XProcException.xsImportFailed(_href, location)
- } else {
- throw ex
- }
- }
-
- val parser = new Parser(config)
-
- val declContainer = root.getNodeName match {
- case XProcConstants.p_declare_step =>
- parser.parseDeclareStep(root)
- case XProcConstants.p_library =>
- parser.parseLibrary(root)
- case _ =>
- throw XProcException.xsBadImport(root.getNodeName, location)
- }
-
- declContainer match {
- case decl: DeclareStep =>
- if (decl.stepType.isEmpty) {
- throw XProcException.xsStepTypeRequired(location)
- }
- case _ => ()
- }
-
- config.addImportedURI(_href, declContainer)
-
- declContainer.loadImports()
- declContainer
- } else {
- config.importedURI(_href).get
- }
- }
-
- override def toString: String = {
- s"p:import $tumble_id ${_href}"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/ImportFunctions.scala b/src/main/scala/com/xmlcalabash/model/xml/ImportFunctions.scala
deleted file mode 100644
index 41f2561..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/ImportFunctions.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-
-import java.net.URI
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import net.sf.saxon.s9api.XdmNode
-
-class ImportFunctions(override val config: XMLCalabash) extends Artifact(config) {
- private var _href: URI = _
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._href)) {
- _href = node.getBaseURI.resolve(attr(XProcConstants._href).get)
- } else {
- throw XProcException.xsMissingRequiredAttribute(XProcConstants._href, location)
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override def toString: String = {
- s"p:import-functions $tumble_id ${_href}"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Inline.scala b/src/main/scala/com/xmlcalabash/model/xml/Inline.scala
deleted file mode 100644
index bbedb8f..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Inline.scala
+++ /dev/null
@@ -1,287 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.messages.Message
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
-import com.xmlcalabash.runtime.StaticContext
-import com.xmlcalabash.runtime.params.InlineLoaderParams
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import com.xmlcalabash.util.{MediaType, S9Api}
-import net.sf.saxon.s9api.{Axis, QName, XdmNode, XdmNodeKind}
-
-import java.io.UnsupportedEncodingException
-import java.util.Base64
-import scala.collection.mutable
-
-class Inline(override val config: XMLCalabash, srcNode: XdmNode, implied: Boolean) extends DataSource(config) {
- private var _node: XdmNode = srcNode
- private var _contentType = Option.empty[MediaType]
- private var _documentProperties = Option.empty[String]
- private var _encoding = Option.empty[String]
- private var _exclude_inline_prefixes = Option.empty[String]
- private var _context_provided = false
- private val nameBindings = mutable.HashSet.empty[QName]
- private val _statics = mutable.HashMap.empty[String, Message]
- private var _expandText = false
-
- if (srcNode.getNodeKind != XdmNodeKind.DOCUMENT) {
- throw XProcException.xiThisCantHappen(s"Attempt to create p:inline from something that isn't a document node: ${srcNode}")
- }
-
- def this(config: XMLCalabash, srcNode: XdmNode) = {
- this(config, srcNode, false)
- }
-
- def this(copy: Inline) = {
- this(copy.config, copy._node, true) // Why can't I put copy._synthetic in the constructor?
- _synthetic = copy._synthetic
- depends ++= copy.depends
- _contentType = copy._contentType
- _documentProperties = copy._documentProperties
- _encoding = copy._encoding
- _exclude_inline_prefixes = copy._exclude_inline_prefixes
- _context_provided = copy._context_provided
- nameBindings ++= copy.nameBindings
- _inScopeDynamics = copy._inScopeDynamics
- _inScopeStatics = copy._inScopeStatics
- _expandText = copy._expandText
- staticContext = copy.staticContext
- }
-
- def node: XdmNode = _node
- def contentType: Option[MediaType] = _contentType
- def documentProperties: Option[String] = _documentProperties
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- _synthetic = implied
-
- if (node.getNodeName == XProcConstants.p_inline) {
- _contentType = MediaType.parse(attributes.get(XProcConstants._content_type))
- if (_contentType.isDefined) {
- _contentType.get.assertValid
- }
- _documentProperties = attr(XProcConstants._document_properties)
- _encoding = attr(XProcConstants._encoding)
- _exclude_inline_prefixes = attr(XProcConstants._exclude_inline_prefixes)
-
- if (_contentType.isDefined && _encoding.isEmpty) {
- if (_contentType.get.charset.isDefined) {
- throw XProcException.xdCharsetWithoutEncoding(attributes(XProcConstants._content_type), location)
- }
- }
-
- if (_encoding.isDefined) {
- if (_encoding.get == "base64") {
- if (_contentType.isDefined && _contentType.get.markupContentType) {
- throw XProcException.xdCannotEncodeMarkup(_encoding.get, _contentType.get, location)
- }
-
- val charset = if (_contentType.isDefined) {
- _contentType.get.charset.getOrElse("UTF-8")
- } else {
- "UTF-8"
- }
-
- // Can I trust you?
- try {
- // Apparently the decode won't acept newlines in the data...
- val str = srcNode.getStringValue.trim.replace("\n", "")
- val bytes = Base64.getDecoder.decode(str)
- if (contentType.isDefined && contentType.get.textContentType) {
- new String(bytes, charset)
- }
- } catch {
- case ex: IllegalArgumentException =>
- throw XProcException.xdIncorrectEncoding(_encoding.get, location)
- case _: UnsupportedEncodingException =>
- throw XProcException.xdUnsupportedCharset(charset, location)
- case ex: Exception =>
- throw ex
- }
- } else {
- throw XProcException.xsUnsupportedEncoding(_encoding.get, location)
- }
- }
-
- if (_contentType.isEmpty) {
- _contentType = Some(MediaType.XML)
- }
-
- _expandText = inScopeExpandText(node)
- }
- }
-
- def expandText: Boolean = _expandText
- def encoding: Option[String] = _encoding
-
- def excludeURIs: Set[String] = {
- if (_exclude_inline_prefixes.isDefined) {
- S9Api.urisForPrefixes(node, _exclude_inline_prefixes.get.split("\\s+").toSet)
- } else {
- Set()
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- val env = environment()
- val drp = env.defaultReadablePort
-
- if (allChildren.isEmpty && drp.isDefined && !parent.get.isInstanceOf[DeclareInput]) {
- _context_provided = true
- val pipe = new Pipe(config)
- pipe.port = drp.get.port
- pipe.step = drp.get.step.stepName
- pipe.link = drp.get
- addChild(pipe)
- }
-
- val ctype = if (_contentType.isDefined) {
- _contentType.get
- } else {
- MediaType.XML
- }
-
- // Is this sufficient? We don't want to attempt to parse AVTs in JSON!
- if (expand_text && (ctype.markupContentType || ctype.textContentType)) {
- findVariablesInTVT(_node, expand_text)
- for (ref <- nameBindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw XProcException.xsNoBindingInExpression(ref, location)
- }
- if (binding.get.static) {
- _statics.put(ref.getClarkName, binding.get.staticValue.get)
- } else {
- val pipe = new NamePipe(config, ref, binding.get.tumble_id, binding.get)
- addChild(pipe)
- }
- }
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- // Trim the leading and trailing whitespace
- if (synthetic) {
- var children = S9Api.axis(srcNode, Axis.CHILD)
- if (children.nonEmpty && children.head.getNodeKind == XdmNodeKind.TEXT && children.head.getStringValue.trim == "") {
- children = children.tail
- }
- if (children.nonEmpty && children.last.getNodeKind == XdmNodeKind.TEXT && children.last.getStringValue.trim == "") {
- children = children.dropRight(1)
- }
- val tree = new SaxonTreeBuilder(config)
- tree.startDocument(srcNode.getBaseURI)
- for (child <- children) {
- tree.addSubtree(child)
- }
- tree.endDocument()
- _node = tree.result
- _contentType = Some(MediaType.XML)
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- // nop
- }
-
- def inlineContext: StaticContext = {
- staticContext.withStatics(inScopeStatics)
- }
-
- override protected[model] def normalizeToPipes(): Unit = {
- val params = new InlineLoaderParams(_node, _contentType, _documentProperties, _encoding, _exclude_inline_prefixes, expand_text, _context_provided, inlineContext)
- normalizeDataSourceToPipes(XProcConstants.cx_inline_loader, params)
- }
-
- private def findVariablesInTVT(node: XdmNode, expandText: Boolean): Unit = {
- node.getNodeKind match {
- case XdmNodeKind.DOCUMENT =>
- val iter = node.axisIterator(Axis.CHILD)
- while (iter.hasNext) {
- val child = iter.next()
- findVariablesInTVT(child, expandText)
- }
- case XdmNodeKind.ELEMENT =>
- var newExpand = expandText
- var iter = node.axisIterator(Axis.ATTRIBUTE)
- while (iter.hasNext) {
- var discardAttribute = false
- val attr = iter.next()
- if (attr.getNodeName == XProcConstants.p_inline_expand_text) {
- if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- throw XProcException.xsInlineExpandTextNotAllowed(location)
- }
- discardAttribute = true
- newExpand = attr.getStringValue == "true"
- }
- if (attr.getNodeName == XProcConstants._inline_expand_text) {
- if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- discardAttribute = true
- newExpand = attr.getStringValue == "true"
- }
- }
- if (!discardAttribute) {
- if (expandText) {
- findVariablesInString(attr.getStringValue)
- }
- }
- }
- iter = node.axisIterator(Axis.CHILD)
- while (iter.hasNext) {
- val child = iter.next()
- findVariablesInTVT(child, newExpand)
- }
- case XdmNodeKind.TEXT =>
- val str = node.getStringValue
- if (expandText && str.contains("{")) {
- findVariablesInNodes(str)
- }
- case _ => ()
- }
- }
-
- private def findVariablesInString(text: String): Unit = {
- val expr = staticContext.parseAvt(text)
- for (name <- ValueParser.findVariableRefsInAvt(config, expr)) {
- nameBindings += name
- }
- }
-
- private def findVariablesInNodes(text: String): Unit = {
- val expr = staticContext.parseAvt(text)
- for (name <- ValueParser.findVariableRefsInAvt(config, expr)) {
- nameBindings += name
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- var root = Option.empty[QName]
- val iter = _node.axisIterator(Axis.CHILD)
- while (root.isEmpty && iter.hasNext) {
- val item = iter.next()
- if (item.getNodeKind == XdmNodeKind.ELEMENT) {
- root = Some(item.getNodeName)
- }
- }
-
- xml.startInline(tumble_id, root)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endInline()
- }
-
- override def toString: String = {
- val root = S9Api.documentElement(_node)
- if (root.isDefined) {
- s"p:inline <${root.get.getNodeName}> $tumble_id"
- } else {
- s"p:inline <> $tumble_id"
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Library.scala b/src/main/scala/com/xmlcalabash/model/xml/Library.scala
deleted file mode 100644
index a2fa0db..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Library.scala
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import net.sf.saxon.s9api.{XdmNode, XdmNodeKind}
-
-class Library(override val config: XMLCalabash) extends DeclContainer(config) {
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._version)) {
- val vstr = attr(XProcConstants._version).get
- try {
- _version = Some(vstr.toDouble)
- } catch {
- case ex: NumberFormatException =>
- throw XProcException.xsBadVersion(vstr, location)
- }
- if (_version.get != 3.0) {
- throw XProcException.xsInvalidVersion(_version.get, location)
- }
- }
- if (_version.isEmpty) {
- if (node.getParent.getNodeKind == XdmNodeKind.DOCUMENT) {
- throw XProcException.xsVersionRequired(location)
- }
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- for (child <- allChildren) {
- child match {
- case decl: DeclareStep =>
- decl.makeStructureExplicit()
- case variable: Variable =>
- variable.makeStructureExplicit()
- case function: DeclareFunction =>
- function.makeStructureExplicit()
- case _ =>
- throw new RuntimeException(s"Invalid element: $child")
- }
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- for (child <- allChildren) {
- child match {
- case decl: DeclareStep =>
- decl.makeBindingsExplicit()
- case variable: Variable =>
- if (!variable.static) {
- throw new RuntimeException("Variables in libraries must be static")
- }
- variable.makeBindingsExplicit()
- case function: DeclareFunction =>
- ()
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child.validateStructure()
- child match {
- case variable: Variable =>
- if (!variable.static) {
- throw new RuntimeException("Only static variables are allowed in a p:library")
- }
- case step: DeclareStep => ()
- case function: DeclareFunction => ()
- case _ =>
- throw new RuntimeException(s"Child not allowed: $child")
- }
- }
- }
-
- override def toString: String = {
- s"p:library $location $tumble_id"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/NameBinding.scala b/src/main/scala/com/xmlcalabash/model/xml/NameBinding.scala
deleted file mode 100644
index e49a634..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/NameBinding.scala
+++ /dev/null
@@ -1,417 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.jafpl.messages.Message
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.DocumentRequest
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.messages.{XdmNodeItemMessage, XdmValueItemMessage}
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.{XMLCalabashRuntime, XProcMetadata, XProcVtExpression, XProcXPathExpression}
-import com.xmlcalabash.util.{TvtExpander, TypeUtils}
-import net.sf.saxon.ma.map.MapType
-import net.sf.saxon.om.StructuredQName
-import net.sf.saxon.s9api.{QName, SaxonApiException, SequenceType, XdmAtomicValue, XdmNode}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class NameBinding(override val config: XMLCalabash) extends Artifact(config) {
- protected var _name: QName = _
- protected var _declaredType = Option.empty[SequenceType]
- protected var _as = Option.empty[SequenceType]
- protected var _values = Option.empty[String]
- protected var _static = Option.empty[Boolean]
- protected var _required = Option.empty[Boolean]
- protected var _select = Option.empty[String]
- protected var _avt = Option.empty[String]
- protected var _visibility = Option.empty[String]
- protected var _allowedValues = Option.empty[List[XdmAtomicValue]]
- protected var _staticValue = Option.empty[XdmValueItemMessage]
- protected var _dependentNameBindings: ListBuffer[NamePipe] = ListBuffer.empty[NamePipe]
- protected var collection = false
-
- private var _qnameKeys = false
- private var resolvedStatically = false
- private val structuredQName = new StructuredQName("xs", XProcConstants.ns_xs, "QName")
- protected val depends: ListBuffer[String] = ListBuffer.empty[String]
-
- protected var _href = Option.empty[String]
- protected var _pipe = Option.empty[String]
-
- private var typeUtils: TypeUtils = _
-
- def name: QName = _name
- def as: Option[SequenceType] = _as
- protected[model] def as_=(seq: SequenceType): Unit = {
- _as = Some(seq)
- }
-
- def declaredType: SequenceType = {
- if (_declaredType.isEmpty) {
- _declaredType = staticContext.parseSequenceType(Some("xs:string"))
- }
- _declaredType.get
- }
- protected[model] def declaredType_=(decltype: SequenceType): Unit = {
- _declaredType = Some(decltype)
- }
-
- def values: Option[String] = _values
- def required: Boolean = _required.getOrElse(false)
- def select: Option[String] = _select
- protected[model] def select_=(select: String): Unit = {
- _select = Some(select)
- }
- def avt: Option[String] = _avt
- protected[model] def avt_=(expr: String): Unit = {
- if (select.isDefined) {
- throw new RuntimeException("Cannot define AVT if select is present")
- }
- _avt = Some(expr)
- }
-
- def static: Boolean = _static.getOrElse(false)
- def visibility: String = _visibility.getOrElse("public")
- def allowedValues: Option[List[XdmAtomicValue]] = _allowedValues
- def qnameKeys: Boolean = _qnameKeys
- protected[xml] def qnameKeys_=(keys: Boolean): Unit = {
- _qnameKeys = keys
- }
-
- protected[xmlcalabash] def staticValue: Option[XdmValueItemMessage] = _staticValue
- protected[model] def staticValue_=(value: XdmValueItemMessage): Unit = {
- _staticValue = Some(value)
- }
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- typeUtils = new TypeUtils(config, staticContext)
-
- if (attributes.contains(XProcConstants._name)) {
- val name = attr(XProcConstants._name).get
- try {
- _name = staticContext.parseQName(name)
- } catch {
- case ex: XProcException =>
- if (ex.code == XProcException.err_xd0015) {
- throw XProcException.xsOptionUndeclaredNamespace(name, ex.location)
- } else {
- throw ex
- }
- case ex: Throwable =>
- throw ex
- }
- this match {
- case _: WithOption =>
- () // This would be ok if the step has an option declared in the p: namespace
- case _ =>
- if (_name.getNamespaceURI == XProcConstants.ns_p) {
- throw XProcException.xsOptionInXProcNamespace(_name, location)
- }
- }
- } else {
- throw XProcException.xsMissingRequiredAttribute(XProcConstants._name, location)
- }
-
- val seqTypeString = attr(XProcConstants._as)
- _as = typeUtils.parseSequenceType(seqTypeString)
-
- if (as.isDefined)
- as.get.getUnderlyingSequenceType.getPrimaryType match {
- case map: MapType =>
- if (map.getKeyType.getPrimitiveItemType.getTypeName == structuredQName) {
- // We have to lie about the type of maps with QName keys because we're
- // going to allow users to put strings in there.
- _qnameKeys = true
- _as = Some(typeUtils.parseFakeMapSequenceType(seqTypeString.get))
- }
- case _ => ()
- }
-
- _values = attr(XProcConstants._values)
-
- if (_values.isDefined) {
- val exprEval = config.expressionEvaluator.newInstance()
- val expr = new XProcXPathExpression(staticContext, _values.get, None, None, None)
- val value = exprEval.value(expr, List(), Map(), None)
- val iter = value.item.iterator()
- val allowed = ListBuffer.empty[XdmAtomicValue]
- while (iter.hasNext) {
- val token = iter.next()
- token match {
- case atom: XdmAtomicValue =>
- allowed += atom
- case _ =>
- throw XProcException.xsInvalidValues(_values.get, location)
- }
- }
- _allowedValues = Some(allowed.toList)
- }
-
- _static = staticContext.parseBoolean(attr(XProcConstants._static))
- _required = staticContext.parseBoolean(attr(XProcConstants._required))
- _select = attr(XProcConstants._select)
- _visibility = attr(XProcConstants._visibility)
-
- if (_required.isDefined && _required.get && _select.isDefined) {
- throw XProcException.xsRequiredAndDefaulted(_name, location)
- }
-
- val _collection = attr(XProcConstants._collection)
- if (_collection.isDefined) {
- val coll = _collection.get
- if (List("1", "true", "yes").contains(coll)) {
- collection = true
- } else {
- if (List("0", "false", "no").contains(coll)) {
- collection = false
- } else {
- throw XProcException.xsBadTypeValue(coll, "xs:boolean", location)
- }
- }
- }
-
- _href = attr(XProcConstants._href)
- _pipe = attr(XProcConstants._pipe)
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- if (_href.isDefined && _pipe.isDefined) {
- throw XProcException.xsPipeAndHref(location)
- }
-
- if (_href.isDefined && allChildren.nonEmpty) {
- throw XProcException.xsHrefAndOtherSources(location)
- }
-
- if (_pipe.isDefined && allChildren.nonEmpty) {
- throw XProcException.xsPipeAndOtherSources(location)
- }
-
- if (_href.isDefined) {
- val doc = new Document(config)
- doc.href = _href.get
- addChild(doc)
- }
-
- if (_pipe.isDefined) {
- var port = Option.empty[String]
- var step = Option.empty[String]
- if (_pipe.get.contains("@")) {
- val re = "(.*)@(.*)".r
- _pipe.get match {
- case re(pname, sname) =>
- if (pname != "") {
- port = Some(pname)
- }
- step = Some(sname)
- }
- } else {
- if (_pipe.get.trim() != "") {
- port = _pipe
- }
- }
-
- val pipe = new Pipe(config)
- if (step.isDefined) {
- pipe.step = step.get
- }
- if (port.isDefined) {
- pipe.port = port.get
- }
- addChild(pipe)
- }
-
- for (child <- allChildren) {
- child.makeStructureExplicit()
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- // If the ancestor of a data source has a dependency, so does the data source
- var p = parent
- while (p.isDefined) {
- p.get match {
- case step: Step =>
- for (name <- step.depends) {
- if (!depends.contains(name)) {
- depends += name
- }
- }
- case _ =>
- ()
- }
- p = p.get.parent
- }
-
- var exprString = ""
- val usesContextItem = try {
- if (_avt.isDefined) {
- exprString = _avt.toString
- val avt = staticContext.parseAvt(_avt.get)
- staticContext.dependsOnContextAvt(avt)
- } else {
- exprString = _select.getOrElse("()")
- staticContext.dependsOnContextString(_select.getOrElse("()"))
- }
- } catch {
- case ex: Throwable =>
- throw XProcException.xsStaticErrorInExpression(exprString, ex.getMessage, location)
- }
-
- val ds = ListBuffer.empty[DataSource]
- for (child <- allChildren) {
- child match {
- case pipe: Pipe =>
- if (static) {
- throw XProcException.xsStaticRefsContext("Static variables cannot refer to the context.", location)
- }
- ds += pipe
- case source: DataSource =>
- ds += source
- case _ =>
- throw new RuntimeException(s"Unexpected child: $child")
- }
- }
-
- val env = environment()
- val drp = env.defaultReadablePort
-
- if (ds.isEmpty) {
- if (drp.isDefined && !static && usesContextItem) {
- val winput = new WithInput(config)
- winput.port = "source"
- addChild(winput)
- val pipe = new Pipe(config)
- pipe.port = drp.get.port
- pipe.step = drp.get.step.stepName
- pipe.link = drp.get
- winput.addChild(pipe)
- }
- } else {
- removeChildren()
- val winput = new WithInput(config)
- winput.port = "source"
- addChild(winput)
- for (source <- ds) {
- winput.addChild(source)
- }
- }
-
- if (static) {
- val context = ListBuffer.empty[Message]
- for (child <- ds) {
- child match {
- case inline: Inline =>
- val exprContext = staticContext.withStatics(inScopeStatics)
- val expander = new TvtExpander(config, None, exprContext, Map(), location)
-
- try {
- // FIXME: what should the defaults for initiallyExpand and exludeURIs be?
- val result = expander.expand(inline.node, true, Set())
- context += new XdmNodeItemMessage(result, XProcMetadata.XML, inline.staticContext)
- } catch {
- case ex: XProcException =>
- if (ex.code == XProcException.err_xs0107 && ex.details(1).toString.contains("Undeclared variable")) {
- throw XProcException.xsStaticRefsNonStaticStr(ex.details.head.toString, location)
- }
- throw ex
- }
-
- case doc: Document =>
- for (ref <- staticContext.findVariableRefsInAvt(doc.hrefAvt)) {
- if (!inScopeStatics.contains(ref.getClarkName)) {
- throw XProcException.xsStaticRefsNonStatic(ref, location)
- }
- }
- val econtext = staticContext.withStatics(inScopeStatics)
- val exprEval = config.expressionEvaluator.newInstance()
- val vtexpr = new XProcVtExpression(econtext, doc.hrefAvt)
- val value = exprEval.singletonValue(vtexpr, List(), inScopeStatics, None)
- val parts = ListBuffer.empty[String]
- val viter = value.item.iterator()
- while (viter.hasNext) {
- parts += viter.next().getStringValue
- }
-
- val href = staticContext.baseURI.get.resolve(parts.mkString(""))
-
- val request = new DocumentRequest(href, None, location)
- val response = config.documentManager.parse(request)
- context += new XdmValueItemMessage(response.value, XProcMetadata.XML, doc.staticContext)
- case empty: Empty =>
- ()
- }
- }
-
- val expr = new XProcXPathExpression(staticContext, select.get)
- val exeval = config.expressionEvaluator.newInstance()
- val msg = exeval.value(expr, context.toList, inScopeStatics, None)
- staticValue = msg
- resolvedStatically = true
- }
-
- if (_select.isDefined) {
- val bindings = mutable.HashSet.empty[QName]
- bindings ++= staticContext.findVariableRefsInString(_select.get)
- if (bindings.isEmpty) {
- try {
- val depends = staticContext.dependsOnContextString(_select.get)
- // FIXME: if depends is false, we can resolve this statically
- } catch {
- case sax: SaxonApiException => ()
- }
- } else {
- for (ref <- bindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw XProcException.xsNoBindingInExpression(ref, location);
- }
- if (!binding.get.static) {
- val pipe = new NamePipe(config, ref, binding.get.tumble_id, binding.get)
- _dependentNameBindings += pipe
- addChild(pipe)
- }
- }
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- var hasEmpty = false
- var hasNonEmpty = false
-
- for (child <- allChildren) {
- child.validateStructure()
- child match {
- case winput: WithInput => ()
- case npipe: NamePipe => ()
- case _ =>
- throw new RuntimeException(s"Invalid content in $this")
- }
- }
-
- if (hasEmpty && hasNonEmpty) {
- throw XProcException.xsNoSiblingsOnEmpty(location)
- }
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- if (resolvedStatically) {
- return
- }
-
- for (child <- allChildren) {
- child.graphEdges(runtime, _graphNode.get)
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/NamePipe.scala b/src/main/scala/com/xmlcalabash/model/xml/NamePipe.scala
deleted file mode 100644
index eea0ef4..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/NamePipe.scala
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-class NamePipe(override val config: XMLCalabash, val name: QName, val step: String, val link: NameBinding) extends Artifact(config) {
- private var _node = link._graphNode
-
- override def parse(node: XdmNode): Unit = {
- throw new RuntimeException("This is a purely synthetic element")
- }
-
- override protected[model] def validateStructure(): Unit = {
- if (allChildren.nonEmpty) {
- throw new RuntimeException(s"Invalid content in $this")
- }
- }
-
- // Name pipes for options that depend on the values of preceding options in the same
- // declaration get constructed before the corresponding with-option nodes for evaluating
- // them. This patch hack let's the node be updated later.
- protected[model] def patchNode(node: Node): Unit = {
- _node = Some(node)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parNode: Node): Unit = {
- if (_node.isEmpty) {
- _node = link._graphNode
- }
-
- if (_node.isEmpty) {
- throw XProcException.xiThisCantHappen(s"Attempt to link from non-existant graph node for ${link}", None)
- }
-
- val toNode = parNode
- val toPort = "#bindings"
- val fromNode = _node.get
- val fromPort = "result"
- runtime.graph.addEdge(fromNode, fromPort, toNode, toPort)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startNamePipe(tumble_id, step)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endNamePipe()
- }
-
- override def toString: String = {
- s"p:name-pipe $name $step"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/NamedArtifact.scala b/src/main/scala/com/xmlcalabash/model/xml/NamedArtifact.scala
deleted file mode 100644
index 23e6a96..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/NamedArtifact.scala
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.xmlcalabash.model.xml
-
-trait NamedArtifact {
- def stepName: String
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Otherwise.scala b/src/main/scala/com/xmlcalabash/model/xml/Otherwise.scala
deleted file mode 100644
index f000a99..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Otherwise.scala
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XProcXPathExpression
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-class Otherwise(override val config: XMLCalabash) extends ChooseBranch(config) {
-
- // An otherwise is not atomic, even if it contains only synthetic children
- override def atomic: Boolean = false
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- _collection = false
- _test = "true()"
- testExpr = new XProcXPathExpression(staticContext, _test)
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- if (synthetic && environment().defaultReadablePort.isEmpty) {
- val ident = children[Step].head
- val winput = ident.firstWithInput
- val empty = new Empty(config)
- winput.get.addChild(empty)
- }
-
- super.makeBindingsExplicit()
- }
-
- override protected[model] def normalizeToPipes(): Unit = {
- super.normalizeToPipes()
-
- // There's never any need for a with-input on p:otherwise
- val winput = firstWithInput
- if (winput.isDefined) {
- removeChild(winput.get)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startWhen(tumble_id, stepName, "true()")
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endWhen()
- }
-
- override def toString: String = {
- s"p:otherwise $stepName"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Parser.scala b/src/main/scala/com/xmlcalabash/model/xml/Parser.scala
deleted file mode 100644
index 64beb4c..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Parser.scala
+++ /dev/null
@@ -1,614 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.DocumentRequest
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
-import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcLocation, XProcXPathExpression}
-import com.xmlcalabash.util.{MediaType, S9Api}
-import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap, FingerprintedQName}
-import net.sf.saxon.s9api.{Axis, QName, XdmNode, XdmNodeKind}
-import org.xml.sax.InputSource
-
-import java.net.URI
-import javax.xml.transform.sax.SAXSource
-import scala.collection.mutable.ListBuffer
-
-class Parser(config: XMLCalabash) {
- private var _builtInSteps = config.builtinSteps
- private var matcher: ProcessMatch = _
-
- // Someone has to load the default steps; feels like here's as good a place as any.
- // It doesn't feel like something the user should have to do explicitly.
- if (_builtInSteps.isEmpty) {
- config.builtinSteps = init_builtins()
- _builtInSteps = config.builtinSteps
- }
-
- def builtInSteps: List[Library] = _builtInSteps
-
- def loadLibrary(root: XdmNode): Library = {
- val node = stripUseWhen(root)
- if (node.getNodeKind != XdmNodeKind.ELEMENT || node.getNodeName != XProcConstants.p_library) {
- throw new RuntimeException(s"Not a library: ${root.getNodeName}")
- }
-
- parseLibrary(node)
- }
-
- def loadDeclareStep(uri: URI): DeclareStep = {
- val request = new DocumentRequest(uri, MediaType.XML)
- val response = config.documentManager.parse(request)
- loadDeclareStep(response.value.asInstanceOf[XdmNode])
- }
-
- def loadDeclareStep(root: XdmNode): DeclareStep = {
- val node = stripUseWhen(root)
- if (node.getNodeKind != XdmNodeKind.ELEMENT || node.getNodeName != XProcConstants.p_declare_step) {
- throw new RuntimeException(s"Not a declare-step: ${root.getNodeName}")
- }
-
- val decl = parseDeclareStep(node)
-
- if (node.getParent.getNodeKind == XdmNodeKind.DOCUMENT) {
- decl.loadImports()
- decl.updateInScopeSteps()
- decl.parseDeclarationSignature()
-
- val toElaborate = ListBuffer.empty[DeclContainer]
- for (lib <- config.builtinSteps) {
- toElaborate += lib
- }
- for (uri <- config.importedURIs) {
- toElaborate += config.importedURI(uri).get
- }
- toElaborate += decl
-
- for (root <- toElaborate) {
- root.makeStructureExplicit()
- root.makeBindingsExplicit()
- root.validateStructure()
- }
- }
-
- config.clearImportedURIs()
-
- decl
- }
-
- private def stripUseWhen(root: XdmNode): XdmNode = {
- val staticContext = new StaticContext(config, None)
- matcher = new ProcessMatch(config, new ProcessUseWhen(staticContext), staticContext)
- matcher.process(root, "*")
- val node = matcher.result
- if (node.getNodeKind == XdmNodeKind.DOCUMENT) {
- S9Api.documentElement(node).get
- } else {
- node
- }
- }
-
- private def parseContainer[T <: Container](node: XdmNode, container: T): T = {
- container match {
- case _: DeclareStep =>
- parseContainer(node, container, List(), List(XProcConstants.p_with_input))
- case _: If =>
- parseContainer(node, container, List(), List(XProcConstants.p_input, XProcConstants.p_declare_step,
- XProcConstants.p_import, XProcConstants.p_import_functions))
- case _: Choose =>
- parseContainer(node, container, List(XProcConstants.p_with_input, XProcConstants.p_when, XProcConstants.p_otherwise), List())
- case _ =>
- parseContainer(node, container, List(), List(XProcConstants.p_input, XProcConstants.p_declare_step,
- XProcConstants.p_import, XProcConstants.p_import_functions))
- }
- }
-
- private def parseContainer[T <: Container](node: XdmNode, container: T, allowed: List[QName], forbidden: List[QName]): T = {
- container.parse(node)
-
- for (child <- children(node)) {
- child.getNodeKind match {
- case XdmNodeKind.ELEMENT =>
- if (allowed.nonEmpty && !allowed.contains(child.getNodeName)) {
- throw new RuntimeException(s"Not allowed here: ${child.getNodeName}")
- }
- if (forbidden.nonEmpty && forbidden.contains(child.getNodeName)) {
- throw new RuntimeException(s"Not allowed here: ${child.getNodeName}")
- }
-
- child.getNodeName match {
- case XProcConstants.p_input =>
- container.addChild(parseInput(child))
- case XProcConstants.p_with_input =>
- container.addChild(parseWithInput(child))
- case XProcConstants.p_output =>
- container.addChild(parseOutput(child))
- case XProcConstants.p_option =>
- container.addChild(parseOption(child))
- case XProcConstants.p_variable =>
- container.addChild(parseVariable(child))
- case XProcConstants.p_declare_step =>
- container.addChild(parseDeclareStep(child))
- case XProcConstants.p_choose =>
- container.addChild(parseChoose(child))
- case XProcConstants.p_when =>
- container.addChild(parseWhen(child))
- case XProcConstants.p_otherwise =>
- container.addChild(parseOtherwise(child))
- case XProcConstants.p_if =>
- container.addChild(parseIf(child))
- case XProcConstants.p_for_each =>
- container.addChild(parseForEach(child))
- case XProcConstants.cx_until =>
- container.addChild(parseUntil(child))
- case XProcConstants.cx_while =>
- container.addChild(parseWhile(child))
- case XProcConstants.cx_loop =>
- container.addChild(parseLoop(child))
- case XProcConstants.p_viewport =>
- container.addChild(parseViewport(child))
- case XProcConstants.p_group =>
- container.addChild(parseGroup(child))
- case XProcConstants.p_try =>
- container.addChild(parseTry(child))
- case XProcConstants.p_catch =>
- container.addChild(parseCatch(child))
- case XProcConstants.p_finally =>
- container.addChild(parseFinally(child))
- case XProcConstants.p_import =>
- container.addChild(parseImport(child))
- case XProcConstants.p_import_functions =>
- container.addChild(parseImportFunctions(child))
- case XProcConstants.p_documentation =>
- container.addChild(parseDocumentation(child))
- case XProcConstants.p_pipeinfo =>
- container.addChild(parsePipeInfo(child))
- case _ =>
- // If we don't recognize it, assume it's an atomic step
- container.addChild(parseAtomicStep(child))
- }
- case XdmNodeKind.COMMENT => ()
- case XdmNodeKind.PROCESSING_INSTRUCTION => ()
- case XdmNodeKind.TEXT =>
- throw XProcException.xsTextNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- case _ =>
- throw new RuntimeException(s"Unexpected element kind: ${child.getNodeKind}")
- }
- }
-
- container
- }
-
- protected[model] def parseLibrary(node: XdmNode): Library = {
- val library = new Library(config)
- library.parse(node)
-
- for (child <- children(node)) {
- child.getNodeKind match {
- case XdmNodeKind.ELEMENT =>
- child.getNodeName match {
- case XProcConstants.p_declare_step =>
- library.addChild(parseDeclareStep(child))
- case XProcConstants.p_variable =>
- library.addChild(parseVariable(child))
- case XProcConstants.p_function =>
- library.addChild(parseFunction(child))
- case XProcConstants.p_import =>
- library.addChild(parseImport(child))
- case XProcConstants.p_import_functions =>
- library.addChild(parseImportFunctions(child))
- case XProcConstants.p_documentation =>
- library.addChild(parseDocumentation(child))
- case XProcConstants.p_pipeinfo =>
- library.addChild(parsePipeInfo(child))
- case _ =>
- throw new RuntimeException(s"Unexpected element: ${child.getNodeName}")
- }
- case XdmNodeKind.COMMENT => ()
- case XdmNodeKind.PROCESSING_INSTRUCTION => ()
- case XdmNodeKind.TEXT =>
- throw XProcException.xsTextNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- case _ =>
- throw new RuntimeException(s"Unexpected element kind: ${child.getNodeKind}")
- }
- }
-
- library
- }
-
- protected[model] def parseDeclareStep(node: XdmNode): DeclareStep = {
- val decl = new DeclareStep(config)
- parseContainer(node, decl)
- decl
- }
-
- private def parseImport(node: XdmNode): Import = {
- parseNoChildrenAllowed(node, new Import(config))
- }
-
- private def parseImportFunctions(node: XdmNode): ImportFunctions = {
- parseNoChildrenAllowed(node, new ImportFunctions(config))
- }
-
- private def parseChoose(node: XdmNode): Choose = {
- parseContainer(node, new Choose(config))
- }
-
- private def parseWhen(node: XdmNode): When = {
- parseContainer(node, new When(config))
- }
-
- private def parseOtherwise(node: XdmNode): Otherwise = {
- parseContainer(node, new Otherwise(config))
- }
-
- private def parseIf(node: XdmNode): If = {
- parseContainer(node, new If(config))
- }
-
- private def parseForEach(node: XdmNode): ForEach = {
- parseContainer(node, new ForEach(config))
- }
-
- private def parseUntil(node: XdmNode): ForUntil = {
- parseContainer(node, new ForUntil(config))
- }
-
- private def parseWhile(node: XdmNode): ForWhile = {
- parseContainer(node, new ForWhile(config))
- }
-
- private def parseLoop(node: XdmNode): ForLoop = {
- parseContainer(node, new ForLoop(config))
- }
-
- private def parseViewport(node: XdmNode): Viewport = {
- parseContainer(node, new Viewport(config))
- }
-
- private def parseGroup(node: XdmNode): Group = {
- parseContainer(node, new Group(config))
- }
-
- private def parseTry(node: XdmNode): Try = {
- parseContainer(node, new Try(config))
- }
-
- private def parseCatch(node: XdmNode): Catch = {
- parseContainer(node, new Catch(config))
- }
-
- private def parseFinally(node: XdmNode): Finally = {
- parseContainer(node, new Finally(config))
- }
-
- private def parseConnections[T <: Artifact](node: XdmNode, art: T): T = {
- art match {
- case input: DeclareInput =>
- parseConnections(node, art, List(XProcConstants.p_pipe))
- case _ =>
- parseConnections(node, art, List())
- }
- }
-
- private def parseConnections[T <: Artifact](node: XdmNode, art: T, forbidden: List[QName]): T = {
- art.parse(node)
-
- var implicitInline = false
- for (child <- children(node)) {
- if (forbidden.nonEmpty && forbidden.contains(child.getNodeName)) {
- throw XProcException.xsElementNotAllowed(child.getNodeName, Some(new XProcLocation(child)))
- }
-
- child.getNodeKind match {
- case XdmNodeKind.ELEMENT =>
- child.getNodeName match {
- case XProcConstants.p_empty =>
- art.addChild(parseEmpty(child))
- case XProcConstants.p_document =>
- art.addChild(parseDocument(child))
- case XProcConstants.p_inline =>
- art.addChild(parseInline(child))
- case XProcConstants.p_pipe =>
- art.addChild(parsePipe(child))
- case XProcConstants.p_documentation =>
- art.addChild(parseDocumentation(child))
- case XProcConstants.p_pipeinfo =>
- art.addChild(parsePipeInfo(child))
- case _ =>
- implicitInline = true
- art.addChild(parseSyntheticInline(child))
- }
- case XdmNodeKind.COMMENT =>
- if (implicitInline) {
- throw XProcException.xsInlineCommentNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- }
- case XdmNodeKind.PROCESSING_INSTRUCTION =>
- if (implicitInline) {
- throw XProcException.xsInlinePiNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- }
- case XdmNodeKind.TEXT =>
- if (implicitInline) {
- throw XProcException.xsInlineTextNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- }
- throw XProcException.xsTextNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- case _ =>
- throw new RuntimeException(s"Unexpected element kind: ${child.getNodeKind}")
- }
- }
-
- art
- }
-
- private def parseInput(node: XdmNode): DeclareInput = {
- parseConnections(node, new DeclareInput(config))
- }
-
- private def parseOutput(node: XdmNode): DeclareOutput = {
- parseConnections(node, new DeclareOutput(config))
- }
-
- private def parseOption(node: XdmNode): DeclareOption = {
- parseNoChildrenAllowed(node, new DeclareOption(config))
- }
-
- private def parseVariable(node: XdmNode): Variable = {
- parseConnections(node, new Variable(config))
- }
-
- private def parseAtomicStep(node: XdmNode): AtomicStep = {
- val atomic = new AtomicStep(config)
- atomic.parse(node)
-
- for (child <- children(node)) {
- child.getNodeKind match {
- case XdmNodeKind.ELEMENT =>
- child.getNodeName match {
- case XProcConstants.p_with_input =>
- atomic.addChild(parseWithInput(child))
- case XProcConstants.p_with_option =>
- atomic.addChild(parseWithOption(child))
- case XProcConstants.p_documentation =>
- atomic.addChild(parseDocumentation(child))
- case XProcConstants.p_pipeinfo =>
- atomic.addChild(parsePipeInfo(child))
- case _ =>
- throw new RuntimeException(s"Unexpected element: ${child.getNodeName}")
- }
- case XdmNodeKind.COMMENT => ()
- case XdmNodeKind.PROCESSING_INSTRUCTION => ()
- case XdmNodeKind.TEXT =>
- throw XProcException.xsTextNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- case _ =>
- throw new RuntimeException(s"Unexpected element kind: ${child.getNodeKind}")
- }
- }
-
- atomic
- }
-
- private def parseWithInput(node: XdmNode): WithInput = {
- parseConnections(node, new WithInput(config))
- }
-
- private def parseWithOption(node: XdmNode): WithOption = {
- parseConnections(node, new WithOption(config))
- }
-
- private def parseNoChildrenAllowed[T <: Artifact](node: XdmNode, empty: T): T = {
- empty.parse(node)
-
- for (child <- children(node)) {
- child.getNodeKind match {
- case XdmNodeKind.ELEMENT =>
- child.getNodeName match {
- case XProcConstants.p_documentation =>
- empty.addChild(parseDocumentation(child))
- case XProcConstants.p_pipeinfo =>
- empty.addChild(parsePipeInfo(child))
- case _ =>
- throw new RuntimeException("no elements allowed")
- }
- case XdmNodeKind.COMMENT => ()
- case XdmNodeKind.PROCESSING_INSTRUCTION => ()
- case XdmNodeKind.TEXT =>
- throw XProcException.xsTextNotAllowed(child.getStringValue.trim(), Some(new XProcLocation(child)))
- case _ =>
- throw new RuntimeException(s"Unexpected element kind: ${child.getNodeKind}")
- }
- }
-
- empty
- }
-
- private def parseEmpty(node: XdmNode): Empty = {
- parseNoChildrenAllowed(node, new Empty(config))
- }
-
- private def parseDocument(node: XdmNode): Document = {
- parseNoChildrenAllowed(node, new Document(config))
- }
-
- private def parsePipe(node: XdmNode): Pipe = {
- parseNoChildrenAllowed(node, new Pipe(config))
- }
-
- private def parseFunction(node: XdmNode): DeclareFunction = {
- parseNoChildrenAllowed(node, new DeclareFunction(config))
- }
-
- private def parseInline(node: XdmNode): Inline = {
- val builder = new SaxonTreeBuilder(config)
- builder.startDocument(node.getBaseURI)
- val iter = node.axisIterator(Axis.CHILD)
- while (iter.hasNext) {
- val child = iter.next()
- builder.addSubtree(child)
- }
- builder.endDocument()
-
- val excludeURIs = S9Api.excludeInlineURIs(node)
- val inlineNode = S9Api.removeNamespaces(config, builder.result, excludeURIs, true)
-
- val inline = new Inline(config, inlineNode)
- inline.parse(node)
- inline
- }
-
- private def parseSyntheticInline(node: XdmNode): Inline = {
- val builder = new SaxonTreeBuilder(config)
- builder.startDocument(node.getBaseURI)
- builder.addSubtree(node)
- builder.endDocument()
-
- val excludeURIs = S9Api.excludeInlineURIs(node)
- val inlineNode = S9Api.removeNamespaces(config, builder.result, excludeURIs, true)
-
- if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- throw new RuntimeException("Elements in the XProc namespace cannot be synthetic inlines")
- }
-
- val inline = new Inline(config, inlineNode, true)
- inline.parse(node)
- inline
- }
-
- private def parseDocumentation(node: XdmNode): Documentation = {
- val builder = new SaxonTreeBuilder(config)
- builder.startDocument(node.getBaseURI)
- val iter = node.axisIterator(Axis.CHILD)
- while (iter.hasNext) {
- val child = iter.next()
- builder.addSubtree(child)
- }
- builder.endDocument()
-
- val docs = new Documentation(config, builder.result)
- docs.parse(node)
- docs
- }
-
- private def parsePipeInfo(node: XdmNode): PipeInfo = {
- val builder = new SaxonTreeBuilder(config)
- builder.startDocument(node.getBaseURI)
- val iter = node.axisIterator(Axis.CHILD)
- while (iter.hasNext) {
- val child = iter.next()
- builder.addSubtree(child)
- }
- builder.endDocument()
-
- val info = new PipeInfo(config, builder.result)
- info.parse(node)
- info
- }
-
- def init_builtins(): List[Library] = {
- val libs = ListBuffer.empty[Library]
- val xpls = getClass.getClassLoader.getResources("com.xmlcalabash.library.xpl")
- while (xpls.hasMoreElements) {
- val xpl = xpls.nextElement()
- val xmlbuilder = config.processor.newDocumentBuilder()
- val stream = xpl.openStream()
- val source = new SAXSource(new InputSource(stream))
- xmlbuilder.setDTDValidation(false)
- xmlbuilder.setLineNumbering(true)
- val libnode = xmlbuilder.build(source)
- val library = loadLibrary(libnode)
- library.loadImports()
- library.updateInScopeSteps()
- libs += library
- }
- libs.toList
- }
-
- // ============================================================================
-
- private def children(node: XdmNode): List[XdmNode] = {
- children(node, true)
- }
-
- private def children(node: XdmNode, ignoreWS: Boolean): List[XdmNode] = {
- val list = ListBuffer.empty[XdmNode]
- val iter = node.axisIterator(Axis.CHILD)
- while (iter.hasNext) {
- val child = iter.next()
- child.getNodeKind match {
- case XdmNodeKind.TEXT =>
- if (!ignoreWS || child.getStringValue.trim() != "") {
- list += child
- }
- case _ => list += child
- }
- }
- list.toList
- }
-
- private class ProcessUseWhen(val staticContext: StaticContext) extends ProcessMatchingNodes {
- private var useStack = ListBuffer.empty[Boolean]
- private var inlineStack = ListBuffer.empty[Boolean]
-
- override def startDocument(node: XdmNode): Boolean = {
- throw XProcException.xiThisCantHappen("Processing use-when matched a document node", None)
- }
-
- override def endDocument(node: XdmNode): Unit = {
- matcher.endDocument()
- }
-
- override def startElement(node: XdmNode, attributes: AttributeMap): Boolean = {
- val useWhenName = if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- new FingerprintedQName("", "", "use-when")
- } else {
- new FingerprintedQName("p", XProcConstants.ns_p, "use-when")
- }
-
- val useWhen = Option(attributes.get(useWhenName))
-
- var use = true
- val inline = inlineStack.nonEmpty && inlineStack.last
- if (useWhen.isDefined && !inline) {
- val exprContext = new StaticContext(config, None, node)
- val expr = new XProcXPathExpression(exprContext, useWhen.get.getValue)
- use = config.expressionEvaluator.booleanValue(expr, List(), Map(), None)
- }
-
- if (use) {
- matcher.location = node.getUnderlyingNode.saveLocation()
- if (inline) {
- matcher.addStartElement(node, attributes)
- } else {
- matcher.addStartElement(node, attributes.remove(useWhenName))
- }
- }
- useStack += use
- inlineStack += ((inlineStack.nonEmpty && inlineStack.last) || node.getNodeName == XProcConstants.p_inline)
- use
- }
-
- override def attributes(node: XdmNode, matchingAttributes: AttributeMap, nonMatchingAttributes: AttributeMap): Option[AttributeMap] = {
- throw new RuntimeException("This can't happen")
- }
-
- override def endElement(node: XdmNode): Unit = {
- if (useStack.last) {
- matcher.addEndElement()
- }
- useStack = useStack.dropRight(1)
- inlineStack = inlineStack.dropRight(1)
- }
-
- override def text(node: XdmNode): Unit = {
- throw XProcException.xiThisCantHappen("Processing use-when matched a text node", None)
- }
-
- override def comment(node: XdmNode): Unit = {
- throw XProcException.xiThisCantHappen("Processing use-when matched a comment node", None)
- }
-
- override def pi(node: XdmNode): Unit = {
- throw XProcException.xiThisCantHappen("Processing use-when matched a processing-instruction node", None)
- }
- }
-
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Pipe.scala b/src/main/scala/com/xmlcalabash/model/xml/Pipe.scala
deleted file mode 100644
index 36973ba..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Pipe.scala
+++ /dev/null
@@ -1,204 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-class Pipe(override val config: XMLCalabash, shortcut: Option[String]) extends DataSource(config) {
- def this(config: XMLCalabash) = {
- this(config, None)
- }
- def this(config: XMLCalabash, shortcut: String) = {
- this(config, Some(shortcut))
- }
-
- def this(pipe: Pipe) = {
- this(pipe.config)
- _step = pipe._step
- _port = pipe._port
- _link = pipe._link
- }
-
- private var _step = Option.empty[String]
- private var _port = Option.empty[String]
- private var _link = Option.empty[Port]
-
- def step: String = _step.get
- protected[model] def step_=(step: String): Unit = {
- _step = Some(step)
- }
- def port: String = _port.get
-
- protected[model] def port_=(port: String): Unit = {
- _port = Some(port)
- }
- def link: Option[Port] = _link
- def link_=(port: Port): Unit = {
- _link = Some(port)
- }
-
- override protected[model] def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- _step = attr(XProcConstants._step)
- _port = attr(XProcConstants._port)
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- // nop
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- for (child <- allChildren) {
- child.makeBindingsExplicit()
- }
-
- val env = environment()
- val drp = env.defaultReadablePort
-
- if (_link.isEmpty) {
- if (_step.isEmpty) {
- if (drp.isEmpty) {
- throw XProcException.xsPipeWithoutStepOrDrp(location)
- }
- _step = Some(drp.get.step.stepName)
- }
-
- val step = env.step(_step.get)
-
- // Figure out what our containing step is. But along the way, note if
- // we're inside a variable because they're allowed to read from the
- // containing steps inputs.
- var thisStep: Step = null
- var varbinding = false
-
- var p: Option[Artifact] = Some(this)
- while (p.isDefined) {
- p.get match {
- case step: Step =>
- thisStep = step
- p = None
- case _: Variable =>
- varbinding = true
- p = p.get.parent
- case _ =>
- p = p.get.parent
- }
- }
-
- if (_port.isEmpty) {
- if (step.isEmpty) {
- throw XProcException.xsPortNotReadableNoStep(_step.get, location)
- }
- if (thisStep.ancestor(step.get)) {
- if (step.get.primaryInput.isEmpty) {
- throw XProcException.xsPortNotReadableNoPrimaryInput(_step.get, location)
- } else {
- _port = Some(step.get.primaryInput.get.port)
- }
- } else {
- if (step.get.primaryOutput.isEmpty) {
- throw XProcException.xsPortNotReadableNoPrimaryOutput(_step.get, location)
- } else {
- _port = Some(step.get.primaryOutput.get.port)
- }
- }
- }
-
- if (step.isEmpty) {
- throw XProcException.xsPortNotReadableNoStep(_step.get, location)
- }
-
- if (thisStep == step.get && !varbinding) {
- // Special case of a loop
- throw XProcException.xsLoop(_step.get, _port.get, location)
- }
-
- for (child <- step.get.allChildren) {
- child match {
- case input: DeclareInput =>
- if (input.port == _port.get) {
- _link = Some(input)
- }
- case output: DeclareOutput =>
- if (output.port == _port.get) {
- _link = Some(output)
- }
- case output: WithOutput =>
- if (output.port == _port.get) {
- _link = Some(output)
- }
- case _ => ()
- }
- }
-
- if (_link.isEmpty) {
- throw XProcException.xsPortNotReadable(_step.get, _port.get, location)
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- if (allChildren.nonEmpty) {
- throw new RuntimeException(s"Invalid content in $this")
- }
- }
-
- override protected[model] def normalizeToPipes(): Unit = {
- // nop
- }
-
- override protected[model] def insertPipe(source: Port, pipe: Pipe): Unit = {
- if (link.get == source) {
- val newPipe = new Pipe(config)
- newPipe.step = pipe.step
- newPipe.port = pipe.port
- newPipe.link = pipe.link.get
- parent.get.addChild(newPipe, this)
- }
- }
-
- override protected[model] def replumb(oldSource: Port, newSource: Port): Unit = {
- if (link.get == oldSource) {
- link = newSource
- step = newSource.parent.get.asInstanceOf[Step].stepName
- port = newSource.port
- }
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parNode: Node): Unit = {
- val toNode = parNode
- val toPort = parent.get.asInstanceOf[Port].port
- val fromNode = link.get.parent.get._graphNode.get
- val fromPort = link.get.port
- runtime.graph.addOrderedEdge(fromNode, fromPort, toNode, toPort)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startPipe(tumble_id, step, port)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endPipe()
- }
-
- override def toString: String = {
- val sstep = _step.getOrElse("???")
- val sport = _port.getOrElse("???")
-
- if (tumble_id.startsWith("!syn")) {
- s"p:pipe from $sstep/$sport"
- } else {
- s"p:pipe from $sstep/$sport $tumble_id"
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/PipeInfo.scala b/src/main/scala/com/xmlcalabash/model/xml/PipeInfo.scala
deleted file mode 100644
index 4189826..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/PipeInfo.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{Axis, QName, XdmNode, XdmNodeKind}
-
-class PipeInfo(override val config: XMLCalabash, val info: XdmNode) extends Port(config) {
- override protected[model] def validateStructure(): Unit = {
- // nop
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- var root = Option.empty[QName]
- val iter = info.axisIterator(Axis.CHILD)
- while (root.isEmpty && iter.hasNext) {
- val item = iter.next()
- if (item.getNodeKind == XdmNodeKind.ELEMENT) {
- root = Some(item.getNodeName)
- }
- }
-
- xml.startPipeInfo(tumble_id, root)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endPipeInfo()
- }
- override def toString: String = {
- s"p:pipeinfo $tumble_id"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Port.scala b/src/main/scala/com/xmlcalabash/model/xml/Port.scala
deleted file mode 100644
index 3639063..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Port.scala
+++ /dev/null
@@ -1,157 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.params.SelectFilterParams
-import com.xmlcalabash.util.MediaType
-
-import scala.collection.mutable.ListBuffer
-
-class Port(override val config: XMLCalabash) extends Artifact(config) {
- protected var _port = ""
- protected[xml] var _sequence = Option.empty[Boolean]
- protected[xml] var _primary = Option.empty[Boolean]
- protected[xml] var _select = Option.empty[String]
- protected[xml] var _content_types = List.empty[MediaType]
-
- protected var _href = Option.empty[String]
- protected var _pipe = Option.empty[String]
-
- def port: String = _port
- protected[model] def port_=(port: String): Unit = {
- _port = port
- }
- def sequence: Boolean = _sequence.getOrElse(false)
- protected[model] def sequence_=(seq: Boolean): Unit = {
- _sequence = Some(seq)
- }
- def primary: Boolean = _primary.getOrElse(false)
- protected[model] def primary_=(primary: Boolean): Unit = {
- _primary = Some(primary)
- }
-
- def select: Option[String] = _select
-
- def contentTypes: List[MediaType] = _content_types
- def contentTypes_=(types: List[MediaType]): Unit = {
- _content_types = types
- }
-
- def step: NamedArtifact = {
- if (parent.isDefined) {
- parent.get match {
- case art: NamedArtifact => art
- case _ => throw new RuntimeException("parent of port isn't a named artifact?")
- }
- } else {
- throw new RuntimeException("port has no parent?")
- }
- }
-
- def bindings: List[DataSource] = {
- val lb = ListBuffer.empty[DataSource]
- for (child <- allChildren) {
- child match {
- case ds: DataSource => lb += ds
- case _ => ()
- }
- }
- lb.toList
- }
-
- protected[model] def examineBindings(): Unit = {
- if (_href.isDefined && _pipe.isDefined) {
- throw XProcException.xsPipeAndHref(location)
- }
-
- if (_href.isDefined && allChildren.nonEmpty) {
- throw XProcException.xsHrefAndOtherSources(location)
- }
-
- if (_pipe.isDefined && allChildren.nonEmpty) {
- throw XProcException.xsPipeAndOtherSources(location)
- }
-
- if (_href.isDefined) {
- val doc = new Document(config)
- doc.staticContext = staticContext
- doc.href = _href.get
- addChild(doc)
- }
-
- if (_pipe.isDefined) {
- for (shortcut <- _pipe.get.split("\\s+")) {
- var port = Option.empty[String]
- var step = Option.empty[String]
- if (shortcut.contains("@")) {
- val re = "(.*)@(.*)".r
- shortcut match {
- case re(pname, sname) =>
- if (pname != "") {
- port = Some(pname)
- }
- if (sname == "") {
- throw XProcException.xsInvalidPipeToken(shortcut, location)
- }
- step = Some(sname)
- }
- } else {
- if (shortcut.trim() != "") {
- port = Some(shortcut)
- }
- }
-
- val pipe = new Pipe(config)
- if (step.isDefined) {
- pipe.step = step.get
- }
- if (port.isDefined) {
- pipe.port = port.get
- }
- addChild(pipe)
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child.validateStructure()
- }
-
- var empty = false
- var nonEmpty = false
- var pns = false
- var implinline = false
- for (child <- allChildren) {
- child match {
- case _: Document =>
- nonEmpty = true
- pns = true
- case _: Empty =>
- empty = true
- pns = true
- case source: Inline =>
- nonEmpty = true
- if (source.synthetic) {
- implinline = true
- } else {
- pns = true
- }
- case _: Pipe =>
- nonEmpty = true
- pns = true
- case _: NamePipe => ()
- case _ => throw new RuntimeException(s"Unexpected port binding: $child")
- }
- }
-
- if (empty && nonEmpty) {
- throw XProcException.xsNoSiblingsOnEmpty(location)
- }
-
- if (pns && implinline) {
- throw XProcException.xsInvalidPipeline("Cannot combine implicit inlines with elements from the p: namespace", location)
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Step.scala b/src/main/scala/com/xmlcalabash/model/xml/Step.scala
deleted file mode 100644
index 573831f..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Step.scala
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.TypeUtils
-import net.sf.saxon.s9api.XdmNode
-
-import scala.collection.mutable.ListBuffer
-
-class Step(override val config: XMLCalabash) extends Artifact(config) with NamedArtifact {
- protected[xmlcalabash] var _name = Option.empty[String]
- override def stepName: String = _name.getOrElse(tumble_id)
- protected[model] def stepName_=(name: String): Unit = {
- _name = Some(name)
- }
-
- protected[xml] var _depends = ListBuffer.empty[String]
- def depends: List[String] = _depends.toList
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
- _name = attr(XProcConstants._name)
-
- var _depstr = Option.empty[String]
-
- if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
- _depstr = attr(XProcConstants._depends)
- if (attr(XProcConstants.p_depends).isDefined) {
- throw XProcException.xsXProcNamespaceError(XProcConstants.p_depends, location)
- }
- } else {
- _depstr = attr(XProcConstants.p_depends)
- if (attr(XProcConstants._depends).isDefined) {
- throw XProcException.xsUndeclaredOption(node.getNodeName, XProcConstants._depends, location)
- }
- }
-
- if (_depstr.isDefined) {
- if (_depstr.get.trim == "") {
- throw XProcException.xsBadTypeValue(_depstr.get, "name+", location)
- }
-
- val tutils = new TypeUtils(config)
- for (name <- _depstr.get.trim().split("\\s+")) {
- if (!tutils.valueMatchesType(name, XProcConstants.xs_NCName)) {
- throw XProcException.xsBadTypeValue(name, "NCName", location)
- }
- _depends += name
- }
- }
- }
-
- override protected[model] def validateStructure(): Unit = {
- for (child <- allChildren) {
- child.validateStructure()
- }
- }
-
- protected[model] def primaryInput: Option[Port] = {
- for (child <- allChildren) {
- child match {
- case input: DeclareInput =>
- if (input.primary) {
- return Some(input)
- }
- case winput: WithInput =>
- if (winput.primary) {
- return Some(winput)
- }
- case _ => ()
- }
- }
- None
- }
-
- protected[model] def primaryOutput: Option[Port] = {
- for (child <- allChildren) {
- child match {
- case output: DeclareOutput =>
- if (output.primary) {
- return Some(output)
- }
- case woutput: WithOutput =>
- if (woutput.primary) {
- return Some(woutput)
- }
- case _ => ()
- }
- }
- None
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- if (depends.nonEmpty) {
- val thisNode = _graphNode.get
- val env = environment()
- for (stepName <- depends) {
- val step = env.step(stepName)
- if (step.isEmpty) {
- throw XProcException.xsNotAStep(stepName, location)
- } else {
- thisNode.dependsOn(step.get._graphNode.get)
- }
- }
- }
- }
-
- def graphChildEdges(runtime: XMLCalabashRuntime): Unit = {
- for (child <- allChildren) {
- child match {
- case _: Step =>
- child.graphEdges(runtime, _graphNode.get)
- case _: Variable =>
- child.graphEdges(runtime, _graphNode.get)
- case _ => ()
- }
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Try.scala b/src/main/scala/com/xmlcalabash/model/xml/Try.scala
deleted file mode 100644
index 88bc17e..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Try.scala
+++ /dev/null
@@ -1,202 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ChooseStart, ContainerStart, Node, TryCatchStart}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class Try(override val config: XMLCalabash) extends Container(config) with NamedArtifact {
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- var hasSubpipeline = false
- var hasCatch = false
- var hasFinally = false
- val subpipeline = ListBuffer.empty[Artifact]
- val catches = ListBuffer.empty[Artifact]
-
- for (child <- allChildren) {
- child match {
- case output: DeclareOutput =>
- if (hasSubpipeline || hasCatch || hasFinally) {
- throw XProcException.xsInvalidPipeline("p:output is not allowed here", location)
- }
- subpipeline += output
- case cat: Catch =>
- if (hasFinally) {
- throw XProcException.xsInvalidPipeline("p:catch cannot follow p:finally", location)
- }
- catches += cat
- hasCatch = true
- case fin: Finally =>
- if (hasFinally) {
- throw XProcException.xsInvalidTryCatch("at most one p:finally is allowed", location)
- }
- catches += fin
- hasFinally = true
- case step: Step =>
- if (hasCatch || hasFinally) {
- throw XProcException.xsInvalidPipeline("steps cannot follow p:catch or p:finally", location)
- }
- subpipeline += step
- hasSubpipeline = true
- }
- }
-
- if (!hasSubpipeline || !(hasCatch || hasFinally)) {
- throw XProcException.xsInvalidTryCatch("p:try must have a subpipeline and at least one of p:catch and p:finally", location)
- }
-
- val codes = mutable.HashSet.empty[QName]
- var noCode = false
- for (child <- children[Catch]) {
- if (child.codes.isEmpty) {
- if (noCode) {
- throw XProcException.xsCatchMissingCode(location)
- }
- noCode = true
- } else {
- if (child.codes.nonEmpty && noCode) {
- throw XProcException.xsCatchMissingCode(location)
- }
- }
- for (code <- child.codes) {
- if (codes.contains(code)) {
- throw XProcException.xsCatchBadCode(code, location)
- }
- codes += code
- }
- }
-
- if (subpipeline.size > 1 || !subpipeline.head.isInstanceOf[Group]) {
- val group = new Group(config)
- for (child <- subpipeline) {
- group.addChild(child)
- }
- removeChildren()
- addChild(group)
- for (child <- catches) {
- addChild(child)
- }
- }
-
- for (child <- allChildren) {
- child.makeStructureExplicit()
- }
-
- val outputSet = mutable.HashSet.empty[String]
- var primaryOutput = Option.empty[String]
-
- for (branch <- allChildren) {
- branch match {
- case group: Group =>
- for (child <- group.children[DeclareOutput]) {
- outputSet += child.port
- if (child.primary) {
- if (primaryOutput.isDefined) {
- if (primaryOutput.get != child.port) {
- throw XProcException.xsBadTryOutputs(primaryOutput.get, child.port, location)
- }
- } else {
- primaryOutput = Some(child.port)
- }
- }
- }
- case cat: Catch =>
- for (child <- cat.children[DeclareOutput]) {
- outputSet += child.port
- if (child.primary) {
- if (primaryOutput.isDefined) {
- if (primaryOutput.get != child.port) {
- println(child.primary)
- throw XProcException.xsBadTryOutputs(primaryOutput.get, child.port, location)
- }
- } else {
- primaryOutput = Some(child.port)
- }
- }
- }
- case fin: Finally =>
- for (child <- fin.children[DeclareOutput]) {
- if (outputSet.contains(child.port)) {
- throw XProcException.xsInvalidFinallyPortName(child.port, location)
- throw new RuntimeException("Bad output in p:finally")
- }
- outputSet += child.port
- }
- case _ => ()
- }
- }
-
- val first = firstChild
- for (port <- outputSet) {
- val woutput = new WithOutput(config)
- woutput.port = port
- woutput.primary = (primaryOutput.isDefined && primaryOutput.get == port)
- addChild(woutput, first)
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- for (child <- allChildren) {
- child.makeBindingsExplicit()
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[ContainerStart]
- val node = start.addTryCatch(stepName, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- for (child <- children[Container]) {
- child.graphEdges(runtime, _graphNode.get)
- }
-
- val tryNode = _graphNode.get.asInstanceOf[TryCatchStart]
- for (child <- allChildren) {
- child match {
- case group: Group =>
- for (output <- group.children[DeclareOutput]) {
- runtime.graph.addOrderedEdge(group._graphNode.get, output.port, tryNode, output.port)
- }
- case cat: Catch =>
- for (output <- cat.children[DeclareOutput]) {
- runtime.graph.addOrderedEdge(cat._graphNode.get, output.port, tryNode, output.port)
- }
- case fin: Finally =>
- for (output <- fin.children[DeclareOutput]) {
- runtime.graph.addOrderedEdge(fin._graphNode.get, output.port, tryNode, output.port)
- }
- case _ => ()
- }
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startTry(tumble_id, stepName)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endTry()
- }
-
- override def toString: String = {
- s"p:try $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Variable.scala b/src/main/scala/com/xmlcalabash/model/xml/Variable.scala
deleted file mode 100644
index 6065192..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Variable.scala
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.{ExceptionCode, ModelException}
-import com.xmlcalabash.runtime.params.XPathBindingParams
-import com.xmlcalabash.runtime.{ExprParams, XMLCalabashRuntime, XProcXPathExpression}
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-
-class Variable(override val config: XMLCalabash) extends NameBinding(config) {
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- if (static) {
- // Statics have already been evaluated, they don't appear in the graph
- return
- }
-
- val container = this.parent.get
- val cnode = container._graphNode.get.asInstanceOf[ContainerStart]
-
- val params = new XPathBindingParams(collection)
- val init = new XProcXPathExpression(staticContext, _select.getOrElse("()"), as, _allowedValues, params)
- val node = cnode.addOption(_name.getClarkName, init, xpathBindingParams())
- _graphNode = Some(node)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- if (static) {
- return
- }
-
- xml.startVariable(tumble_id, tumble_id, name)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endWithVariable()
- }
-
- override def toString: String = {
- s"p:variable $name $tumble_id"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/Viewport.scala b/src/main/scala/com/xmlcalabash/model/xml/Viewport.scala
deleted file mode 100644
index efc0bcc..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/Viewport.scala
+++ /dev/null
@@ -1,177 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.jafpl.steps.Manifold
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.runtime.params.ContentTypeCheckerParams
-import com.xmlcalabash.util.MediaType
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class Viewport(override val config: XMLCalabash) extends Container(config) with NamedArtifact {
- private var _match: String = _
- protected var _dependentNameBindings: ListBuffer[NamePipe] = ListBuffer.empty[NamePipe]
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._match)) {
- _match = attr(XProcConstants._match).get
- } else {
- throw new RuntimeException("Viewport must have match")
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeStructureExplicit(): Unit = {
- val first = firstChild
- if (firstWithInput.isDefined) {
- val fwi = firstWithInput.get
- fwi.port match {
- case "" =>
- // It may be anonymous in XProc, but it mustn't be anonymous in the graph
- fwi.port = "source"
- case "source" => ()
- case _ => throw XProcException.xiThisCantHappen(s"Viewport withinput is named '${fwi.port}''", location)
- }
- } else {
- val input = new WithInput(config)
- input.port = "source"
- input.primary = true
- addChild(input, first)
- }
-
- val current = new DeclareInput(config)
- current.port = "current"
- current.primary = true
- addChild(current, first)
-
- makeContainerStructureExplicit()
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- val env = environment()
-
- val bindings = mutable.HashSet.empty[QName]
- bindings ++= staticContext.findVariableRefsInString(_match)
-
- for (ref <- bindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw XProcException.xsStaticErrorInExpression(s"$$${ref.toString}", "Reference to undefined variable", location)
- }
- if (!binding.get.static) {
- val pipe = new NamePipe(config, ref, binding.get.tumble_id, binding.get)
- _dependentNameBindings += pipe
- addChild(pipe)
- }
- }
- }
-
- override protected[model] def normalizeToPipes(): Unit = {
- super.normalizeToPipes()
-
- // Viewport needs a content type checker in front of its source
- val input = children[WithInput].head
-
- logger.debug(s"Adding content-type-checker for viewport source")
- val params = new ContentTypeCheckerParams(input.port, List(MediaType.XML, MediaType.HTML, MediaType.XHTML), staticContext, None,
- XProcException.err_xd0072, inputPort = true, true)
- val atomic = new AtomicStep(config, params)
- atomic.stepType = XProcConstants.cx_content_type_checker
- // Put this outside the viewport so that its output is attached to the viewport input, not the viewport output
- parent.get.addChild(atomic)
-
- val winput = new WithInput(config)
- winput.port = "source"
- atomic.addChild(winput)
-
- val woutput = new WithOutput(config)
- woutput.port = "result"
- atomic.addChild(woutput)
-
- val pipes = ListBuffer.empty[Pipe] ++ input.children[Pipe]
- input.removeChildren()
- for (origpipe <- pipes) {
- winput.addChild(origpipe)
-
- val opipe = new Pipe(config)
- opipe.step = atomic.stepName
- opipe.port = "result"
- opipe.link = atomic.children[WithOutput].head
- input.addChild(opipe)
- opipe.makeBindingsExplicit()
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- val start = parent.asInstanceOf[ContainerStart]
- val context = staticContext.withStatics(inScopeStatics)
- val composer = new XMLViewportComposer(config, context, _match)
- val node = start.addViewport(composer, stepName, containerManifold)
- _graphNode = Some(node)
- super.graphNodes(runtime, parent)
-
- // The binding links we created earlier now need to be patched so that this
- // is the node they go to.
- for (np <- _dependentNameBindings) {
- val binding = findInScopeOption(np.name)
- if (binding.isDefined) {
- np.patchNode(binding.get.graphNode.get)
- }
- }
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- super.graphEdges(runtime, parent)
-
- val winput = firstWithInput
- if (winput.isDefined) {
- for (child <- winput.get.allChildren) {
- child match {
- case pipe: Pipe =>
- pipe.graphEdges(runtime, _graphNode.get)
- case pipe: NamePipe =>
- pipe.graphEdges(runtime, _graphNode.get)
- case _ => ()
- }
- }
- }
-
- for (pipe <- children[NamePipe]) {
- pipe.graphEdges(runtime, _graphNode.get)
- }
-
- for (output <- children[DeclareOutput]) {
- for (pipe <- output.children[Pipe]) {
- runtime.graph.addOrderedEdge(pipe.link.get.parent.get._graphNode.get, pipe.port, _graphNode.get, output.port)
- }
- }
-
- graphChildEdges(runtime)
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startViewport(tumble_id, stepName, _match)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endViewport()
- }
-
- override def toString: String = {
- s"p:viewport $stepName"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xml/When.scala b/src/main/scala/com/xmlcalabash/model/xml/When.scala
deleted file mode 100644
index b20f77b..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/When.scala
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmNode}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class When(override val config: XMLCalabash) extends ChooseBranch(config) {
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- val coll = attr(XProcConstants._collection)
- if (coll.isDefined) {
- if (List("1", "true", "yes").contains(coll.get)) {
- _collection = true
- } else {
- if (List("0", "false", "no").contains(coll.get)) {
- _collection = false
- } else {
- throw XProcException.xsBadTypeValue(coll.get, "xs:boolean", location)
- }
- }
- }
-
- if (attributes.contains(XProcConstants._test)) {
- test = attr(XProcConstants._test).get
- } else {
- throw XProcException.xsMissingRequiredAttribute(XProcConstants._test, location)
- }
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- val bindings = mutable.HashSet.empty[QName]
-
- val env = environment()
-
- for (ref <- bindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw new RuntimeException("Reference to undefined variable")
- }
- if (!binding.get.static) {
- val pipe = new NamePipe(config, ref, binding.get.tumble_id, binding.get)
- addChild(pipe)
- }
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startWhen(tumble_id, stepName, _test)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endWhen()
- }
-
- override def toString: String = {
- s"p:when $stepName"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/WithInput.scala b/src/main/scala/com/xmlcalabash/model/xml/WithInput.scala
deleted file mode 100644
index 81b6709..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/WithInput.scala
+++ /dev/null
@@ -1,226 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.{XMLCalabash, exceptions}
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
-import com.xmlcalabash.runtime.params.SelectFilterParams
-import com.xmlcalabash.runtime.{StaticContext, XMLCalabashRuntime, XmlPortSpecification}
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.functions.ConstantFunction.{False, True}
-import net.sf.saxon.s9api.{QName, XdmNode}
-
-class WithInput(override val config: XMLCalabash) extends Port(config) {
- private val _exclude_inline_prefixes = List.empty[String]
- private var _context: StaticContext = _
- private var portSpecified = false
-
- def exclude_inline_prefixes: List[String] = _exclude_inline_prefixes
-
- override def parse(node: XdmNode): Unit = {
- super.parse(node)
-
- if (attributes.contains(XProcConstants._port)) {
- portSpecified = true
- _port = staticContext.parseNCName(attr(XProcConstants._port)).get
- }
- _context = new StaticContext(config, Some(this), node)
- _select = attr(XProcConstants._select)
-
- _href = attr(XProcConstants._href)
- _pipe = attr(XProcConstants._pipe)
-
- if (attributes.nonEmpty) {
- val badattr = attributes.keySet.head
- throw XProcException.xsBadAttribute(badattr, location)
- }
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- if (portSpecified) {
- parent.get match {
- case _: ForEach =>
- throw XProcException.xsPortNotAllowed(_port, "p:for-each", location)
- case _: Viewport =>
- throw XProcException.xsPortNotAllowed(_port, "p:viewport", location)
- case _: Choose =>
- throw XProcException.xsPortNotAllowed(_port, "p:choose or p:if", location)
- case _: When =>
- throw XProcException.xsPortNotAllowed(_port, "p:when", location)
- case _: ForLoop =>
- throw XProcException.xsPortNotAllowed(_port, "cx:loop", location)
- case _: ForUntil =>
- throw XProcException.xsPortNotAllowed(_port, "cx:until", location)
- case _: ForWhile =>
- throw XProcException.xsPortNotAllowed(_port, "cx:while", location)
- case _ => ()
- }
- }
-
- examineBindings()
- super.makeBindingsExplicit()
-
- var primaryInput = primary
- parent.get match {
- case atom: AtomicStep =>
- val decl = declaration(atom.stepType).get
- primaryInput = decl.input(_port, None).primary
- sequence = decl.input(_port, None).sequence
- case _ => ()
- }
-
- if (allChildren.nonEmpty) {
- // If there are explicit children, we'll check them elsewhere
- return
- }
-
- val env = environment()
- val drp = env.defaultReadablePort
-
- if (primaryInput && drp.isDefined) {
- val pipe = new Pipe(config)
- pipe.port = drp.get.port
- pipe.step = drp.get.step.stepName
- pipe.link = drp.get
- addChild(pipe)
- return
- }
-
- // This is err:XS0032 unless it's a special case
-
- // One of the special cases is, if this is an atomic step, and that
- // step has a default input for this port, then this is not an error,
- // the default input should be used. UGH.
- parent.get match {
- case atomic: AtomicStep =>
- if (declaration(atomic.stepType).isDefined) {
- val sig = declaration(atomic.stepType).get
- val psig = sig.input(_port, location)
- if (psig.defaultBindings.nonEmpty) {
- for (binding <- psig.defaultBindings) {
- binding match {
- case empty: Empty =>
- addChild(new Empty(empty))
- case inline: Inline =>
- val x= new Inline(inline)
- addChild(x)
- case document: Document =>
- addChild(new Document(document))
- case _ =>
- throw XProcException.xiThisCantHappen("Default input is not empty, inline, or document", location)
- }
- }
- return
- }
- }
- case _ => ()
- }
-
- var raiseError = true
- if (synthetic) {
- // All the other special cases involve synthetic elements
- parent.get match {
- case _: Choose => raiseError = false
- case _: When => raiseError = false
- case _: Otherwise => raiseError = false
- case _ =>
- if (parent.get.parent.isDefined
- && parent.get.parent.get.synthetic
- && parent.get.parent.get.isInstanceOf[Otherwise]) {
- raiseError = false
- }
- }
- }
-
- if (raiseError) {
- if (primary) {
- throw XProcException.xsUnconnectedPrimaryInputPort(step.stepName, port, location)
- } else {
- throw XProcException.xsUnconnectedInputPort(step.stepName, port, location)
- }
- }
- }
-
- override protected[model] def addFilters(): Unit = {
- super.addFilters()
- if (select.isEmpty) {
- return
- }
-
- val context = staticContext.withStatics(inScopeStatics)
-
- val ispec = if (sequence) {
- XmlPortSpecification.ANYSOURCESEQ
- } else {
- XmlPortSpecification.ANYSOURCE
- }
-
- val params = new SelectFilterParams(context, select.get, port, ispec)
- val filter = new AtomicStep(config, params)
- filter.stepType = XProcConstants.cx_select_filter
-
- val finput = new WithInput(config)
- finput.port = "source"
- finput.primary = true
-
- val foutput = new WithOutput(config)
- foutput.port = "result"
- foutput.primary = true
-
- filter.addChild(finput)
- filter.addChild(foutput)
-
- for (name <- staticContext.findVariableRefsInString(_select.get)) {
- var binding = _inScopeDynamics.get(name)
- if (binding.isDefined) {
- val npipe = new NamePipe(config, name, binding.get.tumble_id, binding.get)
- filter.addChild(npipe)
- } else {
- binding = _inScopeStatics.get(name.getClarkName)
- if (binding.isEmpty) {
- throw new RuntimeException(s"Reference to variable not in scope: $name")
- }
- }
- }
-
- val step = parent.get
- val container = step.parent.get.asInstanceOf[Container]
- container.addChild(filter, step)
-
- for (child <- allChildren) {
- child match {
- case pipe: Pipe =>
- finput.addChild(pipe)
- }
- }
- removeChildren()
-
- val pipe = new Pipe(config)
- pipe.step = filter.stepName
- pipe.port = "result"
- pipe.link = foutput
- addChild(pipe)
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parNode: Node): Unit = {
- for (child <- allChildren) {
- child.graphEdges(runtime, parNode)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startWithInput(tumble_id, tumble_id, port)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endWithInput()
- }
-
- override def toString: String = {
- if (tumble_id.startsWith("!syn")) {
- s"p:with-input $port"
- } else {
- s"p:with-input $port $tumble_id"
- }
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/WithOption.scala b/src/main/scala/com/xmlcalabash/model/xml/WithOption.scala
deleted file mode 100644
index d1a1d57..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/WithOption.scala
+++ /dev/null
@@ -1,206 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.{ContainerStart, Node}
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.OptionSignature
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.messages.{XdmNodeItemMessage, XdmValueItemMessage}
-import com.xmlcalabash.model.util.ValueParser
-import com.xmlcalabash.runtime._
-import com.xmlcalabash.runtime.params.XPathBindingParams
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import com.xmlcalabash.util.{S9Api, TypeUtils}
-import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmMap, XdmValue}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class WithOption(override val config: XMLCalabash) extends NameBinding(config) {
- val typeUtils = new TypeUtils(config)
- private val _precOptions = ListBuffer.empty[OptionSignature]
-
- def this(config: XMLCalabash, name: QName) = {
- this(config)
- _name = name
- }
-
- def precedingOption(opt: OptionSignature): Unit = {
- _precOptions += opt
- }
-
- override protected[model] def makeBindingsExplicit(): Unit = {
- super.makeBindingsExplicit()
-
- val env = environment()
-
- val bindings = mutable.HashSet.empty[QName]
- if (_avt.isDefined) {
- val avt = staticContext.parseAvt(_avt.get)
- bindings ++= staticContext.findVariableRefsInAvt(avt)
- if (bindings.isEmpty && parent.get.isInstanceOf[AtomicStep]) {
- val depends = staticContext.dependsOnContextAvt(avt)
- if (!depends) {
- // When you come back to optimize this, make sure ab-option-056 passes.
- /*
- val expr = new XProcVtExpression(staticContext, _avt.get, true)
- var msg = try {
- config.expressionEvaluator.newInstance().value(expr, List(), inScopeStatics, None)
- } catch {
- case ex: Throwable =>
- throw XProcException.xdGeneralError(ex.getMessage, location)
- }
- // Ok, now we have a string value
- val avalue = msg.item.getUnderlyingValue.getStringValue
- var tvalue = typeUtils.castAtomicAs(XdmAtomicValue.makeAtomicValue(avalue), Some(declaredType), staticContext)
- if (as.isDefined) {
- tvalue = typeUtils.castAtomicAs(tvalue, as, staticContext)
- }
- msg = new XdmValueItemMessage(tvalue, XProcMetadata.XML, staticContext)
- staticValue = msg
- */
- }
- }
- } else if (_select.isDefined) {
- bindings ++= staticContext.findVariableRefsInString(_select.get)
- if (bindings.isEmpty && parent.get.isInstanceOf[AtomicStep]) {
- //val depends = collection || staticContext.dependsOnContextString(_select.get)
- val depends = true
- if (!depends) {
- val checkas = if (qnameKeys) {
- None
- } else {
- as
- }
- val opts = new XPathBindingParams(Map.empty[QName, XdmValue], collection)
- val expr = new XProcXPathExpression(staticContext, _select.get, checkas, allowedValues, opts)
- val msg = config.expressionEvaluator.newInstance().value(expr, List(), inScopeStatics, Some(opts))
-
- if (qnameKeys) {
- msg.item match {
- case map: XdmMap =>
- staticValue = new XdmValueItemMessage(S9Api.forceQNameKeys(map.getUnderlyingValue, staticContext), XProcMetadata.XML, staticContext)
- case _ => throw new RuntimeException("qname map type didn't evaluate to a map")
- }
- } else {
- staticValue = new XdmValueItemMessage(msg.item, XProcMetadata.XML, staticContext)
- }
- }
- }
- } else {
- throw new RuntimeException("With option has neither AVT nor select?")
- }
-
- var nonStaticBindings = false
- for (ref <- bindings) {
- val binding = env.variable(ref)
- if (binding.isEmpty) {
- throw new RuntimeException("Reference to undefined variable")
- }
- nonStaticBindings = nonStaticBindings || !binding.get.static
- }
-
- if (nonStaticBindings) {
- var winput = firstWithInput
- if (winput.isEmpty) {
- val input = new WithInput(config)
- input.port = "source"
- addChild(input)
- winput = Some(input)
- }
- }
-
- // FIXME: does this result in duplicates sometimes?
- for (ref <- bindings) {
- val binding = env.variable(ref).get
- if (!binding.static) {
- val pipe = new NamePipe(config, ref, binding.tumble_id, binding)
- _dependentNameBindings += pipe
- addChild(pipe)
- }
- }
- }
-
- override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- if (staticValue.isDefined) {
- return
- }
-
- val container = this.parent.get.parent.get
- val cnode = container._graphNode.get.asInstanceOf[ContainerStart]
- val statics = mutable.HashMap.empty[QName, XdmValue]
- for ((name,smsg) <- inScopeStatics) {
- val qname = ValueParser.parseClarkName(name)
- smsg match {
- case msg: XdmNodeItemMessage =>
- statics.put(qname, msg.item)
- case msg: XdmValueItemMessage =>
- statics.put(qname, msg.item)
- }
- }
-
- val params = new XPathBindingParams(statics.toMap, collection)
- val init = if (_avt.isDefined) {
- val expr = staticContext.parseAvt(_avt.get)
- new XProcVtExpression(staticContext, expr, true)
- } else {
- val params = new XPathBindingParams(statics.toMap, collection)
- new XProcXPathExpression(staticContext, _select.getOrElse("()"), as, _allowedValues, params)
- }
- val node = cnode.addOption(_name.getClarkName, init, params)
- _graphNode = Some(node)
-
- // The binding links we created earlier now need to be patched so that this
- // is the node they go to.
- for (np <- _dependentNameBindings) {
- val binding = findInScopeOption(np.name)
- if (binding.isDefined) {
- np.patchNode(binding.get.graphNode.get)
- }
- }
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parNode: Node): Unit = {
- if (staticValue.isDefined) {
- return
- }
-
- val env = environment()
- for (stepName <- depends) {
- val step = env.step(stepName)
- if (step.isEmpty) {
- throw XProcException.xsNotAStep(stepName, location)
- } else {
- _graphNode.get.dependsOn(step.get._graphNode.get)
- }
- }
-
- val toNode = parNode
- val fromPort = "result"
- val fromNode = _graphNode.get
- val toPort = "#bindings"
- runtime.graph.addEdge(fromNode, fromPort, toNode, toPort)
-
- for (child <- allChildren) {
- child.graphEdges(runtime, _graphNode.get)
- }
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- if (staticValue.isEmpty) {
- xml.startWithOption(tumble_id, tumble_id, name)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endWithOption()
- }
- }
-
- override def toString: String = {
- if (tumble_id.startsWith("!syn")) {
- s"p:with-option $name"
- } else {
- s"p:with-option $name $tumble_id"
- }
- }
-
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/WithOutput.scala b/src/main/scala/com/xmlcalabash/model/xml/WithOutput.scala
deleted file mode 100644
index 369fad4..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/WithOutput.scala
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import com.jafpl.graph.Node
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.runtime.XMLCalabashRuntime
-import com.xmlcalabash.util.xc.ElaboratedPipeline
-import net.sf.saxon.s9api.XdmNode
-
-class WithOutput(override val config: XMLCalabash) extends Port(config) {
- override def parse(node: XdmNode): Unit = {
- throw new RuntimeException("This is a purely synthetic element")
- }
-
- override def graphEdges(runtime: XMLCalabashRuntime, parent: Node): Unit = {
- // nop
- }
-
- override def xdump(xml: ElaboratedPipeline): Unit = {
- xml.startWithOutput(tumble_id, tumble_id, port, sequence)
- for (child <- rawChildren) {
- child.xdump(xml)
- }
- xml.endWithOutput()
- }
-
- override def toString: String = {
- s"p:with-output $port${if (sequence) "*" else ""}"
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xml/XMLContext.scala b/src/main/scala/com/xmlcalabash/model/xml/XMLContext.scala
deleted file mode 100644
index 16bb73d..0000000
--- a/src/main/scala/com/xmlcalabash/model/xml/XMLContext.scala
+++ /dev/null
@@ -1,254 +0,0 @@
-package com.xmlcalabash.model.xml
-
-import java.net.URI
-import com.jafpl.graph.Location
-import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
-import com.xmlcalabash.runtime.StaticContext
-import com.xmlcalabash.util.{MediaType, TypeUtils, ValueTemplateParser}
-import net.sf.saxon.expr.parser.ExpressionTool
-import net.sf.saxon.s9api.{ItemType, QName, SaxonApiException, SequenceType, XdmAtomicValue}
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-object XMLContext {
- private val contextFunctions =
- List(XProcConstants.p_iteration_size, XProcConstants.p_iteration_position,
- XProcConstants.fn_collection, XProcConstants._collection)
-}
-
-class XMLContext(override val config: XMLCalabash, override val artifact: Option[Artifact]) extends StaticContext(config, artifact) {
- def this(config: XMLCalabash, artifact: Artifact) = {
- this(config, Some(artifact))
- }
-
- def this(config: XMLCalabash) = {
- this(config, None)
- }
-
- def this(config: XMLCalabash, artifact: Artifact, baseURI: Option[URI], ns: Map[String,String], location: Option[Location]) = {
- this(config, Some(artifact))
- _baseURI = baseURI
- _inScopeNS = ns
- _location = location
- }
-
- def this(config: XMLCalabash, baseURI: Option[URI], ns: Map[String,String], location: Option[Location]) = {
- this(config, None)
- _baseURI = baseURI
- _inScopeNS = ns
- _location = location
- }
-
- val typeUtils = new TypeUtils(config, this)
-
- def parseBoolean(value: Option[String]): Option[Boolean] = {
- if (value.isDefined) {
- if (value.get == "true" || value.get == "false") {
- Some(value.get == "true")
- } else {
- throw XProcException.xsBadTypeValue(value.get, "boolean", location)
- }
- } else {
- None
- }
- }
-
- def parseQName(name: String): QName = {
- parseQName(Some(name)).get
- }
-
- def parseQName(name: Option[String]): Option[QName] = {
- if (name.isDefined) {
- val eqname = "^Q\\{(.*)\\}(\\S+)$".r
- val qname = name.get match {
- case eqname(uri,local) => Some(new QName(uri, local))
- case _ =>
- if (name.get.contains(":")) {
- val pos = name.get.indexOf(':')
- val prefix = name.get.substring(0, pos)
- val local = name.get.substring(pos+1)
- if (nsBindings.contains(prefix)) {
- Some(new QName(prefix, nsBindings(prefix), local))
- } else {
- throw XProcException.xdCannotResolveQName(name.get, location)
- }
- } else {
- Some(new QName("", name.get))
- }
- }
-
- val prefix = qname.get.getPrefix
- val local = qname.get.getLocalName
- if (prefix != null && !"".equals(prefix)) {
- if (parseNCName(Some(prefix)).isDefined && parseNCName(Some(local)).isDefined) {
- return qname
- }
- } else {
- if (parseNCName(Some(local)).isDefined) {
- return qname
- }
- }
- // This will have already happened in the calls to parseNCName above, but
- // putting it here satisfies the compiler.
- throw XProcException.xsBadTypeValue(name.get, "NCName", location)
- } else {
- None
- }
- }
-
- def parseNCName(name: Option[String]): Option[String] = {
- if (name.isDefined) {
- try {
- val ncname = TypeUtils.castAtomicAs(new XdmAtomicValue(name.get), ItemType.NCNAME, null)
- Some(ncname.getStringValue)
- } catch {
- case _: SaxonApiException =>
- throw XProcException.xsBadTypeValue(name.get, "NCName", location)
- case e: Exception =>
- throw e
- }
- } else {
- None
- }
- }
-
- def parseContentTypes(ctypes: Option[String]): List[MediaType] = {
- if (ctypes.isDefined) {
- try {
- MediaType.parseList(ctypes.get).toList
- } catch {
- case ex: XProcException =>
- if (ex.code == XProcException.err_xc0070) {
- // Map to the static error...
- throw XProcException.xsUnrecognizedContentTypeShortcut(ex.details.head.toString, ex.location)
- } else {
- throw ex
- }
- }
- } else {
- List.empty[MediaType]
- }
- }
-
- def parseSequenceType(seqType: Option[String]): Option[SequenceType] = {
- typeUtils.parseSequenceType(seqType)
- }
-
- def parseAvt(value: String): List[String] = {
- val parser = new ValueTemplateParser(value)
- parser.template()
- }
-
- def findVariableRefsInAvt(list: List[String]): Set[QName] = {
- val variableRefs = mutable.HashSet.empty[QName]
-
- var avt = false
- for (substr <- list) {
- if (avt) {
- variableRefs ++= findVariableRefsInString(substr)
- }
- avt = !avt
- }
-
- variableRefs.toSet
- }
-
- def findVariableRefsInString(text: String): Set[QName] = {
- findVariableRefsInString(Some(text))
- }
-
- def findVariableRefsInString(text: Option[String]): Set[QName] = {
- val variableRefs = mutable.HashSet.empty[QName]
-
- if (text.isDefined) {
- val parser = config.expressionParser
- parser.parse(text.get)
- for (ref <- parser.variableRefs) {
- val qname = ValueParser.parseQName(ref, this)
- variableRefs += qname
- }
- }
-
- variableRefs.toSet
- }
-
- def findFunctionRefsInAvt(list: List[String]): Set[QName] = {
- val functionRefs = mutable.HashSet.empty[QName]
-
- var avt = false
- for (substr <- list) {
- if (avt) {
- functionRefs ++= findFunctionRefsInString(substr)
- }
- avt = !avt
- }
-
- functionRefs.toSet
- }
-
- def findFunctionRefsInString(text: String): Set[QName] = {
- findFunctionRefsInString(Some(text))
- }
-
- def findFunctionRefsInString(text: Option[String]): Set[QName] = {
- val functionRefs = mutable.HashSet.empty[QName]
-
- if (text.isDefined) {
- val parser = config.expressionParser
- parser.parse(text.get)
- for (ref <- parser.functionRefs) {
- val qname = ValueParser.parseQName(ref, this)
- functionRefs += qname
- }
- }
-
- functionRefs.toSet
- }
-
- def dependsOnContextAvt(list: List[String]): Boolean = {
- var depends = false
-
- var avt = false
- for (substr <- list) {
- if (avt) {
- depends = depends || dependsOnContextString(substr)
- }
- avt = !avt
- }
-
- depends
- }
-
- def dependsOnContextString(expr: String): Boolean = {
- var depends = false
- for (func <- findFunctionRefsInString(expr)) {
- depends = depends || XMLContext.contextFunctions.contains(func)
- }
- val vars = findVariableRefsInString(expr)
-
- if (!depends) {
- val xcomp = config.processor.newXPathCompiler()
- for ((prefix, uri) <- nsBindings) {
- xcomp.declareNamespace(prefix, uri)
- }
- for (name <- vars) {
- xcomp.declareVariable(name)
- }
- val xexec = xcomp.compile(expr)
- val xexpr = xexec.getUnderlyingExpression.getInternalExpression
- ExpressionTool.dependsOnFocus(xexpr)
- } else {
- true
- }
- }
-
- object StateChange {
- val STRING = 0
- val EXPR = 1
- val SQUOTE = 2
- val DQUOTE = 3
- }
-}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/Loader.scala b/src/main/scala/com/xmlcalabash/model/xxml/Loader.scala
new file mode 100644
index 0000000..9ac8ef1
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/Loader.scala
@@ -0,0 +1,546 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.util.{TypeUtils, XdmLocation}
+import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
+import net.sf.saxon.s9api.{Axis, XdmNode, XdmNodeKind}
+
+import scala.collection.mutable.ListBuffer
+import scala.jdk.CollectionConverters.IteratorHasAsScala
+
+class Loader(parser: XParser, hier: NodeHierarchy) {
+ private val config = parser.config
+ private val _exceptions = ListBuffer.empty[Exception]
+ private var _declContainer: XDeclContainer = _
+ private var _syntheticLibrary = Option.empty[XLibrary]
+
+ if (!useWhen(hier.root)) {
+ throw XProcException.xiUserError("Use-when is false on the root element")
+ }
+
+ if (hier.root.getNodeName == XProcConstants.p_declare_step) {
+ _declContainer = parseDeclareStep(hier.root)
+ } else {
+ _declContainer = parseLibrary(hier.root)
+ }
+
+ def declaredStep: Option[XDeclareStep] = {
+ _declContainer match {
+ case step: XDeclareStep => Some(step)
+ case _ => None
+ }
+ }
+
+ def library: Option[XLibrary] = {
+ _declContainer match {
+ case lib: XLibrary =>
+ Some(lib)
+ case step: XDeclareStep =>
+ if (_syntheticLibrary.isEmpty) {
+ val library = new XLibrary(config, hier.baseURI)
+ library.staticContext = new XArtifactContext(library, hier.root)
+ library.synthetic = true
+ library.syntheticName = XProcConstants.p_library
+ library.addChild(step)
+ _syntheticLibrary = Some(library)
+ }
+ _syntheticLibrary
+ case _ =>
+ throw XProcException.xiThisCantHappen("Declared container is neither step nor library?")
+ }
+ }
+
+ def exceptions: List[Exception] = _exceptions.toList
+
+ private def useWhen(node: XdmNode): Boolean = hier.useWhen(node)
+
+ private def parseChildren(node: XdmNode): List[XArtifact] = {
+ val children = ListBuffer.empty[XArtifact]
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ try {
+ child.getNodeKind match {
+ case XdmNodeKind.TEXT =>
+ if (child.getStringValue.trim != "") {
+ throw XProcException.xsTextNotAllowed(child.getStringValue, XdmLocation.from(node))
+ }
+ case XdmNodeKind.COMMENT =>
+ ()
+ case XdmNodeKind.PROCESSING_INSTRUCTION =>
+ ()
+ case XdmNodeKind.ELEMENT =>
+ if (useWhen(child)) {
+ child.getNodeName match {
+ case XProcConstants.p_declare_step =>
+ children += parseDeclareStep(child)
+ case XProcConstants.p_library =>
+ children += parseLibrary(child)
+ case XProcConstants.p_catch =>
+ children += parseCatch(child)
+ case XProcConstants.p_choose =>
+ children += parseChoose(child)
+ case XProcConstants.p_document =>
+ children += parseDocument(child)
+ case XProcConstants.p_documentation =>
+ children += parseDocumentation(child)
+ case XProcConstants.p_empty =>
+ children += parseEmpty(child)
+ case XProcConstants.p_finally =>
+ children += parseFinally(child)
+ case XProcConstants.p_for_each =>
+ children += parseForEach(child)
+ case XProcConstants.p_group =>
+ children += parseGroup(child)
+ case XProcConstants.p_if =>
+ children += parseIf(child)
+ case XProcConstants.p_import =>
+ val imported = parseImport(child)
+ if (imported.isDefined) {
+ children += imported.get
+ }
+ case XProcConstants.p_import_functions =>
+ children += parseImportFunctions(child)
+ case XProcConstants.p_inline =>
+ children += parseInline(child)
+ case XProcConstants.p_input =>
+ children += parseInput(child)
+ case XProcConstants.p_option =>
+ children += parseOption(child)
+ case XProcConstants.p_otherwise =>
+ children += parseOtherwise(child)
+ case XProcConstants.p_output =>
+ children += parseOutput(child)
+ case XProcConstants.p_pipe =>
+ children += parsePipe(child)
+ case XProcConstants.p_pipeinfo =>
+ children += parsePipeinfo(child)
+ case XProcConstants.p_try =>
+ children += parseTry(child)
+ case XProcConstants.p_variable =>
+ children += parseVariable(child)
+ case XProcConstants.p_viewport =>
+ children += parseViewport(child)
+ case XProcConstants.p_when =>
+ children += parseWhen(child)
+ case XProcConstants.p_with_input =>
+ children += parseWithInput(child)
+ case XProcConstants.p_with_option =>
+ children += parseWithOption(child)
+ case XProcConstants.cx_loop =>
+ children += parseForLoop(child)
+ case XProcConstants.cx_until =>
+ children += parseUntilLoop(child)
+ case XProcConstants.cx_while =>
+ children += parseWhileLoop(child)
+
+ case _ =>
+ children += parseAtomicStep(child)
+ }
+ }
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Unexpected child node kind: ${child}")
+ }
+ } catch {
+ case ex: Exception =>
+ _exceptions += ex
+ }
+ }
+ children.toList
+ }
+
+ private def parseAtomicStep(node: XdmNode): XAtomicStep = {
+ val decl = new XAtomicStep(config, node.getNodeName)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseDeclareStep(node: XdmNode): XDeclareStep = {
+ val decl = new XDeclareStep(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseLibrary(node: XdmNode): XLibrary = {
+ val decl = new XLibrary(config, Option(node.getBaseURI))
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseCatch(node: XdmNode): XCatch = {
+ val decl = new XCatch(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseChoose(node: XdmNode): XChoose = {
+ val decl = new XChoose(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseDocument(node: XdmNode): XDocument = {
+ val decl = new XDocument(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseDocumentation(node: XdmNode): XDocumentation = {
+ val decl = new XDocumentation(config, nodeContent(node, false))
+ decl.parse(node, List())
+ decl
+ }
+
+ private def parseEmpty(node: XdmNode): XEmpty = {
+ val decl = new XEmpty(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseFinally(node: XdmNode): XFinally = {
+ val decl = new XFinally(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseForEach(node: XdmNode): XForEach = {
+ val decl = new XForEach(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseForLoop(node: XdmNode): XForLoop = {
+ val decl = new XForLoop(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseUntilLoop(node: XdmNode): XForUntil = {
+ val decl = new XForUntil(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseWhileLoop(node: XdmNode): XForWhile = {
+ val decl = new XForWhile(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseGroup(node: XdmNode): XGroup = {
+ val decl = new XGroup(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseIf(node: XdmNode): XIf = {
+ val decl = new XIf(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseImport(node: XdmNode): Option[XImport] = {
+ val ihier = hier.imported(node)
+ if (ihier.isEmpty) {
+ var found = Option.empty[NodeHierarchy]
+ for (rehier <- hier.reimports) {
+ if (rehier.baseURI.isDefined
+ && rehier.baseURI.get == node.getBaseURI.resolve(node.getAttributeValue(XProcConstants._href))) {
+ found = Some(rehier)
+ }
+ }
+ if (found.isDefined && found.get.ximport.isDefined) {
+ return found.get.ximport
+ } else {
+ throw XProcException.xiThisCantHappen("Reimport that doesn't have an XImport?")
+ }
+ }
+
+ val loader = new Loader(parser, ihier.get)
+
+ _exceptions ++= loader.exceptions
+
+ if (!ihier.get.useWhen(hier.root)) {
+ _exceptions += XProcException.xsInvalidPipeline("Root element use-when is false; no document", None)
+ }
+
+ if (_exceptions.nonEmpty) {
+ throw _exceptions.head
+ }
+
+ val lib = loader.library.get
+
+ lib.builtinLibraries = parser.builtinLibraries
+ for (imp <- lib.children[XImport]) {
+ lib.addInScopeSteps(imp.library.inScopeSteps)
+ }
+
+ lib.xelaborate()
+ _exceptions ++= lib.errors
+
+ if (_exceptions.nonEmpty) {
+ throw _exceptions.head
+ }
+
+ val decl = new XImport(config, lib)
+ decl.parse(node, parseChildren(node))
+ ihier.get.ximport = decl
+ Some(decl)
+ }
+
+ private def parseImportFunctions(node: XdmNode): XImportFunctions = {
+ val decl = new XImportFunctions(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseInline(node: XdmNode): XInline = {
+ val decl = new XInline(config, checkAttributes(nodeContent(node, false)), false)
+ decl.parse(node, List())
+ decl.parseExpandText(node)
+ decl.parseExcludedUris(node)
+ decl
+ }
+
+ private def nodeContent(node: XdmNode, includeNode: Boolean): XdmNode = {
+ val builder = new SaxonTreeBuilder(config)
+
+ try {
+ builder.startDocument(Option(node.getBaseURI))
+ } catch {
+ case _: IllegalStateException =>
+ _exceptions += XProcException.xdInvalidURI(node.getUnderlyingNode.getBaseURI, None)
+ case ex: Exception =>
+ _exceptions += ex
+ }
+
+ if (includeNode) {
+ filterUseWhen(builder, node)
+ } else {
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ filterUseWhen(builder, child)
+ }
+ }
+
+ builder.endDocument()
+ builder.result
+ }
+
+ private def filterUseWhen(builder: SaxonTreeBuilder, node: XdmNode): Unit = {
+ node.getNodeKind match {
+ case XdmNodeKind.DOCUMENT =>
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ filterUseWhen(builder, child)
+ }
+ case XdmNodeKind.ELEMENT =>
+ if (useWhen(node)) {
+ val usewhen = if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
+ XProcConstants._use_when
+ } else {
+ XProcConstants.p_use_when
+ }
+ var attrMap: AttributeMap = EmptyAttributeMap.getInstance()
+ val iter = node.axisIterator(Axis.ATTRIBUTE)
+ while (iter.hasNext) {
+ val attr = iter.next()
+ if (attr.getNodeName != usewhen) {
+ attrMap = attrMap.put(TypeUtils.attributeInfo(attr.getNodeName, attr.getStringValue))
+ }
+ }
+ builder.addStartElement(node, attrMap)
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ filterUseWhen(builder, child)
+ }
+ builder.addEndElement()
+ }
+ case _ =>
+ builder.addSubtree(node)
+ }
+ }
+
+ private def parseInput(node: XdmNode): XInput = {
+ val decl = new XInput(config)
+ decl.parse(node, parseConnectionChildren(node))
+ decl
+ }
+
+ private def parseConnectionChildren(node: XdmNode): List[XArtifact] = {
+ var connection = false
+ var conncount = 0
+ var empty = Option.empty[XdmNode]
+ var nonElementDecls = false
+ val implicitInlines = ListBuffer.empty[XdmNode]
+
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ child.getNodeKind match {
+ case XdmNodeKind.ELEMENT =>
+ if (useWhen(child)) {
+ child.getNodeName match {
+ case XProcConstants.p_inline =>
+ connection = true
+ conncount += 1
+ case XProcConstants.p_empty =>
+ connection = true
+ conncount += 1
+ empty = Some(child)
+ case XProcConstants.p_pipe =>
+ connection = true
+ conncount += 1
+ case XProcConstants.p_document =>
+ connection = true
+ conncount += 1
+ case XProcConstants.p_pipeinfo =>
+ ()
+ case XProcConstants.p_documentation =>
+ ()
+ case _ =>
+ implicitInlines += child
+ conncount += 1
+ }
+ }
+ case XdmNodeKind.COMMENT =>
+ connection = true
+ nonElementDecls = true
+ conncount += 1
+ case XdmNodeKind.TEXT =>
+ if (child.getStringValue.trim() != "") {
+ connection = true
+ nonElementDecls = true
+ conncount += 1
+ }
+ case XdmNodeKind.PROCESSING_INSTRUCTION =>
+ connection = true
+ nonElementDecls = true
+ conncount += 1
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Connection children included ${child}")
+ }
+ }
+
+ if (connection) {
+ if (conncount > 1 && empty.isDefined) {
+ throw XProcException.xsNoSiblingsOnEmpty(XdmLocation.from(empty.get))
+ }
+ if (implicitInlines.nonEmpty) {
+ if (nonElementDecls) {
+ throw XProcException.xsInlineNotAllowed(XdmLocation.from(implicitInlines.head))
+ }
+ throw XProcException.xsCantMixConnectionsWithImplicitInlines(implicitInlines.head.getNodeName, XdmLocation.from(implicitInlines.head))
+ }
+ return parseChildren(node)
+ }
+
+ val inlines = ListBuffer.empty[XInline]
+ for (child <- implicitInlines) {
+ val decl = new XInline(config, checkAttributes(nodeContent(child, true)), true)
+ decl.parse(node, List())
+ decl.parseExpandText(node)
+ decl.parseExcludedUris(node)
+ inlines += decl
+ }
+
+ inlines.toList
+ }
+
+ private def checkAttributes(node: XdmNode): XdmNode = {
+ node.getNodeKind match {
+ case XdmNodeKind.DOCUMENT =>
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ checkAttributes(child)
+ }
+ case XdmNodeKind.ELEMENT =>
+ if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
+ for (attr <- node.axisIterator(Axis.ATTRIBUTE).asScala) {
+ if (attr.getNodeName == XProcConstants._expand_text || attr.getNodeName == XProcConstants._inline_expand_text) {
+ if (attr.getStringValue != "true" && attr.getStringValue != "false") {
+ _exceptions += XProcException.xsInvalidExpandText(attr.getNodeName, attr.getStringValue, XdmLocation.from(node))
+ }
+ }
+ }
+ } else {
+ for (attr <- node.axisIterator(Axis.ATTRIBUTE).asScala) {
+ if (attr.getNodeName == XProcConstants.p_expand_text || attr.getNodeName == XProcConstants.p_inline_expand_text) {
+ if (attr.getStringValue != "true" && attr.getStringValue != "false") {
+ _exceptions += XProcException.xsInvalidExpandText(attr.getNodeName, attr.getStringValue, XdmLocation.from(node))
+ }
+ }
+ }
+ }
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ checkAttributes(child)
+ }
+ case _ => ()
+ }
+
+ node
+ }
+
+ private def parseOption(node: XdmNode): XOption = {
+ if (node.getAttributeValue(XProcConstants._static) == "true") {
+ // We've already worked out this value while parsing the nodes
+ }
+
+ val decl = new XOption(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseOtherwise(node: XdmNode): XOtherwise = {
+ val decl = new XOtherwise(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseOutput(node: XdmNode): XOutput = {
+ val decl = new XOutput(config)
+ decl.parse(node, parseConnectionChildren(node))
+ decl
+ }
+
+ private def parsePipe(node: XdmNode): XPipe = {
+ val decl = new XPipe(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parsePipeinfo(node: XdmNode): XPipeinfo = {
+ val decl = new XPipeinfo(config, nodeContent(node, false))
+ decl.parse(node, List())
+ decl
+ }
+
+ private def parseTry(node: XdmNode): XTry = {
+ val decl = new XTry(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseVariable(node: XdmNode): XVariable = {
+ val decl = new XVariable(config)
+ decl.parse(node, parseConnectionChildren(node))
+ decl
+ }
+
+ private def parseViewport(node: XdmNode): XViewport = {
+ val decl = new XViewport(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseWhen(node: XdmNode): XWhen = {
+ val decl = new XWhen(config)
+ decl.parse(node, parseChildren(node))
+ decl
+ }
+
+ private def parseWithInput(node: XdmNode): XWithInput = {
+ val decl = new XWithInput(config)
+ decl.parse(node, parseConnectionChildren(node))
+ decl
+ }
+
+ private def parseWithOption(node: XdmNode): XWithOption = {
+ val decl = new XWithOption(config)
+ decl.parse(node, parseConnectionChildren(node))
+ decl
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/NodeHierarchy.scala b/src/main/scala/com/xmlcalabash/model/xxml/NodeHierarchy.scala
new file mode 100644
index 0000000..3a37c96
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/NodeHierarchy.scala
@@ -0,0 +1,501 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.config.DocumentRequest
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XProcXPathExpression}
+import com.xmlcalabash.util.{MediaType, S9Api, XdmLocation}
+import net.sf.saxon.ma.map.{MapItem, MapType}
+import net.sf.saxon.s9api.{Axis, QName, XdmMap, XdmNode, XdmNodeKind}
+import org.slf4j.{Logger, LoggerFactory}
+
+import java.net.URI
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+import scala.jdk.CollectionConverters.IteratorHasAsScala
+
+object NodeHierarchy {
+ def newInstance(config: XMLCalabash, node: XdmNode): NodeHierarchy = {
+ config.staticStepsIndeterminate = true
+
+ val hier = new NodeHierarchy(config, node, Option(node.getBaseURI), List())
+
+ var changed = false
+ if (hier.conditionalImports.nonEmpty) {
+ hier._conditionalImports.clear()
+ changed = hier.resolve()
+ while (changed) {
+ hier._conditionalImports.clear()
+ changed = hier.resolve()
+ }
+ if (hier.conditionalImports.nonEmpty) {
+ val node = hier.conditionalImports.head
+ throw XProcException.xsUseWhenDeadlock(hier.useWhenExpression(node).get, XdmLocation.from(node))
+ }
+ }
+
+ config.staticStepsIndeterminate = false
+
+ changed = hier.resolve()
+ while (changed) {
+ changed = hier.resolve()
+ }
+
+ hier.resolveDeadlocks()
+
+ hier.unifyStatus()
+
+ hier
+ }
+}
+
+class NodeHierarchy private(config: XMLCalabash, node: XdmNode, val baseURI: Option[URI], ancestors: List[NodeHierarchy]) {
+ protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
+ private val _imports = mutable.HashMap.empty[URI, NodeHierarchy]
+ private val _reimports = mutable.HashSet.empty[NodeHierarchy]
+ private val useWhenStatus = mutable.HashMap.empty[XdmNode, Option[Boolean]]
+ private val _knownSteps = mutable.HashSet.empty[QName]
+ private val _conditionalSteps = mutable.HashMap.empty[QName, mutable.HashSet[XdmNode]]
+ private val _conditionalImports = mutable.HashSet.empty[XdmNode]
+ private val _root = S9Api.documentElement(node)
+ private var changed = false
+ private var _ximport = Option.empty[XImport]
+
+ if (_root.isEmpty) {
+ throw XProcException.xsNotAPipeline()
+ }
+
+ if (root.getNodeName != XProcConstants.p_library && root.getNodeName != XProcConstants.p_declare_step) {
+ throw XProcException.xsNotAPipeline(root.getNodeName)
+ }
+
+ recurse(root)
+
+ def root: XdmNode = _root.get
+ def imports: List[NodeHierarchy] = _imports.values.toList
+ def reimports: Set[NodeHierarchy] = _reimports.toSet
+ def knownSteps: Set[QName] = _knownSteps.toSet
+ def conditionalSteps: Set[QName] = {
+ val steps = mutable.HashSet.empty[QName] ++ _conditionalSteps.keySet
+ steps.toSet
+ }
+ def conditionalImports: Set[XdmNode] = _conditionalImports.toSet
+ def useWhen(node: XdmNode): Boolean = {
+ if (useWhenStatus.contains(node)) {
+ (useWhenStatus(node).get)
+ } else {
+ true
+ }
+ }
+
+ protected[xxml] def ximport: Option[XImport] = _ximport
+ protected[xxml] def ximport_=(xi: XImport): Unit = {
+ _ximport = Some(xi)
+ }
+
+ def imported(node: XdmNode): Option[NodeHierarchy] = {
+ val href = if (Option(node.getAttributeValue(XProcConstants._href)).isDefined) {
+ if (Option(node.getBaseURI).isDefined) {
+ node.getBaseURI.resolve(node.getAttributeValue(XProcConstants._href))
+ } else {
+ // If this wasn't absolute, we'd have thrown an exception in NodeHierarchy
+ new URI(node.getAttributeValue(XProcConstants._href))
+ }
+ } else {
+ throw XProcException.xsMissingRequiredAttribute(XProcConstants._href, XdmLocation.from(node))
+ }
+
+ _imports.get(href)
+ }
+
+ private def recurse(node: XdmNode): Unit = {
+ val useWhen = testUseWhen(node)
+
+ if (useWhen.isEmpty && node.getNodeName == XProcConstants.p_import) {
+ _conditionalImports += node
+ }
+
+ if (useWhen.getOrElse(false)) {
+ node.getNodeName match {
+ case XProcConstants.p_import =>
+ processImport(node)
+
+ case XProcConstants.p_option =>
+ val context = new XMLStaticContext(node)
+ if (Option(node.getAttributeValue(XProcConstants._name)).isDefined) {
+ try {
+ val name = context.parseQName(node.getAttributeValue(XProcConstants._name))
+
+ if (Option(node.getAttributeValue(XProcConstants._static)).getOrElse("false") == "true") {
+ val value = staticOptionValue(node)
+ config.addStatic(name, value)
+ } else {
+ if (node.getParent.getNodeName == XProcConstants.p_library) {
+ throw XProcException.xsOptionMustBeStatic(name, XdmLocation.from(node))
+ }
+ }
+ } catch {
+ case ex: XProcException =>
+ // Map err:XD0015 to err:XS0087 in this case
+ if (ex.code == XProcException.errxd(15)) {
+ throw XProcException.xsOptionUndeclaredNamespace(node.getAttributeValue(XProcConstants._name), ex.location)
+ } else {
+ throw ex
+ }
+ case ex: Exception =>
+ throw ex
+ }
+ }
+
+ case _ => ()
+ }
+
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ if (child.getNodeKind == XdmNodeKind.ELEMENT) {
+ recurse(child)
+ }
+ }
+ }
+ }
+
+ private def processImport(node: XdmNode): Unit = {
+ if (Option(node.getAttributeValue(XProcConstants._href)).isDefined) {
+ val uri = if (baseURI.isDefined) {
+ node.getBaseURI.resolve(node.getAttributeValue(XProcConstants._href))
+ } else {
+ val hrefuri = new URI(node.getAttributeValue(XProcConstants._href))
+ if (!hrefuri.isAbsolute) {
+ throw XProcException.xdInvalidURI(hrefuri.toString, None)
+ }
+ hrefuri
+ }
+
+ var ancestor = Option.empty[NodeHierarchy]
+ for (hier <- ancestors) {
+ if (hier.baseURI.isDefined && hier.baseURI.get == uri) {
+ ancestor = Some(hier)
+ }
+ }
+
+ if (_imports.contains(uri)) {
+ // nevermind, there's nothing gained from importing it twice at the same level
+ } else if (ancestor.isDefined) {
+ _reimports += ancestor.get
+ } else {
+ val newstack = ListBuffer.empty[NodeHierarchy] ++ ancestors
+ newstack += this
+ val request = new DocumentRequest(uri, MediaType.XML)
+ try {
+ val response = config.documentManager.parse(request)
+ if (response.contentType.xmlContentType) {
+ val hier = new NodeHierarchy(config, response.value.asInstanceOf[XdmNode], Some(uri), newstack.toList)
+
+ if (hier.root.getNodeName == XProcConstants.p_declare_step
+ && Option(hier.root.getAttributeValue(XProcConstants._type)).isEmpty) {
+ throw XProcException.xsStepTypeRequired(XdmLocation.from(root))
+ }
+
+ _imports.put(uri, hier)
+ _conditionalImports ++= hier._conditionalImports
+ changed = true
+ } else {
+ throw XProcException.xsInvalidPipeline(s"Document is not XML: ${uri}", None)
+ }
+ } catch {
+ case ex: XProcException =>
+ if (ex.code == XProcException.errxs(53)) {
+ throw ex
+ } else {
+ throw XProcException.xsImportFailed(uri, None)
+ }
+ case _: Exception =>
+ throw XProcException.xsImportFailed(uri, None)
+ }
+ }
+ }
+ }
+
+ private def useWhenExpression(node: XdmNode): Option[String] = {
+ if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
+ Option(node.getAttributeValue(XProcConstants._use_when))
+ } else {
+ Option(node.getAttributeValue(XProcConstants.p_use_when))
+ }
+ }
+
+ private def testUseWhen(node: XdmNode): Option[Boolean] = {
+ var context = Option.empty[StaticContext]
+ var stepType = Option.empty[QName]
+ if (node.getNodeName == XProcConstants.p_declare_step
+ && Option(node.getAttributeValue(XProcConstants._type)).isDefined) {
+ context = Some(new StaticContext(config, node))
+ stepType = Some(context.get.parseQName(node.getAttributeValue(XProcConstants._type)))
+ if (_conditionalSteps.contains(stepType.get)) {
+ _conditionalSteps(stepType.get) -= node
+ }
+ }
+ if (node.getNodeName == XProcConstants.p_import) {
+ _conditionalImports -= node
+ }
+
+ val usewhenexpr = useWhenExpression(node)
+ if (usewhenexpr.isEmpty) {
+ if (stepType.isDefined && pipelineStep(node).getOrElse(false)) {
+ _knownSteps += stepType.get
+ }
+ return Some(true)
+ }
+
+ var useWhen = Option.empty[Boolean]
+ if (useWhenStatus.contains(node)) {
+ useWhen = useWhenStatus(node)
+ }
+ if (useWhen.isDefined) {
+ return useWhen
+ }
+
+ if (context.isEmpty) {
+ context = Some(new StaticContext(config, node))
+ }
+
+ try {
+ val expr = new XProcXPathExpression(context.get, usewhenexpr.get)
+ val bindings = mutable.HashMap.empty[String, Message]
+ for ((name, value) <- config.staticOptions) {
+ bindings.put(name.getClarkName, value)
+ }
+ config.staticStepsAvailable = _knownSteps.toSet
+ useWhen = Some(config.expressionEvaluator.booleanValue(expr, List(), bindings.toMap, None))
+ logger.debug(s"Use-when: ${useWhen.get}: ${usewhenexpr.get}")
+ changed = true
+
+ if (node.getNodeName == XProcConstants.p_import) {
+ processImport(node)
+ }
+ } catch {
+ case ex: XProcException =>
+ if (ex.code == XProcException.err_stepAvailableIndeterminate) {
+ useWhen = None
+ } else {
+ throw ex
+ }
+ case ex: Exception =>
+ throw ex
+ }
+ useWhenStatus.put(node, useWhen)
+
+ if (stepType.isDefined) {
+ if (useWhen.isEmpty) {
+ if (_conditionalSteps.contains(stepType.get)) {
+ _conditionalSteps(stepType.get) += node
+ } else {
+ val set = mutable.HashSet.empty[XdmNode]
+ set += node
+ _conditionalSteps.put(stepType.get, set)
+ }
+ } else {
+ if (useWhen.get && pipelineStep(node).getOrElse(false)) {
+ _knownSteps += stepType.get
+ }
+ }
+ }
+
+ if (useWhen.isEmpty && node.getNodeName == XProcConstants.p_import) {
+ _conditionalImports += node
+ }
+
+ useWhen
+ }
+
+ private def resolve(): Boolean = {
+ changed = false
+
+ for (ximport <- imports) {
+ _knownSteps ++= ximport.knownSteps
+ }
+ for (ximport <- _reimports) {
+ _knownSteps ++= ximport.knownSteps
+ }
+
+ walk(_root.get)
+
+ for (ximport <- imports) {
+ ximport.resolve()
+ changed = changed || ximport.changed
+ }
+
+ changed
+ }
+
+ private def walk(node: XdmNode): Unit = {
+ val useWhen = testUseWhen(node)
+
+ if (!useWhen.getOrElse(false)) {
+ return
+ }
+
+ if (node.getNodeName == XProcConstants.p_declare_step && Option(node.getAttributeValue(XProcConstants._type)).isDefined) {
+ val context = new StaticContext(config, node)
+ if (pipelineStep(node).getOrElse(false)) {
+ _knownSteps += context.parseQName(node.getAttributeValue(XProcConstants._type))
+ }
+ }
+ if (node.getNodeName == XProcConstants.p_declare_step || node.getNodeName == XProcConstants.p_library) {
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ if (child.getNodeKind == XdmNodeKind.ELEMENT
+ && child.getNodeName == XProcConstants.p_declare_step
+ && Option(child.getAttributeValue(XProcConstants._type)).isDefined) {
+ val uw = testUseWhen(child)
+ if (uw.getOrElse(false)) {
+ val context = new StaticContext(config, child)
+ if (pipelineStep(child).getOrElse(false)) {
+ _knownSteps += context.parseQName(child.getAttributeValue(XProcConstants._type))
+ }
+ }
+ }
+ }
+ }
+
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ if (child.getNodeKind == XdmNodeKind.ELEMENT) {
+ walk(child)
+ }
+ }
+ }
+
+ private def pipelineStep(node: XdmNode): Option[Boolean] = {
+ for (step <- node.axisIterator(Axis.CHILD).asScala) {
+ if (step.getNodeKind == XdmNodeKind.ELEMENT) {
+ step.getNodeName match {
+ case XProcConstants.p_input => ()
+ case XProcConstants.p_output => ()
+ case XProcConstants.p_option => ()
+ case XProcConstants.p_documentation => ()
+ case XProcConstants.p_pipeinfo => ()
+ case XProcConstants.p_import => ()
+ case XProcConstants.p_import_functions => ()
+ case XProcConstants.p_declare_step => ()
+ case _ =>
+ val usewhenexpr = useWhenExpression(step)
+ if (usewhenexpr.isDefined) {
+ var useWhen = Option.empty[Boolean]
+ if (useWhenStatus.contains(step)) {
+ useWhen = useWhenStatus(step)
+ }
+ if (usewhenexpr.isDefined && useWhen.isEmpty) {
+ return None
+ } else {
+ if (useWhen.get) {
+ return useWhen
+ }
+ }
+ } else {
+ return Some(true)
+ }
+ }
+ }
+ }
+ Some(false)
+ }
+
+ private def resolveDeadlocks(): Unit = {
+ walkDeadlocks(_root.get)
+ for (ximport <- imports) {
+ ximport.resolveDeadlocks()
+ }
+ }
+
+ private def walkDeadlocks(node: XdmNode): Unit = {
+ val usewhenexpr = useWhenExpression(node)
+ var useWhen = Option.empty[Boolean]
+ if (useWhenStatus.contains(node)) {
+ useWhen = useWhenStatus(node)
+ }
+ if (useWhen.isEmpty && usewhenexpr.isDefined) {
+ throw XProcException.xsUseWhenDeadlock(usewhenexpr.get, XdmLocation.from(node))
+ } else {
+ useWhen = Some(true)
+ }
+
+ if (useWhen.getOrElse(false)) {
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ if (child.getNodeKind == XdmNodeKind.ELEMENT) {
+ walkDeadlocks(child)
+ }
+ }
+ }
+ }
+
+ private def unifyStatus(): Unit = {
+ for (ximport <- imports) {
+ ximport.unifyStatus()
+ useWhenStatus ++= ximport.useWhenStatus
+ }
+ }
+
+ private def staticOptionValue(node: XdmNode): XdmValueItemMessage = {
+ if (Option(node.getAttributeValue(XProcConstants._name)).isEmpty) {
+ throw XProcException.xsMissingRequiredAttribute(XProcConstants._name, XdmLocation.from(node))
+ }
+
+ val context = new XMLStaticContext(node)
+ val name = context.parseQName(node.getAttributeValue(XProcConstants._name))
+
+ if (Option(node.getAttributeValue(XProcConstants._required)).isDefined) {
+ if (node.getAttributeValue(XProcConstants._required) == "true") {
+ throw XProcException.xsRequiredAndStatic(name, XdmLocation.from(node))
+ }
+ }
+ if (Option(node.getAttributeValue(XProcConstants._select)).isEmpty) {
+ throw XProcException.xsMissingRequiredAttribute(XProcConstants._select, XdmLocation.from(node))
+ }
+ var value = if (config.options.contains(name)) {
+ new XdmValueItemMessage(config.options(name).value, XProcMetadata.ANY, config.options(name).context)
+ } else {
+ val select = node.getAttributeValue(XProcConstants._select)
+ val expr = new XProcXPathExpression(context, select)
+ val bindings = mutable.HashMap.empty[String, XdmValueItemMessage]
+ for ((name, value) <- config.staticOptions) {
+ bindings.put(name.getClarkName, value)
+ }
+ config.expressionEvaluator.value(expr, List(), bindings.toMap, None)
+ }
+
+ var qnameKeys = false
+ val as = Option(node.getAttributeValue(XProcConstants._as))
+ var declaredType = context.parseSequenceType(as, config.itemTypeFactory)
+ if (declaredType.isDefined) {
+ declaredType.get.getUnderlyingSequenceType.getPrimaryType match {
+ case map: MapType =>
+ if (map.getKeyType.getPrimitiveItemType.getTypeName == XProcConstants.structured_xs_QName) {
+ // We have to lie about the type of maps with QName keys because we're
+ // going to allow users to put strings in there.
+ qnameKeys = true
+ declaredType = Some(context.parseFakeMapSequenceType(as.get, config.itemTypeFactory))
+ }
+ case _ => ()
+ }
+
+ if (qnameKeys) {
+ value.item match {
+ case xmap: XdmMap =>
+ val qnameMap = S9Api.forceQNameKeys(xmap.getUnderlyingValue, value.context)
+ value = new XdmValueItemMessage(qnameMap, value.metadata, value.context)
+ case xmap: MapItem =>
+ val qnameMap = S9Api.forceQNameKeys(xmap, value.context)
+ value = new XdmValueItemMessage(qnameMap, value.metadata, value.context)
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Non-map item has qnameKeys: ${value.item}")
+ }
+ }
+
+ val tokens = XNameBinding.checkValueTokens(config, context, Option(node.getAttributeValue(XProcConstants._values)))
+ XNameBinding.promotedValue(config, name, declaredType, tokens, value)
+ } else {
+ value
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XArtifact.scala b/src/main/scala/com/xmlcalabash/model/xxml/XArtifact.scala
new file mode 100644
index 0000000..7121219
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XArtifact.scala
@@ -0,0 +1,728 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.Location
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, UniqueId, XProcConstants}
+import com.xmlcalabash.util.{TypeUtils, URIUtils}
+import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
+import net.sf.saxon.s9api.{Axis, QName, XdmNode, XdmNodeKind}
+import org.slf4j.{Logger, LoggerFactory}
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+import scala.jdk.CollectionConverters.IteratorHasAsScala
+import scala.reflect.ClassTag
+
+abstract class XArtifact(val config: XMLCalabash) {
+ protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
+ protected var _synthetic = false
+ protected var _syntheticName = Option.empty[QName]
+ private var _tumble_id = Option.empty[String]
+
+ private var _staticContext: XArtifactContext = _
+ private var _xml_id = Option.empty[String]
+ private val _exceptions = ListBuffer.empty[Exception]
+ private val _attributes = mutable.HashMap.empty[QName, String]
+ private var _parent = Option.empty[XArtifact]
+ private val _children = ListBuffer.empty[XArtifact]
+
+ def synthetic: Boolean = _synthetic
+ protected[xxml] def synthetic_=(synth: Boolean): Unit = {
+ _synthetic = synth
+ }
+ def syntheticName: Option[QName] = _syntheticName
+ protected[xxml] def syntheticName_=(name: QName): Unit = {
+ _syntheticName = Some(name)
+ }
+
+ def nodeName: QName = {
+ if (_synthetic) {
+ if (_syntheticName.isDefined) {
+ _syntheticName.get
+ } else {
+ XProcConstants._synthetic
+ }
+ } else {
+ _staticContext.nodeName
+ }
+ }
+ def location: Option[Location] = _staticContext.location
+ def staticContext: XArtifactContext = _staticContext
+ protected[xxml] def staticContext_=(context: XArtifactContext): Unit = {
+ _staticContext = context
+ }
+ protected[xxml] def tumble_id: String = {
+ if (_tumble_id.isEmpty) {
+ _tumble_id = Some(s"!syn_${UniqueId.nextId}")
+ }
+ _tumble_id.get
+ }
+ protected[xxml] def tumble_id_=(id: String): Unit = {
+ _tumble_id = Some(id)
+ }
+
+ def error(ex: Exception): Unit = {
+ ex match {
+ case ex: XProcException =>
+ if (location.isDefined) {
+ _exceptions += ex.withLocation(location.get)
+ } else {
+ _exceptions += ex
+ }
+ case _ =>
+ _exceptions += ex
+ }
+ // FIXME:
+ throw ex
+ }
+
+ def exceptions: List[Exception] = {
+ val exlist = ListBuffer.empty[Exception] ++ _exceptions
+ for (child <- allChildren) {
+ exlist ++= child.exceptions
+ }
+ exlist.toList
+ }
+
+ def parent: Option[XArtifact] = _parent
+ protected[xxml] def parent_=(parent: XArtifact): Unit = {
+ _parent = Some(parent)
+ }
+
+ def root: XArtifact = {
+ var p: Option[XArtifact] = Some(this)
+ while (p.get.parent.isDefined) {
+ p = p.get.parent
+ }
+ p.get
+ }
+
+ def attributes: Map[QName,String] = _attributes.toMap
+
+ protected[xxml] def ancestor[T <: XArtifact](implicit tag: ClassTag[T]): Option[T] = {
+ this match {
+ case art: T => Some(art)
+ case _ =>
+ if (parent.isDefined) {
+ parent.get.ancestor[T]
+ } else {
+ None
+ }
+ }
+ }
+
+ def allChildren: List[XArtifact] = _children.toList
+ protected[xxml] def allChildren_=(children: List[XArtifact]): Unit = {
+ _children.clear()
+ for (child <- children) {
+ addChild(child)
+ }
+ }
+
+ protected[xmlcalabash] def children[T <: XArtifact](implicit tag: ClassTag[T]): List[T] = {
+ allChildren.flatMap {
+ case art: T => Some(art)
+ case _ => None
+ }
+ }
+
+ protected[xxml] def addChild(child: XArtifact): Unit = {
+ child.parent = this
+ _children += child
+ }
+
+ private def findChild(find: XArtifact): Int = {
+ var idx = 0
+ var found = false
+ for (child <- allChildren) {
+ if (found || (child eq find)) {
+ found = true
+ } else {
+ idx += 1
+ }
+ }
+ idx
+ }
+
+ protected[xxml] def removeChild(remove: XArtifact): Unit = {
+ _children.remove(findChild(remove))
+ }
+
+ protected[xxml] def replaceChild(remove: XArtifact, add: XArtifact): Unit = {
+ val idx = findChild(remove)
+ _children.remove(idx)
+ _children.insert(idx, add)
+ }
+
+ protected[xxml] def insertBefore(insert: XArtifact, before: XArtifact): Unit = {
+ val idx = findChild(before)
+ insert.parent = this
+ _children.insert(idx, insert)
+ }
+
+ def attr(name: QName): Option[String] = {
+ if (_attributes.contains(name)) {
+ val value = _attributes(name)
+ _attributes.remove(name)
+ Some(value)
+ } else {
+ None
+ }
+ }
+
+ def parse(node: XdmNode, children: List[XArtifact]): Unit = {
+ _staticContext = new XArtifactContext(this, node)
+ _tumble_id = Some(tumbleId(node))
+
+ if (!synthetic) {
+ if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
+ for (attr <- node.axisIterator(Axis.ATTRIBUTE).asScala) {
+ if (attr.getNodeName != XProcConstants._use_when) {
+ _attributes.put(attr.getNodeName, attr.getStringValue)
+ }
+ if (attr.getNodeName == XProcConstants._expand_text) {
+ if (attr.getStringValue != "true" && attr.getStringValue != "false") {
+ error(XProcException.xsInvalidExpandText(attr.getNodeName, attr.getStringValue, location))
+ }
+ }
+ }
+
+ } else {
+ for (attr <- node.axisIterator(Axis.ATTRIBUTE).asScala) {
+ if (attr.getNodeName != XProcConstants.p_use_when) {
+ _attributes.put(attr.getNodeName, attr.getStringValue)
+ }
+ if (attr.getNodeName == XProcConstants.p_expand_text) {
+ if (attr.getStringValue != "true" && attr.getStringValue != "false") {
+ error(XProcException.xsInvalidExpandText(attr.getNodeName, attr.getStringValue, location))
+ }
+ }
+ }
+ }
+ }
+
+ for (child <- children) {
+ child.parent = this
+ }
+
+ _children ++= children
+ }
+
+ private def tumbleId(node: XdmNode): String = {
+ var count = 1
+ val iter = node.axisIterator(Axis.PRECEDING_SIBLING)
+ while (iter.hasNext) {
+ val child = iter.next()
+ if (child.getNodeKind == XdmNodeKind.ELEMENT) {
+ count += 1
+ }
+ }
+ val parent = Option(node.getParent)
+ if (parent.isEmpty) {
+ "!"
+ } else {
+ val pid = tumbleId(parent.get)
+ if (pid == "!") {
+ s"$pid$count"
+ } else {
+ s"$pid.$count"
+ }
+ }
+ }
+
+ protected def parsePipeAttribute(parent: XArtifact, pipe: String): List[XPipe] = {
+ val pipes = ListBuffer.empty[XPipe]
+ for (token <- pipe.trim.split("\\s+")) {
+ val pos = token.indexOf("@")
+ val pstr = if (pos < 0) {
+ token
+ } else {
+ token.substring(0, pos)
+ }
+ val sstr = if (pos < 0) {
+ ""
+ } else {
+ if (pos+1 == token.length) {
+ error(XProcException.xsInvalidPipeToken(token, location))
+ }
+ token.substring(pos + 1)
+ }
+
+ try {
+ val port = if (pstr == "") {
+ None
+ } else {
+ Some(staticContext.parseNCName(pstr))
+ }
+
+ val step = if (sstr == "") {
+ None
+ } else {
+ Some(staticContext.parseNCName(sstr))
+ }
+
+ pipes += new XPipe(parent, step, port)
+ } catch {
+ case _: XProcException =>
+ error(XProcException.xsInvalidPipeToken(token, location))
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+ pipes.toList
+ }
+
+ protected[xxml] def checkEmptyAttributes(): Unit = {
+ val p_element = nodeName.getNamespaceURI == XProcConstants.ns_p
+ val globalAttributes = if (synthetic || p_element) {
+ List(XProcConstants.xml_base, XProcConstants._exclude_inline_prefixes,
+ XProcConstants._expand_text, XProcConstants._use_when)
+ } else {
+ List(XProcConstants.xml_base, XProcConstants.p_exclude_inline_prefixes,
+ XProcConstants.p_expand_text, XProcConstants.p_use_when)
+ }
+ for (name <- attributes.keySet) {
+ if (!globalAttributes.contains(name)) {
+ if (p_element && name.getNamespaceURI == XProcConstants.ns_p) {
+ error(XProcException.xsXProcNamespaceError(name, nodeName, None))
+ } else {
+ this match {
+ case step: XStep =>
+ if (step.stepDeclaration.isDefined && step.stepDeclaration.get.stepType.isDefined) {
+ error(XProcException.xsUndeclaredOption(step.stepDeclaration.get.stepType.get, name, location))
+ } else {
+ error(XProcException.xsBadAttribute(name, None))
+ }
+ case _ =>
+ error(XProcException.xsBadAttribute(name, None))
+ }
+ }
+ }
+ }
+ }
+
+ protected[xxml] def checkAttributes(): Unit = {
+ val base = attr(XProcConstants.xml_base)
+ if (base.isDefined) {
+ val curbase = staticContext.baseURI.getOrElse(URIUtils.cwdAsURI)
+ staticContext.baseURI = curbase.resolve(base.get)
+ }
+ _xml_id = attr(XProcConstants.xml_id)
+ }
+
+ protected[xxml] def validate(): Unit = {
+ println(s"ERROR: ${this} does not override validate() (${this.getClass.getName})")
+ }
+
+ protected[xxml] def elaborateAttributes(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+ allChildren foreach { _.elaborateAttributes() }
+ }
+
+ protected def hoistSourcesToPipes(): Unit = {
+ val moveMap = mutable.HashMap.empty[XArtifact,XPipe]
+ val stepMap = mutable.HashMap.empty[XArtifact,XArtifact]
+ val pstep: Option[XStep] = if (parent.isDefined) {
+ parent.get match {
+ case xs: XStep => Some(xs)
+ case _ => None
+ }
+ } else {
+ None
+ }
+
+ for (child <- allChildren) {
+ child match {
+ case inline: XInline =>
+ val step = new XInlineLoader(inline, this)
+
+ // If the parent has dependencies, copy them onto the inline.
+ if (pstep.isDefined) {
+ step.dependsOn ++= pstep.get.dependsOn
+ }
+
+ val pipe = new XPipe(this, Some(step.tumble_id), Some("result"))
+ moveMap.put(inline, pipe)
+ stepMap.put(pipe, step)
+ case doc: XDocument =>
+ val step = new XDocumentLoader(doc, this)
+
+ // If the parent has dependencies, copy them onto the inline.
+ if (pstep.isDefined) {
+ step.dependsOn ++= pstep.get.dependsOn
+ }
+
+ val pipe = new XPipe(this, Some(step.tumble_id), Some("result"))
+ moveMap.put(doc, pipe)
+ stepMap.put(pipe, step)
+ case empty: XEmpty =>
+ val step = new XEmptyLoader(this)
+ val pipe = new XPipe(this, Some(step.tumble_id), Some("result"))
+ moveMap.put(empty, pipe)
+ stepMap.put(pipe, step)
+ case _: XPipe =>
+ ()
+ case _ =>
+ child.hoistSourcesToPipes()
+ }
+ }
+
+ if (moveMap.isEmpty) {
+ return
+ }
+
+ val portName = this match {
+ case port: XPort =>
+ port.port
+ case _ => ""
+ }
+
+ // Sometimes we're hoisting from within a p:with-input to the grandparent,
+ // sometimes we're hoisting from within a p:variable to the parent
+ var insertChild = this
+ var insertParent = parent
+ var found = false
+ while (!found && insertParent.isDefined) {
+ insertParent.get match {
+ case _: XTry =>
+ // Only TryCatchBranches allowed
+ ()
+ case _: XChoose =>
+ // Only ChooseBranches allowed
+ ()
+ case _: XWhen =>
+ // Make sure we hoist any inputs for the condition outside the when
+ found = portName != "condition"
+ case _: XLoopingStep =>
+ // Make sure we hoist any inputs for the loop outside the loop
+ found = portName != "#anon"
+ case _: XContainer =>
+ found = true
+ case _ =>
+ ()
+ }
+ if (!found) {
+ insertChild = insertParent.get
+ insertParent = insertChild.parent
+ }
+ }
+
+ if (!found) {
+ throw XProcException.xiThisCantHappen("Failed to find container ancestor for hoisting?")
+ }
+
+ for ((child, pipe) <- moveMap) {
+ replaceChild(child, pipe)
+ val ipipe = stepMap(pipe)
+ insertParent.get.insertBefore(ipipe, insertChild)
+ }
+ }
+
+ protected[xxml] def ancestorContainer: Option[XContainer] = {
+ var p: Option[XArtifact] = Some(this)
+ while (p.isDefined) {
+ p.get match {
+ case cont: XContainer =>
+ return Some(cont)
+ case _ => ()
+ }
+ p = p.get.parent
+ }
+ None
+ }
+
+ protected[xxml] def ancestorStep: Option[XStep] = {
+ var p: Option[XArtifact] = Some(this)
+ while (p.isDefined) {
+ p.get match {
+ case step: XDeclareStep =>
+ return Some(step)
+ case step: XStep =>
+ return Some(step)
+ case _ => ()
+ }
+ p = p.get.parent
+ }
+ None
+ }
+
+ protected[xxml] def ancestorNode: Option[XArtifact] = {
+ var p: Option[XArtifact] = Some(this)
+ while (p.isDefined) {
+ p.get match {
+ case _: XStep => return p
+ case _: XVariable => return p
+ case _: XOption => return p
+ case _ => ()
+ }
+ p = p.get.parent
+ }
+ None
+ }
+
+ protected[xxml] def ancestorOf(step: XArtifact): Boolean = {
+ var p: Option[XArtifact] = Some(step)
+ while (p.isDefined) {
+ if (p.get eq this) {
+ return true
+ }
+ p = p.get.parent
+ }
+ false
+ }
+
+ protected[xxml] def stepDeclaration: Option[XDeclareStep] = {
+ val dstep = ancestor[XStep]
+ if (dstep.isEmpty) {
+ return None
+ }
+
+ dstep.get match {
+ case decl: XDeclareStep =>
+ Some(decl)
+ case atomic: XAtomicStep =>
+ val dcontainer = ancestor[XDeclContainer]
+ if (dcontainer.isDefined) {
+ dcontainer.get.findDeclaration(atomic.stepType)
+ } else {
+ None
+ }
+ case _ =>
+ None
+ }
+ }
+
+ protected[xxml] def elaborateSyntacticSugar(): Unit = {
+ for (child <- allChildren) {
+ child.elaborateSyntacticSugar()
+ }
+ }
+
+ protected[xxml] def computeReadsFrom(): Unit = {
+ for (child <- allChildren) {
+ child.computeReadsFrom()
+ }
+ }
+
+ protected[xxml] def validateExplicitConnections(href: Option[String], pipe: Option[String]): List[XArtifact] = {
+ if (href.isDefined && pipe.isDefined) {
+ error(XProcException.xsPipeAndHref(None))
+ }
+
+ val pipeOk = if (parent.isDefined) {
+ parent.get match {
+ case _: XInput => false
+ case _ => true
+ }
+ } else {
+ true
+ }
+
+ var seenEmpty = false
+ val newChildren = ListBuffer.empty[XArtifact]
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case empty: XEmpty =>
+ if (href.isDefined) {
+ error(XProcException.xsHrefAndOtherSources(None))
+ }
+ if (pipe.isDefined) {
+ error(XProcException.xsPipeAndOtherSources(None))
+ }
+ if (seenEmpty) {
+ error(XProcException.xsNoSiblingsOnEmpty(location))
+ }
+ empty.validate()
+ newChildren += empty
+ seenEmpty = true
+ case doc: XDocument =>
+ if (href.isDefined) {
+ error(XProcException.xsHrefAndOtherSources(None))
+ }
+ if (pipe.isDefined) {
+ error(XProcException.xsPipeAndOtherSources(None))
+ }
+ if (seenEmpty) {
+ error(XProcException.xsNoSiblingsOnEmpty(location))
+ }
+ doc.validate()
+ newChildren += doc
+ case inline: XInline =>
+ if (href.isDefined) {
+ error(XProcException.xsHrefAndOtherSources(None))
+ }
+ if (pipe.isDefined) {
+ error(XProcException.xsPipeAndOtherSources(None))
+ }
+ if (seenEmpty) {
+ error(XProcException.xsNoSiblingsOnEmpty(location))
+ }
+ inline.validate()
+ newChildren += inline
+ case xpipe: XPipe =>
+ if (pipeOk) {
+ if (href.isDefined) {
+ error(XProcException.xsHrefAndOtherSources(None))
+ }
+ if (pipe.isDefined) {
+ error(XProcException.xsPipeAndOtherSources(None))
+ }
+ if (seenEmpty) {
+ error(XProcException.xsNoSiblingsOnEmpty(location))
+ }
+ xpipe.validate()
+ newChildren += xpipe
+ } else {
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ if (exceptions.isEmpty) {
+ if (href.isDefined) {
+ val doc = new XDocument(this, href.get)
+ doc.validate()
+ newChildren += doc
+ if (seenEmpty) {
+ error(XProcException.xsNoSiblingsOnEmpty(location))
+ }
+ }
+ if (pipe.isDefined) {
+ for (pipe <- parsePipeAttribute(this, pipe.get)) {
+ pipe.validate()
+ newChildren += pipe
+ }
+ if (seenEmpty) {
+ error(XProcException.xsNoSiblingsOnEmpty(location))
+ }
+ }
+ }
+
+ newChildren.toList
+ }
+
+ protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ var curdrp = initial
+ for (child <- allChildren) {
+ curdrp = child.elaborateDefaultReadablePort(curdrp)
+ }
+ curdrp
+ }
+
+ protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ var curcontext = initial
+
+ staticContext = staticContext.withConstants(initial)
+
+ for (child <- allChildren) {
+ child match {
+ case decl: XDeclareStep =>
+ decl.elaborateNameBindings(curcontext.onlyStatics)
+ case _ =>
+ curcontext = child.elaborateNameBindings(curcontext)
+ }
+ }
+
+ curcontext
+ }
+
+ protected[xxml] def elaborateScopedFeatures(): Unit = {
+ for (child <- allChildren) {
+ child.elaborateScopedFeatures()
+ }
+ }
+
+ protected[xxml] def elaboratePortConnections(): Unit = {
+ for (child <- allChildren) {
+ child.elaboratePortConnections()
+ }
+ }
+
+ protected[xxml] def elaborateDependsConnections(inScopeSteps: Map[String, XStep]): Unit = {
+ for (child <- children[XStep]) {
+ child.elaborateDependsConnections(inScopeSteps)
+ }
+ }
+
+ protected[xxml] def elaborateDynamicVariables(): Unit = {
+ allChildren foreach { _.elaborateDynamicVariables() }
+ }
+
+ protected[xxml] def elaborateValidatePortConnections(ports: XPortBindingContext): Unit = {
+ var curports = ports
+
+ this match {
+ case cont: XContainer =>
+ curports = curports.withContainer(cont)
+ case _ =>
+ ()
+ }
+
+ // Remove "irrelevant" inputs after we've validated them
+ val irrelevant = ListBuffer.empty[XArtifact]
+ for (child <- allChildren) {
+ child.elaborateValidatePortConnections(curports)
+ child match {
+ case port: XPort =>
+ if (port.irrelevant) {
+ irrelevant += port
+ }
+ case _ => ()
+ }
+ }
+ for (child <- irrelevant) {
+ if (child.exceptions.isEmpty) {
+ removeChild(child)
+ }
+ }
+ }
+
+ def dump: XdmNode = {
+ val sb = new SaxonTreeBuilder(config)
+ sb.startDocument(staticContext.baseURI)
+ dumpTree(sb)
+ sb.endDocument()
+ sb.result
+ }
+
+ def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ throw XProcException.xiThisCantHappen("Don't know how to dump an artifact")
+ }
+
+ def dumpTree(sb: SaxonTreeBuilder, name: String, attr: Map[String,Option[Any]]): Unit = {
+ dumpTree(sb, name, attr, None)
+ }
+
+ def dumpTree(sb: SaxonTreeBuilder, name: String, attr: Map[String,Option[Any]], text: String): Unit = {
+ dumpTree(sb, name, attr, Some(text))
+ }
+
+ private def dumpTree(sb: SaxonTreeBuilder, name: String, attr: Map[String,Option[Any]], text: Option[String]): Unit = {
+ var amap: AttributeMap = EmptyAttributeMap.getInstance()
+ for ((name, value) <- attr) {
+ if (value.isDefined) {
+ amap = amap.put(TypeUtils.attributeInfo(staticContext.parseQName(name), value.get.toString))
+ }
+ }
+ if (_synthetic) {
+ amap = amap.put(TypeUtils.attributeInfo(XProcConstants._synthetic, "true"))
+ }
+
+ if (!attr.contains("name") || attr("name").isEmpty || attr("name").get != tumble_id) {
+ amap = amap.put(TypeUtils.attributeInfo(XProcConstants._tumble_id, tumble_id))
+ }
+
+ sb.addStartElement(staticContext.parseQName(name), amap)
+ if (text.isDefined) {
+ sb.addText(text.get)
+ }
+ allChildren foreach { child => child.dumpTree(sb) }
+ sb.addEndElement()
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XArtifactContext.scala b/src/main/scala/com/xmlcalabash/model/xxml/XArtifactContext.scala
new file mode 100644
index 0000000..b618ff6
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XArtifactContext.scala
@@ -0,0 +1,52 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.Location
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api, XdmLocation}
+import net.sf.saxon.s9api.{QName, XdmNode}
+
+class XArtifactContext(val artifact: XArtifact, nodeName: QName, location: Option[Location]) extends XMLStaticContext(nodeName, location) {
+ def this(artifact: XArtifact, node: XdmNode) = {
+ this(artifact, node.getNodeName, XdmLocation.from(node))
+ _inscopeNamespaces ++= S9Api.inScopeNamespaces(node)
+ }
+
+ def this(artifact: XArtifact, ctx: MinimalStaticContext, name: QName) = {
+ this(artifact, name, ctx.location)
+ }
+
+ def constants: List[XNameBinding] = _inscopeConstants.toList
+
+ def withConstant(const: XNameBinding): XArtifactContext = {
+ val newContext = new XArtifactContext(artifact, nodeName, location)
+ newContext._inscopeConstants ++= _inscopeConstants
+ newContext._inscopeConstants += const
+ newContext._inscopeNamespaces ++= _inscopeNamespaces
+ newContext._baseURI = _baseURI
+ newContext._location = _location
+ newContext
+ }
+
+ def withConstants(constants: List[XNameBinding]): XArtifactContext = {
+ val newContext = new XArtifactContext(artifact, nodeName, location)
+ newContext._inscopeConstants ++= _inscopeConstants
+ newContext._inscopeConstants ++= constants
+ newContext._inscopeNamespaces ++= _inscopeNamespaces
+ newContext._baseURI = _baseURI
+ newContext._location = _location
+ newContext
+ }
+
+ def withConstants(bcontext: XNameBindingContext): XArtifactContext = {
+ if (bcontext.inScopeConstants.isEmpty) {
+ return this
+ }
+
+ val newContext = new XArtifactContext(artifact, nodeName, location)
+ newContext._inscopeConstants ++= _inscopeConstants
+ newContext._inscopeConstants ++= bcontext.inScopeConstants.values
+ newContext._inscopeNamespaces ++= _inscopeNamespaces
+ newContext._baseURI = _baseURI
+ newContext._location = _location
+ newContext
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XAtomicStep.scala b/src/main/scala/com/xmlcalabash/model/xxml/XAtomicStep.scala
new file mode 100644
index 0000000..db5491c
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XAtomicStep.scala
@@ -0,0 +1,365 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.runtime.{StepExecutable, StepProxy, StepRunner, StepWrapper, XMLCalabashRuntime, XmlStep}
+import com.xmlcalabash.util.MediaType
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XAtomicStep(config: XMLCalabash, val stepType: QName) extends XStep(config) {
+ _type = Some(stepType)
+
+ def this(parentStep: XContainer, stepType: QName) = {
+ this(parentStep.config, stepType)
+ staticContext = parentStep.staticContext
+ parent = parentStep
+ _synthetic = true
+ _syntheticName = Some(stepType)
+ }
+
+ override def primaryOutput: Option[XPort] = {
+ children[XWithOutput] find {
+ _.primary
+ }
+ }
+
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ if (ancestor[XDeclContainer].get.findDeclaration(stepType).isEmpty) {
+ return
+ }
+
+ val decl = stepDeclaration.get
+
+ val seenPorts = mutable.HashSet.empty[String]
+ val seenOptions = mutable.HashSet.empty[QName]
+ val newChildren = ListBuffer.empty[XArtifact]
+
+ for (input <- children[XWithInput]) {
+ input.validate()
+ newChildren += input
+ if (!input.portSpecified) {
+ val idecl = decl.children[XInput] find { _.primary }
+ if (idecl.isDefined) {
+ input.port = idecl.get.port
+ } else {
+ error(XProcException.xsNoPrimaryInputPort(stepType, location))
+ return
+ }
+ }
+
+ if (seenPorts.contains(input.port)) {
+ input.error(XProcException.xsDupWithInputPort(input.port, None))
+ } else {
+ if (decl.inputPorts.contains(input.port)) {
+ seenPorts += input.port
+ } else {
+ input.error(XProcException.xsNoSuchPort(input.port, stepType, None))
+ }
+ }
+ }
+
+ for (input <- decl.children[XInput]) {
+ if (!seenPorts.contains(input.port)) {
+ seenPorts += input.port
+ val xwi = new XWithInput(this, input.port)
+ newChildren += xwi
+ xwi.validate()
+ }
+ }
+
+ // Configure the XWithInput like it's XInput declaration
+ for (input <- children[XWithInput]) {
+ val idecl = decl.children[XInput] find { _.port == input.port }
+ input.sequence = idecl.get.sequence
+ input.contentTypes = idecl.get.contentTypes
+ input.primary = idecl.get.primary
+ }
+
+ if (children[XWithOption].nonEmpty) {
+ val input = new XWithInput(this, "#bindings")
+ input.primary = false
+ input.sequence = true
+ input.contentTypes = MediaType.MATCH_ANY
+ input.validate()
+ newChildren += input
+ }
+
+ for (output <- decl.outputs) {
+ seenPorts += output.port
+ val xwo = new XWithOutput(this, output.port)
+ xwo.validate()
+ newChildren += xwo
+ }
+
+ for (option <- children[XWithOption]) {
+ option.validate()
+ if (seenOptions.contains(option.name)) {
+ option.error(XProcException.xsDupWithOptionName(option.name, None))
+ } else {
+ seenOptions += option.name
+ }
+ newChildren += option
+ }
+
+ for (opt <- decl.options) {
+ if (!seenOptions.contains(opt.name)) {
+ if (opt.required) {
+ error(XProcException.xsMissingRequiredOption(opt.name, location))
+ }
+ val expr = opt.select.getOrElse("()")
+ val xwo = new XWithOption(this, opt.name, None, Some(expr))
+ newChildren += xwo
+ }
+ }
+
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XWithInput => ()
+ case _: XWithOutput => ()
+ case _: XWithOption => ()
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ allChildren = newChildren.toList
+ }
+
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ val decl = stepDeclaration
+ if (decl.isEmpty) {
+ if (stepType.getNamespaceURI == XProcConstants.ns_p) {
+ error(XProcException.xsElementNotAllowed(stepType, location))
+ } else {
+ error(XProcException.xsMissingDeclaration(stepType, location))
+ }
+ return
+ }
+
+ for (name <- attributes.keySet) {
+ if (decl.get.option(name).isDefined) {
+ syntheticOption(name, attr(name).get)
+ } else {
+ error(XProcException.xsUndeclaredOption(stepType, name, location))
+ }
+ }
+ }
+
+ /*
+ override def elaborateDeclarations(inScopeNames: Set[String]): Unit = {
+ val seenPorts = mutable.HashSet.empty[String]
+ val seenOptions = mutable.HashSet.empty[QName]
+ val newChildren = ListBuffer.empty[XArtifact]
+
+ val skeleton = declarationContainer.findSkeleton(stepType).get
+
+ for (input <- children[XWithInput]) {
+ if (!input.portSpecified) {
+ val idecl = skeleton.decl.children[XInput] find {
+ _.primary
+ }
+ if (idecl.isDefined) {
+ input.port = idecl.get.port
+ } else {
+ error(XProcException.xsNoPrimaryInputPort(stepType, location))
+ return
+ }
+ }
+ if (seenPorts.contains(input.port)) {
+ input.error(XProcException.xsDupWithInputPort(input.port, None))
+ newChildren += input
+ } else {
+ if (skeleton.inputs.contains(input.port)) {
+ seenPorts += input.port
+ input.elaborateDeclarations()
+ newChildren += input
+ } else {
+ input.error(XProcException.xsNoSuchPort(input.port, stepType, None))
+ newChildren += input // make sure we don't loose the exception!
+ }
+ }
+ }
+
+ for (port <- skeleton.inputs) {
+ if (!seenPorts.contains(port)) {
+ seenPorts += port
+ val input = new XWithInput(this, port)
+ newChildren += input
+ input.elaborateDeclarations()
+ }
+ }
+
+ if (children[XWithOption].nonEmpty) {
+ val input = new XWithInput(this, "#bindings")
+ input.primary = false
+ input.sequence = true
+ input.contentTypes = MediaType.MATCH_ANY
+ input.elaborateDeclarations()
+ newChildren += input
+ }
+
+ for (port <- skeleton.outputs) {
+ seenPorts += port
+ val output = new XWithOutput(this, port)
+ output.elaborateDeclarations()
+ newChildren += output
+ }
+
+ for (option <- children[XWithOption]) {
+ if (seenOptions.contains(option.name)) {
+ option.error(XProcException.xsDupWithOptionName(option.name, None))
+ } else {
+ seenOptions += option.name
+ option.elaborateDeclarations()
+ }
+ newChildren += option
+ }
+
+ for (name <- skeleton.options) {
+ if (!seenOptions.contains(name)) {
+ val odecl = skeleton.decl.option(name).get
+ if (odecl.required) {
+ throw XProcException.xsMissingRequiredOption(name, location)
+ }
+ val expr = odecl.select.getOrElse("()")
+ val opt = new XWithOption(this, name, None, Some(expr))
+ newChildren += opt
+ }
+ }
+
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XWithInput => ()
+ case _: XWithOption => ()
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ allChildren = newChildren.toList
+ }
+*/
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ super.elaborateDefaultReadablePort(initial)
+ val drp = children[XWithOutput] find {
+ _.primary
+ }
+ drp
+ }
+
+ override protected[xxml] def elaborateValidatePortConnections(ports: XPortBindingContext): Unit = {
+ super.elaborateValidatePortConnections(ports)
+
+ val decl = stepDeclaration.get
+
+ for (child <- children[XWithInput]) {
+ if (!child.port.startsWith("#depends_") && child.port != "#bindings") {
+ if (child.children[XDataSource].isEmpty) {
+ val input = decl.children[XInput] find { _.port == child.port }
+ if (input.get.defaultInputs.nonEmpty) {
+ child.allChildren = input.get.defaultInputs
+ } else {
+ error(XProcException.xsUnconnectedInputPort(stepName, child.port, location))
+ }
+ }
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ var bcontext = initial
+ for (child <- allChildren) {
+ bcontext = child.elaborateNameBindings(bcontext)
+ }
+
+ staticContext = staticContext.withConstants(bcontext)
+
+ initial
+ }
+
+ override protected def elaborateDynamicOptions(): Unit = {
+ children[XWithOption] foreach { _.elaborateDynamicOptions() }
+ val xbind = children[XWithInput] find { _.port == "#bindings" }
+ if (xbind.isDefined) {
+ if (xbind.get.allChildren.isEmpty) {
+ removeChild(xbind.get)
+ }
+ }
+ }
+
+ // =======================================================================================
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ContainerStart]
+ val impl = stepImplementation
+ impl.configure(config, stepType, name, None)
+
+ val proxy = new StepProxy(runtime, stepType, impl, staticContext)
+ runtime.addNode(this, start.addAtomic(proxy, stepType.toString))
+ }
+
+ protected def stepImplementation: StepExecutable = {
+ val decl = stepDeclaration
+ if (decl.isEmpty) {
+ throw XProcException.xsMissingDeclaration(stepType, location)
+ }
+ if (decl.get.atomic) {
+ if (decl.get.implementationClass.isDefined) {
+ val implClass = decl.get.implementationClass.get
+ val klass = Class.forName(implClass).getDeclaredConstructor().newInstance()
+ klass match {
+ case step: XmlStep =>
+ new StepWrapper(step, decl.get)
+ case _ =>
+ throw XProcException.xiStepImplementationError(s"Class does not implement an XmlStep: ${stepType}", location)
+ }
+ } else {
+ throw XProcException.xiStepImplementationError(s"No implementation for ${stepType}", location);
+ }
+ } else {
+ new StepRunner(decl.get)
+ }
+ }
+
+ // =======================================================================================
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("type", Some(stepType.getEQName))
+ attr.put("name", Some(stepName))
+ dumpTree(sb, "p:atomic-step", attr.toMap)
+ }
+
+ override def toString: String = {
+ if (stepName != tumble_id) {
+ s"${stepType}(${stepName};${tumble_id})"
+ } else {
+ s"${stepType}(${stepName})"
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XCatch.scala b/src/main/scala/com/xmlcalabash/model/xxml/XCatch.scala
new file mode 100644
index 0000000..bdd9855
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XCatch.scala
@@ -0,0 +1,54 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{Node, TryCatchStart}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+
+class XCatch(config: XMLCalabash) extends XTryCatchBranch(config) {
+ private val _codes = mutable.HashSet.empty[QName]
+
+ def codes: Set[QName] = _codes.toSet
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ if (attributes.contains(XProcConstants._code)) {
+ val str = attr(XProcConstants._code).get
+ var repeated = Option.empty[QName]
+ for (code <- str.split("\\s+")) {
+ try {
+ val qname = staticContext.parseQName(code)
+ if (repeated.isEmpty && _codes.contains(qname)) {
+ repeated = Some(qname)
+ }
+ _codes += qname
+ } catch {
+ case _: Exception =>
+ error(XProcException.xsCatchInvalidCode(code, None))
+ }
+ }
+ if (repeated.isDefined) {
+ error(XProcException.xsCatchRepeatedCode(repeated.get, None))
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ var curdrp: Option[XPort] = children[XInput] find { _.primary == true }
+ for (child <- allChildren) {
+ curdrp = child.elaborateDefaultReadablePort(curdrp)
+ }
+ initial
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[TryCatchStart]
+ val node = start.addCatch(stepName, _codes.toList, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XChoose.scala b/src/main/scala/com/xmlcalabash/model/xxml/XChoose.scala
new file mode 100644
index 0000000..5e9d568
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XChoose.scala
@@ -0,0 +1,215 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.util.MediaType
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XChoose(config: XMLCalabash) extends XContainer(config) {
+
+ def this(parentStep: XArtifact) = {
+ this(parentStep.config)
+ staticContext = parentStep.staticContext
+ parent = parentStep
+ synthetic = true
+ syntheticName = XProcConstants.p_choose
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ var seenWithInput = false
+ var seenWhen = false;
+ var seenOtherwise = false
+
+ //val newScope = checkStepNameScoping(inScopeNames)
+
+ val newChildren = ListBuffer.empty[XArtifact]
+ for (child <- allChildren) {
+ child.validate()
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XOutput =>()
+ case input: XWithInput =>
+ if (seenWithInput) {
+ error(XProcException.xsInvalidPipeline("A p:choose can have at most one p:with-input", location))
+ }
+ if (seenWhen || seenOtherwise) {
+ error(XProcException.xsInvalidPipeline("A p:with-input cannot follow p:when or p:otherwise in p:choose", location))
+ }
+ seenWithInput = true
+ newChildren += input
+ case when: XWhen =>
+ if (seenOtherwise) {
+ error(XProcException.xsInvalidPipeline("A p:when cannot follow p:otherwise in p:choose", location))
+ }
+ seenWhen = true
+ newChildren += when
+ case otherwise: XOtherwise =>
+ if (seenOtherwise) {
+ error(XProcException.xsInvalidPipeline("A p:choose can have at most one p:otherwise", location))
+ }
+ seenOtherwise = true
+ newChildren += otherwise
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ if (!seenWhen && !seenOtherwise) {
+ error(XProcException.xsMissingWhen(None))
+ }
+
+ allChildren = newChildren.toList
+
+ if (exceptions.nonEmpty) {
+ return
+ }
+
+ val branch = children[XChooseBranch].head
+ if (children[XOtherwise].isEmpty) {
+ val other = new XOtherwise(this)
+ addChild(other)
+
+ val identity = new XAtomicStep(other, XProcConstants.p_identity)
+ other.addChild(identity)
+
+ val wi = new XWithInput(identity, "source")
+ identity.addChild(wi)
+
+ if (branch.primaryOutput.isEmpty) {
+ val sink = new XAtomicStep(other, XProcConstants.p_sink)
+ other.addChild(sink)
+ } else {
+ val xout = new XOutput(other, Some(branch.primaryOutput.get.port))
+ xout.primary = true
+ xout.sequence = true
+ other.insertBefore(xout, identity)
+ }
+
+ other.validate()
+ }
+
+ // Make sure all of the branches have consistent primary output ports
+ var cprimary = Option.empty[String]
+ for (branch <- children[XChooseBranch]) {
+ for (output <- branch.children[XOutput]) {
+ if (output.primary) {
+ cprimary = Some(output.port)
+ }
+ }
+ }
+
+ val boutputs = mutable.HashMap.empty[String, XOutput]
+ for (branch <- children[XChooseBranch]) {
+ var bprimary = Option.empty[String]
+ for (output <- branch.children[XOutput]) {
+ if (cprimary.isDefined && output.primary && cprimary.get != output.port) {
+ error(XProcException.xsBadChooseOutputs(output.port, cprimary.get, location))
+ } else {
+ if (boutputs.contains(output.port)) {
+ if (boutputs(output.port).primary != output.primary) {
+ error(XProcException.xsBadChooseOutputs(output.port, location))
+ }
+ } else {
+ boutputs.put(output.port, output)
+ }
+ }
+ if (output.primary) {
+ bprimary = Some(output.port)
+ }
+ }
+ if (cprimary.isDefined != bprimary.isDefined) {
+ if (cprimary.isDefined) {
+ error(XProcException.xsBadChooseOutputs(cprimary.get, location))
+ } else {
+ error(XProcException.xsBadChooseOutputs(bprimary.get, location))
+ }
+ }
+ }
+
+ for (port <- boutputs.keySet) {
+ val oport = if (port == "") {
+ None
+ } else {
+ Some(port)
+ }
+ val output = new XOutput(this, oport)
+ output.primary = boutputs(port).primary
+ output.sequence = true
+ output.contentTypes = MediaType.MATCH_ANY
+ insertBefore(output, allChildren.head)
+
+ for (branch <- children[XChooseBranch]) {
+ val out = branch.children[XOutput] find { _.port == port }
+ if (out.isDefined) {
+ val pipe = new XPipe(output, branch.stepName, port)
+ output.addChild(pipe)
+ }
+ }
+ }
+ }
+
+ override def elaboratePortConnections(): Unit = {
+ if (_drp.isDefined && children[XWithInput].isEmpty) {
+ val input = new XWithInput(config)
+ input.staticContext = staticContext
+ input.parent = this
+ val pipe = new XPipe(_drp.get)
+ pipe.parent = input
+ input.addChild(pipe)
+ insertBefore(input, allChildren.head)
+ }
+
+ super.elaboratePortConnections()
+
+ // The with-input isn't needed anymore
+ val input = children[XWithInput].headOption
+ if (input.isDefined) {
+ input.get.irrelevant = true
+ }
+ }
+
+ override def publicPipeConnections: Map[String,XPort] = {
+ Map()
+ }
+
+ override def privatePipeConnections: Map[String,XPort] = {
+ val ports = mutable.HashMap.empty[String,XPort]
+
+ for (input <- children[XInput]) {
+ val port = input.port
+ ports.put(s"${name}/${port}", input)
+ }
+
+ for (child <- children[XStep]) {
+ val name = child.stepName
+ for (output <- child.children[XOutput]) {
+ val port = output.port
+ ports.put(s"${name}/${port}", output)
+ }
+ for (output <- child.children[XWithOutput]) {
+ val port = output.port
+ ports.put(s"${name}/${port}", output)
+ }
+ }
+
+ // There are no option or variable children of p:choose
+
+ ports.toMap
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ContainerStart]
+ val node = start.addChoose(stepName)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XChooseBranch.scala b/src/main/scala/com/xmlcalabash/model/xxml/XChooseBranch.scala
new file mode 100644
index 0000000..c744b71
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XChooseBranch.scala
@@ -0,0 +1,70 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ChooseStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+import com.xmlcalabash.runtime.params.XPathBindingParams
+import com.xmlcalabash.runtime.{XMLCalabashRuntime, XProcXPathExpression}
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+abstract class XChooseBranch(config: XMLCalabash) extends XContainer(config) {
+ protected var _test = ""
+ protected var _collection = false
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ var curdrp = initial
+ for (child <- allChildren) {
+ curdrp = child.elaborateDefaultReadablePort(curdrp)
+ }
+ initial
+ }
+
+ protected def orderChildren(): Unit = {
+ if (exceptions.nonEmpty) {
+ return
+ }
+ val newChildren = ListBuffer.empty[XArtifact]
+ newChildren ++= children[XWithInput]
+ newChildren ++= children[XOutput]
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XWithInput => ()
+ case _: XOutput => ()
+ case _ =>
+ newChildren += child
+ }
+ }
+ allChildren = newChildren.toList
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ChooseStart]
+
+ val params = new XPathBindingParams(_collection)
+ val testExpr = new XProcXPathExpression(staticContext, _test, None, None, Some(params))
+
+ val node = start.addWhen(testExpr, stepName, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("name", Some(stepName))
+
+ if (this.isInstanceOf[XWhen]) {
+ attr.put("test", Some(_test))
+ }
+
+ if (_collection) {
+ attr.put("collection", Some(_collection))
+ }
+ dumpTree(sb, nodeName.toString, attr.toMap)
+ }
+
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XContainer.scala b/src/main/scala/com/xmlcalabash/model/xxml/XContainer.scala
new file mode 100644
index 0000000..6dc584c
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XContainer.scala
@@ -0,0 +1,373 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.steps.{Manifold, PortCardinality, PortSpecification}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+import com.xmlcalabash.util.MediaType
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+abstract class XContainer(config: XMLCalabash) extends XStep(config) {
+ protected var _drp = Option.empty[XPort]
+
+ def containerManifold: Manifold = {
+ val spec = mutable.HashMap.empty[String, PortCardinality]
+ for (output <- outputs) {
+ if (output.sequence) {
+ spec.put(output.port, PortCardinality.ZERO_OR_MORE)
+ } else {
+ spec.put(output.port, PortCardinality.EXACTLY_ONE)
+ }
+ }
+ new Manifold(Manifold.WILD, new PortSpecification(spec.toMap))
+ }
+
+ override def primaryOutput: Option[XPort] = {
+ children[XOutput] find { _.primary }
+ }
+
+ protected def constructDefaultOutput(): Unit = {
+ val firstStep = children[XStep].head
+ val step = children[XStep].last
+ val pout = step.primaryOutput
+ if (pout.isEmpty) {
+ return // There isn't a default putput
+ }
+
+ if (children[XOutput].nonEmpty) {
+ val implicitPrimary = children[XOutput].length == 1
+ for (xout <- children[XOutput]) {
+ checkDefaultOutput(pout.get, xout, implicitPrimary)
+ }
+ return
+ }
+
+ val output = this match {
+ case _: XViewport =>
+ new XOutput(this, Some("result"))
+ case _ =>
+ new XOutput(this, None)
+ }
+ output.primary = true
+ output.sequence = pout.get.sequence
+ output.contentTypes = pout.get.contentTypes
+
+ if (Option(firstStep).isDefined) {
+ insertBefore(output, firstStep)
+ } else {
+ addChild(output)
+ }
+
+ checkDefaultOutput(pout.get, output, implicitPrimary=true)
+ }
+
+ private def checkDefaultOutput(stepOut: XPort, output: XOutput, implicitPrimary: Boolean): Unit = {
+ if (implicitPrimary && !output.primarySpecified) {
+ output.primary = true
+ }
+
+ if (!output.primary || output.children[XDataSource].nonEmpty) {
+ return
+ }
+
+ val pipe = new XPipe(stepOut)
+ output.addChild(pipe)
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ var curdrp = initial
+ for (child <- allChildren) {
+ curdrp = child.elaborateDefaultReadablePort(curdrp)
+ }
+
+ val drp = children[XOutput] find { _.primary }
+ drp
+ }
+
+ override protected[xxml] def elaborateDependsConnections(inScopeSteps: Map[String,XStep]): Unit = {
+ for (name <- dependsOn.keySet) {
+ if (inScopeSteps.contains(name)) {
+ dependsOn.put(name, Some(inScopeSteps(name)))
+ } else {
+ error(XProcException.xsNotAStep(name, location))
+ }
+ }
+
+ val steps = mutable.HashMap.empty[String,XStep] ++ inScopeSteps
+ for (child <- children[XStep]) {
+ child match {
+ case _: XWhen => ()
+ case _: XOtherwise => ()
+ case _: XCatch => ()
+ case _: XFinally => ()
+ case _ =>
+ if (child.name.isDefined) {
+ steps.put(child.name.get, child)
+ }
+ }
+ }
+
+ for (child <- children[XStep]) {
+ child.elaborateDependsConnections(steps.toMap)
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ for (child <- allChildren) {
+ child.validate()
+ }
+
+ val seenPorts = mutable.HashSet.empty[String]
+ val newChildren = ListBuffer.empty[XArtifact]
+
+ for (input <- children[XInput]) {
+ if (seenPorts.contains(input.port)) {
+ input.error(XProcException.xsDupPortName(input.port, None))
+ } else {
+ seenPorts += input.port
+ newChildren += input
+ }
+ }
+
+ for (output <- children[XOutput]) {
+ if (seenPorts.contains(output.port)) {
+ output.error(XProcException.xsDupPortName(output.port, None))
+ } else {
+ seenPorts += output.port
+ newChildren += output
+ }
+ }
+
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XInput => ()
+ case _: XOutput => ()
+ case _: XWithInput => ()
+ if (this.isInstanceOf[XIf]) {
+ newChildren += child
+ } else {
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ case _: XVariable =>
+ newChildren += child
+ case _: XStep =>
+ newChildren += child
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ allChildren = newChildren.toList
+
+ if (children[XOutput].length == 1) {
+ val output = children[XOutput].head
+ if (!output.primarySpecified) {
+ output.primary = true
+ }
+ }
+
+ constructDefaultOutput()
+ }
+
+ protected def checkStepNameScoping(inScopeNames: Set[String]): Set[String] = {
+ val newScope = mutable.Set.empty[String] ++ inScopeNames
+
+ if (name.isDefined) {
+ if (newScope.contains(name.get)) {
+ error(XProcException.xsDuplicateStepName(name.get, location))
+ }
+ newScope += name.get
+ }
+
+ for (step <- children[XStep]) {
+ if (step.name.isDefined) {
+ if (newScope.contains(step.name.get)) {
+ error(XProcException.xsDuplicateStepName(step.name.get, location))
+ }
+ newScope += step.name.get
+ }
+ }
+
+ for (step <- children[XContainer]) {
+ // newScope contains the names of all the children, but we need
+ // to exclude *this* child's name for each descent.
+ val childScope = mutable.Set.empty[String] ++ newScope
+ if (step.name.isDefined) {
+ childScope -= step.name.get
+ }
+ step.checkStepNameScoping(childScope.toSet)
+ }
+
+ newScope.toSet
+ }
+
+ def publicPipeConnections: Map[String,XPort] = {
+ val ports = mutable.HashMap.empty[String,XPort]
+
+ for (input <- children[XInput]) {
+ val port = input.port
+ ports.put(s"${name.getOrElse(tumble_id)}/${port}", input)
+ }
+
+ for (child <- children[XStep]) {
+ val name = child.stepName
+ for (output <- child.children[XOutput]) {
+ val port = output.port
+ ports.put(s"${name}/${port}", output)
+ }
+ for (output <- child.children[XWithOutput]) {
+ val port = output.port
+ ports.put(s"${name}/${port}", output)
+ }
+ }
+
+ for (child <- children[XOption]) {
+ ports.put(s"${child.tumble_id}/result", child.children[XWithOutput].head)
+ }
+ for (child <- children[XVariable]) {
+ ports.put(s"${child.tumble_id}/result", child.children[XWithOutput].head)
+ }
+
+ ports.toMap
+ }
+
+ def privatePipeConnections: Map[String,XPort] = {
+ // Private pipe connections are ones that must be available
+ // to the pipeline engine, but aren't exposed to the pipeline
+ // author. For example, the pipeline author can't connect to the
+ // output ports on a p:when, but the p:choose step must be able to!
+ Map()
+ }
+
+ override protected[xxml] def elaborateValidatePortConnections(ports: XPortBindingContext): Unit = {
+ super.elaborateValidatePortConnections(ports)
+ if (exceptions.isEmpty) {
+ this match {
+ case _: XLibrary =>
+ ()
+ case decl: XDeclareStep =>
+ if (!decl.atomic) {
+ checkUnboundOutputs()
+ }
+ case _ =>
+ checkUnboundOutputs()
+ }
+ }
+ }
+
+ private def checkUnboundOutputs(): Unit = {
+ for (step <- children[XStep]) {
+ for (io <- step.children[XPort]) {
+ io match {
+ case _: XInput =>
+ ()
+ case _: XWithInput =>
+ ()
+ case port: XPort =>
+ ()
+ }
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateInsertContentTypeFilters(): Unit = {
+ for (child <- children[XInput]) {
+ if (!MediaType.OCTET_STREAM.allowed(child.contentTypes)) {
+ val filterable = true // FIXME: not necessary for compound steps where we can see what the input is
+ if (filterable) {
+ addInputFilter(child, new XContentTypeChecker(this, child))
+ }
+ }
+ }
+
+ for (child <- children[XStep]) {
+ child.elaborateInsertContentTypeFilters()
+ }
+
+ for (child <- children[XOutput]) {
+ if (!MediaType.OCTET_STREAM.allowed(child.contentTypes)) {
+ var filter = false
+ for (pipe <- child.children[XPipe]) {
+ for (ctype <- pipe.from.get.contentTypes filter { _.inclusive }) {
+ filter = filter || !ctype.allowed(child.contentTypes)
+ }
+ }
+ if (filter) {
+ addOutputFilter(child, new XContentTypeChecker(this, child))
+ }
+ }
+ }
+ }
+
+ override protected def addInputFilter(child: XPort, filter: XStep): Unit = {
+ val firstChild = children[XStep].head
+ val xwi = new XWithInput(filter, "source")
+ xwi.primary = true
+ xwi.sequence = child.sequence
+ xwi.contentTypes = MediaType.MATCH_ANY.toList
+ xwi.addChild(new XPipe(xwi, child))
+ val xwo = new XWithOutput(filter, "result")
+ xwo.primary = true
+ xwo.sequence = child.sequence
+ xwo.contentTypes = child.contentTypes
+ filter.addChild(xwi)
+ filter.addChild(xwo)
+ patchBinding(this, child, xwo)
+ insertBefore(filter, firstChild)
+ xwi.validate()
+ xwo.validate()
+ }
+
+ private def addOutputFilter(child: XPort, filter: XStep): Unit = {
+ val xwi = new XWithInput(filter, "source")
+ xwi.primary = true
+ xwi.sequence = child.sequence
+ xwi.contentTypes = MediaType.MATCH_ANY.toList
+ xwi.allChildren = child.allChildren
+ child.allChildren = List()
+
+ val xwo = new XWithOutput(filter, "result")
+ xwo.primary = true
+ xwo.sequence = child.sequence
+ xwo.contentTypes = child.contentTypes
+ filter.addChild(xwi)
+ filter.addChild(xwo)
+ child.addChild(new XPipe(child, filter.stepName, "result"))
+ addChild(filter)
+ xwi.validate()
+ xwo.validate()
+ }
+
+ private def patchBinding(artifact: XArtifact, from: XPort, originPort: XPort): Unit = {
+ // Don't replace them while we're walking over the data structure
+ val replace = ListBuffer.empty[XPipe]
+ for (child <- artifact.allChildren) {
+ child match {
+ case pipe: XPipe =>
+ if (pipe.from.isDefined && pipe.from.get == from) {
+ replace += pipe
+ }
+ case _ =>
+ patchBinding(child, from, originPort)
+ }
+ }
+ for (pipe <- replace) {
+ val newPipe = new XPipe(pipe.parent.get, originPort)
+ pipe.parent.get.replaceChild(pipe, newPipe)
+ }
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("name", Some(stepName))
+ dumpTree(sb, nodeName.toString, attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XContentTypeChecker.scala b/src/main/scala/com/xmlcalabash/model/xxml/XContentTypeChecker.scala
new file mode 100644
index 0000000..6e56931
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XContentTypeChecker.scala
@@ -0,0 +1,30 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.params.ContentTypeCheckerParams
+import com.xmlcalabash.runtime.{StepProxy, XMLCalabashRuntime}
+
+class XContentTypeChecker(parentStep: XArtifact, xport: XPort) extends XAtomicStep(parentStep.config, XProcConstants.cx_content_type_checker) {
+ staticContext = parentStep.staticContext
+ _synthetic = true
+ parent = parentStep
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val inputPort = xport match {
+ case _: XInput => true
+ case _: XWithInput => true
+ case _ => false
+ }
+
+ val params = new ContentTypeCheckerParams(xport.port, xport.contentTypes, parentStep.staticContext,
+ xport.select, inputPort, xport.sequence)
+
+ val start = parent.asInstanceOf[ContainerStart]
+ val impl = stepImplementation
+ impl.configure(config, stepType, name, Some(params))
+
+ val proxy = new StepProxy(runtime, stepType, impl, staticContext)
+ runtime.addNode(this, start.addAtomic(proxy, "cx:content-type-checker"))
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XDataSource.scala b/src/main/scala/com/xmlcalabash/model/xxml/XDataSource.scala
new file mode 100644
index 0000000..3161eb4
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XDataSource.scala
@@ -0,0 +1,77 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.util.MediaType
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable.ListBuffer
+
+abstract class XDataSource(config: XMLCalabash) extends XArtifact(config) {
+ protected var _drp: Option[XPort] = None
+
+ protected[xxml] def drp: Option[XPort] = _drp
+
+ protected def parseContentType(): Option[MediaType] = {
+ try {
+ val contentType = staticContext.parseSingleContentType(attr(XProcConstants._content_type))
+ if (contentType.isDefined) {
+ contentType.get.assertValid
+ return contentType
+ }
+ } catch {
+ case ex: XProcException =>
+ error(ex)
+ }
+
+ None
+ }
+
+ protected def resolveBindings(contextDependent: Boolean, refs: Set[QName], context: XNameBindingContext): Unit = {
+ if (!contextDependent) {
+ _drp = None
+ }
+
+ val namepipe = ListBuffer.empty[XNameBinding]
+ for (ref <- refs) {
+ val cbind = context.inScopeConstants.get(ref)
+ val dbind = context.inScopeDynamics.get(ref)
+ if (cbind.isDefined) {
+ // ok
+ } else if (dbind.isDefined) {
+ dbind.get match {
+ case v: XVariable =>
+ namepipe += v
+ case o: XOption =>
+ namepipe += o
+ case _ =>
+ error(XProcException.xiThisCantHappen(s"Unexpected name binding: ${dbind.get}"))
+ }
+ } else {
+ error(XProcException.xsNoBindingInExpression(ref, None))
+ }
+ }
+
+ if (namepipe.nonEmpty) {
+ val xwi = new XWithInput(this, "#bindings", false, true, MediaType.MATCH_ANY)
+ for (binding <- namepipe) {
+ val xstep = context.inScopeDynamics.get(binding.name)
+ val pipe = new XPipe(xwi, xstep.get.tumble_id, "result")
+ xwi.addChild(pipe)
+ }
+ addChild(xwi)
+ }
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ super.elaborateDefaultReadablePort(initial)
+ }
+
+ override protected[xxml] def elaboratePortConnections(): Unit = {
+ if (drp.isDefined && children[XDataSource].isEmpty) {
+ addChild(new XPipe(drp.get))
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XDeclContainer.scala b/src/main/scala/com/xmlcalabash/model/xxml/XDeclContainer.scala
new file mode 100644
index 0000000..0ca3f5d
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XDeclContainer.scala
@@ -0,0 +1,226 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.config.StepSignature
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+abstract class XDeclContainer(config: XMLCalabash) extends XContainer(config) {
+ protected var _psvi_required = Option.empty[Boolean]
+ protected var _xpath_version = Option.empty[Double]
+ protected var _version = Option.empty[Double]
+ protected var _skeletons: mutable.HashMap[QName, XSkeletonStepSignature] = mutable.HashMap.empty[QName, XSkeletonStepSignature]
+ protected var _builtinLibraries: ListBuffer[XLibrary] = ListBuffer.empty[XLibrary]
+ protected val _precedingStaticOptions: ListBuffer[XOption] = ListBuffer.empty[XOption]
+ protected val _inScopeSteps: mutable.HashSet[XDeclareStep] = mutable.HashSet.empty[XDeclareStep]
+ protected val _xoptions: mutable.HashMap[QName,XOption] = mutable.HashMap.empty[QName, XOption]
+
+
+ def inScopeSteps: List[XDeclareStep] = _inScopeSteps.toList
+ protected[xxml] def addInScopeSteps(steps: List[XDeclareStep]): Unit = {
+ for (step <- steps) {
+ if (step.visibility == "public") {
+ _inScopeSteps += step
+ } else {
+ println("bang")
+ }
+ }
+ }
+
+ def builtinLibraries: List[XLibrary] = _builtinLibraries.toList
+ protected[xxml] def builtinLibraries_=(libs: List[XLibrary]): Unit = {
+ _builtinLibraries.clear()
+ _builtinLibraries ++= libs
+ }
+
+ def findDeclaration(stepType: QName): Option[XDeclareStep] = {
+ for (step <- inScopeSteps) {
+ if (step.stepType.isDefined && step.stepType.get == stepType) {
+ return Some(step)
+ }
+ }
+
+ for (lib <- builtinLibraries) {
+ val decl = lib.findDeclaration(stepType)
+ if (decl.isDefined) {
+ return decl
+ }
+ }
+
+ None
+ }
+
+ protected[xxml] def xelaborate(): Unit = {
+ val stepList = ListBuffer.empty[XDeclareStep]
+
+ val seenOptions = ListBuffer.empty[XOption]
+ val newChildren = ListBuffer.empty[XArtifact]
+ for (child <- allChildren) {
+ child match {
+ case opt: XOption =>
+ // Peek to see if this is a static option; it hasn't been validated
+ // yet, so this will be a bit speculative...
+ val static = opt.attributes.get(XProcConstants._static)
+ if (static.isDefined && static.get == "true") {
+ seenOptions += opt
+ }
+ newChildren += child
+ case decl: XDeclareStep =>
+ decl._precedingStaticOptions ++= _precedingStaticOptions
+ decl._precedingStaticOptions ++= seenOptions.toList
+ stepList += decl
+ case _ =>
+ newChildren += child
+ }
+ }
+
+ allChildren = newChildren.toList
+
+ val stepTypes = mutable.HashSet.empty[QName]
+ for (step <- _inScopeSteps) {
+ if (step.stepType.isDefined) {
+ stepTypes += step.stepType.get
+ }
+ }
+
+ this match {
+ case step: XDeclareStep =>
+ step.elaborateStepApi()
+ step._inScopeSteps += step
+ step._inScopeSteps ++= stepList
+ case lib: XLibrary =>
+ lib._inScopeSteps ++= stepList.toList
+ lib._builtinLibraries = _builtinLibraries
+ lib.elaborateLibraryApi()
+ case _ =>
+ throw XProcException.xiThisCantHappen("Step declaration container is neither declare-step nor library?")
+ }
+
+ for (step <- stepList) {
+ step._inScopeSteps ++= _inScopeSteps
+ step._builtinLibraries = _builtinLibraries
+ step.elaborateStepApi()
+
+ if (step.stepType.isDefined) {
+ if (stepTypes.contains(step.stepType.get)) {
+ throw XProcException.xsDupStepType(step.stepType.get, location)
+ }
+ }
+ }
+
+ validate()
+
+ for (step <- stepList) {
+ step.xelaborate()
+ }
+ }
+
+ protected[xxml] def errors: List[Exception] = {
+ this match {
+ case decl: XDeclareStep =>
+ errors(List(decl))
+ case _ =>
+ exceptions ++ errors(inScopeSteps)
+ }
+ }
+
+ private def errors(steps: List[XDeclareStep]): List[Exception] = {
+ val exlist = ListBuffer.empty[Exception]
+ for (step <- steps) {
+ exlist ++= step.exceptions
+ //exlist ++= errors(step.inScopeSteps)
+ }
+ exlist.toList
+ }
+
+ protected[xxml] def skeletons: List[XSkeletonStepSignature] = _skeletons.values.toList
+
+ protected[xxml] def findSkeleton(stepType: QName): Option[XSkeletonStepSignature] = {
+ if (_skeletons.contains(stepType)) {
+ _skeletons.get(stepType)
+ } else {
+ if (parent.isDefined) {
+ declarationContainer.findSkeleton(stepType)
+ } else {
+ for (lib <- builtinLibraries) {
+ if (lib.findSkeleton(stepType).isDefined) {
+ return lib.findSkeleton(stepType)
+ }
+ }
+ None
+ }
+ }
+ }
+
+ protected[xxml] def addSkeleton(skeleton: XSkeletonStepSignature): Unit = {
+ _skeletons.put(skeleton.stepType, skeleton)
+ }
+
+ def inputPorts: Set[String] = {
+ val ports = mutable.HashSet.empty[String]
+ for (child <- children[XInput]) {
+ ports += child.port
+ }
+ ports.toSet
+ }
+
+ def input(port: String): XInput = {
+ for (child <- children[XInput]) {
+ if (child.port == port) {
+ return child
+ }
+ }
+ throw XProcException.xiThisCantHappen(s"Attempt to get input '${port}' on decl that doesn't have one")
+ }
+
+ def outputPorts: Set[String] = {
+ val ports = mutable.HashSet.empty[String]
+ for (child <- children[XOutput]) {
+ ports += child.port
+ }
+ ports.toSet
+ }
+
+ def output(port: String): XOutput = {
+ for (child <- children[XOutput]) {
+ if (child.port == port) {
+ return child
+ }
+ }
+ throw XProcException.xiThisCantHappen(s"Attempt to get output '${port}' on decl that doesn't have one")
+ }
+
+ def options: List[XNameBinding] = {
+ children[XWithOption] ++ children[XOption]
+ }
+
+ def optionNames: Set[QName] = {
+ val names = mutable.HashSet.empty[QName]
+ children[XWithOption] foreach { names += _.name }
+ children[XOption] foreach { names += _.name }
+ names.toSet
+ }
+
+ def option(name: QName): Option[XOption] = _xoptions.get(name)
+
+ protected[xxml] def extractSteps(): Unit = {
+ val newChildren = ListBuffer.empty[XArtifact]
+ for (child <- allChildren) {
+ child match {
+ case decl: XDeclareStep =>
+ decl.extractSteps()
+ _inScopeSteps += decl
+ case step: XStep =>
+ newChildren += step
+ case _ =>
+ newChildren += child
+ }
+ }
+
+ allChildren = newChildren.toList
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XDeclareStep.scala b/src/main/scala/com/xmlcalabash/model/xxml/XDeclareStep.scala
new file mode 100644
index 0000000..1422832
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XDeclareStep.scala
@@ -0,0 +1,408 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.exceptions.JafplLoopDetected
+import com.jafpl.steps.{Manifold, PortCardinality, PortSpecification}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.util.XProcVarValue
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XDeclareStep(config: XMLCalabash) extends XDeclContainer(config) {
+ private var _visibility = Option.empty[String]
+ private var _implclass = Option.empty[String]
+
+ private var _computedApi = false
+ private val _xinputs = mutable.HashMap.empty[String, XInput]
+ private val _xoutputs = mutable.HashMap.empty[String, XOutput]
+
+ def visibility: String = _visibility.getOrElse("public")
+ def atomic: Boolean = children[XStep].isEmpty
+ def stepType: Option[QName] = _type
+ def implementationClass: Option[String] = _implclass
+
+ // =======================================================================================
+
+ def runtime(): XMLCalabashRuntime = {
+ val runtime = new XMLCalabashRuntime(this)
+ val pipeline = runtime.graph.addPipeline(stepName, manifold)
+
+ for (port <- inputPorts) {
+ runtime.graph.addInput(pipeline, port)
+ }
+
+ for (port <- outputPorts) {
+ runtime.graph.addOutput(pipeline, port)
+ }
+
+ runtime.addNode(this, pipeline)
+
+ graphNodes(runtime, pipeline)
+ graphEdges(runtime)
+
+ try {
+ runtime.init(this)
+ } catch {
+ case _: JafplLoopDetected =>
+ throw XProcException.xsLoop("???", "???", location)
+ }
+
+ runtime
+ }
+
+ private def manifold: Manifold = {
+ val inputMap = mutable.HashMap.empty[String,PortCardinality]
+ for (input <- children[XInput]) {
+ if (input.sequence) {
+ inputMap.put(input.port, PortCardinality.ZERO_OR_MORE)
+ } else {
+ if (input.defaultInputs.nonEmpty) {
+ // Allow it to be empty, the default will provide something
+ inputMap.put(input.port, new PortCardinality(0, 1))
+ } else {
+ inputMap.put(input.port, PortCardinality.EXACTLY_ONE)
+ }
+ }
+ }
+ val outputMap = mutable.HashMap.empty[String,PortCardinality]
+ for (output <- outputs) {
+ if (output.sequence) {
+ outputMap.put(output.port, PortCardinality.ZERO_OR_MORE)
+ } else {
+ outputMap.put(output.port, PortCardinality.EXACTLY_ONE)
+ }
+ }
+ new Manifold(new PortSpecification(inputMap.toMap), new PortSpecification(outputMap.toMap))
+ }
+
+ def patchOptions(bindings: Map[QName,XProcVarValue]): Unit = {
+ for (child <- children[XOption]) {
+ child.runtimeBindings(bindings)
+ }
+ }
+
+ // =======================================================================================
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ try {
+ _type = staticContext.parseQName(attr(XProcConstants._type))
+ if (_type.isDefined) {
+ if (_type.get.getNamespaceURI == "") {
+ throw XProcException.xsBadStepTypeNamespace(location)
+ }
+ if (!config.standardLibraryParser && _type.get.getNamespaceURI == XProcConstants.ns_p) {
+ throw XProcException.xsBadStepTypeNamespace(_type.get.getNamespaceURI, location)
+ }
+ }
+
+ _psvi_required = staticContext.parseBoolean(attr(XProcConstants._psvi_required))
+
+ if (attributes.contains(XProcConstants._xpath_version)) {
+ val vstr = attr(XProcConstants._xpath_version).get
+ _xpath_version = Some(vstr.toDouble)
+ }
+
+ if (attributes.contains(XProcConstants._version)) {
+ val vstr = attr(XProcConstants._version).get
+ try {
+ _version = Some(vstr.toDouble)
+ if (_version.get != 3.0) {
+ error(XProcException.xsInvalidVersion(_version.get, None))
+ }
+ } catch {
+ case _: NumberFormatException =>
+ error(XProcException.xsBadVersion(vstr, None))
+ }
+ }
+ if (_version.isEmpty && (parent.isEmpty || parent.get.synthetic)) {
+ error(XProcException.xsVersionRequired(None))
+ }
+
+ _visibility = attr(XProcConstants._visibility)
+ if (_visibility.isDefined) {
+ if (_visibility.get != "public" && _visibility.get != "private") {
+ error(XProcException.xdBadVisibility(_visibility.get, None))
+ }
+ if (parent.isDefined && parent.get.synthetic) {
+ // If our parent is synthetic, then this was a p:declare-step imported directly
+ // (and the library container has just been synthesized for consistency).
+ // Directly imported p:declare-steps are always visible.
+ _visibility = Some("public")
+ }
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+
+ /**
+ * Sort out the inputs, outputs, and options of the step, ignoring its body.
+ * This way, when we do look at the body of each step, we'll already have sorted
+ * out the API of any steps it refers to.
+ */
+ protected[xxml] def elaborateStepApi(): Unit = {
+ if (_computedApi) {
+ return
+ }
+ _computedApi = true
+
+ checkAttributes()
+ checkEmptyAttributes()
+
+ if (errors.nonEmpty) return
+
+ xcomputeSignature()
+ }
+
+ private def xcomputeSignature(): Unit = {
+ var primaryInput = Option.empty[XInput]
+ var primaryOutput = Option.empty[XOutput]
+ val staticOptions = ListBuffer.empty[XNameBinding]
+
+ staticOptions ++= _precedingStaticOptions.toList
+ staticContext = staticContext.withConstants(staticOptions.toList)
+
+ // N.B. You can't re-order the children
+ for (child <- allChildren) {
+ child match {
+ case input: XInput =>
+ input.checkAttributes()
+ input.checkEmptyAttributes()
+
+ input.staticContext = input.staticContext.withConstants(staticOptions.toList)
+
+ input.checkDefaultInputs()
+
+ if (_xinputs.contains(input.port)) {
+ input.error(XProcException.xsDupPortName(input.port, None))
+ } else {
+ _xinputs.put(input.port, input)
+ if (input.primary) {
+ if (primaryInput.isDefined) {
+ input.error(XProcException.xsDupPrimaryInputPort(input.port, primaryInput.get.port, None))
+ } else {
+ primaryInput = Some(input)
+ }
+ }
+ }
+ case output: XOutput =>
+ output.checkAttributes()
+ output.checkEmptyAttributes()
+
+ output.staticContext = output.staticContext.withConstants(staticOptions.toList)
+
+ if (_xoutputs.contains(output.port)) {
+ output.error(XProcException.xsDupPortName(output.port, None))
+ } else {
+ _xoutputs.put(output.port, output)
+ if (output.primary) {
+ if (primaryOutput.isDefined) {
+ output.error(XProcException.xsDupPrimaryOutputPort(output.port, primaryOutput.get.port, None))
+ } else {
+ primaryOutput = Some(output)
+ }
+ }
+ }
+ case option: XOption =>
+ option.checkAttributes()
+ option.checkEmptyAttributes()
+ if (_xoptions.contains(option.name)) {
+ error(XProcException.xsDuplicateOptionName(option.name, None))
+ } else {
+ _xoptions.put(option.name, option)
+ }
+ if (option.static) {
+ staticOptions += option
+ }
+
+ case _ =>
+ ()
+ }
+ }
+
+ if (primaryInput.isEmpty && inputPorts.size == 1) {
+ val input = children[XInput].head
+ if (!input.primarySpecified) {
+ input.primary = true
+ }
+ }
+
+ if (primaryOutput.isEmpty && outputPorts.size == 1) {
+ val output = children[XOutput].head
+ if (!output.primarySpecified) {
+ output.primary = true
+ }
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ val seenPorts = mutable.HashSet.empty[String]
+ val newChildren = ListBuffer.empty[XArtifact]
+
+ // N.B. You can't re-order the children
+ for (child <- allChildren) {
+ child.validate()
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ // N.B. input, output, and option were validated in elaborateStepApi()
+ case input: XInput =>
+ if (seenPorts.contains(input.port)) {
+ input.error(XProcException.xsDupPortName(input.port, None))
+ } else {
+ seenPorts += input.port
+ }
+ newChildren += input
+ case output: XOutput =>
+ if (seenPorts.contains(output.port)) {
+ output.error(XProcException.xsDupPortName(output.port, None))
+ } else {
+ seenPorts += output.port
+ }
+ newChildren += output
+ case option: XOption =>
+ newChildren += option
+ case _: XImport =>
+ ()
+ case imp: XImportFunctions =>
+ newChildren += imp
+ case v: XVariable =>
+ newChildren += v
+ case step: XStep =>
+ newChildren += step
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ allChildren = newChildren.toList
+
+ checkStepNameScoping(Set())
+
+ if (exceptions.nonEmpty) {
+ return
+ }
+
+ if (atomic) {
+ if (stepType.isDefined) {
+ _implclass = config.externalSteps.get(stepType.get)
+ }
+ return
+ }
+
+ if (children[XOutput].length == 1) {
+ val output = children[XOutput].head
+ if (!output.primarySpecified) {
+ output.primary = true
+ }
+ }
+
+ constructDefaultOutput()
+
+ if (exceptions.isEmpty) {
+ elaborateSyntacticSugar()
+ }
+
+ if (exceptions.isEmpty) {
+ elaborateDefaultReadablePort(None)
+ }
+
+ if (exceptions.isEmpty) {
+ var context = new XNameBindingContext()
+ for (static <- _precedingStaticOptions) {
+ context = context.withBinding(static)
+ }
+ elaborateNameBindings(context)
+ }
+
+ if (exceptions.isEmpty) {
+ hoistSourcesToPipes()
+ }
+
+ if (exceptions.isEmpty) {
+ elaboratePortConnections()
+ if (exceptions.isEmpty) {
+ hoistSourcesToPipes()
+ }
+ }
+
+ if (exceptions.isEmpty) {
+ elaborateValidatePortConnections(new XPortBindingContext())
+ if (exceptions.isEmpty) {
+ hoistSourcesToPipes()
+ if (exceptions.isEmpty) {
+ elaborateValidatePortConnections(new XPortBindingContext())
+ }
+ }
+ }
+
+ if (exceptions.isEmpty) {
+ elaborateDependsConnections(Map())
+ }
+
+ if (exceptions.isEmpty) {
+ elaborateDynamicVariables()
+ }
+
+ if (exceptions.isEmpty) {
+ elaborateDynamicOptions()
+ }
+
+ if (exceptions.isEmpty) {
+ elaborateInsertSelectFilters()
+ if (exceptions.isEmpty) {
+ elaborateValidatePortConnections(new XPortBindingContext())
+ }
+ }
+
+ if (exceptions.isEmpty) {
+ elaborateInsertContentTypeFilters()
+ if (exceptions.isEmpty) {
+ elaborateValidatePortConnections(new XPortBindingContext())
+ }
+ }
+
+ if (exceptions.isEmpty) {
+ computeReadsFrom()
+ }
+ }
+
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+ // =======================================================================================
+
+ override protected[xxml] def elaborateInsertContentTypeFilters(): Unit = {
+ if (!atomic) {
+ super.elaborateInsertContentTypeFilters()
+ }
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ if (_type.isDefined) {
+ attr.put("type", Some(_type.get.getEQName))
+ }
+ attr.put("name", Some(stepName))
+ dumpTree(sb, "p:declare-step", attr.toMap)
+ }
+
+ override def toString: String = {
+ if (_type.isDefined) {
+ s"${_type.get}: ${stepName}"
+ } else {
+ stepName
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XDocument.scala b/src/main/scala/com/xmlcalabash/model/xxml/XDocument.scala
new file mode 100644
index 0000000..41a29f7
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XDocument.scala
@@ -0,0 +1,145 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.config.DocumentRequest
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants.{ValueTemplate, _content_type}
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants, XValueParser}
+import com.xmlcalabash.runtime.params.DocumentLoaderParams
+import com.xmlcalabash.runtime.{StaticContext, XProcVtExpression}
+import com.xmlcalabash.util.{MediaType, Urify}
+import net.sf.saxon.s9api.QName
+import net.sf.saxon.sapling.Saplings.doc
+
+import java.net.URI
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XDocument(config: XMLCalabash) extends XDataSource(config) {
+ private var _href: String = _
+ private var _hrefAvt: ValueTemplate = _
+ private var _contentType = Option.empty[MediaType]
+ private var _documentProperties = Option.empty[String]
+ private var _parameters = Option.empty[String]
+ private var _contextDependent = false
+
+ def contextDependent: Boolean = _contextDependent
+
+ protected[xxml] def this(parent: XArtifact, href: String) = {
+ this(parent.config)
+ this.parent = parent
+ this.staticContext = parent.staticContext
+ _synthetic = true
+ _href = href
+ _hrefAvt = XValueParser.parseAvt(_href)
+ }
+
+ protected[xxml] def this(parent: XArtifact, copy: XDocument) = {
+ this(copy.config)
+ _href = copy._href
+ _hrefAvt = copy._hrefAvt
+ _contentType = copy._contentType
+ _documentProperties = copy._documentProperties
+ _parameters = copy._parameters
+ _contextDependent = copy._contextDependent
+ _drp = copy._drp
+ staticContext = copy.staticContext
+ this.parent = parent
+ }
+
+ def loaderParams: DocumentLoaderParams = {
+ // FIXME: context_provided is always false
+ new DocumentLoaderParams(_hrefAvt, _contentType, _parameters, _documentProperties, _contextDependent, staticContext)
+ }
+
+ def loadDocument(): DocumentRequest = {
+ val expr = new XProcVtExpression(staticContext, _hrefAvt, true)
+ val msg = config.expressionEvaluator.value(expr, List(), staticContext.inscopeConstantBindings, None)
+ val href = if (staticContext.baseURI.isDefined) {
+ staticContext.baseURI.get.resolve(msg.item.toString)
+ } else {
+ new URI(msg.item.toString)
+ }
+ new DocumentRequest(href, _contentType, location)
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ if (attributes.contains(XProcConstants._href)) {
+ _href = attr(XProcConstants._href).get
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._href, None))
+ }
+ _contentType = parseContentType()
+ _documentProperties = attr(XProcConstants._document_properties)
+ _parameters = attr(XProcConstants._parameters)
+ _hrefAvt = XValueParser.parseAvt(_href)
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ if (!synthetic) {
+ checkAttributes()
+ checkEmptyAttributes()
+ }
+
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+ allChildren = List()
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ super.elaborateDefaultReadablePort(initial)
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ try {
+ val refs = mutable.HashSet.empty[QName]
+
+ val parser = new XValueParser(config, staticContext, _hrefAvt)
+ refs ++= parser.variables
+ _contextDependent = _contextDependent || parser.contextDependent
+
+ if (_documentProperties.isDefined) {
+ val parser = new XValueParser(config, staticContext, _documentProperties.get)
+ refs ++= parser.variables
+ _contextDependent = _contextDependent || parser.contextDependent
+ }
+
+ if (_parameters.isDefined) {
+ val parser = new XValueParser(config, staticContext, _parameters.get)
+ refs ++= parser.variables
+ _contextDependent = _contextDependent || parser.contextDependent
+ }
+
+ resolveBindings(_contextDependent, refs.toSet, initial)
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+
+ staticContext = staticContext.withConstants(initial)
+
+ initial
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("href", Option(_href))
+
+ if (drp.isDefined) {
+ attr.put("drp", Some(drp.get.tumble_id))
+ } else {
+ attr.put("drp", Some("!undefined"))
+ }
+
+ dumpTree(sb, "p:document", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XDocumentLoader.scala b/src/main/scala/com/xmlcalabash/model/xxml/XDocumentLoader.scala
new file mode 100644
index 0000000..100f948
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XDocumentLoader.scala
@@ -0,0 +1,33 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.{StepProxy, XMLCalabashRuntime}
+
+class XDocumentLoader(doc: XDocument, parentStep: XArtifact) extends XAtomicStep(doc.config, if (doc.contextDependent) { XProcConstants.cx_document_loader_vt } else { XProcConstants.cx_document_loader }) {
+ staticContext = doc.staticContext
+ _synthetic = true
+ parent = parentStep
+
+ allChildren = doc.allChildren
+ if (doc.contextDependent) {
+ val xwi = new XWithInput(this, "source")
+ xwi.drp = doc.drp
+ doc.allChildren = List()
+ addChild(xwi)
+ }
+
+ val output = new XWithOutput(this, "result")
+ output.validate()
+ addChild(output)
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ContainerStart]
+ val impl = stepImplementation
+ val params = Some(doc.loaderParams)
+ impl.configure(config, stepType, name, params)
+
+ val proxy = new StepProxy(runtime, stepType, impl, staticContext)
+ runtime.addNode(this, start.addAtomic(proxy, stepType.toString))
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XDocumentation.scala b/src/main/scala/com/xmlcalabash/model/xxml/XDocumentation.scala
new file mode 100644
index 0000000..cb5425a
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XDocumentation.scala
@@ -0,0 +1,15 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+import net.sf.saxon.s9api.XdmNode
+
+class XDocumentation(config: XMLCalabash, val content: XdmNode) extends XArtifact(config) {
+ override protected[xxml] def validate(): Unit = {
+ // nop
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ // supppress
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XEmpty.scala b/src/main/scala/com/xmlcalabash/model/xxml/XEmpty.scala
new file mode 100644
index 0000000..013c38b
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XEmpty.scala
@@ -0,0 +1,34 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+
+class XEmpty(config: XMLCalabash) extends XDataSource(config) {
+ def this(parent: XArtifact) = {
+ this(parent.config)
+ staticContext = parent.staticContext
+ this.parent = parent
+ _synthetic = true
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ for (child <- allChildren) {
+ child.validate()
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+ allChildren = List()
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ dumpTree(sb, "p:empty", Map())
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XEmptyLoader.scala b/src/main/scala/com/xmlcalabash/model/xxml/XEmptyLoader.scala
new file mode 100644
index 0000000..021ada2
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XEmptyLoader.scala
@@ -0,0 +1,26 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.{StepProxy, XMLCalabashRuntime}
+import com.xmlcalabash.runtime.params.EmptyLoaderParams
+
+class XEmptyLoader(parentStep: XArtifact) extends XAtomicStep(parentStep.config, XProcConstants.cx_empty_loader) {
+ staticContext = parentStep.staticContext
+ _synthetic = true
+ parent = parentStep
+
+ val output = new XWithOutput(this, "result")
+ output.validate()
+ addChild(output)
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val params = new EmptyLoaderParams(staticContext)
+ val start = parent.asInstanceOf[ContainerStart]
+ val impl = stepImplementation
+ impl.configure(config, stepType, name, Some(params))
+
+ val proxy = new StepProxy(runtime, stepType, impl, staticContext)
+ runtime.addNode(this, start.addAtomic(proxy, "p:empty"))
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XFinally.scala b/src/main/scala/com/xmlcalabash/model/xxml/XFinally.scala
new file mode 100644
index 0000000..749a400
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XFinally.scala
@@ -0,0 +1,26 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{Node, TryCatchStart}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+
+class XFinally(config: XMLCalabash) extends XTryCatchBranch(config) {
+
+ override protected[xxml] def validate(): Unit = {
+ super.validate()
+
+ val primary = children[XOutput] find { _.primary == true }
+ if (primary.isDefined) {
+ error(XProcException.xsPrimaryOutputOnFinally(primary.get.port, location))
+ }
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[TryCatchStart]
+ val node = start.addFinally(stepName, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XForEach.scala b/src/main/scala/com/xmlcalabash/model/xxml/XForEach.scala
new file mode 100644
index 0000000..bdd7d29
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XForEach.scala
@@ -0,0 +1,15 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+
+class XForEach(config: XMLCalabash) extends XLoopingStep(config) {
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ContainerStart]
+ val node = start.addForEach(stepName, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XForLoop.scala b/src/main/scala/com/xmlcalabash/model/xxml/XForLoop.scala
new file mode 100644
index 0000000..fc845e9
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XForLoop.scala
@@ -0,0 +1,50 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import net.sf.saxon.s9api.QName
+
+class XForLoop(config: XMLCalabash) extends XLoopingStep(config) {
+ private val _from = new QName("from")
+ private val _to = new QName("to")
+ private val _by = new QName("by")
+ private var countFrom = 1L
+ private var countTo = 0L
+ private var countBy = 1L
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ if (attributes.contains(_from)) {
+ countFrom = attr(_from).get.toLong
+ }
+
+ if (attributes.contains(_to)) {
+ countTo = attr(_to).get.toLong
+ } else {
+ throw new RuntimeException("to is required")
+ }
+
+ if (attributes.contains(_by)) {
+ countBy = attr(_by).get.toLong
+ }
+
+ if (countBy == 0) {
+ throw XProcException.xiAttempToCountByZero(location)
+ }
+
+ if ((countFrom > countTo && countBy > 0)
+ || (countFrom < countTo && countBy < 0)) {
+ logger.debug(s"Counting from $countFrom to $countTo by $countBy will never execute")
+ }
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ContainerStart]
+ val node = start.addFor(stepName, countFrom, countTo, countBy, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XForUntil.scala b/src/main/scala/com/xmlcalabash/model/xxml/XForUntil.scala
new file mode 100644
index 0000000..f2b4188
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XForUntil.scala
@@ -0,0 +1,74 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.util.{MediaType, XmlItemComparator}
+import net.sf.saxon.s9api.QName
+
+class XForUntil(config: XMLCalabash) extends XLoopingStep(config) {
+ private val _max_iterations = new QName("max-iterations")
+ private val _comparator = new QName("comparator")
+ private val _return = new QName("return")
+ private var maxIterations: Long = -1
+ private var comparator: String = "deep-equal($a,$b)"
+ private var returnSet: String = "last"
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ if (attributes.contains(_max_iterations)) {
+ maxIterations = attr(_max_iterations).get.toInt
+ }
+
+ if (attributes.contains(_comparator)) {
+ comparator = attr(_comparator).get
+ }
+
+ if (attributes.contains(_return)) {
+ returnSet = attr(_return).get
+ if (returnSet != "last" && returnSet != "all") {
+ throw new RuntimeException("return must be last or all")
+ }
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ super.validate()
+
+ var testOutput = Option.empty[XOutput]
+ for (child <- children[XOutput]) {
+ if (child.port == "test") {
+ testOutput = Some(child)
+ }
+ }
+
+ if (testOutput.isEmpty) {
+ val output = new XOutput(this, Some("test"))
+ output.primary = false
+ output.sequence = false
+ output.contentTypes = MediaType.MATCH_ANY
+
+ val firstStep = children[XStep].head
+ val step = children[XStep].last
+ val pout = step.primaryOutput
+ if (pout.isDefined) {
+ val pipe = new XPipe(step.primaryOutput.get)
+ output.addChild(pipe)
+ if (Option(firstStep).isDefined) {
+ insertBefore(output, firstStep)
+ } else {
+ addChild(output)
+ }
+ }
+ }
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val compare = new XmlItemComparator(config, comparator, maxIterations, this)
+ val start = parent.asInstanceOf[ContainerStart]
+ val node = start.addUntil(compare, returnSet=="all", stepName, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XForWhile.scala b/src/main/scala/com/xmlcalabash/model/xxml/XForWhile.scala
new file mode 100644
index 0000000..9bdff9b
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XForWhile.scala
@@ -0,0 +1,45 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.util.XmlItemTester
+import net.sf.saxon.s9api.QName
+
+class XForWhile(config: XMLCalabash) extends XLoopingStep(config) {
+ private val _max_iterations = new QName("max-iterations")
+ private val _return = new QName("return")
+ private var maxIterations: Long = -1
+ private var test: String = ""
+ private var returnSet: String = "last"
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ if (attributes.contains(_max_iterations)) {
+ maxIterations = attr(_max_iterations).get.toInt
+ }
+
+ if (attributes.contains(XProcConstants._test)) {
+ test = attr(XProcConstants._test).get
+ } else {
+ throw new RuntimeException("test is required")
+ }
+
+ if (attributes.contains(_return)) {
+ returnSet = attr(_return).get
+ if (returnSet != "last" && returnSet != "all") {
+ throw new RuntimeException("return must be last or all")
+ }
+ }
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val tester = new XmlItemTester(runtime, test, maxIterations, this)
+ val start = parent.asInstanceOf[ContainerStart]
+ val node = start.addWhile(tester, returnSet == "all", stepName, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XGraphableArtifact.scala b/src/main/scala/com/xmlcalabash/model/xxml/XGraphableArtifact.scala
new file mode 100644
index 0000000..1b02a3c
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XGraphableArtifact.scala
@@ -0,0 +1,8 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.Node
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+
+trait XGraphableArtifact {
+ def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XGroup.scala b/src/main/scala/com/xmlcalabash/model/xxml/XGroup.scala
new file mode 100644
index 0000000..e24cbf7
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XGroup.scala
@@ -0,0 +1,28 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node, TryCatchStart}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+
+class XGroup(config: XMLCalabash) extends XContainer(config) {
+ def this(parent: XContainer) = {
+ this(parent.config)
+ this.parent = parent
+ staticContext = parent.staticContext
+ _synthetic = true
+ _syntheticName = Some(XProcConstants.p_group)
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parNode: Node): Unit = {
+ val node = if (parent.get.isInstanceOf[XTry]) {
+ val start = parNode.asInstanceOf[TryCatchStart]
+ start.addTry(stepName, containerManifold)
+ } else {
+ val start = parNode.asInstanceOf[ContainerStart]
+ start.addGroup(stepName, containerManifold)
+ }
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XIf.scala b/src/main/scala/com/xmlcalabash/model/xxml/XIf.scala
new file mode 100644
index 0000000..0513bd6
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XIf.scala
@@ -0,0 +1,61 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+
+class XIf(config: XMLCalabash) extends XChooseBranch(config) {
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ val coll = attr(XProcConstants._collection)
+ if (coll.isDefined) {
+ coll.get match {
+ case "true" => _collection = true
+ case "false" => _collection = false
+ case _ =>
+ error(XProcException.xsBadTypeValue(coll.get, "xs:boolean", None))
+ }
+ }
+
+ if (attributes.contains(XProcConstants._test)) {
+ _test = attr(XProcConstants._test).get
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._test, None))
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ super.validate()
+ val xwi = children[XWithInput].headOption
+ if (xwi.isDefined) {
+ if (xwi.get.port != "#anon") {
+ error(XProcException.xsPortNotAllowed(xwi.get.port, stepName, location))
+ }
+ xwi.get.port = "condition"
+ }
+
+ val primary = children[XOutput] find { _.primary }
+ if (primary.isEmpty) {
+ error(XProcException.xsPrimaryOutputRequired(location))
+ }
+ }
+
+ override def elaborateSyntacticSugar(): Unit = {
+ val choose = new XChoose(parent.get)
+ choose.dependsOn ++= dependsOn
+ choose.tumble_id = tumble_id
+ if (name.isDefined) {
+ choose.stepName = name.get
+ }
+ val when = new XWhen(choose, _test, _collection, true)
+ choose.addChild(when)
+ for (child <- allChildren) {
+ when.addChild(child)
+ }
+ allChildren = List()
+ parent.get.replaceChild(this, choose)
+ choose.elaborateSyntacticSugar()
+ choose.validate()
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XImport.scala b/src/main/scala/com/xmlcalabash/model/xxml/XImport.scala
new file mode 100644
index 0000000..dcec797
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XImport.scala
@@ -0,0 +1,56 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+
+import java.net.URI
+import scala.collection.mutable
+
+class XImport(config: XMLCalabash, val library: XLibrary) extends XArtifact(config) {
+ private var _href: URI = _
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ try {
+ if (attributes.contains(XProcConstants._href)) {
+ _href = staticContext.baseURI.get.resolve(attr(XProcConstants._href).get)
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._href, None))
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+ allChildren = List()
+
+ val cont = ancestor[XDeclContainer].get
+ for (step <- library.inScopeSteps) {
+ if (step.stepType.isDefined) {
+ for (istep <- cont.inScopeSteps) {
+ if (istep.stepType.isDefined && istep.stepType.get == step.stepType.get) {
+ throw XProcException.xsDupStepType(step.stepType.get, istep.location)
+ }
+ }
+ }
+ }
+ cont.addInScopeSteps(library.inScopeSteps)
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("href", Option(_href))
+ dumpTree(sb, "p:import", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XImportFunctions.scala b/src/main/scala/com/xmlcalabash/model/xxml/XImportFunctions.scala
new file mode 100644
index 0000000..303e8f0
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XImportFunctions.scala
@@ -0,0 +1,44 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+
+import java.net.URI
+import scala.collection.mutable
+
+class XImportFunctions(config: XMLCalabash) extends XArtifact(config) {
+ private var _href: URI = _
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ try {
+ if (attributes.contains(XProcConstants._href)) {
+ _href = staticContext.baseURI.get.resolve(attr(XProcConstants._href).get)
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._href, None))
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ protected[xxml] def elaborateDeclarations(): Unit = {
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+ allChildren = List()
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("href", Option(_href))
+ dumpTree(sb, "p:import", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XInline.scala b/src/main/scala/com/xmlcalabash/model/xxml/XInline.scala
new file mode 100644
index 0000000..31599fa
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XInline.scala
@@ -0,0 +1,272 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants, XValueParser}
+import com.xmlcalabash.util.{MediaType, S9Api}
+import net.sf.saxon.s9api.{Axis, QName, XdmNode, XdmNodeKind}
+
+import java.io.UnsupportedEncodingException
+import java.util.Base64
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+import scala.jdk.CollectionConverters.IteratorHasAsScala
+
+class XInline(config: XMLCalabash, val content: XdmNode, implied: Boolean) extends XDataSource(config) {
+ private var _contentType = Option.empty[MediaType]
+ private var _documentProperties = Option.empty[String]
+ private var _encoding = Option.empty[String]
+ private var _exclude_inline_prefixes = Option.empty[String]
+ private var _expandText = false
+ private val _excludeURIs = mutable.HashSet.empty[String]
+ private val _valueTemplates = ListBuffer.empty[String]
+ private var _contextDependent = false
+ _synthetic = implied
+
+ if (Option(content).isEmpty || content.getNodeKind != XdmNodeKind.DOCUMENT) {
+ throw XProcException.xiThisCantHappen(s"Attempt to create p:inline from something that isn't a document node: ${content}")
+ }
+
+ def this(config: XMLCalabash, parent: XArtifact, srcNode: XdmNode) = {
+ this(config, srcNode, true)
+ this.parent = parent
+ staticContext = parent.staticContext
+ }
+
+ protected[xxml] def this(parent: XArtifact, copy: XInline) = {
+ this(copy.config, copy.content, copy.synthetic)
+ _contentType = copy._contentType
+ _documentProperties = copy._documentProperties
+ _encoding = copy._encoding
+ _exclude_inline_prefixes = copy._exclude_inline_prefixes
+ _expandText = copy._expandText
+ _excludeURIs ++= copy._excludeURIs
+ _valueTemplates ++= copy._valueTemplates
+ _contextDependent = copy._contextDependent
+ _drp = copy._drp
+ staticContext = copy.staticContext
+ this.parent = parent
+ }
+
+ def contentType: Option[MediaType] = _contentType
+
+ def encoding: Option[String] = _encoding
+
+ def documentProperties: Option[String] = _documentProperties
+
+ def expandText: Boolean = _expandText
+
+ def excludeURIs: Set[String] = _excludeURIs.toSet
+
+ def contextDependent: Boolean = _contextDependent
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ val saveContentType = attributes.get(XProcConstants._content_type)
+
+ _contentType = parseContentType()
+ _documentProperties = attr(XProcConstants._document_properties)
+ _encoding = attr(XProcConstants._encoding)
+ _exclude_inline_prefixes = attr(XProcConstants._exclude_inline_prefixes)
+
+ if (_contentType.isDefined && _encoding.isEmpty) {
+ if (_contentType.get.charset.isDefined) {
+ error(XProcException.xdCharsetWithoutEncoding(saveContentType.get, None))
+ }
+ }
+
+ if (_encoding.isDefined) {
+ if (_encoding.get == "base64") {
+ if (_contentType.isDefined && _contentType.get.markupContentType) {
+ error(XProcException.xdCannotEncodeMarkup(_encoding.get, _contentType.get, None))
+ }
+
+ val charset = if (_contentType.isDefined) {
+ _contentType.get.charset.getOrElse("UTF-8")
+ } else {
+ "UTF-8"
+ }
+
+ // Can I trust you?
+ try {
+ // Apparently the decoder won't acept newlines in the data...
+ val str = content.getStringValue.trim.replace("\n", "")
+ val bytes = Base64.getDecoder.decode(str)
+ if (contentType.isDefined && contentType.get.textContentType) {
+ new String(bytes, charset)
+ }
+ } catch {
+ case _: IllegalArgumentException =>
+ error(XProcException.xdIncorrectEncoding(_encoding.get, None))
+ case _: UnsupportedEncodingException =>
+ error(XProcException.xdUnsupportedCharset(charset, None))
+ case ex: Exception =>
+ error(ex)
+ }
+ } else {
+ error(XProcException.xsUnsupportedEncoding(_encoding.get, None))
+ }
+ }
+
+ if (_contentType.isEmpty) {
+ _contentType = Some(MediaType.XML)
+ }
+ }
+
+ protected[xxml] def parseExpandText(node: XdmNode): Unit = {
+ var elem: Option[XdmNode] = Some(node)
+ var ename = Option.empty[QName]
+ var eattr = Option.empty[String]
+ while (elem.isDefined && eattr.isEmpty) {
+ elem.get.getNodeKind match {
+ case XdmNodeKind.ELEMENT =>
+ if (elem.get.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
+ ename = Some(XProcConstants._expand_text)
+ eattr = Option(elem.get.getAttributeValue(ename.get))
+ } else {
+ ename = Some(XProcConstants.p_expand_text)
+ eattr = Option(elem.get.getAttributeValue(ename.get))
+ }
+ case _ => ()
+ }
+ elem = Option(elem.get.getParent)
+ }
+
+ val value = eattr.getOrElse("true")
+ if (value == "true" || value == "false") {
+ _expandText = (value == "true")
+ } else {
+ error(XProcException.xsInvalidExpandText(ename.get, value, location))
+ }
+ }
+
+ protected[xxml] def parseExcludedUris(node: XdmNode): Unit = {
+ _excludeURIs += XProcConstants.ns_p
+
+ var elem: Option[XArtifact] = Some(this)
+ var eattr = Option.empty[String]
+
+ elem = Some(this)
+ while (elem.isDefined) {
+ if (elem.get.nodeName.getNamespaceURI == XProcConstants.ns_p) {
+ eattr = elem.get.attributes.get(XProcConstants._exclude_inline_prefixes)
+ } else {
+ eattr = elem.get.attributes.get(XProcConstants.p_exclude_inline_prefixes)
+ }
+ if (eattr.isDefined && elem.get.exceptions.isEmpty) {
+ val prefixes = mutable.HashSet.empty[String]
+ for (token <- eattr.get.trim.split("\\s+")) {
+ prefixes += token
+ }
+
+ try {
+ _excludeURIs ++= S9Api.urisForPrefixes(node, prefixes.toSet)
+ } catch {
+ case ex: Exception =>
+ elem.get.error(ex)
+ }
+ }
+ elem = elem.get.parent
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ val refs = mutable.HashSet.empty[QName]
+
+ if (expandText) {
+ findValueTemplates()
+
+ try {
+ for (avt <- _valueTemplates) {
+ val parser = new XValueParser(config, staticContext, XValueParser.parseAvt(avt))
+ refs ++= parser.variables
+ _contextDependent = _contextDependent || parser.contextDependent
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+
+ if (exceptions.nonEmpty) {
+ return initial
+ }
+
+ if (documentProperties.isDefined) {
+ try {
+ val parser = new XValueParser(config, staticContext, documentProperties.get)
+ refs ++= parser.variables
+ _contextDependent = _contextDependent || parser.contextDependent
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ resolveBindings(_contextDependent, refs.toSet, initial)
+
+ staticContext = staticContext.withConstants(initial)
+ }
+
+
+
+
+ initial
+ }
+
+ private def findValueTemplates(): Unit = {
+ findValueTemplates(content, expandText)
+ }
+
+ private def findValueTemplates(node: XdmNode, expand: Boolean): Unit = {
+ node.getNodeKind match {
+ case XdmNodeKind.DOCUMENT =>
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ findValueTemplates(child, expand)
+ }
+ case XdmNodeKind.ELEMENT =>
+ val inlineExpand = if (node.getNodeName.getNamespaceURI == XProcConstants.ns_p) {
+ Option(node.getAttributeValue(XProcConstants._inline_expand_text))
+ } else {
+ Option(node.getAttributeValue(XProcConstants.p_inline_expand_text))
+ }
+ val expandNode = if (inlineExpand.isDefined) {
+ inlineExpand.get == "true"
+ } else {
+ expand
+ }
+ if (expandNode) {
+ for (attr <- node.axisIterator(Axis.ATTRIBUTE).asScala) {
+ if (attr.getStringValue.contains("{")) {
+ _valueTemplates += attr.getStringValue
+ }
+ }
+ }
+ for (child <- node.axisIterator(Axis.CHILD).asScala) {
+ findValueTemplates(child, expandNode)
+ }
+ case XdmNodeKind.TEXT =>
+ if (expand) {
+ if (node.getStringValue.contains("{")) {
+ _valueTemplates += node.getStringValue
+ }
+ }
+ case _ => ()
+ }
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("content-type", _contentType)
+ attr.put("document-properties", _documentProperties)
+ attr.put("encoding", _encoding)
+ attr.put("exclude-inline-prefixes", _exclude_inline_prefixes)
+ attr.put("expand-text", Some(_expandText))
+ dumpTree(sb, "p:inline", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XInlineLoader.scala b/src/main/scala/com/xmlcalabash/model/xxml/XInlineLoader.scala
new file mode 100644
index 0000000..2ec7be4
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XInlineLoader.scala
@@ -0,0 +1,44 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.params.InlineLoaderParams
+import com.xmlcalabash.runtime.{StepProxy, XMLCalabashRuntime}
+
+class XInlineLoader(inline: XInline, parentStep: XArtifact)
+ extends XAtomicStep(inline.config, if (inline.contextDependent) { XProcConstants.cx_inline_loader_vt } else { XProcConstants.cx_inline_loader }) {
+ staticContext = inline.staticContext
+ _synthetic = true
+ parent = parentStep
+
+ allChildren = inline.allChildren
+ if (inline.contextDependent) {
+ val xwi = new XWithInput(this, "source")
+ xwi.drp = inline.drp
+ inline.allChildren = List()
+ addChild(xwi)
+ }
+
+ val output = new XWithOutput(this, "result")
+ output.validate()
+ addChild(output)
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val xwi = children[XWithInput] find { _.port == "source" }
+ val pipe = if (xwi.isDefined) {
+ xwi.get.children[XPipe].headOption
+ } else {
+ None
+ }
+
+ val params = new InlineLoaderParams(inline.content, inline.contentType, inline.documentProperties,
+ inline.encoding, inline.excludeURIs, inline.expandText, pipe.isDefined, staticContext)
+
+ val start = parent.asInstanceOf[ContainerStart]
+ val impl = stepImplementation
+ impl.configure(config, stepType, name, Some(params))
+
+ val proxy = new StepProxy(runtime, stepType, impl, staticContext)
+ runtime.addNode(this, start.addAtomic(proxy, "p:inline"))
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XInput.scala b/src/main/scala/com/xmlcalabash/model/xxml/XInput.scala
new file mode 100644
index 0000000..efbd2d8
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XInput.scala
@@ -0,0 +1,131 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.util.MediaType
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XInput(config: XMLCalabash) extends XPort(config) {
+ // FIXME: support select with variables on p:input
+
+ def this(step: XContainer, port: Option[String]) = {
+ this(step.config)
+ staticContext = step.staticContext
+ synthetic = true
+ syntheticName = XProcConstants.p_input
+ parent = step
+ if (port.isDefined) {
+ _port = port.get
+ }
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ try {
+ parent.get match {
+ case loop: XLoopingStep =>
+ if (attributes.contains(XProcConstants._port)) {
+ error(XProcException.xsPortNotAllowed(attributes(XProcConstants._port), loop.stepName, location))
+ }
+ case _ =>
+ if (attributes.contains(XProcConstants._port)) {
+ _port = staticContext.parseNCName(attr(XProcConstants._port)).get
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._port, None))
+ }
+ }
+
+ _sequence = staticContext.parseBoolean(attr(XProcConstants._sequence))
+ _primary = staticContext.parseBoolean(attr(XProcConstants._primary))
+ _select = attr(XProcConstants._select)
+
+ _content_types = staticContext.parseContentTypes(attr(XProcConstants._content_types))
+ if (_content_types.isEmpty) {
+ _content_types = List(MediaType.OCTET_STREAM)
+ }
+
+ _href = attr(XProcConstants._href)
+ // pipe isn't allowed on p:input
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ override protected[xxml] def checkEmptyAttributes(): Unit = {
+ super.checkEmptyAttributes()
+ _attrChecked = true
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ if (!_attrChecked) {
+ checkAttributes()
+ checkEmptyAttributes()
+ }
+
+ super.validate()
+ }
+
+ protected[xxml] def checkDefaultInputs(): Unit = {
+ // Any children of a p:input in this case are just default bindings;
+ // squirrel them away in case we need them later
+ if (_pipe.isDefined) {
+ throw XProcException.xsBadAttribute(XProcConstants._pipe, location)
+ }
+ if (children[XPipe].nonEmpty) {
+ throw XProcException.xsElementNotAllowed(XProcConstants.p_pipe, location)
+ }
+
+ for (child <- allChildren) {
+ child match {
+ case _: XDocumentation => ()
+ case _: XPipeinfo => ()
+ case ds: XDataSource =>
+ if (_href.isDefined) {
+ throw XProcException.xsHrefAndOtherSources(child.location)
+ }
+ ds.validate()
+ _defaultInputs += ds
+ case _ =>
+ throw XProcException.xiThisCantHappen("Default p:input contains something that isn't an XDataSource?")
+ }
+ }
+
+ if (_href.isDefined) {
+ val doc = new XDocument(this, _href.get)
+ _defaultInputs += doc
+ _href = None
+ }
+
+ allChildren = List()
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ super.elaborateDefaultReadablePort(initial)
+ if (primary) {
+ Some(this)
+ } else {
+ initial
+ }
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ super.elaborateNameBindings(initial)
+ for (child <- defaultInputs) {
+ child.elaborateNameBindings(initial)
+ }
+ initial
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("port", Some(_port))
+ attr.put("select", _select)
+ attr.put("primary", _primary)
+ attr.put("sequence", _sequence)
+ dumpTree(sb, "p:input", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XLibrary.scala b/src/main/scala/com/xmlcalabash/model/xxml/XLibrary.scala
new file mode 100644
index 0000000..701f013
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XLibrary.scala
@@ -0,0 +1,74 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import net.sf.saxon.s9api.QName
+
+import java.net.URI
+
+class XLibrary(config: XMLCalabash, val href: Option[URI]) extends XDeclContainer(config) {
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ try {
+ if (attributes.contains(XProcConstants._version)) {
+ val vstr = attr(XProcConstants._version).get
+ try {
+ _version = Some(vstr.toDouble)
+ } catch {
+ case _: NumberFormatException =>
+ error(XProcException.xsBadVersion(vstr, None))
+ }
+ if (_version.get != 3.0) {
+ error(XProcException.xsInvalidVersion(_version.get, None))
+ }
+ }
+ if (_version.isEmpty && parent.isEmpty && !synthetic) {
+ error(XProcException.xsVersionRequired(None))
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ protected[xxml] def elaborateLibraryApi(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ for (option <- children[XOption]) {
+ option.checkAttributes()
+ option.checkEmptyAttributes()
+ if (_xoptions.contains(option.name)) {
+ error(XProcException.xsDuplicateOptionName(option.name, None))
+ } else {
+ _xoptions.put(option.name, option)
+ }
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ // Anything to do here?
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ dumpTree(sb, "p:library", Map())
+ }
+
+ override def toString: String = {
+ var baseuri = if (href.isDefined) {
+ href.get.toString
+ } else {
+ ""
+ }
+ if (baseuri != "") {
+ baseuri = ": " + baseuri
+ }
+ if (stepName != tumble_id) {
+ s"${staticContext.nodeName}(${stepName};${tumble_id})${baseuri}"
+ } else {
+ s"${staticContext.nodeName}(${stepName})${baseuri}"
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XLoopingStep.scala b/src/main/scala/com/xmlcalabash/model/xxml/XLoopingStep.scala
new file mode 100644
index 0000000..729e271
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XLoopingStep.scala
@@ -0,0 +1,175 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.util.MediaType
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+abstract class XLoopingStep(config: XMLCalabash) extends XContainer(config) {
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ val seenPorts = mutable.HashSet.empty[String]
+ val newChildren = ListBuffer.empty[XArtifact]
+
+ var primaryInput = Option.empty[XWithInput]
+ var primaryOutput = Option.empty[XOutput]
+
+ for (input <- children[XWithInput]) {
+ input.validate()
+ if (primaryInput.isDefined) {
+ error(XProcException.xsInvalidPipeline(s"At most one p:with-input is allowed on loops", location))
+ } else {
+ primaryInput = Some(input)
+ newChildren += input
+ }
+ }
+
+ if (primaryInput.isEmpty && !this.isInstanceOf[XForLoop]) {
+ val xwi = new XWithInput(this, "#anon", true, true, MediaType.MATCH_ANY)
+ xwi.validate()
+ primaryInput = Some(xwi)
+ newChildren += xwi
+ }
+
+ val current = new XInput(this, Some("current"))
+ current.sequence = false
+ current.primary = true
+ current.contentTypes = MediaType.MATCH_ANY
+ current.validate()
+ newChildren += current
+ seenPorts += "current"
+
+ for (output <- children[XOutput]) {
+ output.validate()
+ if (seenPorts.contains(output.port)) {
+ output.error(XProcException.xsDupPortName(output.port, None))
+ } else {
+ seenPorts += output.port
+ if (output.primary) {
+ if (primaryOutput.isDefined) {
+ output.error(XProcException.xsDupPrimaryInputPort(output.port, primaryInput.get.port, None))
+ } else {
+ primaryOutput = Some(output)
+ }
+ }
+ newChildren += output
+ }
+ }
+
+ //val newScope = checkStepNameScoping(inScopeNames)
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XWithInput => ()
+ case _: XOutput => ()
+ case v: XVariable =>
+ v.validate()
+ newChildren += v
+ case step: XStep =>
+ step.validate()
+ newChildren += step
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ allChildren = newChildren.toList
+
+ if (children[XOutput].length == 1) {
+ val output = children[XOutput].head
+ if (!output.primarySpecified) {
+ output.primary = true
+ }
+ }
+
+ constructDefaultOutput()
+
+ this match {
+ case _: XForWhile => constructTestOutput()
+ case _: XForUntil => constructTestOutput()
+ case _ => ()
+ }
+ }
+
+ private def constructTestOutput(): Unit = {
+ var testOutput = Option.empty[XOutput]
+ for (child <- children[XOutput]) {
+ if (child.port == "test") {
+ testOutput = Some(child)
+ }
+ }
+
+ if (testOutput.isEmpty) {
+ val output = new XOutput(this, Some("test"))
+ output.primary = false
+ output.sequence = false
+ output.contentTypes = MediaType.MATCH_ANY
+
+ val firstStep = children[XStep].head
+ val step = children[XStep].last
+ val pout = step.primaryOutput
+ if (pout.isDefined) {
+ val pipe = new XPipe(step.primaryOutput.get)
+ output.addChild(pipe)
+ if (Option(firstStep).isDefined) {
+ insertBefore(output, firstStep)
+ } else {
+ addChild(output)
+ }
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ children[XWithInput] foreach { _.elaborateDefaultReadablePort(initial) }
+
+ var curdrp: Option[XPort] = Some(children[XInput].head)
+ for (child <- allChildren) {
+ child match {
+ case _: XWithInput =>
+ () // Don't make this one point to current!
+ case _ =>
+ curdrp = child.elaborateDefaultReadablePort(curdrp)
+ }
+ }
+
+ children[XOutput] find { _.primary }
+ }
+
+ override protected def addInputFilter(child: XPort, filter: XStep): Unit = {
+ val container = parent.get match {
+ case cont: XContainer => cont
+ case _ =>
+ error(XProcException.xiThisCantHappen("Parent of filtering step isn't a container?"))
+ return
+ }
+
+ val filterxwi = new XWithInput(this, "source", true, true, MediaType.MATCH_ANY)
+ filterxwi.allChildren = child.allChildren
+
+ val filterxwo = new XWithOutput(filter, "result")
+
+ val stepxwi = new XWithInput(this, "source")
+
+ val pipe = new XPipe(stepxwi, filter.stepName, "result")
+
+ stepxwi.addChild(pipe)
+ insertBefore(stepxwi, child)
+ removeChild(child)
+
+ stepxwi.validate()
+
+ filter.addChild(filterxwi)
+ filter.addChild(filterxwo)
+
+ container.insertBefore(filter, this)
+ filterxwi.validate()
+ filterxwo.validate()
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XMLStaticContext.scala b/src/main/scala/com/xmlcalabash/model/xxml/XMLStaticContext.scala
new file mode 100644
index 0000000..df6cc30
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XMLStaticContext.scala
@@ -0,0 +1,56 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.Location
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api, URIUtils, Urify, XdmLocation}
+import net.sf.saxon.s9api.{QName, XdmNode}
+
+import java.net.URI
+import scala.collection.mutable
+
+class XMLStaticContext(val nodeName: QName, location: Option[Location]) extends XStaticContext() {
+ _location = location
+
+ def this(nodeName: QName, location: Location) = {
+ this(nodeName, Some(location))
+ }
+
+ def this(nodeName: QName, location: Location, nsmap: Map[String,String]) = {
+ this(nodeName, Some(location))
+ _inscopeNamespaces ++= nsmap
+ }
+
+ def this(node: XdmNode) = {
+ this(node.getNodeName, XdmLocation.from(node))
+ _inscopeNamespaces ++= S9Api.inScopeNamespaces(node)
+ }
+
+ def forNode(node: XdmNode): XMLStaticContext = {
+ val newcontext = new XMLStaticContext(node)
+ newcontext._config = _config
+ newcontext._inscopeConstants ++= _inscopeConstants
+ newcontext._inscopeNamespaces ++= _inscopeNamespaces
+ newcontext._baseURI = Option(node.getBaseURI)
+ newcontext
+ }
+
+ /*
+ def withConstants(bcontext: XNameBindingContext): XMLStaticContext = {
+ if (bcontext.inScopeConstants.isEmpty) {
+ return this
+ }
+
+ val newContext = new XMLStaticContext(nodeName, location)
+ newContext._inscopeConstants ++= inscopeConstants
+ newContext._inscopeNamespaces ++= inscopeNamespaces
+ newContext._baseURI = _baseURI
+ newContext._location = _location
+
+ for ((name,binding) <- bcontext.inScopeConstants) {
+ newContext._inscopeConstants.put(name, binding)
+ }
+
+ newContext
+ }
+
+ */
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XNameBinding.scala b/src/main/scala/com/xmlcalabash/model/xxml/XNameBinding.scala
new file mode 100644
index 0000000..3bb4542
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XNameBinding.scala
@@ -0,0 +1,407 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.{XProcConstants, XValueParser}
+import com.xmlcalabash.runtime.{XMLCalabashRuntime, XProcVtExpression, XProcXPathExpression}
+import com.xmlcalabash.steps.internal.ValueComputation
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, TypeUtils}
+import net.sf.saxon.ma.map.{MapItem, MapType}
+import net.sf.saxon.om.StructuredQName
+import net.sf.saxon.s9api.{QName, SequenceType, XdmAtomicValue, XdmMap}
+
+import java.net.URISyntaxException
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+object XNameBinding {
+ def checkValueTokens(config: XMLCalabash,
+ context: MinimalStaticContext,
+ values: Option[String]): Option[List[XdmAtomicValue]] = {
+ if (values.isDefined) {
+ val exprEval = config.expressionEvaluator.newInstance()
+
+ val expr = new XProcXPathExpression(context, values.get, None, None, None)
+ val value = exprEval.value(expr, List(), Map(), None)
+ val iter = value.item.iterator()
+ val allowed = ListBuffer.empty[XdmAtomicValue]
+ while (iter.hasNext) {
+ val token = iter.next()
+ token match {
+ case atom: XdmAtomicValue =>
+ allowed += atom
+ case _ =>
+ throw XProcException.xsInvalidValues(values.get, None)
+ }
+ }
+
+ return Some(allowed.toList)
+ }
+
+ None
+ }
+
+ def promotedValue(config: XMLCalabash,
+ name: QName,
+ declaredType: Option[SequenceType],
+ tokenList: Option[List[XdmAtomicValue]],
+ staticValueMsg: XdmValueItemMessage): XdmValueItemMessage = {
+ val typeUtils = new TypeUtils(config, staticValueMsg.context)
+ try {
+ typeUtils.convertType(name, staticValueMsg, declaredType, tokenList)
+ } catch {
+ case ex: XProcException =>
+ if (ex.code == XProcException.errxd(19)) {
+ throw ex
+ } else {
+ // FIXME: if this was a namespace error, we lose critical information about the error
+ throw XProcException.xdBadType(name, staticValueMsg.item.toString, declaredType.get.toString, None)
+ }
+ case ex: URISyntaxException =>
+ val dtype = declaredType.get.getItemType.getTypeName
+ if (dtype == XProcConstants.xs_anyURI) {
+ throw XProcException.xdInvalidURI(staticValueMsg.item.toString, None)
+ } else {
+ throw ex
+ }
+ case ex: Exception =>
+ throw ex
+ }
+ }
+}
+
+abstract class XNameBinding(config: XMLCalabash) extends XArtifact(config) {
+ private val structured_xs_QName = new StructuredQName("xs", XProcConstants.ns_xs, "QName")
+
+ protected var _qnameKeys: Boolean = false
+ protected var _name: QName = _
+ protected var _as = Option.empty[String]
+ protected var _declaredType = Option.empty[SequenceType]
+ protected var _values = Option.empty[String]
+ protected var _static = Option.empty[Boolean]
+ protected var _constant = false
+ protected var _required = Option.empty[Boolean]
+ protected var _select = Option.empty[String]
+ protected var _avt = Option.empty[String]
+ protected var _visibility = Option.empty[String]
+ protected var _variableReferences: Set[QName] = Set()
+
+ protected var _constantValue = Option.empty[XdmValueItemMessage]
+ protected var _computeValue = Option.empty[ValueComputation]
+ protected var collection = false
+
+ protected var _href = Option.empty[String]
+ protected var _pipe = Option.empty[String]
+
+ private var _drp: Option[XPort] = None
+
+ def name: QName = _name
+
+ protected[xxml] def drp: Option[XPort] = _drp
+
+ protected[xxml] def drp_=(port: Option[XPort]): Unit = {
+ _drp = port
+ }
+
+ def static: Boolean = _static.getOrElse(false)
+ def constant: Boolean = _constant
+ def visibility: String = _visibility.getOrElse("public")
+
+ def constantValue: Option[XdmValueItemMessage] = _constantValue
+
+ def as: Option[String] = _as
+ def declaredType: Option[SequenceType] = _declaredType
+
+ def required: Boolean = _required.getOrElse(false)
+
+ def select: Option[String] = _select
+
+ def qnameKeys: Boolean = _qnameKeys
+
+ def usedByPipeline: Boolean = {
+ children[XWithOutput].head.readBy.nonEmpty
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ if (synthetic) {
+ return
+ }
+
+ super.checkAttributes()
+
+ try {
+ if (attributes.contains(XProcConstants._name)) {
+ val name = attr(XProcConstants._name).get
+ try {
+ _name = staticContext.parseQName(name)
+ } catch {
+ case ex: XProcException =>
+ if (ex.code == XProcException.err_xd0015) {
+ error(XProcException.xsOptionUndeclaredNamespace(name, ex.location))
+ } else {
+ error(ex)
+ }
+ }
+
+ this match {
+ case _: XWithOption =>
+ () // This would be ok if the step has an option declared in the p: namespace
+ case _ =>
+ if (_name.getNamespaceURI == XProcConstants.ns_p) {
+ error(XProcException.xsOptionInXProcNamespace(_name, None))
+ }
+ }
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._name, None))
+ }
+
+ _as = attr(XProcConstants._as)
+ _declaredType = staticContext.parseSequenceType(_as, config.itemTypeFactory)
+
+ if (declaredType.isDefined)
+ declaredType.get.getUnderlyingSequenceType.getPrimaryType match {
+ case map: MapType =>
+ if (map.getKeyType.getPrimitiveItemType.getTypeName == structured_xs_QName) {
+ // We have to lie about the type of maps with QName keys because we're
+ // going to allow users to put strings in there.
+ _qnameKeys = true
+ _declaredType = Some(staticContext.parseFakeMapSequenceType(as.get, config.itemTypeFactory))
+ }
+ case _ => ()
+ }
+
+ _static = staticContext.parseBoolean(attr(XProcConstants._static))
+ _required = staticContext.parseBoolean(attr(XProcConstants._required))
+ _select = attr(XProcConstants._select)
+ _visibility = attr(XProcConstants._visibility)
+
+ if (visibility != "public" && visibility != "private") {
+ error(XProcException.xsBadVisibility(visibility, location))
+ }
+
+ if (_required.isDefined && _required.get && _select.isDefined) {
+ error(XProcException.xsRequiredAndDefaulted(_name, location))
+ }
+
+ if (_required.isDefined && _required.get && static) {
+ error(XProcException.xsRequiredAndStatic(_name, location))
+ }
+
+ val _collection = attr(XProcConstants._collection)
+ if (_collection.isDefined) {
+ val coll = _collection.get
+ if (coll == "true" || coll == "false") {
+ collection = coll == "true"
+ } else {
+ error(XProcException.xsBadTypeValue(coll, "xs:boolean", None))
+ }
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ protected def checkValueTokens: Option[List[XdmAtomicValue]] = {
+ _values = attr(XProcConstants._values)
+ if (_values.isDefined) {
+ val exprEval = config.expressionEvaluator.newInstance()
+
+ val expr = new XProcXPathExpression(staticContext, _values.get, None, None, None)
+ val value = exprEval.value(expr, List(), Map(), None)
+ val iter = value.item.iterator()
+ val allowed = ListBuffer.empty[XdmAtomicValue]
+ while (iter.hasNext) {
+ val token = iter.next()
+ token match {
+ case atom: XdmAtomicValue =>
+ allowed += atom
+ case _ =>
+ error(XProcException.xsInvalidValues(_values.get, None))
+ }
+ }
+
+ return Some(allowed.toList)
+ }
+
+ None
+ }
+
+ protected def valueParser(): XValueParser = {
+ if (_avt.isDefined) {
+ new XValueParser(config, staticContext, XValueParser.parseAvt(_avt.get))
+ } else if (_select.isDefined) {
+ new XValueParser(config, staticContext, _select.get)
+ } else {
+ throw XProcException.xsInvalidPipeline(s"No value provided for ${name}", location)
+ }
+ }
+
+ protected[xxml] def graphEdges(runtime: XMLCalabashRuntime): Unit = {
+ if (constantValue.isDefined) {
+ return
+ }
+
+ for (input <- children[XWithInput]) {
+ for (child <- input.allChildren) {
+ child match {
+ case pipe: XPipe =>
+ pipe.graphEdges(runtime)
+ case _: XWithOutput =>
+ ()
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Name binding has unexpected child: ${child}")
+ }
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ super.elaborateDefaultReadablePort(initial)
+ _drp
+ }
+
+ override protected[xxml] def elaboratePortConnections(): Unit = {
+ // This smells bad. Clearly it's defending against the case where there are no XDataSources
+ // in the children but there's a DRP. Except in the case of variable, if there is an XDataSource,
+ // it'll be in a XWithInput. So why are there ever "naked" XDataSource elements in the
+ // children?
+ if (drp.isDefined && children[XDataSource].isEmpty && children[XWithInput].isEmpty) {
+ addChild(new XPipe(drp.get))
+ }
+
+ super.elaboratePortConnections()
+
+ // Now collect any unwrapped data sources under a p:with-input
+ val newChildren = ListBuffer.empty[XArtifact]
+ val input = if (children[XWithInput].isEmpty) {
+ val xwi = new XWithInput(this, "source")
+ xwi.sequence = true
+ xwi.primary = false
+ xwi.contentTypes = MediaType.MATCH_ANY
+ xwi
+ } else {
+ children[XWithInput].head
+ }
+ newChildren += input
+
+ for (child <- allChildren) {
+ child match {
+ case xwi: XWithInput =>
+ if (xwi ne input) {
+ throw XProcException.xiThisCantHappen("p:with-option has more than one p:with-input child?")
+ }
+ case ds: XDataSource =>
+ input.addChild(ds)
+ case _ =>
+ newChildren += child
+ }
+ }
+
+ allChildren = newChildren.toList
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ var bcontext = initial
+
+ if (_avt.isDefined || _select.isDefined) {
+ try {
+ val parser = valueParser()
+ _variableReferences = parser.variables
+ val contextDependent = parser.contextDependent
+
+ _constant = !(this.isInstanceOf[XOption]) // p:options can never be constants
+ val namepipe = ListBuffer.empty[XNameBinding]
+ for (ref <- _variableReferences) {
+ val cbind = initial.inScopeConstants.get(ref)
+ val dbind = initial.inScopeDynamics.get(ref)
+ if (cbind.isDefined) {
+ // ok
+ } else if (dbind.isDefined) {
+ _constant = false
+ dbind.get match {
+ case v: XVariable =>
+ namepipe += v
+ case opt: XOption =>
+ namepipe += opt
+ case _ =>
+ error(XProcException.xiThisCantHappen(s"Unexpected name binding: ${dbind.get}"))
+ }
+ } else {
+ error(XProcException.xsNoBindingInExpression(ref, None))
+ }
+ }
+
+ if (exceptions.nonEmpty) {
+ return initial
+ }
+
+ if (contextDependent) {
+ _constant = false
+ } else {
+ drp = None // irrelevant
+ }
+
+ if (constant) {
+ val expr = if (_avt.isDefined) {
+ new XProcVtExpression(staticContext, XValueParser.parseAvt(_avt.get), true)
+ } else {
+ new XProcXPathExpression(staticContext, _select.get)
+ }
+
+ val bindings = mutable.HashMap.empty[String,Message]
+ for ((name,value) <- initial.inScopeConstants) {
+ bindings.put(name.getClarkName, value.constantValue.get)
+ }
+
+ var computed = config.expressionEvaluator.value(expr, List(), bindings.toMap, None)
+ if (qnameKeys) {
+ computed.item match {
+ case xmap: XdmMap =>
+ val qnameMap = S9Api.forceQNameKeys(xmap.getUnderlyingValue, computed.context)
+ computed = new XdmValueItemMessage(qnameMap, computed.metadata, computed.context)
+ case xmap: MapItem =>
+ val qnameMap = S9Api.forceQNameKeys(xmap, computed.context)
+ computed = new XdmValueItemMessage(qnameMap, computed.metadata, computed.context)
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Non-map item has qnameKeys: ${computed.item}")
+ }
+ }
+
+ _constantValue = Some(promotedStaticValue(computed))
+ bcontext = bcontext.withBinding(this)
+ } else {
+ if (namepipe.nonEmpty) {
+ val xwi = new XWithInput(this, "#bindings")
+ xwi.sequence = true
+ xwi.primary = false
+ xwi.contentTypes = MediaType.MATCH_ANY
+ addChild(xwi)
+ for (binding <- namepipe) {
+ val xstep = bcontext.inScopeDynamics.get(binding.name)
+ val pipe = new XPipe(xwi, xstep.get.tumble_id, "result")
+ xwi.addChild(pipe)
+ }
+ }
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ super.elaborateNameBindings(initial)
+
+ bcontext
+ }
+
+ protected def promotedStaticValue(staticValueMsg: XdmValueItemMessage): XdmValueItemMessage = {
+ val sig = stepDeclaration
+ val optdecl = sig.get.option(name).get
+
+ XNameBinding.promotedValue(config, name, optdecl.declaredType, optdecl.tokenList, staticValueMsg)
+ }
+}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XNameBindingContext.scala b/src/main/scala/com/xmlcalabash/model/xxml/XNameBindingContext.scala
new file mode 100644
index 0000000..ea50a8f
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XNameBindingContext.scala
@@ -0,0 +1,47 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+
+class XNameBindingContext private(val inScopeStatics: Set[QName],
+ val inScopeConstants: Map[QName, XNameBinding],
+ val inScopeDynamics: Map[QName, XNameBinding]) {
+ def this() = {
+ this(Set(), Map(), Map())
+ }
+
+ def withBinding(binding: XNameBinding): XNameBindingContext = {
+ if (inScopeStatics.contains(binding.name)) {
+ throw XProcException.xsShadowsStatic(binding.name, binding.location)
+ }
+
+ val statics = mutable.Set.empty[QName] ++ inScopeStatics
+ if (binding.static) {
+ statics += binding.name
+ }
+
+ val constants = mutable.HashMap.empty[QName, XNameBinding] ++= inScopeConstants
+ val dynamics = mutable.HashMap.empty[QName, XNameBinding] ++= inScopeDynamics
+
+ if (binding.constant) {
+ constants.put(binding.name, binding)
+ } else {
+ dynamics.put(binding.name, binding)
+ }
+
+ new XNameBindingContext(statics.toSet, constants.toMap, dynamics.toMap)
+ }
+
+ def onlyStatics: XNameBindingContext = {
+ val constants = mutable.HashMap.empty[QName, XNameBinding]
+ for ((name, value) <- inScopeConstants) {
+ if (inScopeStatics.contains(name)) {
+ constants.put(name, value)
+ }
+ }
+ new XNameBindingContext(inScopeStatics, constants.toMap, Map())
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XNamePipe.scala b/src/main/scala/com/xmlcalabash/model/xxml/XNamePipe.scala
new file mode 100644
index 0000000..2066663
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XNamePipe.scala
@@ -0,0 +1,22 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+
+import scala.collection.mutable
+
+class XNamePipe(config: XMLCalabash, val binding: XNameBinding) extends XArtifact(config) {
+ staticContext = binding.staticContext
+
+ def this(binding: XNameBinding) = {
+ this(binding.config, binding)
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("name", Some(binding.name))
+ attr.put("ref", Some(binding.tumble_id))
+ dumpTree(sb, "p:name-pipe", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XNamedArtifact.scala b/src/main/scala/com/xmlcalabash/model/xxml/XNamedArtifact.scala
new file mode 100644
index 0000000..9fc210e
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XNamedArtifact.scala
@@ -0,0 +1,5 @@
+package com.xmlcalabash.model.xxml
+
+trait XNamedArtifact {
+ def stepName: String
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XOption.scala b/src/main/scala/com/xmlcalabash/model/xxml/XOption.scala
new file mode 100644
index 0000000..fb90a18
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XOption.scala
@@ -0,0 +1,140 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.runtime.params.XPathBindingParams
+import com.xmlcalabash.runtime.{XMLCalabashRuntime, XProcMetadata, XProcXPathExpression}
+import com.xmlcalabash.util.{TypeUtils, XProcVarValue}
+import net.sf.saxon.s9api.{QName, SaxonApiException, XdmAtomicValue}
+import net.sf.saxon.trans.XPathException
+
+import scala.collection.mutable
+
+class XOption(config: XMLCalabash) extends XNameBinding(config) with XGraphableArtifact {
+ private var _allowedValues = Option.empty[List[XdmAtomicValue]]
+ private var _runtimeBindings = Map.empty[QName, XProcVarValue]
+ private var _cx_as = Option.empty[String]
+
+ def this(config: XMLCalabash, name: QName, value: XdmValueItemMessage) = {
+ this(config)
+ _name = name
+ _constantValue = Some(value)
+ _synthetic = true
+ _constant = true
+ staticContext = new XArtifactContext(this, value.context, XProcConstants.p_option)
+ }
+
+ def tokenList: Option[List[XdmAtomicValue]] = _allowedValues
+
+ def allowedValues: Option[List[XdmAtomicValue]] = _allowedValues
+
+ override def usedByPipeline: Boolean = {
+ // always evaluate these so we find static errors
+ true
+ }
+
+ def runtimeBindings(bindings: Map[QName, XProcVarValue]): Unit = {
+ _runtimeBindings = bindings
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ _cx_as = attr(XProcConstants.cx_as)
+ _allowedValues = checkValueTokens
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ if (!static) {
+ parent.get match {
+ case _: XLibrary =>
+ error(XProcException.xsOptionMustBeStatic(name, None))
+ case _ => ()
+ }
+ }
+
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo =>
+ case _: XDocumentation =>
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ allChildren = List()
+
+ val xwo = new XWithOutput(this, "result")
+ addChild(xwo)
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ super.elaborateNameBindings(initial)
+
+ var newContext = initial
+ if (static) {
+ _constant = true
+ _constantValue = Some(config.staticOptions(name))
+ newContext = initial.withBinding((this))
+ } else {
+ newContext = initial.withBinding(this)
+ if (select.isDefined) {
+ val compiler = config.processor.newXPathCompiler()
+ for (name <- _variableReferences) {
+ compiler.declareVariable(name)
+ }
+ for ((prefix,uri) <- staticContext.inscopeNamespaces) {
+ compiler.declareNamespace(prefix, uri)
+ }
+ try {
+ compiler.compile(select.get)
+ } catch {
+ /*
+ case ex: SaxonApiException =>
+ throw XProcException.xsStaticErrorInExpression(select.get, ex.getMessage, location)
+ case ex: Exception =>
+ throw ex
+ */
+ case ex: Exception =>
+ logger.debug(ex.getMessage)
+ }
+ }
+ }
+
+ newContext
+ }
+
+ override protected[xxml] def elaboratePortConnections(): Unit = {
+ // p:option doesn't have any port connections
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ if (!static && usedByPipeline) {
+ val params = new XPathBindingParams(false)
+ val expr = new XProcXPathExpression(staticContext, _select.getOrElse("()"), declaredType, _allowedValues, params)
+ val start = parent.asInstanceOf[ContainerStart]
+ runtime.addNode(this, start.addOption(name.getClarkName, expr, params, true))
+ }
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ if (Option(_name).isDefined) {
+ attr.put("name", Some(_name.getEQName))
+ }
+
+ if (constantValue.isDefined) {
+ attr.put("constant-value", Some(constantValue.get.item.toString))
+ } else {
+ attr.put("select", _select)
+ attr.put("avt", _avt)
+ }
+
+ attr.put("as", _as)
+ attr.put("required", _required)
+ attr.put("visiblity", _visibility)
+ dumpTree(sb, "p:option", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XOtherwise.scala b/src/main/scala/com/xmlcalabash/model/xxml/XOtherwise.scala
new file mode 100644
index 0000000..8ee5db5
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XOtherwise.scala
@@ -0,0 +1,71 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+
+import scala.collection.mutable
+
+class XOtherwise(config: XMLCalabash) extends XChooseBranch(config) {
+ def this(choose: XChoose) = {
+ this(choose.config)
+ staticContext = choose.staticContext
+ parent = choose
+ synthetic = true
+ syntheticName = XProcConstants.p_otherwise
+ _test = "true()"
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ _test = "true()"
+ }
+
+ override def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ var seenPipeline = false
+
+ //val newScope = checkStepNameScoping(inScopeNames)
+ for (child <- allChildren) {
+ child.validate()
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XOutput =>
+ if (seenPipeline) {
+ error(XProcException.xsInvalidPipeline("p:output cannot follow steps in p:otherwise", location))
+ }
+ case _: XVariable =>
+ seenPipeline = true
+ case _: XStep =>
+ seenPipeline = true
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ if (children[XOutput].length == 1) {
+ val output = children[XOutput].head
+ if (!output.primarySpecified) {
+ output.primary = true
+ }
+ }
+
+ constructDefaultOutput()
+
+ orderChildren()
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ if (synthetic && initial.isEmpty) {
+ // Special case, return an empty sequence
+ val step = children[XStep].head
+ val input = step.children[XWithInput].head
+ input.addChild(new XEmpty(input))
+ }
+
+ super.elaborateDefaultReadablePort(initial)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XOutput.scala b/src/main/scala/com/xmlcalabash/model/xxml/XOutput.scala
new file mode 100644
index 0000000..65c5b2b
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XOutput.scala
@@ -0,0 +1,181 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.runtime.XProcXPathExpression
+import com.xmlcalabash.util.{MediaType, S9Api}
+import net.sf.saxon.s9api.{QName, SaxonApiException, XdmMap}
+
+import scala.collection.mutable
+import scala.jdk.CollectionConverters.MapHasAsScala
+
+class XOutput(config: XMLCalabash) extends XPort(config) {
+ private var serializationExpr = Option.empty[String]
+ private val _serialization = mutable.Map.empty[QName,String]
+
+ def this(step: XContainer, port: Option[String]) = {
+ this(step.config)
+ staticContext = step.staticContext
+ parent = step
+ synthetic = true
+ syntheticName = XProcConstants.p_output
+ if (port.isDefined) {
+ _port = port.get
+ }
+ _attrChecked = true
+ }
+
+ def serialization: Map[QName,String] = {
+ _serialization.toMap
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ if (parent.isDefined) {
+ parent.get match {
+ case _: XDeclareStep =>
+ checkDeclareStepAttributes()
+ case _ =>
+ checkCompoundStepAttributes()
+ }
+ } else {
+ checkCompoundStepAttributes() // This can't happen
+ }
+ }
+
+ override protected[xxml] def checkEmptyAttributes(): Unit = {
+ super.checkEmptyAttributes()
+ _attrChecked = true
+ }
+
+ private def checkDeclareStepAttributes(): Unit = {
+ checkCompoundStepAttributes()
+ serializationExpr = attr(XProcConstants._serialization)
+ }
+
+ private def checkCompoundStepAttributes(): Unit = {
+ try {
+ if (attributes.contains(XProcConstants._port)) {
+ _port = staticContext.parseNCName(attr(XProcConstants._port)).get
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._port, None))
+ }
+
+ _sequence = staticContext.parseBoolean(attr(XProcConstants._sequence))
+ _primary = staticContext.parseBoolean(attr(XProcConstants._primary))
+ _content_types = staticContext.parseContentTypes(attr(XProcConstants._content_types))
+ if (_content_types.isEmpty) {
+ _content_types = List(MediaType.OCTET_STREAM)
+ }
+
+ _href = attr(XProcConstants._href)
+ _pipe = attr(XProcConstants._pipe)
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ if (!_attrChecked) {
+ checkAttributes()
+ checkEmptyAttributes()
+ }
+ super.validate()
+
+ if (parent.isDefined) {
+ parent.get match {
+ case decl: XDeclareStep =>
+ if (decl.atomic && children[XDataSource].nonEmpty) {
+ if (decl.stepType.isDefined) {
+ error(XProcException.xsAtomicOutputWithBinding(port, decl.stepType.get, location))
+ } else {
+ error(XProcException.xsAtomicOutputWithBinding(port, location))
+ }
+ }
+ case _ =>
+ ()
+ }
+ }
+
+ if (serializationExpr.isDefined) {
+ val exprEval = config.expressionEvaluator.newInstance()
+ val expr = new XProcXPathExpression(staticContext, serializationExpr.get)
+ val value = try {
+ exprEval.value(expr, List(), staticContext.inscopeConstantBindings, None)
+ } catch {
+ case sae: SaxonApiException =>
+ throw XProcException.xsStaticErrorInExpression(serializationExpr.get, sae.getMessage, None)
+ }
+ value.item match {
+ case map: XdmMap =>
+ val sermap = S9Api.forceQNameKeys(map.getUnderlyingValue, staticContext)
+ for ((key,value) <- sermap.asImmutableMap().asScala) {
+ val name = key.getQNameValue
+ _serialization(name) = value.getUnderlyingValue.getStringValue
+ }
+ case _ =>
+ throw XProcException.xdValueDoesNotSatisfyType(value.item.toString, location)
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ if (parent.isDefined) {
+ parent.get match {
+ case cont: XContainer =>
+ val steps = cont.children[XStep]
+ if (steps.nonEmpty) {
+ for (child <- allChildren) {
+ child.elaborateDefaultReadablePort(steps.last.primaryOutput)
+ }
+ initial
+ } else {
+ super.elaborateDefaultReadablePort(initial)
+ }
+ case _ =>
+ super.elaborateDefaultReadablePort(initial)
+ }
+ } else {
+ super.elaborateDefaultReadablePort(initial)
+ }
+ }
+
+ override def elaboratePortConnections(): Unit = {
+ super.elaboratePortConnections()
+ if (parent.isDefined) {
+ parent.get match {
+ case decl: XDeclareStep =>
+ if (!decl.atomic) {
+ checkOutputBinding(decl)
+ }
+ case cont: XContainer =>
+ checkOutputBinding(cont)
+ case _ =>
+ throw XProcException.xiThisCantHappen("p:output is not a child of a container?")
+ }
+ } else {
+ throw XProcException.xiThisCantHappen("p:output has no parent?")
+ }
+ }
+
+ private def checkOutputBinding(cont: XContainer): Unit = {
+ if (children[XDataSource].isEmpty && primary) {
+ val last = cont.children[XStep].last
+ if (last.primaryOutput.isDefined) {
+ val pipe = new XPipe(this, Some(last.stepName), Some(last.primaryOutput.get.port))
+ addChild(pipe)
+ }
+ }
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("port", Some(_port))
+ attr.put("select", _select)
+ attr.put("primary", _primary)
+ attr.put("sequence", _sequence)
+ dumpTree(sb, "p:output", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XParser.scala b/src/main/scala/com/xmlcalabash/model/xxml/XParser.scala
new file mode 100644
index 0000000..cb68832
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XParser.scala
@@ -0,0 +1,141 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.config.DocumentRequest
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.model.xxml.XParser._builtinLibraries
+import com.xmlcalabash.util.MediaType
+import net.sf.saxon.s9api.XdmNode
+import org.slf4j.{Logger, LoggerFactory}
+import org.xml.sax.InputSource
+
+import java.net.URI
+import javax.xml.transform.sax.SAXSource
+import scala.collection.mutable.ListBuffer
+
+object XParser {
+ private var _builtinLibraries: ListBuffer[XLibrary] = _
+}
+
+class XParser(val config: XMLCalabash) {
+ protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
+ private val _exceptions = ListBuffer.empty[Exception]
+
+ // Load all of the built in steps
+ if (Option(_builtinLibraries).isEmpty) {
+ _builtinLibraries = ListBuffer.empty[XLibrary]
+
+ config.standardLibraryParser = true
+ val xpls = getClass.getClassLoader.getResources("com.xmlcalabash.library.xpl")
+ while (xpls.hasMoreElements) {
+ val xpl = xpls.nextElement()
+ val xmlbuilder = config.processor.newDocumentBuilder()
+ val stream = xpl.openStream()
+ val source = new SAXSource(new InputSource(stream))
+ xmlbuilder.setDTDValidation(false)
+ xmlbuilder.setLineNumbering(true)
+ val libnode = xmlbuilder.build(source)
+ val library = loadLibrary(libnode)
+
+ _exceptions ++= library.errors
+
+ _builtinLibraries += library
+ }
+
+ config.standardLibraryParser = false
+ }
+
+ logger.debug("Built in libraries loaded")
+
+ protected[xxml] def builtinLibraries: List[XLibrary] = _builtinLibraries.toList
+ def exceptions: List[Exception] = _exceptions.toList
+
+ def exceptions(artifact: XArtifact): List[Exception] = {
+ if (Option(artifact).isDefined) {
+ _exceptions.toList ++ artifact.exceptions
+ } else {
+ _exceptions.toList
+ }
+ }
+
+ def loadLibrary(uri: URI): XLibrary = {
+ val request = new DocumentRequest(uri, MediaType.XML)
+ val response = config.documentManager.parse(request)
+ if (response.contentType.xmlContentType) {
+ loadLibrary(response.value.asInstanceOf[XdmNode])
+ } else {
+ throw XProcException.xsInvalidPipeline(s"Document is not XML: ${uri}", None)
+ }
+ }
+
+ def loadLibrary(node: XdmNode): XLibrary = {
+ val decl = load(node)
+
+ val library = decl match {
+ case lib: XLibrary =>
+ lib
+ case _ =>
+ val lib = new XLibrary(config, Option(node.getBaseURI))
+ lib.staticContext = new XArtifactContext(lib, node)
+ lib.synthetic = true
+ lib.syntheticName = XProcConstants.p_library
+ lib.addChild(decl)
+ lib
+ }
+
+ library.xelaborate()
+ library
+ }
+
+ def loadDeclareStep(uri: URI): XDeclareStep = {
+ val request = new DocumentRequest(uri, MediaType.XML)
+ val response = config.documentManager.parse(request)
+ if (response.contentType.xmlContentType) {
+ loadDeclareStep(response.value.asInstanceOf[XdmNode])
+ } else {
+ throw XProcException.xsInvalidPipeline(s"Document is not XML: ${uri}", None)
+ }
+ }
+
+ def loadDeclareStep(node: XdmNode): XDeclareStep = {
+ val decl = load(node)
+
+ decl match {
+ case step: XDeclareStep =>
+ step.xelaborate()
+ step
+ case _ =>
+ throw XProcException.xiUserError(s"Pipeline document did not contain a p:declare-step.")
+ }
+ }
+
+ private def load(node: XdmNode): XDeclContainer = {
+ val hier = NodeHierarchy.newInstance(config, node)
+ val loader = new Loader(this, hier)
+ _exceptions ++= loader.exceptions
+
+ if (!hier.useWhen(hier.root)) {
+ _exceptions += XProcException.xsInvalidPipeline("Root element use-when is false; no document", None)
+ }
+
+ if (_exceptions.nonEmpty) {
+ throw _exceptions.head
+ }
+
+ val decl = if (loader.declaredStep.isEmpty) {
+ loader.library.get
+ } else {
+ loader.declaredStep.get
+ }
+
+ decl.builtinLibraries = _builtinLibraries.toList
+ _exceptions ++= decl.errors
+
+ if (_exceptions.nonEmpty) {
+ throw _exceptions.head
+ }
+
+ decl
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XPipe.scala b/src/main/scala/com/xmlcalabash/model/xxml/XPipe.scala
new file mode 100644
index 0000000..e4f9446
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XPipe.scala
@@ -0,0 +1,227 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+
+import scala.collection.mutable
+
+class XPipe(config: XMLCalabash) extends XDataSource(config) {
+ private var _step = Option.empty[String]
+ private var _port = Option.empty[String]
+
+ private var _fromPort: XPort = _
+ private var _fromStep: XArtifact = _
+ private var _toStep: XArtifact = _
+
+ def step: Option[String] = _step
+ def port: Option[String] = _port
+ def from: Option[XPort] = Option(_fromPort)
+
+ def this(parent: XArtifact, step: Option[String], port: Option[String]) = {
+ this(parent.config)
+ this.parent = parent
+ staticContext = parent.staticContext
+ _synthetic = true
+ _step = step
+ _port = port
+ }
+
+ def this(parent: XArtifact, step: String, port: String) = {
+ this(parent, Some(step), Some(port))
+ }
+
+ // This constructor works even if we don't subsequently attempt to validate the connection
+ def this(parent: XArtifact, originPort: XPort) = {
+ this(parent.config)
+ this.parent = parent
+ staticContext = parent.staticContext
+ _synthetic = true
+ _fromPort = originPort
+ _fromStep = originPort.ancestorNode.get
+ _step = Some(originPort.parent.get.asInstanceOf[XStep].stepName)
+ _port = Some(originPort.port)
+ _toStep = parent.ancestorNode.get
+ }
+
+ def this(port: XPort) = {
+ this(port.config)
+ val pstep = port.ancestorStep
+ staticContext = port.staticContext
+ _synthetic = true
+ _step = Some(pstep.get.stepName)
+ _port = Some(port.port)
+ }
+
+ def this(pipe: XPipe) = {
+ this(pipe.config)
+ staticContext = pipe.staticContext
+ _synthetic = true
+ _step = pipe._step
+ _port = pipe._port
+ }
+
+ protected[xxml] def graphEdges(runtime: XMLCalabashRuntime): Unit = {
+ // FIXME: make handling of dropped variables more robust
+ var connect = true
+ _toStep match {
+ case bind: XNameBinding =>
+ connect = bind.usedByPipeline
+ case _ =>
+ ()
+ }
+
+ if (connect) {
+ val fromNode = runtime.node(_fromStep)
+ val toNode = runtime.node(_toStep)
+
+ parent.get match {
+ case xport: XPort =>
+ var sport = xport.port
+ var rport = port.get
+
+ if (sport == "#anon") {
+ sport = "source"
+ if (_toStep.ancestorOf(_fromStep)) {
+ sport = "#anon_result"
+ }
+ }
+
+ if (rport == "#anon") {
+ rport = "#anon_result"
+ }
+
+ if (_fromStep eq _toStep) {
+ // This is a special case that the Graph doesn't catch.
+ // FIXME: fix this bug in the graph construction code
+
+ val ps = _fromPort.parent.get
+ ps match {
+ case _: XContainer =>
+ () // this is ok
+ case _ =>
+ throw XProcException.xsLoop(ps.asInstanceOf[XStep].stepName, rport, location)
+ }
+ }
+
+ //println(s"Edge from ${_fromStep}/${rport} to ${_toStep}/${sport}")
+ runtime.graph.addOrderedEdge(fromNode, rport, toNode, sport)
+ case _: XNameBinding =>
+ //println(s"Edge from ${_fromStep}/${port.get} to ${_toStep}/source")
+ runtime.graph.addOrderedEdge(fromNode, port.get, toNode, "source")
+ }
+ }
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ try {
+ if (attributes.contains(XProcConstants._step)) {
+ _step = Some(staticContext.parseNCName(attr(XProcConstants._step).get))
+ }
+ if (attributes.contains(XProcConstants._port)) {
+ _port = Some(staticContext.parseNCName(attr(XProcConstants._port).get))
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+ for (child <- allChildren) {
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+ allChildren = List()
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ initial
+ }
+
+ override protected[xxml] def elaboratePortConnections(): Unit = {
+ if (step.isEmpty) {
+ if (drp.isEmpty) {
+ error(XProcException.xsPipeWithoutStepOrDrp(location))
+ } else {
+ drp.get.parent.get match {
+ case xstep: XStep =>
+ _step = Some(xstep.stepName)
+ case _ =>
+ error(XProcException.xiThisCantHappen("Parent of drp is not a step?"))
+ }
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateValidatePortConnections(ports: XPortBindingContext): Unit = {
+ val gparent = if (parent.isDefined) {
+ parent.get.parent
+ } else {
+ None
+ }
+
+ val internal = if (gparent.isDefined) {
+ gparent.get match {
+ case _: XChoose => true
+ case _ => false
+ }
+ } else {
+ false
+ }
+
+ val from = if (port.isEmpty) {
+ ports.primaryPort(_step.get)
+ } else {
+ if (internal) {
+ ports.privatePort(this)
+ } else {
+ ports.port(this)
+ }
+ }
+
+ if (from.isEmpty) {
+ if (port.isEmpty) {
+ error(XProcException.xsPortNotReadableNoPrimaryInput(_step.get, location))
+ } else {
+ error(XProcException.xsPortNotReadable(_step.get, _port.get, location))
+ }
+ return
+ }
+
+ _fromPort = from.get
+ _fromStep = from.get.ancestorNode.get
+ _port = Some(_fromPort.port)
+ _toStep = parent.get.ancestorNode.get
+ }
+
+ override protected[xxml] def computeReadsFrom(): Unit = {
+ super.computeReadsFrom()
+ val step = _fromPort.parent.get
+ for (child <- step.children[XWithOutput]) {
+ if (child.port == _fromPort.port) {
+ child.readBy = _toStep
+ }
+ }
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("step", _step)
+ attr.put("port", _port)
+ dumpTree(sb, "p:pipe", attr.toMap)
+ }
+
+ override def toString: String = {
+ s"from ${step.getOrElse("(drp step)")}/${port.getOrElse("(primary output)")}"
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XPipeinfo.scala b/src/main/scala/com/xmlcalabash/model/xxml/XPipeinfo.scala
new file mode 100644
index 0000000..3048b69
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XPipeinfo.scala
@@ -0,0 +1,15 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+import net.sf.saxon.s9api.XdmNode
+
+class XPipeinfo(config: XMLCalabash, val content: XdmNode) extends XArtifact(config) {
+ override protected[xxml] def validate(): Unit = {
+ // nop
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ dumpTree(sb, "p:pipeinfo", Map(), "« content elided »")
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XPort.scala b/src/main/scala/com/xmlcalabash/model/xxml/XPort.scala
new file mode 100644
index 0000000..ddcdf80
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XPort.scala
@@ -0,0 +1,91 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.util.MediaType
+
+import scala.collection.mutable.ListBuffer
+
+abstract class XPort(config: XMLCalabash) extends XArtifact(config) {
+ protected var _port: String = "#anon"
+ protected var _sequence = Option.empty[Boolean]
+ protected var _primary = Option.empty[Boolean]
+ protected var _select = Option.empty[String]
+ protected var _selectBindings = Option.empty[XWithInput]
+ protected var _content_types = List.empty[MediaType]
+ protected val _defaultInputs: ListBuffer[XDataSource] = ListBuffer.empty[XDataSource]
+ // XInput and XOutput on p:declare-step are checked when the step API is
+ // computed. Elsewhere, they need to be checked during validation.
+ protected var _attrChecked = false
+ private var _irrelevant = false
+
+ protected var _href = Option.empty[String]
+ protected var _pipe = Option.empty[String]
+
+ private var _drp: Option[XPort] = None
+
+ def portSpecified: Boolean = !_port.startsWith("#")
+
+ def port: String = _port
+ protected[xxml] def port_=(name: String): Unit = {
+ _port = name
+ }
+ def sequence: Boolean = _sequence.getOrElse(false)
+ protected[xxml] def sequence_=(seq: Boolean): Unit = {
+ _sequence = Some(seq)
+ }
+ def primary: Boolean = _primary.getOrElse(false)
+ def primarySpecified: Boolean = _primary.nonEmpty
+ protected[xxml] def primary_=(primary: Boolean): Unit = {
+ _primary = Some(primary)
+ }
+ def select: Option[String] = _select
+ protected[xxml] def selectBindings: Option[XWithInput] = _selectBindings
+ def contentTypes: List[MediaType] = _content_types
+ protected[xxml] def contentTypes_=(types: List[MediaType]): Unit = {
+ _content_types = types
+ }
+
+ def irrelevant: Boolean = _irrelevant
+ protected[xxml] def irrelevant_=(irrelevant: Boolean): Unit = {
+ _irrelevant = irrelevant
+ }
+
+ def defaultInputs: List[XDataSource] = _defaultInputs.toList
+
+ protected[xxml] def drp: Option[XPort] = _drp
+ protected[xxml] def drp_=(port: Option[XPort]): Unit = {
+ _drp = port
+ }
+
+ protected[xxml] def graphEdges(runtime: XMLCalabashRuntime): Unit = {
+ for (child <- allChildren) {
+ child match {
+ case pipe: XPipe =>
+ pipe.graphEdges(runtime)
+ case _ =>
+ throw XProcException.xiThisCantHappen("Port has a child that isn't a pipe?")
+ }
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ allChildren = validateExplicitConnections(_href, _pipe)
+ _href = None
+ _pipe = None
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ super.elaborateDefaultReadablePort(initial)
+ }
+
+ override def toString: String = {
+ if (parent.isDefined) {
+ s"${parent.get} :: ${port}"
+ } else {
+ port
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XPortBindingContext.scala b/src/main/scala/com/xmlcalabash/model/xxml/XPortBindingContext.scala
new file mode 100644
index 0000000..b513eac
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XPortBindingContext.scala
@@ -0,0 +1,46 @@
+package com.xmlcalabash.model.xxml
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XPortBindingContext private(portMap: Map[String,XPort], internalPortMap: Map[String,XPort]) {
+ def this() = {
+ this(Map(), Map())
+ }
+
+ def withContainer(step: XContainer): XPortBindingContext = {
+ val newPorts = step.publicPipeConnections
+ val newInternalPorts = step.privatePipeConnections
+ new XPortBindingContext(portMap ++ newPorts, internalPortMap ++ newInternalPorts)
+ }
+
+ def primaryPort(stepName: String): Option[XPort] = {
+ val prefix = s"${stepName}/"
+ for (port <- portMap.keySet) {
+ if (port.startsWith(prefix)) {
+ if (portMap(port).primary) {
+ return portMap.get(port)
+ }
+ }
+ }
+ None
+ }
+
+ def port(pipe: XPipe): Option[XPort] = {
+ val key = s"${pipe.step.getOrElse("???")}/${pipe.port.getOrElse("???")}"
+ portMap.get(key)
+ }
+
+ def privatePort(pipe: XPipe): Option[XPort] = {
+ val key = s"${pipe.step.getOrElse("???")}/${pipe.port.getOrElse("???")}"
+ if (portMap.contains(key)) {
+ portMap.get(key)
+ } else {
+ internalPortMap.get(key)
+ }
+ }
+
+ def validate(pipe: XPipe): Boolean = {
+ port(pipe).isDefined
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XSelectFilter.scala b/src/main/scala/com/xmlcalabash/model/xxml/XSelectFilter.scala
new file mode 100644
index 0000000..aae0c5a
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XSelectFilter.scala
@@ -0,0 +1,23 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.params.{InlineLoaderParams, SelectFilterParams}
+import com.xmlcalabash.runtime.{StepProxy, XMLCalabashRuntime}
+
+class XSelectFilter(parentStep: XArtifact, filterStep: XStep, input: XPort) extends XAtomicStep(filterStep.config, XProcConstants.cx_select_filter) {
+ staticContext = filterStep.staticContext
+ _synthetic = true
+ parent = parentStep
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val params = new SelectFilterParams(filterStep.staticContext, input.select.get, input.port, input.sequence)
+
+ val start = parent.asInstanceOf[ContainerStart]
+ val impl = stepImplementation
+ impl.configure(config, stepType, name, Some(params))
+
+ val proxy = new StepProxy(runtime, stepType, impl, staticContext)
+ runtime.addNode(this, start.addAtomic(proxy, "cx:select-filter"))
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XSkeletonStepSignature.scala b/src/main/scala/com/xmlcalabash/model/xxml/XSkeletonStepSignature.scala
new file mode 100644
index 0000000..3035536
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XSkeletonStepSignature.scala
@@ -0,0 +1,45 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XSkeletonStepSignature(val decl: XDeclareStep) {
+ private val _inputs = mutable.HashSet.empty[String]
+ private val _outputs = mutable.HashSet.empty[String]
+ private val _options = ListBuffer.empty[QName]
+
+ private val typeAttr = decl.attributes.get(XProcConstants._type)
+ if (typeAttr.isEmpty) {
+ throw XProcException.xiThisCantHappen("Attempt to create skeleton step signature without type")
+ }
+
+ private val _stepType = decl.staticContext.parseQName(typeAttr.get)
+
+ for (child <- decl.allChildren) {
+ child match {
+ case _: XInput =>
+ if (child.attributes.contains(XProcConstants._port)) {
+ _inputs += child.attributes(XProcConstants._port)
+ }
+ case _: XOutput =>
+ if (child.attributes.contains(XProcConstants._port)) {
+ _outputs += child.attributes(XProcConstants._port)
+ }
+ case _: XOption =>
+ if (child.attributes.contains(XProcConstants._name)) {
+ _options += decl.staticContext.parseQName(child.attributes(XProcConstants._name))
+ }
+ case _ =>
+ ()
+ }
+ }
+
+ def stepType: QName = _stepType
+ def inputs: Set[String] = _inputs.toSet
+ def outputs: Set[String] = _outputs.toSet
+ def options: List[QName] = _options.toList
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XStaticContext.scala b/src/main/scala/com/xmlcalabash/model/xxml/XStaticContext.scala
new file mode 100644
index 0000000..d98360d
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XStaticContext.scala
@@ -0,0 +1,86 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.Location
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.messages.{XdmNodeItemMessage, XdmValueItemMessage}
+import com.xmlcalabash.util.{MinimalStaticContext, URIUtils, Urify, VoidLocation}
+import net.sf.saxon.s9api.{QName, XdmValue}
+
+import java.net.URI
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XStaticContext() extends MinimalStaticContext() {
+ protected var _config: XMLCalabash = _
+ protected var _baseURI: Option[URI] = None
+ protected var _location: Option[Location] = None
+ protected val _inscopeNamespaces: mutable.HashMap[String, String] = mutable.HashMap.empty[String, String]
+ protected var _inscopeConstants: ListBuffer[XNameBinding] = ListBuffer.empty[XNameBinding]
+
+ def this(location: Option[Location], inscopeNamespaces: Map[String,String]) = {
+ this()
+ _location = location
+ _inscopeNamespaces ++= inscopeNamespaces
+ }
+
+ def this(location: Location, inscopeNamespaces: Map[String,String]) = this(Some(location), inscopeNamespaces)
+
+ def this(baseURI: URI, inscopeNamespaces: Map[String,String]) = {
+ this()
+ _baseURI = Some(baseURI)
+ _inscopeNamespaces ++= inscopeNamespaces
+ }
+
+ def baseURI: Option[URI] = {
+ if (_baseURI.isDefined) {
+ _baseURI
+ } else {
+ if (location.isDefined && location.get.uri.isDefined) {
+ Some(new URI(Urify.urify(location.get.uri.get)))
+ } else {
+ Some(URIUtils.cwdAsURI)
+ }
+ }
+ }
+ protected[xxml] def baseURI_=(base: URI): Unit = {
+ _baseURI = Some(base)
+ }
+
+ def location: Option[Location] = _location
+ protected[xxml] def location_=(loc: Location): Unit = {
+ _location = Some(loc)
+ }
+
+ override def inscopeNamespaces: Map[String, String] = _inscopeNamespaces.toMap
+
+ override def inscopeConstants: Map[QName, XNameBinding] = {
+ val map = mutable.HashMap.empty[QName, XNameBinding]
+ for (opt <- _inscopeConstants) {
+ map.put(opt.name, opt)
+ }
+ map.toMap
+ }
+
+ def inscopeConstantBindings: Map[String, Message] = {
+ val bindings = mutable.HashMap.empty[String,Message]
+ for ((name,value) <- inscopeConstants) {
+ bindings.put(name.getClarkName, value.constantValue.get)
+ }
+ bindings.toMap
+ }
+
+ def inscopeConstantValues: Map[QName, XdmValue] = {
+ val statics = mutable.HashMap.empty[QName,XdmValue]
+ for ((name, value) <- inscopeConstantBindings) {
+ val qname = parseClarkName(name)
+ value match {
+ case msg: XdmNodeItemMessage =>
+ statics.put(qname,msg.item)
+ case msg: XdmValueItemMessage =>
+ statics.put(qname,msg.item)
+ }
+ }
+ statics.toMap
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XStep.scala b/src/main/scala/com/xmlcalabash/model/xxml/XStep.scala
new file mode 100644
index 0000000..69b90c9
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XStep.scala
@@ -0,0 +1,351 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.Node
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.util.MediaType
+import net.sf.saxon.s9api.{QName, SaxonApiException}
+
+import scala.collection.mutable
+
+abstract class XStep(config: XMLCalabash) extends XArtifact(config) with XNamedArtifact with XGraphableArtifact {
+ private var _name = Option.empty[String]
+ private var _drp = Option.empty[XPort]
+ protected var _type: Option[QName] = None
+ protected[xxml] val dependsOn = mutable.HashMap.empty[String, Option[XStep]]
+
+ def name: Option[String] = _name
+
+ def stepName: String = _name.getOrElse(tumble_id)
+
+ protected[xxml] def stepName_=(name: String): Unit = {
+ try {
+ _name = Some(staticContext.parseNCName(name))
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+ }
+
+ protected[xxml] def drp: Option[XPort] = _drp
+
+ protected[xxml] def drp_=(port: Option[XPort]): Unit = {
+ _drp = port
+ }
+
+ def primaryOutput: Option[XPort]
+
+ def outputs: Set[XOutput] = {
+ val decl = stepDeclaration
+ if (decl.isDefined) {
+ decl.get.children[XOutput].toSet
+ } else {
+ children[XOutput].toSet
+ }
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ val pstep = if (synthetic) {
+ syntheticName.get.getNamespaceURI == XProcConstants.ns_p
+ } else {
+ staticContext.nodeName.getNamespaceURI == XProcConstants.ns_p
+ }
+
+ for (name <- attributes.keySet) {
+ if (pstep && name.getNamespaceURI == XProcConstants.ns_p) {
+ error(XProcException.xsXProcNamespaceError(name, staticContext.nodeName, location))
+ }
+
+ name match {
+ case XProcConstants._name =>
+ val aname = attr(XProcConstants._name)
+ try {
+ stepName = staticContext.parseNCName(aname.get)
+ } catch {
+ case _: SaxonApiException =>
+ error(XProcException.xsBadTypeValue(aname.get, "xs:NCName", None))
+ }
+ case XProcConstants.p_message =>
+ if (!pstep) {
+ syntheticOption(XProcConstants.p_message, attr(name).get)
+ }
+ case XProcConstants._message =>
+ if (pstep) {
+ syntheticOption(XProcConstants._message, attr(name).get)
+ }
+ case XProcConstants.p_depends =>
+ if (!pstep) {
+ depends(attr(name).get)
+ }
+ case XProcConstants._depends =>
+ if (pstep) {
+ staticContext.nodeName.getLocalName match {
+ case "when" => error(XProcException.xsBadAttribute(name, location))
+ case "otherwise" => error(XProcException.xsBadAttribute(name, location))
+ case "catch" => error(XProcException.xsBadAttribute(name, location))
+ case "finally" => error(XProcException.xsBadAttribute(name, location))
+ case _ => depends(attr(name).get)
+ }
+ }
+ case XProcConstants.p_expand_text =>
+ if (!pstep) {
+ val value = attr(name).get
+ if (value != "true" && value != "false") {
+ error(XProcException.xsInvalidExpandText(name, value, location))
+ }
+ } else {
+ error(XProcException.xsBadAttribute(name, None))
+ }
+ case XProcConstants._expand_text =>
+ if (pstep) {
+ val value = attr(name).get
+ if (value != "true" && value != "false") {
+ error(XProcException.xsInvalidExpandText(name, value, location))
+ }
+ } else {
+ error(XProcException.xsBadAttribute(name, None))
+ }
+ case XProcConstants.p_timeout =>
+ if (!pstep) {
+ syntheticOption(name, attr(name).get)
+ }
+ case XProcConstants._timeout =>
+ if (pstep) {
+ syntheticOption(name, attr(name).get)
+ }
+ case _ =>
+ ()
+ }
+ }
+ }
+
+ protected def syntheticOption(name: QName, value: String): Unit = {
+ val option = new XWithOption(this, name, Some(value), None)
+ addChild(option)
+ }
+
+ private def depends(value: String): Unit = {
+ if (value.trim == "") {
+ error(XProcException.xsBadTypeEmpty(value, location))
+ return
+ }
+
+ for (name <- value.trim.split("\\s+")) {
+ try {
+ staticContext.parseNCName(name)
+ dependsOn.put(name, None)
+ } catch {
+ case _: Exception =>
+ error(XProcException.xsBadTypeValue(name, "xs:NCName", location))
+ }
+ }
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ super.elaborateDefaultReadablePort(initial)
+ }
+
+ override protected[xxml] def elaborateDependsConnections(inScopeSteps: Map[String, XStep]): Unit = {
+ for (name <- dependsOn.keySet) {
+ if (inScopeSteps.contains(name)) {
+ dependsOn.put(name, Some(inScopeSteps(name)))
+ } else {
+ error(XProcException.xsNotAStep(name, location))
+ }
+ }
+ super.elaborateDependsConnections(inScopeSteps)
+ }
+
+ def container: XContainer = {
+ val art = if (parent.isDefined) {
+ parent.get
+ } else {
+ this
+ }
+
+ art match {
+ case decl: XContainer =>
+ decl
+ case _ =>
+ throw XProcException.xiThisCantHappen("Parent of step isn't a container?")
+ }
+ }
+
+ def declarationContainer: XDeclContainer = {
+ var art = parent
+ while (art.isDefined) {
+ art.get match {
+ case decl: XDeclContainer =>
+ return decl
+ case _ => ()
+ }
+ art = art.get.parent
+ }
+
+ if (art.isEmpty) {
+ this match {
+ case decl: XDeclContainer =>
+ return decl
+ case _ => ()
+ }
+ }
+
+ throw XProcException.xiThisCantHappen("No ancestor of step is a declaration container?")
+ }
+
+ protected def elaborateDynamicOptions(): Unit = {
+ children[XStep] foreach { _.elaborateDynamicOptions() }
+ }
+
+ protected[xxml] def elaborateInsertSelectFilters(): Unit = {
+ if (parent.isDefined && parent.get.isInstanceOf[XContainer]) {
+ // If we're in a container, we could add filters
+ val container = parent.get.asInstanceOf[XContainer]
+
+ for (child <- children[XPort]) {
+ val filterable = child match {
+ case _: XInput => true
+ case _: XWithInput => true
+ case _ => false
+ }
+ if (filterable && child.select.isDefined) {
+ addInputFilter(child, new XSelectFilter(container, this, child))
+ }
+ }
+ }
+
+ children[XStep] foreach { _.elaborateInsertSelectFilters() }
+ }
+
+ protected[xxml] def elaborateInsertContentTypeFilters(): Unit = {
+ if (parent.isDefined && parent.get.isInstanceOf[XContainer]) {
+ // If we're in a container, we could add filters
+ val container = parent.get.asInstanceOf[XContainer]
+
+ // We don't have to filter compound steps because they'll have
+ // filters for their inputs.
+ if (stepDeclaration.get.atomic) {
+ for (child <- children[XWithInput]) {
+ if (!MediaType.OCTET_STREAM.allowed(child.contentTypes)) {
+ var filter = false
+ for (pipe <- child.children[XPipe]) {
+ val from = pipe.from
+ if (from.isDefined) {
+ for (ctype <- pipe.from.get.contentTypes filter { _.inclusive }) {
+ filter = filter || !ctype.allowed(child.contentTypes)
+ }
+ } else {
+ throw XProcException.xiThisCantHappen("Pipe has unknown origin.")
+ }
+ }
+
+ if (filter) {
+ addInputFilter(child, new XContentTypeChecker(container, child))
+ }
+ }
+ }
+ }
+ }
+
+ for (child <- children[XStep]) {
+ child.elaborateInsertContentTypeFilters()
+ }
+ }
+
+ protected def addInputFilter(child: XPort, filter: XStep): Unit = {
+ val container = parent.get match {
+ case cont: XContainer => cont
+ case _ =>
+ error(XProcException.xiThisCantHappen("Parent of filtering step isn't a container?"))
+ return
+ }
+
+ val filterxwi = new XWithInput(filter, "source")
+ filterxwi.allChildren = child.allChildren
+ val filterxwo = new XWithOutput(filter, "result")
+
+ val stepxwi = new XWithInput(this, child.port)
+
+ insertBefore(stepxwi, child)
+ removeChild(child)
+
+ stepxwi.validate()
+
+ filter.addChild(filterxwi)
+
+ if (child.selectBindings.nonEmpty) {
+ filter.addChild(child.selectBindings.get)
+ }
+
+ filter.addChild(filterxwo)
+
+ val pipe = new XPipe(stepxwi, filterxwo)
+ stepxwi.addChild(pipe)
+
+ container.insertBefore(filter, this)
+ filterxwi.validate()
+ filterxwo.validate()
+ }
+
+ def withinTryCatch: Boolean = {
+ var par: Option[XArtifact] = Some(this)
+ while (par.isDefined) {
+ par.get match {
+ case _: XTry =>
+ return true
+ case _ =>
+ ()
+ }
+ par = par.get.parent
+ }
+ false
+ }
+
+ def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ for (child <- allChildren) {
+ child match {
+ case graphable: XGraphableArtifact =>
+ graphable.graphNodes(runtime, parent)
+ case _ => ()
+ }
+ }
+ }
+
+ protected def graphEdges(runtime: XMLCalabashRuntime): Unit = {
+ for (child <- allChildren) {
+ child match {
+ case step: XStep =>
+ step.graphEdges(runtime)
+ case input: XInput =>
+ input.graphEdges(runtime)
+ case input: XWithInput =>
+ input.graphEdges(runtime)
+ case output: XOutput =>
+ output.graphEdges(runtime)
+ case nb: XNameBinding =>
+ nb.graphEdges(runtime)
+ case _ =>
+ ()
+ }
+ }
+
+ for (name <- dependsOn.keySet) {
+ val step = dependsOn(name).get
+ val node = runtime.node(this)
+ runtime.graph.addOrderedEdge(runtime.node(step), "#depends_from", node, "#depends_to")
+ }
+ }
+
+ override def toString: String = {
+ if (stepName != tumble_id) {
+ s"${staticContext.nodeName}(${stepName};${tumble_id})"
+ } else {
+ s"${staticContext.nodeName}(${stepName})"
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XTry.scala b/src/main/scala/com/xmlcalabash/model/xxml/XTry.scala
new file mode 100644
index 0000000..ea647bc
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XTry.scala
@@ -0,0 +1,191 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+import com.xmlcalabash.util.MediaType
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XTry(config: XMLCalabash) extends XContainer(config) {
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ _drp = initial
+ var curdrp = initial
+ for (child <- allChildren) {
+ child match {
+ case _: XTryCatchBranch =>
+ curdrp = initial
+ child.elaborateDefaultReadablePort(curdrp)
+ case _ =>
+ curdrp = child.elaborateDefaultReadablePort(curdrp)
+ }
+ }
+
+ children[XOutput] find { _.primary }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ wrapTryInGroup()
+
+ val codeList = mutable.Set.empty[QName]
+ var seenCatch = false
+ var seenCatchWithoutCode = false
+ var seenFinally = false
+ var seenPipeline = false
+
+ //val newScope = checkStepNameScoping(inScopeNames)
+ val newChildren = ListBuffer.empty[XArtifact]
+ for (child <- allChildren) {
+ child.validate()
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case xcatch: XCatch =>
+ if (seenFinally) {
+ error(XProcException.xsInvalidTryCatch("In a p:try, p:finally must be last", location))
+ }
+ seenCatch = true
+ if (seenCatchWithoutCode) {
+ error(XProcException.xsCatchMissingCode(location))
+ }
+ if (xcatch.codes.isEmpty) {
+ seenCatchWithoutCode = true
+ } else {
+ for (code <- xcatch.codes) {
+ if (codeList.contains(code)) {
+ error(XProcException.xsCatchBadCode(code, location))
+ } else {
+ codeList += code
+ }
+ }
+ }
+ newChildren += xcatch
+ case xfinally: XFinally =>
+ if (seenFinally) {
+ error(XProcException.xsInvalidTryCatch("In a p:try, there can be at most one p:finally", location))
+ }
+ seenFinally = true
+ newChildren += xfinally
+ case step: XGroup =>
+ if (seenCatch || seenFinally) {
+ error(XProcException.xsInvalidTryCatch("In a p:try, no steps may follow p:catch or p:finally", location))
+ } else {
+ seenPipeline = true
+ newChildren += step
+ }
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ if ((!seenCatch && !seenFinally) || !seenPipeline) {
+ error(XProcException.xsInvalidTryCatch("Catch or finally and pipeline required", None))
+ return
+ }
+
+ allChildren = newChildren.toList
+
+ val outports = mutable.HashMap.empty[String, Option[Boolean]]
+
+ for (branch <- children[XContainer]) {
+ for (output <- branch.children[XOutput]) {
+ if (outports.contains(output.port)) {
+ val p1 = outports(output.port)
+ val p2 = output
+ if ((p1.isDefined && !p2.primarySpecified) || (p1.isEmpty && p2.primarySpecified)) {
+ // FIXME: setup default for primary on p:output
+ error(XProcException.xiUserError("Catch branches with different primary ports"))
+ } else if (p1.isDefined) {
+ if (p1.get != p2.primary) {
+ error(XProcException.xiUserError("Catch branch with different primacy"))
+ }
+ }
+ } else {
+ if (output.primarySpecified) {
+ outports.put(output.port, Some(output.primary))
+ } else {
+ outports.put(output.port, None)
+ }
+ }
+ }
+ }
+
+ for (port <- outports.keySet) {
+ val oport = if (port == "") {
+ None
+ } else {
+ Some(port)
+ }
+ val output = new XOutput(this, oport)
+ output.primary = outports(port).getOrElse(false)
+ output.sequence = true
+ output.contentTypes = MediaType.MATCH_ANY
+ insertBefore(output, allChildren.head)
+
+ for (branch <- children[XContainer]) {
+ val out = branch.children[XOutput] find { _.port == port }
+ if (out.isDefined) {
+ val pipe = new XPipe(output, branch.stepName, port)
+ output.addChild(pipe)
+ }
+ }
+ }
+ }
+
+ private def wrapTryInGroup(): Unit = {
+ val tryElements = ListBuffer.empty[XArtifact]
+ var firstCatch = Option.empty[XTryCatchBranch]
+
+ for (child <- allChildren) {
+ if (firstCatch.isEmpty) {
+ child match {
+ case tc: XTryCatchBranch =>
+ firstCatch = Some(tc)
+ case _ =>
+ tryElements += child
+ }
+ }
+ }
+
+ if (tryElements.isEmpty) {
+ throw XProcException.xsInvalidTryCatch("A p:try must have at least one step", location)
+ }
+
+ if (tryElements.length == 1 && tryElements.head.isInstanceOf[XGroup]) {
+ return
+ }
+
+ val group = new XGroup(this)
+ group.allChildren = tryElements.toList
+
+ val newChildren = ListBuffer.empty[XArtifact]
+ newChildren += group
+
+ var found = false
+ for (child <- allChildren) {
+ if (!found && firstCatch.isDefined) {
+ found = firstCatch.get eq child
+ }
+ if (found) {
+ newChildren += child
+ }
+ }
+
+ allChildren = newChildren.toList
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ContainerStart]
+ val node = start.addTryCatch(stepName, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XTryCatchBranch.scala b/src/main/scala/com/xmlcalabash/model/xxml/XTryCatchBranch.scala
new file mode 100644
index 0000000..427afb1
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XTryCatchBranch.scala
@@ -0,0 +1,67 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+import com.xmlcalabash.runtime.XProcXPathExpression
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+abstract class XTryCatchBranch(config: XMLCalabash) extends XContainer(config) {
+
+ protected def orderChildren(): Unit = {
+ // nop
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+ var seenPipeline = false
+
+ //val newScope = checkStepNameScoping(inScopeNames)
+ for (child <- allChildren) {
+ child.validate()
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XOutput =>
+ if (seenPipeline) {
+ error(XProcException.xiUserError("output can't follow steps"))
+ }
+ case _: XVariable =>
+ seenPipeline = true
+ case _: XStep =>
+ seenPipeline = true
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ if (children[XOutput].length == 1) {
+ val output = children[XOutput].head
+ if (!output.primarySpecified) {
+ output.primary = true
+ }
+ }
+
+ val xi = new XInput(this, Some("error"))
+ xi.primary = true
+ xi.sequence = true
+ if (allChildren.isEmpty) {
+ addChild(xi)
+ } else {
+ insertBefore(xi, allChildren.head)
+ }
+
+ constructDefaultOutput()
+
+ orderChildren()
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("name", Some(stepName))
+ dumpTree(sb, nodeName.toString, attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XVariable.scala b/src/main/scala/com/xmlcalabash/model/xxml/XVariable.scala
new file mode 100644
index 0000000..8a50ef0
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XVariable.scala
@@ -0,0 +1,127 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants, XValueParser}
+import com.xmlcalabash.runtime.params.XPathBindingParams
+import com.xmlcalabash.runtime.{StepProxy, XMLCalabashRuntime, XProcVtExpression, XProcXPathExpression}
+import com.xmlcalabash.steps.internal.ValueComputation
+import com.xmlcalabash.util.{MediaType, TypeUtils}
+import net.sf.saxon.s9api.XdmAtomicValue
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XVariable(config: XMLCalabash) extends XNameBinding(config) with XGraphableArtifact {
+ private var _allowedValues = Option.empty[List[XdmAtomicValue]]
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ if (_select.isEmpty) {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._select, location))
+ }
+
+ _allowedValues = checkValueTokens
+ _href = attr(XProcConstants._href)
+ _pipe = attr(XProcConstants._pipe)
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ val inputs = validateExplicitConnections(_href, _pipe)
+ _href = None
+ _pipe = None
+ val newChildren = ListBuffer.empty[XArtifact]
+
+ if (inputs.nonEmpty) {
+ val xwi = new XWithInput(this, "source")
+ xwi.primary = true
+ xwi.sequence = false
+ xwi.contentTypes = MediaType.MATCH_ANY
+ newChildren += xwi
+ xwi.allChildren = inputs
+ }
+
+ val xwo = new XWithOutput(this, "result")
+ xwo.primary = true
+ xwo.sequence = true
+ xwo.contentTypes = MediaType.MATCH_ANY
+ newChildren += xwo
+
+ allChildren = newChildren.toList
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ val bcontext = super.elaborateNameBindings(initial)
+
+ var newContext = bcontext
+ try {
+ newContext = bcontext.withBinding(this)
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+
+ newContext
+ }
+
+ override protected def promotedStaticValue(staticValueMsg: XdmValueItemMessage): XdmValueItemMessage = {
+ XNameBinding.promotedValue(config, name, declaredType, None, staticValueMsg)
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ if (constantValue.isDefined || !usedByPipeline) {
+ return
+ }
+
+ val start = parent.asInstanceOf[ContainerStart]
+ val params = new XPathBindingParams(collection)
+ val init = new XProcXPathExpression(staticContext, _select.getOrElse("()"), _declaredType, _allowedValues, params)
+ // FIXME: does params here have to include all the statics?
+ runtime.addNode(this, start.addOption(_name.getClarkName, init, params))
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ if (Option(_name).isDefined) {
+ attr.put("name", Some(_name.getEQName))
+ }
+
+ if (constantValue.isDefined) {
+ attr.put("constant-value", Some(constantValue.get.item.toString))
+ } else {
+ attr.put("select", _select)
+ attr.put("avt", _avt)
+ }
+
+ attr.put("as", as)
+ attr.put("required", _required)
+ attr.put("visiblity", _visibility)
+
+ if (drp.isDefined) {
+ attr.put("drp", Some(drp.get.tumble_id))
+ }
+
+ dumpTree(sb, "p:variable", attr.toMap)
+ }
+
+ override def toString: String = {
+ if (constantValue.isDefined) {
+ s"${name}: ${constantValue.get.item} (constant)"
+ } else if (_avt.isDefined) {
+ s"${name}: ${_avt.get} (avt)"
+ } else {
+ if (_select.isDefined) {
+ s"${name}: ${_select.get} (select)"
+ } else {
+ s"${name}: ???"
+ }
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XViewport.scala b/src/main/scala/com/xmlcalabash/model/xxml/XViewport.scala
new file mode 100644
index 0000000..bdc6976
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XViewport.scala
@@ -0,0 +1,41 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.model.util.{XMLViewportComposer, XProcConstants}
+import com.xmlcalabash.runtime.XMLCalabashRuntime
+
+class XViewport(config: XMLCalabash) extends XLoopingStep(config) {
+ private var _match: String = _
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+
+ if (attributes.contains(XProcConstants._match)) {
+ _match = attr(XProcConstants._match).get
+ } else {
+ throw new RuntimeException("Viewport must have match")
+ }
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ super.validate()
+
+ var found = false
+ for (child <- children[XOutput]) {
+ if (found) {
+ throw new RuntimeException("Viewport must not have more than one output")
+ }
+ found = true
+ child.port = "result"
+ }
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val start = parent.asInstanceOf[ContainerStart]
+ val composer = new XMLViewportComposer(config, staticContext, _match)
+ val node = start.addViewport(composer, stepName, containerManifold)
+ runtime.addNode(this, node)
+ super.graphNodes(runtime, node)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XWhen.scala b/src/main/scala/com/xmlcalabash/model/xxml/XWhen.scala
new file mode 100644
index 0000000..ee00d32
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XWhen.scala
@@ -0,0 +1,209 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.{XProcConstants, XValueParser}
+import com.xmlcalabash.runtime.{XProcVtExpression, XProcXPathExpression}
+import net.sf.saxon.s9api.QName
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XWhen(config: XMLCalabash) extends XChooseBranch(config) {
+ private val _namebindings = ListBuffer.empty[XNameBinding]
+ private var _constantValue = Option.empty[XdmValueItemMessage]
+ private var _contextDependent = false
+ private var _wasIf = false
+
+ def this(choose: XChoose, test: String, collection: Boolean) = {
+ this(choose.config)
+ staticContext = choose.staticContext
+ parent = choose
+ synthetic = true
+ syntheticName = XProcConstants.p_when
+ _test = test
+ _collection = collection
+ }
+
+ def this(choose: XChoose, test: String, collection: Boolean, wasIf: Boolean) = {
+ this(choose, test, collection)
+ _wasIf = wasIf
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ val coll = attr(XProcConstants._collection)
+ if (coll.isDefined) {
+ coll.get match {
+ case "true" => _collection = true
+ case "false" => _collection = false
+ case _ =>
+ error(XProcException.xsBadTypeValue(coll.get, "xs:boolean", None))
+ }
+ }
+
+ if (attributes.contains(XProcConstants._test)) {
+ _test = attr(XProcConstants._test).get
+ } else {
+ error(XProcException.xsMissingRequiredAttribute(XProcConstants._test, None))
+ }
+ }
+
+ override def validate(): Unit = {
+ if (synthetic) {
+ // There are no attributes and we've already validated the children
+ return
+ }
+ checkAttributes()
+ checkEmptyAttributes()
+
+ var seenWithInput = false
+ var seenPipeline = false
+
+ //val newScope = checkStepNameScoping(inScopeNames)
+ for (child <- allChildren) {
+ child.validate()
+ child match {
+ case _: XPipeinfo => ()
+ case _: XDocumentation => ()
+ case _: XWithInput =>
+ if (seenWithInput) {
+ error(XProcException.xiUserError("More than one p:with-input in choose"))
+ }
+ if (seenPipeline) {
+ error(XProcException.xiUserError("with-input can't follow steps"))
+ }
+ seenWithInput = true
+ case _: XOutput =>
+ if (seenPipeline) {
+ error(XProcException.xiUserError("output can't follow steps"))
+ }
+ case _: XVariable =>
+ seenPipeline = true
+ case _: XStep =>
+ seenPipeline = true
+ case _ =>
+ error(XProcException.xsElementNotAllowed(child.nodeName, None))
+ }
+ }
+
+ if (children[XOutput].length == 1) {
+ val output = children[XOutput].head
+ if (!output.primarySpecified) {
+ output.primary = true
+ }
+ }
+
+ constructDefaultOutput()
+
+ if (_wasIf) {
+ val primary = children[XOutput] find { _.primary == true }
+ if (primary.isEmpty) {
+ error(XProcException.xsPrimaryOutputRequired(location))
+ }
+ }
+
+ orderChildren()
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ try {
+ val refs = mutable.HashSet.empty[QName]
+
+ val parser = new XValueParser(config, staticContext, _test)
+ refs ++= parser.variables
+ _contextDependent = parser.contextDependent
+
+ var constant = parser.static
+ for (ref <- refs) {
+ val cbind = initial.inScopeConstants.get(ref)
+ val dbind = initial.inScopeDynamics.get(ref)
+ if (cbind.isDefined) {
+ // ok
+ } else if (dbind.isDefined) {
+ constant = false
+ dbind.get match {
+ case v: XVariable =>
+ _namebindings += v
+ case opt: XOption =>
+ _namebindings += opt
+ case _ =>
+ error(XProcException.xiThisCantHappen(s"Unexpected name binding: ${dbind.get}"))
+ }
+ } else {
+ error(XProcException.xsNoBindingInExpression(ref, None))
+ }
+ }
+
+ if (exceptions.nonEmpty) {
+ return initial
+ }
+
+ if (constant) {
+ drp = None // If it's constant, there's no need for an input
+
+ val expr = new XProcXPathExpression(staticContext, _test)
+ val bindings = mutable.HashMap.empty[String,Message]
+ for ((name,value) <- initial.inScopeConstants) {
+ bindings.put(name.getClarkName, value.constantValue.get)
+ }
+
+ try {
+ val constantVal = config.expressionEvaluator.value(expr, List(), bindings.toMap, None)
+ _constantValue = Some(constantVal)
+ } catch {
+ case ex: Exception =>
+ if (withinTryCatch) {
+ // nevermind, just let it go bang later
+ } else {
+ throw ex
+ }
+ }
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+
+ super.elaborateNameBindings(initial)
+ }
+
+ override def elaboratePortConnections(): Unit = {
+ val input = children[XWithInput].headOption
+ if (input.isDefined) {
+ for (binding <- _namebindings) {
+ input.get.addChild(new XNamePipe(binding))
+ }
+ } else {
+ if (_contextDependent || _namebindings.nonEmpty) {
+ val newinput = new XWithInput(parent.get, "condition")
+ newinput.staticContext = staticContext
+ newinput.parent = this
+
+ if (parent.get.children[XWithInput].nonEmpty) {
+ for (ds <- parent.get.children[XWithInput].head.children[XDataSource]) {
+ ds match {
+ case pipe: XPipe =>
+ val newpipe = new XPipe(pipe)
+ newinput.addChild(newpipe)
+ case _ =>
+ error(XProcException.xiThisCantHappen("Choose with-input is not a pipe?"))
+ }
+ }
+ }
+
+ for (binding <- _namebindings) {
+ val pipe = new XPipe(newinput, binding.tumble_id, "result")
+ newinput.addChild(pipe)
+ }
+
+ insertBefore(newinput, allChildren.head)
+ }
+ }
+
+ super.elaboratePortConnections()
+ }
+
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XWithInput.scala b/src/main/scala/com/xmlcalabash/model/xxml/XWithInput.scala
new file mode 100644
index 0000000..d6b2597
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XWithInput.scala
@@ -0,0 +1,246 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants, XValueParser}
+import com.xmlcalabash.util.MediaType
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XWithInput(config: XMLCalabash) extends XPort(config) {
+
+ def this(parentStep: XArtifact, port: String) = {
+ this(parentStep.config)
+ parent = parentStep
+ _synthetic = true
+ _syntheticName = Some(XProcConstants.p_with_input)
+ _port = port
+ _primary = Some(true)
+ _sequence = Some(true)
+ _content_types = MediaType.MATCH_ANY
+ staticContext = parentStep.staticContext
+ }
+
+ def this(parentStep: XArtifact, port: String, primary: Boolean, sequence: Boolean, contentTypes: List[MediaType]) = {
+ this(parentStep.config)
+ parent = parentStep
+ _synthetic = true
+ _syntheticName = Some(XProcConstants.p_with_input)
+ _port = port
+ _primary = Some(primary)
+ _sequence = Some(sequence)
+ _content_types = contentTypes
+ staticContext = parentStep.staticContext
+ }
+
+ override protected[xxml] def port_=(port: String): Unit = {
+ if (portSpecified) {
+ throw XProcException.xiThisCantHappen("Attempt to change port on p:input")
+ }
+ _synthetic = true
+ _syntheticName = Some(XProcConstants.p_with_input)
+ _port = port
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ try {
+ if (attributes.contains(XProcConstants._port)) {
+ _port = staticContext.parseNCName(attr(XProcConstants._port).get)
+ }
+ } catch {
+ case ex: XProcException =>
+ error(ex)
+ }
+
+ if (_port != "#anon" && parent.isDefined) {
+ parent.get match {
+ case c: XForEach =>
+ // FIXME: it's a weird sharp edge that the port has to be called 'source' in the graph
+ if (_port != "source") {
+ error(XProcException.xsPortNotAllowed(_port, c.name.getOrElse(c.tumble_id), location))
+ }
+ case c: XViewport =>
+ error(XProcException.xsPortNotAllowed(_port, c.name.getOrElse(c.tumble_id), location))
+ case c: XChoose =>
+ error(XProcException.xsPortNotAllowed(_port, c.name.getOrElse(c.tumble_id), location))
+ case c: XWhen =>
+ error(XProcException.xsPortNotAllowed(_port, c.name.getOrElse(c.tumble_id), location))
+ case c: XIf =>
+ error(XProcException.xsPortNotAllowed(_port, c.name.getOrElse(c.tumble_id), location))
+ case _ =>
+ ()
+ }
+ }
+
+ _select = attr(XProcConstants._select)
+ _href = attr(XProcConstants._href)
+ _pipe = attr(XProcConstants._pipe)
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ val decl = stepDeclaration
+ if (decl.isDefined) {
+ if (decl.get.inputPorts.contains(port)) {
+ val dinput = decl.get.input(port)
+ primary = dinput.primary
+ sequence = dinput.sequence
+ contentTypes = dinput.contentTypes
+ }
+ } else {
+ parent.get match {
+ case _: XWhen =>
+ _port = "condition"
+ case _: XForEach =>
+ primary = false
+ sequence = true
+ contentTypes = MediaType.MATCH_ANY
+ case _ =>
+ ()
+ }
+ }
+
+ super.validate()
+ }
+
+ override protected[xxml] def elaborateDefaultReadablePort(initial: Option[XPort]): Option[XPort] = {
+ super.elaborateDefaultReadablePort(initial)
+ if (!primary) {
+ // If the input isn't primary, it doesn't get automatically joined to the DRP
+ // Except for looping steps where the "current" port is primary for the inner steps
+ // even though this port is primary for the loop itself. Sigh.
+ parent.get match {
+ case _: XLoopingStep =>
+ ()
+ case _ =>
+ drp = None
+ }
+ }
+ initial
+ }
+
+ override protected[xxml] def elaborateNameBindings(initial: XNameBindingContext): XNameBindingContext = {
+ super.elaborateNameBindings(initial);
+
+ if (_select.isEmpty) {
+ return initial
+ }
+
+ try {
+ val parser = new XValueParser(config, staticContext, _select.get)
+
+ val refs = parser.variables
+ var static = parser.static
+
+ val namepipe = ListBuffer.empty[XNameBinding]
+ for (ref <- refs) {
+ val cbind = initial.inScopeConstants.get(ref)
+ val dbind = initial.inScopeDynamics.get(ref)
+ if (cbind.isDefined) {
+ // ok
+ } else if (dbind.isDefined) {
+ static = false
+ dbind.get match {
+ case v: XVariable =>
+ namepipe += v
+ case o: XOption =>
+ namepipe += o
+ case _ =>
+ error(XProcException.xiThisCantHappen(s"Unexpected name binding: ${dbind.get}"))
+ }
+ } else {
+ error(XProcException.xsNoBindingInExpression(ref, None))
+ }
+ }
+
+ if (exceptions.nonEmpty) {
+ return initial
+ }
+
+ if (static) {
+ throw XProcException.xiThisCantHappen("Static binding in with-input unsupported")
+ } else {
+ if (namepipe.nonEmpty) {
+ val xwi = new XWithInput(this, "#bindings", false, true, MediaType.MATCH_ANY)
+ for (binding <- namepipe) {
+ val xstep = initial.inScopeDynamics.get(binding.name)
+ val pipe = new XPipe(xwi, xstep.get.tumble_id, "result")
+ xwi.addChild(pipe)
+ }
+ _selectBindings = Some(xwi)
+ }
+ }
+ } catch {
+ case ex: Exception =>
+ error(ex)
+ }
+
+ initial
+ }
+
+ override protected[xxml] def elaboratePortConnections(): Unit = {
+ if (drp.isDefined) {
+ if (children[XDataSource].isEmpty && port != "#bindings") {
+ val pipe = new XPipe(drp.get)
+ addChild(pipe)
+ }
+ } else {
+ if (children[XDataSource].isEmpty && primary) {
+ if (parent.isDefined) {
+ parent.get match {
+ case _: XWhen =>
+ // Can't raise this statically in case it doesn't actually arise
+ logger.debug("The p:when expression will fail dynamically because no context item is available")
+ case step: XAtomicStep =>
+ val decl = step.stepDeclaration.get
+ val input = decl.input(port)
+ if (input.defaultInputs.nonEmpty) {
+ for (ds <- input.defaultInputs) {
+ ds match {
+ case inline: XInline =>
+ inline.staticContext = inline.staticContext.withConstants(input.staticContext.constants)
+ addChild(new XInline(this, inline))
+ case doc: XDocument =>
+ doc.staticContext = doc.staticContext.withConstants(input.staticContext.constants)
+ addChild(new XDocument(this, doc))
+ case _: XEmpty =>
+ addChild(new XEmpty(this))
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Default inputs contain ${ds}")
+ }
+ }
+ } else {
+ throw XProcException.xsUnconnectedPrimaryInputPort(step.name.getOrElse(step.tumble_id), port, location)
+ }
+ case step: XStep =>
+ throw XProcException.xsUnconnectedPrimaryInputPort(step.name.getOrElse(step.tumble_id), port, location)
+ case _ =>
+ throw XProcException.xiThisCantHappen("Parent of p:with-input is not a step?")
+ }
+ } else {
+ throw XProcException.xiThisCantHappen("A p:with-input has no parent?")
+ }
+ }
+ }
+
+ super.elaboratePortConnections()
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("port", Some(_port))
+ attr.put("select", _select)
+ attr.put("primary", _primary)
+ attr.put("sequence", _sequence)
+
+ if (drp.isDefined) {
+ attr.put("drp", Some(drp.get.tumble_id))
+ }
+
+ dumpTree(sb, "p:with-input", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XWithOption.scala b/src/main/scala/com/xmlcalabash/model/xxml/XWithOption.scala
new file mode 100644
index 0000000..0070535
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XWithOption.scala
@@ -0,0 +1,183 @@
+package com.xmlcalabash.model.xxml
+
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants, XValueParser}
+import com.xmlcalabash.runtime.{XProcVtExpression, XProcXPathExpression}
+import com.xmlcalabash.steps.internal.ValueComputation
+import com.xmlcalabash.util.{S9Api, TypeUtils}
+import net.sf.saxon.ma.arrays.ArrayItemType
+import net.sf.saxon.ma.map.{MapItem, MapType}
+import net.sf.saxon.s9api.{QName, XdmMap}
+
+import java.net.URISyntaxException
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XWithOption(config: XMLCalabash) extends XNameBinding(config) {
+ protected[xxml] def this(parent: XArtifact, name: QName, avt: Option[String], select: Option[String]) = {
+ this(parent.config)
+
+ if ((avt.isDefined && select.isDefined) || (avt.isEmpty && select.isEmpty)) {
+ throw XProcException.xiThisCantHappen("Exactly one of avt or select must be defined on XWithOption")
+ }
+
+ this.parent = parent
+ staticContext = parent.staticContext
+ _synthetic = true
+ _name = name
+ _avt = avt
+ _select = select
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ super.checkAttributes()
+ _href = attr(XProcConstants._href)
+ _pipe = attr(XProcConstants._pipe)
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ val stepdecl = stepDeclaration
+ if (stepdecl.isDefined) {
+ val optdecl = stepdecl.get.option(name)
+ if (optdecl.isEmpty) {
+ error(XProcException.xsUndeclaredOption(stepdecl.get.stepType.get, name, location))
+ } else if (optdecl.get.static) {
+ error(XProcException.xsRedeclareStatic(name, location))
+ }
+ }
+
+ // Any option shortcut will have been marked as an AVT.
+ // But if the actual option type is a map or array, that's not really an AVT!
+ if (_avt.isDefined) {
+ if (stepdecl.isDefined) {
+ val optdecl = stepdecl.get.option(name).get
+ if (optdecl.declaredType.isDefined) {
+ val opttype = optdecl.declaredType.get.getItemType
+ opttype.getUnderlyingItemType match {
+ case _: MapType =>
+ _select = _avt
+ _avt = None
+ _qnameKeys = optdecl.qnameKeys
+ case _: ArrayItemType =>
+ _select = _avt
+ _avt = None
+ case _ =>
+ ()
+ }
+ }
+ }
+ }
+
+ allChildren = validateExplicitConnections(_href, _pipe)
+ _href = None
+ _pipe = None
+ }
+
+ protected[xxml] def elaborateDynamicOptions(): Unit = {
+ if (constantValue.isDefined) {
+ return
+ }
+
+ val container = ancestorContainer.get
+ val compute = if (_avt.isDefined) {
+ new ValueComputation(container, name, XValueParser.parseAvt(_avt.get), collection)
+ } else {
+ new ValueComputation(container, name, _select.get, collection)
+ }
+
+ compute.dependsOn ++= parent.get.asInstanceOf[XStep].dependsOn
+
+ _computeValue = Some(compute)
+
+ val xwi_source = new XWithInput(compute, "source")
+ val xwi_bindings = new XWithInput(compute, "#bindings")
+ val optchildren = ListBuffer.empty[XArtifact]
+ for (child <- allChildren) {
+ child match {
+ case xi: XWithInput =>
+ optchildren ++= xi.allChildren
+ case _ =>
+ optchildren += child
+ }
+ }
+ allChildren = List()
+
+ for (child <- optchildren) {
+ child match {
+ case nb: XNamePipe =>
+ val pipe = new XPipe(this, nb.binding.tumble_id, "result")
+ xwi_bindings.addChild(pipe)
+ case pipe: XPipe =>
+ xwi_source.addChild(pipe)
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Children of p:with-option include ${child}?")
+ }
+ }
+
+ if (xwi_source.allChildren.nonEmpty) {
+ compute.addChild(xwi_source)
+ }
+
+ if (xwi_bindings.allChildren.nonEmpty) {
+ compute.addChild(xwi_bindings)
+ }
+
+ val xwo = new XWithOutput(compute, "result")
+ compute.addChild(xwo)
+
+ var bwi = parent.get.children[XWithInput] find { _.port == "#bindings" }
+ if (bwi.isEmpty) {
+ bwi = Some(new XWithInput(this, "#bindings"))
+ addChild(bwi.get)
+ }
+ bwi.get.addChild(new XPipe(bwi.get, compute.stepName, "result"))
+
+ container.insertBefore(compute, parent.get)
+
+ xwi_source.validate()
+ xwi_bindings.validate()
+ xwo.validate()
+ }
+
+ // =======================================================================================
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ if (Option(_name).isDefined) {
+ attr.put("name", Some(_name.getEQName))
+ }
+
+ if (constantValue.isDefined) {
+ attr.put("constant-value", Some(constantValue.get.item.toString))
+ } else {
+ attr.put("select", _select)
+ attr.put("avt", _avt)
+ }
+
+ if (drp.isDefined) {
+ attr.put("drp", Some(drp.get.tumble_id))
+ }
+
+ dumpTree(sb, "p:with-option", attr.toMap)
+ }
+
+ override def toString: String = {
+ if (constantValue.isDefined) {
+ s"${name}: ${constantValue.get.item} (constant)"
+ } else if (_avt.isDefined) {
+ s"${name}: ${_avt.get} (avt)"
+ } else {
+ if (_select.isDefined) {
+ s"${name}: ${_select.get} (select)"
+ } else {
+ s"${name}: ???"
+ }
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/model/xxml/XWithOutput.scala b/src/main/scala/com/xmlcalabash/model/xxml/XWithOutput.scala
new file mode 100644
index 0000000..6dcce59
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/model/xxml/XWithOutput.scala
@@ -0,0 +1,68 @@
+package com.xmlcalabash.model.xxml
+
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.util.MediaType
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+class XWithOutput(parart: XArtifact, port: String) extends XPort(parart.config) {
+ private val _readBy = ListBuffer.empty[XArtifact]
+
+ _synthetic = true
+ staticContext = parart.staticContext
+ this.parent = parart
+ _port = port
+
+ protected[xxml] def readBy: List[XArtifact] = _readBy.toList
+ protected[xxml] def readBy_=(art: XArtifact): Unit = {
+ _readBy += art
+ }
+
+ override protected[xxml] def checkAttributes(): Unit = {
+ // it's synthetic
+ }
+
+ override protected[xxml] def validate(): Unit = {
+ checkAttributes()
+ checkEmptyAttributes()
+
+ val decl = stepDeclaration
+
+ if (decl.isEmpty) {
+ parent.get match {
+ case atomic: XAtomicStep =>
+ if (atomic.stepType == XProcConstants.cx_document_loader
+ || atomic.stepType == XProcConstants.cx_inline_loader) {
+ _primary = Some(true)
+ _sequence = Some(false)
+ _content_types = MediaType.MATCH_ANY.toList
+ } else {
+ error(XProcException.xiThisCantHappen("Parent of with-output isn't a cx: loader?"))
+ }
+ case _ =>
+ error(XProcException.xiThisCantHappen("Grandparent of with-output isn't an atomic step?"))
+ }
+
+ return
+ }
+
+ if (decl.isDefined && decl.get.outputPorts.contains(port)) {
+ val doutput = decl.get.output(port)
+ primary = doutput.primary
+ sequence = doutput.sequence
+ contentTypes = doutput.contentTypes
+ }
+
+ super.validate()
+ }
+
+ override def dumpTree(sb: SaxonTreeBuilder): Unit = {
+ val attr = mutable.HashMap.empty[String, Option[Any]]
+ attr.put("port", Some(_port))
+ attr.put("primary", _primary)
+ attr.put("sequence", _sequence)
+ dumpTree(sb, "p:with-output", attr.toMap)
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/runtime/BufferingConsumer.scala b/src/main/scala/com/xmlcalabash/runtime/BufferingConsumer.scala
index 9b0c52c..d3e97dc 100644
--- a/src/main/scala/com/xmlcalabash/runtime/BufferingConsumer.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/BufferingConsumer.scala
@@ -4,11 +4,11 @@ import com.jafpl.messages.Message
import com.jafpl.steps.DataConsumer
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XProcItemMessage
-import com.xmlcalabash.model.xml.DeclareOutput
+import com.xmlcalabash.model.xxml.XOutput
import scala.collection.mutable.ListBuffer
-class BufferingConsumer(output: DeclareOutput) extends DataConsumer {
+class BufferingConsumer(output: XOutput) extends DataConsumer {
private val _items = ListBuffer.empty[XProcItemMessage]
def messages: List[XProcItemMessage] = _items.toList
diff --git a/src/main/scala/com/xmlcalabash/runtime/DynamicContext.scala b/src/main/scala/com/xmlcalabash/runtime/DynamicContext.scala
index 07d595b..096db02 100644
--- a/src/main/scala/com/xmlcalabash/runtime/DynamicContext.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/DynamicContext.scala
@@ -1,13 +1,13 @@
package com.xmlcalabash.runtime
-import java.net.URI
import com.jafpl.graph.{Location, LoopStart}
import com.jafpl.messages.Message
-import com.xmlcalabash.model.xml.{Artifact, ForEach, ForLoop, ForUntil}
+import com.xmlcalabash.model.xxml.{XArtifact, XStep}
import net.sf.saxon.om.Item
import net.sf.saxon.s9api.{QName, XdmNode, XdmValue}
import net.sf.saxon.value.StringValue
+import java.net.URI
import scala.collection.mutable
import scala.util.DynamicVariable
@@ -18,7 +18,7 @@ object DynamicContext {
}
class DynamicContext() {
- private var _artifact = Option.empty[Artifact]
+ private var _artifact = Option.empty[XArtifact]
private var _iterationPosition = 1L
private var _iterationSize = 1L
private val _documents = mutable.HashMap.empty[Any,Message]
@@ -31,27 +31,33 @@ class DynamicContext() {
private var _injId = Option.empty[String]
private var _injType = Option.empty[QName]
- def this(artifact: Option[Artifact]) = {
+ def this(artifact: XArtifact) = {
this()
+ _artifact = Some(artifact)
+ }
+
+ def this(runtime: XMLCalabashRuntime, artifact: XArtifact) = {
+ this(artifact)
- _artifact = artifact
var found = false
- var p: Option[Artifact] = artifact
+ var p: Option[XArtifact] = Some(artifact)
while (!found && p.isDefined) {
- if (p.get.graphNode.isDefined) {
- p.get.graphNode.get match {
- case node: LoopStart =>
- found = true
- _iterationPosition = node.iterationPosition
- _iterationSize = node.iterationSize
- case _ => ()
- }
+ p.get match {
+ case step: XStep =>
+ runtime.node(step) match {
+ case node: LoopStart =>
+ found = true
+ _iterationPosition = node.iterationPosition
+ _iterationSize = node.iterationSize
+ case _ => ()
+ }
+ case _ => ()
}
p = p.get.parent
}
}
- def artifact: Option[Artifact] = _artifact
+ def artifact: Option[XArtifact] = _artifact
def iterationPosition: Long = _iterationPosition
def iterationSize: Long = _iterationSize
diff --git a/src/main/scala/com/xmlcalabash/runtime/NameValueBinding.scala b/src/main/scala/com/xmlcalabash/runtime/NameValueBinding.scala
index b98b0e2..b3f442b 100644
--- a/src/main/scala/com/xmlcalabash/runtime/NameValueBinding.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/NameValueBinding.scala
@@ -1,9 +1,10 @@
package com.xmlcalabash.runtime
import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmValue}
-class NameValueBinding(val name: QName, val value: XdmValue, val meta: XProcMetadata, val context: StaticContext) {
+class NameValueBinding(val name: QName, val value: XdmValue, val meta: XProcMetadata, val context: MinimalStaticContext) {
def this(name: QName, message: XdmValueItemMessage) =
this(name, message.item, message.metadata, message.context)
def this(name: QName, value: XdmValue, message: XdmValueItemMessage) =
diff --git a/src/main/scala/com/xmlcalabash/runtime/PrintingConsumer.scala b/src/main/scala/com/xmlcalabash/runtime/PrintingConsumer.scala
index 04e6007..23d6c43 100644
--- a/src/main/scala/com/xmlcalabash/runtime/PrintingConsumer.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/PrintingConsumer.scala
@@ -7,26 +7,26 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{AnyItemMessage, XProcItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.UniqueId
-import com.xmlcalabash.model.xml.DeclareOutput
+import com.xmlcalabash.model.xxml.XOutput
import com.xmlcalabash.util.S9Api
import net.sf.saxon.s9api.{QName, Serializer}
import org.slf4j.{Logger, LoggerFactory}
-class PrintingConsumer private(config: XMLCalabashRuntime, output: DeclareOutput, outputs: Option[List[String]]) extends DataConsumer {
+class PrintingConsumer private(config: XMLCalabashRuntime, output: XOutput, outputs: Option[List[String]]) extends DataConsumer {
protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
private val _id = UniqueId.nextId.toString
private var index = 0
- def this(config: XMLCalabashRuntime, output: DeclareOutput) = {
+ def this(config: XMLCalabashRuntime, output: XOutput) = {
this(config, output, None)
}
- def this(config: XMLCalabashRuntime, output: DeclareOutput, outputs: List[String]) = {
+ def this(config: XMLCalabashRuntime, output: XOutput, outputs: List[String]) = {
this(config, output, Some(outputs))
}
override def consume(port: String, message: Message): Unit = {
- logger.debug(s"Received message on ${port}")
+ logger.debug(s"PrintingConsumer received message on ${port}")
message match {
case msg: XProcItemMessage =>
// Check that the message content type is allowed on the output port
@@ -60,7 +60,7 @@ class PrintingConsumer private(config: XMLCalabashRuntime, output: DeclareOutput
outstream.write(buf, 0, len)
len = instream.read(buf, 0, buf.length)
}
- logger.debug(s"${outstream.toByteArray.length} byte message")
+ //logger.debug(s"${outstream.toByteArray.length} byte message")
pos.write(outstream.toByteArray)
case msg: XdmValueItemMessage =>
val stream = new ByteArrayOutputStream()
@@ -75,7 +75,7 @@ class PrintingConsumer private(config: XMLCalabashRuntime, output: DeclareOutput
S9Api.serialize(config.config, msg.item, serializer)
val content = stream.toString("UTF-8")
- logger.debug(s"Message: ${content}")
+ //logger.debug(s"Message: ${content}")
pos.print(content)
if (!content.endsWith("\n")) {
pos.println()
diff --git a/src/main/scala/com/xmlcalabash/runtime/ProcessMatch.scala b/src/main/scala/com/xmlcalabash/runtime/ProcessMatch.scala
index 2b2873c..a32f80e 100644
--- a/src/main/scala/com/xmlcalabash/runtime/ProcessMatch.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/ProcessMatch.scala
@@ -5,6 +5,7 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XdmValueItemMessage
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.om.{AttributeInfo, AttributeMap, NameOfNode, NamespaceResolver}
import net.sf.saxon.s9api._
import net.sf.saxon.serialize.SerializationProperties
@@ -18,15 +19,15 @@ import scala.jdk.CollectionConverters.{IteratorHasAsJava, SeqHasAsJava}
class ProcessMatch(config: XMLCalabash,
processor: ProcessMatchingNodes,
- context: StaticContext,
+ context: MinimalStaticContext,
bindings: Option[Map[String,Message]]) extends SaxonTreeBuilder(config) {
- def this(runtime: XMLCalabashRuntime, processor: ProcessMatchingNodes, context: StaticContext) = {
+ def this(runtime: XMLCalabashRuntime, processor: ProcessMatchingNodes, context: MinimalStaticContext) = {
this(runtime.config, processor, context, None)
}
- def this(runtime: XMLCalabash, processor: ProcessMatchingNodes, context: StaticContext) = {
+ def this(runtime: XMLCalabash, processor: ProcessMatchingNodes, context: MinimalStaticContext) = {
this(runtime, processor, context, None)
}
- def this(config: XMLCalabash, processor: ProcessMatchingNodes, context: StaticContext, bindings: Map[String,Message]) = {
+ def this(config: XMLCalabash, processor: ProcessMatchingNodes, context: MinimalStaticContext, bindings: Map[String,Message]) = {
this(config, processor, context, Some(bindings))
}
@@ -267,7 +268,7 @@ class ProcessMatch(config: XMLCalabash,
}
}
- for ((prefix, uri) <- context.nsBindings) {
+ for ((prefix, uri) <- context.inscopeNamespaces) {
xcomp.declareNamespace(prefix, uri)
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionEvaluator.scala b/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionEvaluator.scala
index e27af00..994eb82 100644
--- a/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionEvaluator.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionEvaluator.scala
@@ -7,8 +7,9 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{AnyItemMessage, XdmNodeItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
+import com.xmlcalabash.model.xxml.XArtifactContext
import com.xmlcalabash.runtime.params.XPathBindingParams
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.expr.XPathContext
import net.sf.saxon.lib.{CollectionFinder, Resource, ResourceCollection}
import net.sf.saxon.ma.arrays.ArrayItem
@@ -31,18 +32,18 @@ object SaxonExpressionEvaluator {
protected val _dynContext = new DynamicVariable[DynamicContext](null)
}
-class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvaluator {
+class SaxonExpressionEvaluator(xproc: XMLCalabashProcessor) extends ExpressionEvaluator {
protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
def withContext[T](context: DynamicContext)(thunk: => T): T = SaxonExpressionEvaluator._dynContext.withValue(context)(thunk)
def dynContext: Option[DynamicContext] = Option(SaxonExpressionEvaluator._dynContext.value)
override def newInstance(): SaxonExpressionEvaluator = {
- new SaxonExpressionEvaluator(xmlCalabash)
+ new SaxonExpressionEvaluator(xproc)
}
override def singletonValue(xpath: Any, context: List[Message], bindings: Map[String, Message], params: Option[BindingParams]): XdmValueItemMessage = {
- val xdmval = value(xpath, context, bindings, params).item.asInstanceOf[XdmValue]
+ val xdmval = value(xpath, context, bindings, params).item
if (xdmval.size() == 1) {
new XdmValueItemMessage(xdmval, XProcMetadata.XML, xpath.asInstanceOf[XProcExpression].context)
@@ -69,7 +70,6 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
override def value(xpath: Any, context: List[Message], bindings: Map[String, Message], params: Option[BindingParams]): XdmValueItemMessage = {
val proxies = mutable.HashMap.empty[Any, XdmItem]
- // FIXME: this is ugly
val exprContext = xpath match {
case expr: XProcXPathExpression => Some(expr.context)
case expr: XProcVtExpression => Some(expr.context)
@@ -77,7 +77,17 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
}
val newContext = if (exprContext.isDefined) {
- new DynamicContext(exprContext.get.artifact)
+ exprContext.get match {
+ case xa: XArtifactContext =>
+ xproc match {
+ case runtime: XMLCalabashRuntime =>
+ new DynamicContext(runtime, xa.artifact)
+ case _ =>
+ new DynamicContext(xa.artifact)
+ }
+ case _ =>
+ new DynamicContext()
+ }
} else {
new DynamicContext()
}
@@ -226,13 +236,8 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
proxies: Map[Any,XdmItem], options: XPathBindingParams): XdmValue = {
val patchBindings = mutable.HashMap.empty[QName, XdmValue]
- for ((str, value) <- xpath.context.statics) {
- value match {
- case msg: XdmValueItemMessage =>
- patchBindings.put(ValueParser.parseClarkName(str), msg.item)
- case _ =>
- throw XProcException.xiInvalidMessage(None, value)
- }
+ for ((str, constant) <- xpath.context.inscopeConstants) {
+ patchBindings.put(str, constant.constantValue.get.item)
}
for ((str, value) <- bindings) {
@@ -282,7 +287,7 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
}
}
- val typeFactory = new ItemTypeFactory(xmlCalabash.processor)
+ val typeFactory = new ItemTypeFactory(xproc.processor)
val untypedAtomicType = typeFactory.getAtomicType(XProcConstants.xs_untypedAtomic)
xdmval = new XdmAtomicValue(sbuf.toString, untypedAtomicType)
} else {
@@ -336,15 +341,15 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
private def computeValue(xpath: String,
as: Option[SequenceType],
contextItem: List[Message],
- exprContext: StaticContext,
+ exprContext: MinimalStaticContext,
bindings: Map[QName,XdmValue],
proxies: Map[Any, XdmItem],
extensionsOk: Boolean,
params: XPathBindingParams,
useCollection: Boolean): XdmValue = {
- val config = xmlCalabash.processor.getUnderlyingConfiguration
+ val config = xproc.processor.getUnderlyingConfiguration
- val sconfig = xmlCalabash.processor.getUnderlyingConfiguration
+ val sconfig = xproc.processor.getUnderlyingConfiguration
val curfinder = sconfig.getCollectionFinder
if (useCollection) {
@@ -353,7 +358,7 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
}
try {
- val xcomp = xmlCalabash.processor.newXPathCompiler()
+ val xcomp = xproc.processor.newXPathCompiler()
val baseURI = if (exprContext.baseURI.isDefined) {
exprContext.baseURI.get
} else {
@@ -366,13 +371,13 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
for (varname <- bindings.keySet) {
xcomp.declareVariable(varname)
}
- for (varname <- params.statics.keySet) {
+ for (varname <- params.constants.keySet) {
if (!bindings.contains(varname)) {
xcomp.declareVariable(varname)
}
}
- for ((prefix, uri) <- exprContext.nsBindings) {
+ for ((prefix, uri) <- exprContext.inscopeNamespaces) {
xcomp.declareNamespace(prefix, uri)
}
@@ -395,7 +400,7 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
for ((varname, varvalue) <- bindings) {
selector.setVariable(varname, varvalue)
}
- for ((varname, varvalue) <- params.statics) {
+ for ((varname, varvalue) <- params.constants) {
if (!bindings.contains(varname)) {
selector.setVariable(varname, varvalue)
}
@@ -521,9 +526,9 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
}
msg.metadata match {
- case xproc: XProcMetadata =>
- val props = xproc.properties
- val builder = new SaxonTreeBuilder(xmlCalabash)
+ case meta: XProcMetadata =>
+ val props = meta.properties
+ val builder = new SaxonTreeBuilder(xproc)
builder.startDocument(None)
builder.addStartElement(XProcConstants.c_document_properties)
for ((key,value) <- props) {
@@ -545,7 +550,7 @@ class SaxonExpressionEvaluator(xmlCalabash: XMLCalabash) extends ExpressionEvalu
}
private def emptyProxy(): XdmNode = {
- val builder = new SaxonTreeBuilder(xmlCalabash)
+ val builder = new SaxonTreeBuilder(xproc)
builder.startDocument(None)
builder.addStartElement(XProcConstants.c_document_properties)
builder.addEndElement()
diff --git a/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionOptions.scala b/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionOptions.scala
index 5790ef2..c5b49ee 100644
--- a/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionOptions.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/SaxonExpressionOptions.scala
@@ -81,7 +81,7 @@ class SaxonExpressionOptions private(map: Option[Map[String,Any]]) {
private def checkBoolean(key: String, value: Any): Boolean = {
value match {
case b: Boolean => b
- case _ => throw XProcException.xdBadValue(value.toString, "boolean", None)
+ case _ => throw XProcException.xdBadType(value.toString, "boolean", None)
}
}
@@ -96,7 +96,7 @@ class SaxonExpressionOptions private(map: Option[Map[String,Any]]) {
private def checkDouble(key: String, value: Any): Double = {
value match {
case d: Double => d
- case _ => throw XProcException.xdBadValue(value.toString, "double", None)
+ case _ => throw XProcException.xdBadType(value.toString, "double", None)
}
}
@@ -111,7 +111,7 @@ class SaxonExpressionOptions private(map: Option[Map[String,Any]]) {
private def checkQName(key: String, value: Any): QName = {
value match {
case q: QName => q
- case _ => throw XProcException.xdBadValue(value.toString, "QName", None)
+ case _ => throw XProcException.xdBadType(value.toString, "QName", None)
}
}
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/StaticContext.scala b/src/main/scala/com/xmlcalabash/runtime/StaticContext.scala
index 2348346..a6cc8b6 100644
--- a/src/main/scala/com/xmlcalabash/runtime/StaticContext.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/StaticContext.scala
@@ -4,52 +4,32 @@ import java.net.URI
import com.jafpl.graph.Location
import com.jafpl.messages.Message
import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.model.xml.{Artifact, NameBinding}
-import com.xmlcalabash.util.S9Api
-import net.sf.saxon.s9api.XdmNode
+import com.xmlcalabash.model.xxml.{XArtifact, XNameBinding, XOption}
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api, URIUtils}
+import net.sf.saxon.s9api.{QName, XdmNode}
import scala.collection.mutable
-class StaticContext(val config: XMLCalabash, val artifact: Option[Artifact]) {
+class StaticContext(val config: XMLCalabash) extends MinimalStaticContext() {
protected var _baseURI: Option[URI] = None
protected var _inScopeNS = Map.empty[String,String]
protected var _location: Option[Location] = None
- protected var _statics = Map.empty[String,Message]
+ protected var _constants = Map.empty[String,Message]
- def this(config: XMLCalabash) = {
- this(config, None)
+ def this(runtime: XMLCalabashRuntime) = {
+ this(runtime.config)
}
- def this(config: XMLCalabashRuntime) = {
- this(config.config, None)
- }
-
- def this(config: XMLCalabash, artifact: Artifact) = {
- this(config, Some(artifact))
- }
-
- def this(runtime: XMLCalabashRuntime, artifact: Option[Artifact]) = {
- this(runtime.config, artifact)
- }
-
- def this(runtime: XMLCalabashRuntime, artifact: Artifact) = {
- this(runtime.config, Some(artifact))
- }
-
- def this(context: StaticContext, artifact: Option[Artifact]) = {
- this(context.config, artifact)
+ def this(context: StaticContext) = {
+ this(context.config)
_baseURI = context._baseURI
_inScopeNS = context._inScopeNS
_location = context._location
- _statics = context._statics
- }
-
- def this(context: StaticContext, artifact: Artifact) = {
- this(context, Some(artifact))
+ _constants = context._constants
}
- def this(config: XMLCalabash, artifact: Option[Artifact], node: XdmNode) = {
- this(config, artifact)
+ def this(config: XMLCalabash, node: XdmNode) = {
+ this(config)
_baseURI = Option(node.getBaseURI)
_inScopeNS = S9Api.inScopeNamespaces(node)
_location = Some(new XProcLocation(node))
@@ -70,19 +50,23 @@ class StaticContext(val config: XMLCalabash, val artifact: Option[Artifact]) {
_location = Some(loc)
}
- def statics: Map[String,Message] = _statics
+ override def inscopeNamespaces: Map[String, String] = _inScopeNS
+
+ override def inscopeConstants: Map[QName, XOption] = Map()
+
+ def constants: Map[String,Message] = _constants
- def withStatics(statics: Map[String,Message]): StaticContext = {
- val context = new StaticContext(this, artifact)
- context._statics = statics
+ def withConstants(constants: Map[String,Message]): StaticContext = {
+ val context = new StaticContext(this)
+ context._constants = constants
context
}
- def withStatics(bindings: List[NameBinding]): StaticContext = {
- val statics = mutable.HashMap.empty[String, Message]
+ def withConstants(bindings: List[XNameBinding]): StaticContext = {
+ val constants = mutable.HashMap.empty[String, Message]
for (bind <- bindings) {
- statics.put(bind.name.getClarkName, bind.staticValue.get)
+ constants.put(bind.name.getClarkName, bind.constantValue.get)
}
- withStatics(statics.toMap)
+ withConstants(constants.toMap)
}
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/StepExecutable.scala b/src/main/scala/com/xmlcalabash/runtime/StepExecutable.scala
index 214f106..8be9554 100644
--- a/src/main/scala/com/xmlcalabash/runtime/StepExecutable.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/StepExecutable.scala
@@ -1,7 +1,7 @@
package com.xmlcalabash.runtime
-import com.xmlcalabash.config.StepSignature
+import com.xmlcalabash.model.xxml.XDeclareStep
trait StepExecutable extends XmlStep {
- def signature: StepSignature
+ def declaration: XDeclareStep
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/StepProxy.scala b/src/main/scala/com/xmlcalabash/runtime/StepProxy.scala
index 0acbef4..48cb876 100644
--- a/src/main/scala/com/xmlcalabash/runtime/StepProxy.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/StepProxy.scala
@@ -8,7 +8,7 @@ import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{AnyItemMessage, XdmNodeItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
-import com.xmlcalabash.runtime.params.StepParams
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.util.{MediaType, S9Api, TypeUtils}
import net.sf.saxon.ma.map.MapItem
import net.sf.saxon.s9api.{Axis, QName, SequenceType, XdmAtomicValue, XdmItem, XdmMap, XdmNode, XdmNodeKind, XdmValue}
@@ -20,7 +20,7 @@ import java.net.URI
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
-class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutable, params: Option[ImplParams], staticContext: StaticContext) extends Step with XProcDataConsumer {
+class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutable, staticContext: XStaticContext) extends Step with XProcDataConsumer {
private var _id: String = _
private val openStreams = ListBuffer.empty[InputStream]
protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
@@ -111,15 +111,15 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
new QName("", bindmsg.name)
}
- if (step.signature.stepType.isDefined) {
- val ns = step.signature.stepType.get.getNamespaceURI
+ if (step.declaration.stepType.isDefined) {
+ val ns = step.declaration.stepType.get.getNamespaceURI
if ((ns == XProcConstants.ns_p && qname == XProcConstants._message)
|| (ns != XProcConstants.ns_p && qname == XProcConstants.p_message)) {
System.err.println(bindmsg.message.toString)
return
}
} else {
- if (qname == XProcConstants.p_message) {
+ if (qname == XProcConstants._message) {
System.err.println(bindmsg.message.toString)
return
}
@@ -128,9 +128,9 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
bindings += qname
bindingsMap.put(qname.getClarkName, bindmsg.message)
- val stepsig = step.signature
- if (stepsig.optionNames.contains(qname)) {
- val optsig = stepsig.option(qname, staticContext.location)
+ val decl = step.declaration
+ if (decl.optionNames.contains(qname)) {
+ val optsig = decl.option(qname).get
val opttype: Option[SequenceType] = optsig.declaredType
val optlist: Option[List[XdmAtomicValue]] = optsig.tokenList
@@ -173,7 +173,7 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
val xvalue = valuemsg.item.getUnderlyingValue
xvalue match {
case map: MapItem =>
- if (optsig.forceQNameKeys) {
+ if (optsig.qnameKeys) {
val qmap = S9Api.forceQNameKeys(map, staticContext)
step.receiveBinding(new NameValueBinding(qname, qmap, valuemsg))
} else {
@@ -209,6 +209,15 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
override def run(): Unit = {
running = true
+ for (name <- step.declaration.optionNames) {
+ if (!bindings.contains(name)) {
+ if (staticContext.inscopeConstants.contains(name)) {
+ val value = staticContext.inscopeConstants(name)
+ step.receiveBinding(new NameValueBinding(name, value.constantValue.get))
+ }
+ }
+ }
+
for (port <- inputBuffer.keySet) {
for (message <- inputBuffer(port)) {
processInput(port, message)
@@ -223,26 +232,6 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
}
outputBuffer.clear()
- // If there are statically computed options for this step, pass them along
- if (params.isDefined && params.get.isInstanceOf[StepParams]) {
- val atomic = params.get.asInstanceOf[StepParams]
- for ((name,value) <- atomic.staticallyComputedOptions) {
- val bindmsg = new BindingMessage(name, value)
- receiveBinding(bindmsg)
- }
- }
-
- for (qname <- step.signature.optionNames) {
- if (!bindings.contains(qname)) {
- val optsig = step.signature.option(qname, staticContext.location)
- val opttype: Option[SequenceType] = optsig.declaredType
- if (optsig.defaultSelect.isDefined) {
- val value = TypeUtils.castAtomicAs(new XdmAtomicValue(optsig.defaultSelect.get), opttype, staticContext)
- step.receiveBinding(new NameValueBinding(qname, value, XProcMetadata.ANY, staticContext))
- }
- }
- }
-
try {
DynamicContext.withContext(dynamicContext) {
step.run(staticContext)
@@ -265,7 +254,7 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
try {
stream.close()
} catch {
- case ex: IOException => ()
+ case _: IOException => ()
case ex: Exception =>
thrown = Some(ex)
}
@@ -311,7 +300,7 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
received.put(port, received.getOrElse(port, 1))
inputSpec.checkInputCardinality(port, received(port))
- val mtypes = step.signature.input(port, staticContext.location).contentTypes
+ val mtypes = step.declaration.input(port).contentTypes
if (mtypes.nonEmpty) {
val ctype = message match {
case msg: XdmNodeItemMessage => Some(msg.metadata.contentType)
@@ -399,7 +388,7 @@ class StepProxy(config: XMLCalabashRuntime, stepType: QName, step: StepExecutabl
}
// Is the content type ok?
- val mtypes = step.signature.output(port, staticContext.location).contentTypes
+ val mtypes = step.declaration.output(port).contentTypes
if (mtypes.nonEmpty) {
// FIXME: rethrow XC0070 with a location
if (!metadata.contentType.allowed(mtypes)) {
diff --git a/src/main/scala/com/xmlcalabash/runtime/StepRunner.scala b/src/main/scala/com/xmlcalabash/runtime/StepRunner.scala
index ae31be0..476b5e7 100644
--- a/src/main/scala/com/xmlcalabash/runtime/StepRunner.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/StepRunner.scala
@@ -7,15 +7,14 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.config.StepSignature
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{AnyItemMessage, XdmValueItemMessage}
-import com.xmlcalabash.model.util.XProcConstants
-import com.xmlcalabash.model.xml.DeclareStep
-import com.xmlcalabash.util.{S9Api, XProcVarValue}
-import net.sf.saxon.s9api.{QName, XdmItem, XdmNode, XdmValue}
+import com.xmlcalabash.model.xxml.{XDeclareStep, XInput, XOption, XOutput, XStaticContext}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, XProcVarValue}
+import net.sf.saxon.s9api.QName
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
-class StepRunner(private val pruntime: XMLCalabash, val decl: DeclareStep, val signature: StepSignature) extends StepExecutable {
+class StepRunner(val decl: XDeclareStep) extends StepExecutable {
private var runtime: XMLCalabashRuntime = _
private var _location = Option.empty[Location]
private val consumers = mutable.HashMap.empty[String, ConsumerMap]
@@ -25,32 +24,40 @@ class StepRunner(private val pruntime: XMLCalabash, val decl: DeclareStep, val s
private val cardMap = mutable.HashMap.empty[String,PortCardinality]
private val typeMap = mutable.HashMap.empty[String,List[String]]
- for (port <- signature.inputPorts) {
- val portSig = signature.input(port, decl.location)
- portSig.cardinality match {
- case "1" => cardMap.put(portSig.port, new PortCardinality(1,1))
- case "*" => cardMap.put(portSig.port, new PortCardinality(0))
- case "+" => cardMap.put(portSig.port, new PortCardinality(1))
- case _ => throw new RuntimeException("WTF? Cardinality=" + portSig.cardinality)
+ for (input <- decl.children[XInput]) {
+ if (input.sequence) {
+ cardMap.put(input.port, new PortCardinality(0))
+ } else {
+ cardMap.put(input.port, new PortCardinality(1, 1))
+ }
+
+ val ctypes = ListBuffer.empty[String]
+ for (ctype <- input.contentTypes) {
+ ctypes += ctype.toString
}
- typeMap.put(portSig.port, List("application/octet-stream")) // FIXME: THIS IS A LIE
+ typeMap.put(input.port, ctypes.toList)
}
private val iSpec = new XmlPortSpecification(cardMap.toMap, typeMap.toMap)
cardMap.clear()
typeMap.clear()
- for (port <- signature.outputPorts) {
- val portSig = signature.output(port, decl.location)
- portSig.cardinality match {
- case "1" => cardMap.put(portSig.port, new PortCardinality(1,1))
- case "*" => cardMap.put(portSig.port, new PortCardinality(0))
- case "+" => cardMap.put(portSig.port, new PortCardinality(1))
- case _ => throw new RuntimeException("WTF? Cardinality=" + portSig.cardinality)
+ for (output <- decl.children[XOutput]) {
+ if (output.sequence) {
+ cardMap.put(output.port, new PortCardinality(0))
+ } else {
+ cardMap.put(output.port, new PortCardinality(1, 1))
}
- typeMap.put(portSig.port, List("application/octet-stream")) // FIXME: THIS IS A LIE
+
+ val ctypes = ListBuffer.empty[String]
+ for (ctype <- output.contentTypes) {
+ ctypes += ctype.toString
+ }
+ typeMap.put(output.port, ctypes.toList)
}
private val oSpec = new XmlPortSpecification(cardMap.toMap, typeMap.toMap)
+ override def declaration: XDeclareStep = decl
+
override def inputSpec: XmlPortSpecification = iSpec
override def outputSpec: XmlPortSpecification = oSpec
@@ -59,8 +66,8 @@ class StepRunner(private val pruntime: XMLCalabash, val decl: DeclareStep, val s
override def setConsumer(consumer: XProcDataConsumer): Unit = {
// It's too early to set in the runtime, save for later
- for (port <- signature.outputPorts) {
- consumers.put(port, new ConsumerMap(port, consumer))
+ for (output <- decl.children[XOutput]) {
+ consumers.put(output.port, new ConsumerMap(output.port, consumer))
}
}
@@ -70,7 +77,7 @@ class StepRunner(private val pruntime: XMLCalabash, val decl: DeclareStep, val s
override def receiveBinding(variable: NameValueBinding): Unit = {
// It's too early to set in the runtime, save for later
- bindings.put(variable.name, new XProcVarValue(variable.value, new StaticContext(variable.context, decl)))
+ bindings.put(variable.name, new XProcVarValue(variable.value, new XStaticContext())) // FIXME: context?
}
// Input to the pipeline
@@ -98,7 +105,7 @@ class StepRunner(private val pruntime: XMLCalabash, val decl: DeclareStep, val s
_usedPorts = ports
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
//println("=======================================")
//decl.dump()
diff --git a/src/main/scala/com/xmlcalabash/runtime/StepWrapper.scala b/src/main/scala/com/xmlcalabash/runtime/StepWrapper.scala
index 617b61c..2e6628b 100644
--- a/src/main/scala/com/xmlcalabash/runtime/StepWrapper.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/StepWrapper.scala
@@ -5,9 +5,16 @@ import com.jafpl.runtime.RuntimeConfiguration
import com.jafpl.steps.BindingSpecification
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.config.StepSignature
+import com.xmlcalabash.model.xxml.{XDeclareStep, XOption}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmValue}
-class StepWrapper(protected[xmlcalabash] val step: XmlStep, val signature: StepSignature) extends StepExecutable {
+import scala.collection.mutable
+
+class StepWrapper(protected[xmlcalabash] val step: XmlStep, decl: XDeclareStep) extends StepExecutable {
+ private val seenOptions = mutable.HashSet.empty[QName]
+
+ override def declaration: XDeclareStep = decl
override def inputSpec: XmlPortSpecification = step.inputSpec
override def outputSpec: XmlPortSpecification = step.outputSpec
override def bindingSpec: BindingSpecification = step.bindingSpec
@@ -15,6 +22,7 @@ class StepWrapper(protected[xmlcalabash] val step: XmlStep, val signature: StepS
override def setLocation(location: Location): Unit = step.setLocation(location)
override def receiveBinding(variable: NameValueBinding): Unit = {
+ seenOptions += variable.name
step.receiveBinding(variable)
}
override def receive(port: String, item: Any, metadata: XProcMetadata): Unit = {
@@ -26,7 +34,14 @@ class StepWrapper(protected[xmlcalabash] val step: XmlStep, val signature: StepS
override def initialize(config: RuntimeConfiguration): Unit = {
step.initialize(config)
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
+ for (option <- decl.children[XOption]) {
+ if (!seenOptions.contains(option.name) && context.inscopeConstants.contains(option.name)) {
+ val value = new NameValueBinding(option.name, context.inscopeConstants(option.name).constantValue.get)
+ step.receiveBinding(value)
+ }
+ }
+
step.run(context)
}
override def reset(): Unit = step.reset()
diff --git a/src/main/scala/com/xmlcalabash/runtime/XMLCalabashProcessor.scala b/src/main/scala/com/xmlcalabash/runtime/XMLCalabashProcessor.scala
new file mode 100644
index 0000000..cc056fa
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/runtime/XMLCalabashProcessor.scala
@@ -0,0 +1,7 @@
+package com.xmlcalabash.runtime
+
+import net.sf.saxon.s9api.Processor
+
+trait XMLCalabashProcessor {
+ def processor: Processor
+}
diff --git a/src/main/scala/com/xmlcalabash/runtime/XMLCalabashRuntime.scala b/src/main/scala/com/xmlcalabash/runtime/XMLCalabashRuntime.scala
index 0ab4670..ef4d4a1 100644
--- a/src/main/scala/com/xmlcalabash/runtime/XMLCalabashRuntime.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/XMLCalabashRuntime.scala
@@ -2,33 +2,31 @@ package com.xmlcalabash.runtime
import com.jafpl.config.Jafpl
import com.jafpl.exceptions.JafplException
-import com.jafpl.graph.Graph
+import com.jafpl.graph.{Graph, Node}
import com.jafpl.messages.Message
import com.jafpl.runtime.{GraphRuntime, RuntimeConfiguration}
import com.jafpl.steps.DataConsumer
import com.jafpl.util.{ErrorListener, TraceEventManager}
import com.xmlcalabash.XMLCalabash
-import com.xmlcalabash.config.{DocumentManager, DocumentRequest, DocumentResponse, Signatures, XProcConfigurer}
+import com.xmlcalabash.config.{DocumentManager, DocumentRequest, Signatures, XProcConfigurer}
import com.xmlcalabash.exceptions.{ConfigurationException, ExceptionCode, ModelException, XProcException}
import com.xmlcalabash.messages.{AnyItemMessage, XdmNodeItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.{ExpressionParser, XProcConstants}
-import com.xmlcalabash.model.xml.{Artifact, DataSource, DeclareStep, Document, Empty, Inline}
-import com.xmlcalabash.steps.internal.InlineExpander
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.model.xxml.{XArtifact, XDeclareStep, XDocument, XEmpty, XInline, XInput, XStaticContext}
+import com.xmlcalabash.steps.internal.{InlineExpander, XPathSelector}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils}
import com.xmlcalabash.util.stores.{DataStore, FallbackDataStore, FileDataStore, HttpDataStore}
import net.sf.saxon.lib.{ModuleURIResolver, UnparsedTextURIResolver}
import net.sf.saxon.s9api.{Processor, QName, XdmAtomicValue, XdmNode, XdmValue}
import org.slf4j.{Logger, LoggerFactory}
import org.xml.sax.EntityResolver
-import java.io.{File, FileOutputStream}
import java.net.URI
-import java.nio.charset.StandardCharsets
import javax.xml.transform.URIResolver
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
-class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends RuntimeConfiguration {
+class XMLCalabashRuntime protected[xmlcalabash] (val decl: XDeclareStep) extends XMLCalabashProcessor with RuntimeConfiguration {
val config: XMLCalabash = decl.config
//FIXME: why?
@@ -47,54 +45,45 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
private var _episode = config.computeEpisode
private var _defaultSerializationOptions: Map[String,Map[QName,String]] = Map.empty[String,Map[QName,String]]
private var _trim_inline_whitespace = config.trimInlineWhitespace
- private val idMap = mutable.HashMap.empty[String,Artifact]
+ private val idMap = mutable.HashMap.empty[String,XArtifact]
private var ran = false
private var _signatures: Signatures = _
private var runtime: GraphRuntime = _
+ private var _staticOptions: Map[QName,XdmValueItemMessage] = _
private var _datastore = Option.empty[DataStore]
- private val defaultInputs = mutable.HashMap.empty[String, List[DocumentRequest]]
private val _usedPorts = mutable.HashSet.empty[String]
+ private val _graphNodes = mutable.HashMap.empty[XArtifact, Node]
val jafpl: Jafpl = Jafpl.newInstance()
val graph: Graph = jafpl.newGraph()
- if (decl._name.isDefined) {
- graph.label = decl._name.get
+ if (decl.name.isDefined) {
+ graph.label = decl.name.get
}
- protected[xmlcalabash] def init(decl: DeclareStep): Unit = {
- try {
- for (input <- decl.inputs) {
- if (input.defaultInputs.nonEmpty) {
- val defaults = ListBuffer.empty[DocumentRequest]
- for (default <- input.defaultInputs) {
- default match {
- case _: Empty =>
- ()
- case inline: Inline =>
- val expander = new InlineExpander(inline)
- if (inline.documentProperties.isDefined) {
- expander.documentProperties = inline.documentProperties.get
- }
- defaults += expander.loadDocument(inline.expandText)
- case doc: Document =>
- defaults += doc.loadDocument()
- case _ =>
- throw XProcException.xiThisCantHappen(s"Unexpected default input type: ${default}", None)
- }
- }
- if (defaults.nonEmpty) {
- defaultInputs.put(input.port, defaults.toList)
- }
- }
- }
+ def hasNode(art: XArtifact): Boolean = {
+ _graphNodes.contains(art)
+ }
+
+ def node(art: XArtifact): Node = {
+ _graphNodes(art)
+ }
+ def addNode(art: XArtifact, node: Node): Unit = {
+ _graphNodes.put(art, node)
+ }
+ def staticOptions: Map[QName, XdmValueItemMessage] = _staticOptions
+
+ protected[xmlcalabash] def init(decl: XDeclareStep): Unit = {
+ try {
config.debugOptions.dumpPipeline(decl)
config.debugOptions.dumpOpenGraph(decl, graph)
runtime = new GraphRuntime(graph, this)
config.debugOptions.dumpGraph(decl, graph)
+ _staticOptions = config.staticOptions
+
runtime.traceEventManager = _traceEventManager
} catch {
case ex: JafplException =>
@@ -111,15 +100,15 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
// ===================================================================================
- def inputs: List[String] = decl.inputPorts
- def outputs: List[String] = decl.outputPorts
+ def inputs: Set[String] = decl.inputPorts
+ def outputs: Set[String] = decl.outputPorts
protected[runtime] def inputMessage(port: String, msg: Message): Unit = {
runtime.inputs(port).send(msg)
}
def input(port: String, item: Any, metadata: XProcMetadata): Unit = {
- val context = new StaticContext(this)
+ val context = new XStaticContext()
item match {
case xnode: XdmNode =>
input(port, new XdmNodeItemMessage(xnode, metadata, context))
@@ -149,9 +138,13 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
decl.output(port).serialization
}
- def option(name: QName, value: XdmValue, context: StaticContext): Unit = {
+ def option(name: QName, value: XdmValue, context: MinimalStaticContext): Unit = {
if (runtime.bindings.contains(name.getClarkName)) {
- runtime.bindings(name.getClarkName).setValue(new XdmValueItemMessage(value, XProcMetadata.XML, context))
+ val optdecl = decl.option(name).get
+ val typeUtils = new TypeUtils(processor, context)
+ val msg = new XdmValueItemMessage(value, XProcMetadata.ANY, context)
+ val cmsg = typeUtils.convertType(name, msg, optdecl.declaredType, optdecl.tokenList)
+ runtime.bindings(name.getClarkName).setValue(cmsg)
}
}
@@ -164,11 +157,45 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
throw new RuntimeException("You must call reset() before running a pipeline a second time.")
}
- for ((port, defaults) <- defaultInputs) {
- if (!_usedPorts.contains(port)) {
+ for (xinput <- decl.children[XInput]) {
+ if (!_usedPorts.contains(xinput.port) && xinput.defaultInputs.nonEmpty) {
+ val defaults = ListBuffer.empty[DocumentRequest]
+ for (default <- xinput.defaultInputs) {
+ default match {
+ case _: XEmpty =>
+ ()
+ case inline: XInline =>
+ val expander = new InlineExpander(inline)
+ if (inline.documentProperties.isDefined) {
+ expander.documentProperties = inline.documentProperties.get
+ }
+ expander.copyStaticOptionsToBindings(this)
+ defaults += expander.loadDocument(inline.expandText)
+ case doc: XDocument =>
+ defaults += doc.loadDocument()
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Unexpected default input type: ${default}", None)
+ }
+ }
for (source <- defaults) {
val resp = config.documentManager.parse(source)
- input(port, resp.value, new XProcMetadata(resp.contentType, resp.props))
+ if (xinput.select.isDefined) {
+ val items = Tuple2(resp.value, new XProcMetadata(resp.contentType, resp.props))
+ val bindings = mutable.HashMap.empty[String, Message]
+ for ((name,value) <- config.staticOptions) {
+ bindings.put(name.getClarkName, value)
+ }
+ val xpselector = new XPathSelector(config, List(items), xinput.select.get, xinput.staticContext, bindings.toMap)
+ val results = xpselector.select()
+ if (results.length != 1 && !xinput.sequence) {
+ throw XProcException.xdInputSequenceNotAllowed(xinput.port, None)
+ }
+ for (result <- results) {
+ input(xinput.port, result, new XProcMetadata(resp.contentType, resp.props))
+ }
+ } else {
+ input(xinput.port, resp.value, new XProcMetadata(resp.contentType, resp.props))
+ }
}
}
}
@@ -230,7 +257,6 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
def staticBaseURI: URI = config.staticBaseURI
def episode: String = _episode
- // FIXME: Setters for these
def entityResolver: EntityResolver = _entityResolver
def uriResolver: URIResolver = _uriResolver
def moduleURIResolver: ModuleURIResolver = _moduleURIResolver
@@ -252,7 +278,10 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
}
override def traceEnabled(trace: String): Boolean = _traceEventManager.traceEnabled(trace)
- override def expressionEvaluator: SaxonExpressionEvaluator = config.expressionEvaluator
+ // We need expression evaluators with access to the *runtime*
+ private val _expressionEvaluator = new SaxonExpressionEvaluator(this)
+ override def expressionEvaluator: SaxonExpressionEvaluator = _expressionEvaluator
+
def expressionParser: ExpressionParser = config.expressionParser
def datastore: DataStore = {
@@ -267,11 +296,11 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
// ====================================================================================
- def addNode(id: String, artifact: Artifact): Unit = {
+ def addNode(id: String, artifact: XArtifact): Unit = {
idMap.put(id, artifact)
}
- def node(id: String): Option[Artifact] = idMap.get(id)
+ def node(id: String): Option[XArtifact] = idMap.get(id)
def signatures: Signatures = {
@@ -301,32 +330,4 @@ class XMLCalabashRuntime protected[xmlcalabash] (val decl: DeclareStep) extends
def trimInlineWhitespace_=(trim: Boolean): Unit = {
_trim_inline_whitespace = trim
}
-
- // ==============================================================================================
-
- def stepImplementation(stepType: QName, staticContext: StaticContext): StepWrapper = {
- stepImplementation(stepType, staticContext, None)
- }
-
- def stepImplementation(stepType: QName, staticContext: StaticContext, implParams: Option[ImplParams]): StepWrapper = {
- val location = staticContext.location
-
- if (!_signatures.stepTypes.contains(stepType)) {
- throw new ModelException(ExceptionCode.NOTYPE, stepType.toString, location)
- }
-
- val sig = _signatures.step(stepType)
- val implClass = sig.implementation
- if (implClass.isEmpty) {
- throw new ModelException(ExceptionCode.NOIMPL, stepType.toString, location)
- }
-
- val klass = Class.forName(implClass.head).getDeclaredConstructor().newInstance()
- klass match {
- case step: XmlStep =>
- new StepWrapper(step, sig)
- case _ =>
- throw new ModelException(ExceptionCode.IMPLNOTSTEP, stepType.toString, location)
- }
- }
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/XProcExpression.scala b/src/main/scala/com/xmlcalabash/runtime/XProcExpression.scala
index 29bfcad..a11e548 100644
--- a/src/main/scala/com/xmlcalabash/runtime/XProcExpression.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/XProcExpression.scala
@@ -1,7 +1,9 @@
package com.xmlcalabash.runtime
-class XProcExpression(val context: StaticContext, val extensionFunctionsAllowed: Boolean) {
- def this(context: StaticContext) = {
+import com.xmlcalabash.util.MinimalStaticContext
+
+class XProcExpression(val context: MinimalStaticContext, val extensionFunctionsAllowed: Boolean) {
+ def this(context: MinimalStaticContext) = {
this(context, false)
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/XProcMetadata.scala b/src/main/scala/com/xmlcalabash/runtime/XProcMetadata.scala
index 97981ce..3df5281 100644
--- a/src/main/scala/com/xmlcalabash/runtime/XProcMetadata.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/XProcMetadata.scala
@@ -117,7 +117,10 @@ class XProcMetadata(private val initialContentType: Option[MediaType],
if (value.size == 0) {
_contentType = Some(MediaType.OCTET_STREAM)
} else {
- _contentType = Some(MediaType.parse(value.itemAt(0).getStringValue, charset)) // FIXME: what about a sequence?
+ if (value.size > 1) {
+ throw XProcException.xiUserError("Content-type property must be a single value, not a list")
+ }
+ _contentType = Some(MediaType.parse(value.itemAt(0).getStringValue, charset))
}
} else {
_contentType = Some(MediaType.OCTET_STREAM)
@@ -134,7 +137,10 @@ class XProcMetadata(private val initialContentType: Option[MediaType],
if (value.size() == 0) {
_baseURI = None
} else {
- _baseURI = Some(new URI(value.itemAt(0).getStringValue)) // FIXME: what about a sequence?
+ if (value.size > 1) {
+ throw XProcException.xiUserError("Base-uri property must be a single value, not a list")
+ }
+ _baseURI = Some(new URI(value.itemAt(0).getStringValue))
}
}
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/XProcVtExpression.scala b/src/main/scala/com/xmlcalabash/runtime/XProcVtExpression.scala
index 0df8672..bc4e603 100644
--- a/src/main/scala/com/xmlcalabash/runtime/XProcVtExpression.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/XProcVtExpression.scala
@@ -2,23 +2,24 @@ package com.xmlcalabash.runtime
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.ValueParser
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.expr.parser.ExpressionTool
-class XProcVtExpression private(override val context: StaticContext, val params: Option[ExprParams]) extends XProcExpression(context) {
+class XProcVtExpression private(override val context: MinimalStaticContext, val params: Option[ExprParams]) extends XProcExpression(context) {
private var _avt: List[String] = _
private var _string = false
- def this(context: StaticContext, avt: List[String], stringResult: Boolean) = {
+ def this(context: MinimalStaticContext, avt: List[String], stringResult: Boolean) = {
this(context, None)
_avt = avt
_string = stringResult
}
- def this(context: StaticContext, avt: List[String]) = {
+ def this(context: MinimalStaticContext, avt: List[String]) = {
this(context, avt, false)
}
- def this(context: StaticContext, expr: String, stringResult: Boolean) = {
+ def this(context: MinimalStaticContext, expr: String, stringResult: Boolean) = {
this(context, None)
val avt = ValueParser.parseAvt(expr)
if (avt.isEmpty) {
@@ -28,7 +29,7 @@ class XProcVtExpression private(override val context: StaticContext, val params:
_string = stringResult
}
- def this(context: StaticContext, expr: String) = {
+ def this(context: MinimalStaticContext, expr: String) = {
this(context, expr, false)
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/XProcXPathExpression.scala b/src/main/scala/com/xmlcalabash/runtime/XProcXPathExpression.scala
index e4e5553..bfb8acd 100644
--- a/src/main/scala/com/xmlcalabash/runtime/XProcXPathExpression.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/XProcXPathExpression.scala
@@ -1,24 +1,25 @@
package com.xmlcalabash.runtime
import com.xmlcalabash.runtime.params.XPathBindingParams
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{SequenceType, XdmAtomicValue}
-class XProcXPathExpression(override val context: StaticContext,
+class XProcXPathExpression(override val context: MinimalStaticContext,
val expr: String,
val as: Option[SequenceType],
val values: Option[List[XdmAtomicValue]],
val params: Option[XPathBindingParams])
extends XProcExpression(context) {
- def this(context: StaticContext, expr: String) = {
+ def this(context: MinimalStaticContext, expr: String) = {
this(context, expr, None, None, None)
}
- def this(context: StaticContext, expr: String, as: Option[SequenceType]) = {
+ def this(context: MinimalStaticContext, expr: String, as: Option[SequenceType]) = {
this(context, expr, as, None, None)
}
- def this(context: StaticContext, expr: String, as: Option[SequenceType], values: Option[List[XdmAtomicValue]], params: XPathBindingParams) = {
+ def this(context: MinimalStaticContext, expr: String, as: Option[SequenceType], values: Option[List[XdmAtomicValue]], params: XPathBindingParams) = {
this(context, expr, as, values, Some(params))
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/XmlPortSpecification.scala b/src/main/scala/com/xmlcalabash/runtime/XmlPortSpecification.scala
index 244baf1..950220d 100644
--- a/src/main/scala/com/xmlcalabash/runtime/XmlPortSpecification.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/XmlPortSpecification.scala
@@ -123,8 +123,19 @@ class XmlPortSpecification(spec: immutable.Map[String,PortCardinality],
if (list.contains("application/octet-stream")) {
true
} else {
- // FIXME: Handle the subtle cases like application/xml+rdf => application/xml
- list.contains(contentType)
+ if (list.contains(contentType)) {
+ true
+ } else {
+ // Match application/xml+rdf against application/xml
+ // The logic here is a bit crude.
+ val pos = contentType.indexOf("+")
+ if (pos > 0) {
+ val basetype = contentType.substring(0, pos)
+ list.contains(basetype)
+ } else {
+ false
+ }
+ }
}
} else {
true
diff --git a/src/main/scala/com/xmlcalabash/runtime/XmlStep.scala b/src/main/scala/com/xmlcalabash/runtime/XmlStep.scala
index 2cf5cce..61b3b62 100644
--- a/src/main/scala/com/xmlcalabash/runtime/XmlStep.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/XmlStep.scala
@@ -4,6 +4,7 @@ import com.jafpl.graph.Location
import com.jafpl.runtime.RuntimeConfiguration
import com.jafpl.steps.BindingSpecification
import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmValue}
trait XmlStep {
@@ -16,7 +17,7 @@ trait XmlStep {
def receive(port: String, item: Any, metadata: XProcMetadata): Unit
def configure(config: XMLCalabash, stepType: QName, stepName: Option[String], params: Option[ImplParams]): Unit
def initialize(config: RuntimeConfiguration): Unit
- def run(context: StaticContext): Unit
+ def run(context: MinimalStaticContext): Unit
def reset(): Unit
def abort(): Unit
def stop(): Unit
diff --git a/src/main/scala/com/xmlcalabash/runtime/params/ContentTypeCheckerParams.scala b/src/main/scala/com/xmlcalabash/runtime/params/ContentTypeCheckerParams.scala
index 937a8f8..66062ad 100644
--- a/src/main/scala/com/xmlcalabash/runtime/params/ContentTypeCheckerParams.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/params/ContentTypeCheckerParams.scala
@@ -1,18 +1,19 @@
package com.xmlcalabash.runtime.params
import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.runtime.{ImplParams, StaticContext}
+import com.xmlcalabash.model.xxml.XStaticContext
+import com.xmlcalabash.runtime.ImplParams
import com.xmlcalabash.util.MediaType
import net.sf.saxon.s9api.QName
class ContentTypeCheckerParams(val port: String,
val contentTypes: List[MediaType],
- val context: StaticContext,
+ val context: XStaticContext,
val select: Option[String],
val errCode: QName,
val inputPort: Boolean,
val sequence: Boolean) extends ImplParams {
- def this(port: String, contentTypes: List[MediaType], context: StaticContext, select: Option[String], inputPort: Boolean, sequence: Boolean) = {
+ def this(port: String, contentTypes: List[MediaType], context: XStaticContext, select: Option[String], inputPort: Boolean, sequence: Boolean) = {
this(port, contentTypes, context, select, XProcException.err_xd0038, inputPort, sequence)
}
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/params/DocumentLoaderParams.scala b/src/main/scala/com/xmlcalabash/runtime/params/DocumentLoaderParams.scala
index b2b1057..f6b540d 100644
--- a/src/main/scala/com/xmlcalabash/runtime/params/DocumentLoaderParams.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/params/DocumentLoaderParams.scala
@@ -1,13 +1,13 @@
package com.xmlcalabash.runtime.params
-import com.xmlcalabash.runtime.{ImplParams, StaticContext}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.runtime.ImplParams
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
class DocumentLoaderParams(val hrefAvt: List[String],
val content_type: Option[MediaType],
val parameters: Option[String],
val document_properties: Option[String],
val context_provided: Boolean,
- val context: StaticContext) extends ImplParams {
+ val context: MinimalStaticContext) extends ImplParams {
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/params/EmptyLoaderParams.scala b/src/main/scala/com/xmlcalabash/runtime/params/EmptyLoaderParams.scala
index 2526c5f..1d0e5a1 100644
--- a/src/main/scala/com/xmlcalabash/runtime/params/EmptyLoaderParams.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/params/EmptyLoaderParams.scala
@@ -1,7 +1,8 @@
package com.xmlcalabash.runtime.params
-import com.xmlcalabash.runtime.{ImplParams, StaticContext}
+import com.xmlcalabash.model.xxml.XStaticContext
+import com.xmlcalabash.runtime.ImplParams
-class EmptyLoaderParams(val context: StaticContext) extends ImplParams {
+class EmptyLoaderParams(val context: XStaticContext) extends ImplParams {
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/params/InlineLoaderParams.scala b/src/main/scala/com/xmlcalabash/runtime/params/InlineLoaderParams.scala
index db0a468..4f28ec1 100644
--- a/src/main/scala/com/xmlcalabash/runtime/params/InlineLoaderParams.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/params/InlineLoaderParams.scala
@@ -1,15 +1,15 @@
package com.xmlcalabash.runtime.params
import com.xmlcalabash.runtime.{ImplParams, StaticContext}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.XdmNode
class InlineLoaderParams(val document: XdmNode,
val content_type: Option[MediaType],
val document_properties: Option[String],
val encoding: Option[String],
- val exclude_inline_prefixes: Option[String],
+ val exclude_uris: Set[String],
val expand_text: Boolean,
val context_provided: Boolean,
- val context: StaticContext) extends ImplParams {
+ val context: MinimalStaticContext) extends ImplParams {
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/params/SelectFilterParams.scala b/src/main/scala/com/xmlcalabash/runtime/params/SelectFilterParams.scala
index 9e9b0cd..9829acb 100644
--- a/src/main/scala/com/xmlcalabash/runtime/params/SelectFilterParams.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/params/SelectFilterParams.scala
@@ -1,6 +1,7 @@
package com.xmlcalabash.runtime.params
-import com.xmlcalabash.runtime.{ImplParams, StaticContext, XmlPortSpecification}
+import com.xmlcalabash.model.xxml.XStaticContext
+import com.xmlcalabash.runtime.{ImplParams, XmlPortSpecification}
-class SelectFilterParams(val context: StaticContext, val select: String, val port: String, val ispec: XmlPortSpecification) extends ImplParams {
+class SelectFilterParams(val context: XStaticContext, val select: String, val port: String, val sequence: Boolean) extends ImplParams {
}
diff --git a/src/main/scala/com/xmlcalabash/runtime/params/XPathBindingParams.scala b/src/main/scala/com/xmlcalabash/runtime/params/XPathBindingParams.scala
index fdb7b46..321d971 100644
--- a/src/main/scala/com/xmlcalabash/runtime/params/XPathBindingParams.scala
+++ b/src/main/scala/com/xmlcalabash/runtime/params/XPathBindingParams.scala
@@ -8,7 +8,7 @@ object XPathBindingParams {
def EMPTY: XPathBindingParams = _empty
}
-class XPathBindingParams(val statics: Map[QName, XdmValue], val collection: Boolean) extends BindingParams {
+class XPathBindingParams(val constants: Map[QName, XdmValue], val collection: Boolean) extends BindingParams {
def this(collection: Boolean) = {
this(Map.empty[QName,XdmValue], collection)
}
diff --git a/src/main/scala/com/xmlcalabash/steps/AddAttribute.scala b/src/main/scala/com/xmlcalabash/steps/AddAttribute.scala
index 06500f4..77423bd 100644
--- a/src/main/scala/com/xmlcalabash/steps/AddAttribute.scala
+++ b/src/main/scala/com/xmlcalabash/steps/AddAttribute.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{S9Api, TypeUtils}
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api, TypeUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap, NamespaceMap}
import net.sf.saxon.s9api.{Axis, QName, XdmNode}
import net.sf.saxon.value.QNameValue
@@ -32,7 +32,7 @@ class AddAttribute() extends DefaultXmlStep with ProcessMatchingNodes {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
attrName = qnameBinding(_attribute_name).get
attrValue = stringBinding(_attribute_value)
pattern = stringBinding(XProcConstants._match)
diff --git a/src/main/scala/com/xmlcalabash/steps/Archive.scala b/src/main/scala/com/xmlcalabash/steps/Archive.scala
index aa81f79..83970d5 100644
--- a/src/main/scala/com/xmlcalabash/steps/Archive.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Archive.scala
@@ -7,8 +7,9 @@ import com.jafpl.steps.PortCardinality
import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.runtime.{BinaryNode, NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api, TypeUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, TypeUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
import net.sf.saxon.s9api.{Axis, QName, XdmAtomicValue, XdmNode, XdmNodeKind, XdmValue}
import org.apache.commons.compress.archivers.ArchiveOutputStream
@@ -29,7 +30,7 @@ class Archive extends DefaultXmlStep {
private var defaultMethod = "deflated"
private var defaultLevel = "default"
- private var context: StaticContext = _
+ private var context: MinimalStaticContext = _
private var sources = ListBuffer.empty[DocumentWrapper]
@@ -88,7 +89,7 @@ class Archive extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
this.context = context
format = if (qnameBinding(XProcConstants._format).isDefined) {
diff --git a/src/main/scala/com/xmlcalabash/steps/ArchiveManifest.scala b/src/main/scala/com/xmlcalabash/steps/ArchiveManifest.scala
index a8de94d..5530207 100644
--- a/src/main/scala/com/xmlcalabash/steps/ArchiveManifest.scala
+++ b/src/main/scala/com/xmlcalabash/steps/ArchiveManifest.scala
@@ -6,7 +6,7 @@ import java.util.zip.ZipEntry
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, TypeUtils, URIUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils, URIUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
import net.sf.saxon.s9api.{QName, XdmArray, XdmValue}
import org.apache.commons.compress.archivers.zip.{ZipArchiveEntry, ZipFile}
@@ -44,7 +44,7 @@ class ArchiveManifest extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
format = if (qnameBinding(XProcConstants._format).isDefined) {
@@ -97,7 +97,7 @@ class ArchiveManifest extends DefaultXmlStep {
consumer.receive("result", result, new XProcMetadata(MediaType.XML))
}
- private def zipArchive(context: StaticContext, builder: SaxonTreeBuilder): Unit = {
+ private def zipArchive(context: MinimalStaticContext, builder: SaxonTreeBuilder): Unit = {
// ZIP requires random access: https://commons.apache.org/proper/commons-compress/zip.html
source match {
case bn: BinaryNode =>
@@ -107,7 +107,7 @@ class ArchiveManifest extends DefaultXmlStep {
}
}
- private def zipArchiveFile(context: StaticContext, builder: SaxonTreeBuilder, zfile: File): Unit = {
+ private def zipArchiveFile(context: MinimalStaticContext, builder: SaxonTreeBuilder, zfile: File): Unit = {
val zipIn = new ZipFile(zfile)
val enum = zipIn.getEntries
while (enum.hasMoreElements) {
diff --git a/src/main/scala/com/xmlcalabash/steps/B64Decode.scala b/src/main/scala/com/xmlcalabash/steps/B64Decode.scala
index 41b54ac..49a3d6d 100644
--- a/src/main/scala/com/xmlcalabash/steps/B64Decode.scala
+++ b/src/main/scala/com/xmlcalabash/steps/B64Decode.scala
@@ -5,7 +5,7 @@ import java.net.URI
import java.util.Base64
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, TypeUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils}
import net.sf.saxon.s9api.{QName, XdmNode, XdmValue}
class B64Decode extends DefaultXmlStep {
@@ -20,7 +20,7 @@ class B64Decode extends DefaultXmlStep {
smeta = Some(metadata)
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val baseValue = if (smeta.isDefined) {
diff --git a/src/main/scala/com/xmlcalabash/steps/B64Encode.scala b/src/main/scala/com/xmlcalabash/steps/B64Encode.scala
index a3f309c..e57f68f 100644
--- a/src/main/scala/com/xmlcalabash/steps/B64Encode.scala
+++ b/src/main/scala/com/xmlcalabash/steps/B64Encode.scala
@@ -5,7 +5,7 @@ import java.net.URI
import java.util.Base64
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api, TypeUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, TypeUtils}
import net.sf.saxon.s9api.{QName, Serializer, XdmEmptySequence, XdmNode, XdmValue}
import scala.collection.mutable
@@ -39,7 +39,7 @@ class B64Encode extends DefaultXmlStep {
smeta = Some(metadata)
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val baseValue = if (smeta.isDefined) {
diff --git a/src/main/scala/com/xmlcalabash/steps/CastContentType.scala b/src/main/scala/com/xmlcalabash/steps/CastContentType.scala
index 836d623..1045fb4 100644
--- a/src/main/scala/com/xmlcalabash/steps/CastContentType.scala
+++ b/src/main/scala/com/xmlcalabash/steps/CastContentType.scala
@@ -11,7 +11,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XdmValueItemMessage
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, NameValueBinding, StaticContext, XProcMetadata, XProcXPathExpression, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api, TypeUtils, ValueUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, TypeUtils, ValueUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
import net.sf.saxon.s9api.{Axis, QName, SaxonApiException, XdmAtomicValue, XdmItem, XdmMap, XdmNode, XdmNodeKind, XdmValue}
@@ -41,7 +41,7 @@ class CastContentType() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
if (castTo.xmlContentType) {
@@ -59,7 +59,7 @@ class CastContentType() extends DefaultXmlStep {
}
}
- def castToXML(context: StaticContext): Unit = {
+ def castToXML(context: MinimalStaticContext): Unit = {
val contentType = metadata.get.contentType
contentType.classification match {
@@ -142,7 +142,7 @@ class CastContentType() extends DefaultXmlStep {
}
}
- def castToText(context: StaticContext): Unit = {
+ def castToText(context: MinimalStaticContext): Unit = {
val contentType = metadata.get.contentType
contentType.classification match {
@@ -238,7 +238,7 @@ class CastContentType() extends DefaultXmlStep {
consumer.receive("result", builder.result, metadata.get.castTo(castTo))
}
- def castToJSON(context: StaticContext): Unit = {
+ def castToJSON(context: MinimalStaticContext): Unit = {
val contentType = metadata.get.contentType
contentType.classification match {
@@ -341,7 +341,7 @@ class CastContentType() extends DefaultXmlStep {
}
}
- def castToHTML(context: StaticContext): Unit = {
+ def castToHTML(context: MinimalStaticContext): Unit = {
val contentType = metadata.get.contentType
contentType.classification match {
@@ -373,7 +373,7 @@ class CastContentType() extends DefaultXmlStep {
}
}
- def castToBinary(context: StaticContext): Unit = {
+ def castToBinary(context: MinimalStaticContext): Unit = {
val contentType = metadata.get.contentType
contentType.classification match {
diff --git a/src/main/scala/com/xmlcalabash/steps/Compress.scala b/src/main/scala/com/xmlcalabash/steps/Compress.scala
index 1de6691..e26b8d2 100644
--- a/src/main/scala/com/xmlcalabash/steps/Compress.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Compress.scala
@@ -5,7 +5,7 @@ import java.util.zip.GZIPOutputStream
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmValue}
import scala.collection.mutable
@@ -34,7 +34,7 @@ class Compress extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
format = qnameBinding(XProcConstants._format)
diff --git a/src/main/scala/com/xmlcalabash/steps/Count.scala b/src/main/scala/com/xmlcalabash/steps/Count.scala
index d343f9b..1a7f6d7 100644
--- a/src/main/scala/com/xmlcalabash/steps/Count.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Count.scala
@@ -2,7 +2,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, URIUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, URIUtils}
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmValue}
class Count() extends DefaultXmlStep {
@@ -36,7 +36,7 @@ class Count() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
sendCount()
diff --git a/src/main/scala/com/xmlcalabash/steps/DefaultXmlStep.scala b/src/main/scala/com/xmlcalabash/steps/DefaultXmlStep.scala
index f197218..70661ad 100644
--- a/src/main/scala/com/xmlcalabash/steps/DefaultXmlStep.scala
+++ b/src/main/scala/com/xmlcalabash/steps/DefaultXmlStep.scala
@@ -8,16 +8,17 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XdmValueItemMessage
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.runtime._
import com.xmlcalabash.steps.DefaultXmlStep.showRunningMessage
-import com.xmlcalabash.util.{MediaType, S9Api}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api}
import net.sf.saxon.`type`.TypeHierarchy
import net.sf.saxon.expr.parser.RoleDiagnostic
import net.sf.saxon.lib.NamespaceConstant
import net.sf.saxon.om.NamespaceMap
import net.sf.saxon.s9api._
import net.sf.saxon.trans.XPathException
-import net.sf.saxon.value.QNameValue
+import net.sf.saxon.value.{QNameValue, UntypedAtomicValue}
import org.slf4j.{Logger, LoggerFactory}
import java.io.{InputStream, OutputStream}
@@ -130,7 +131,7 @@ class DefaultXmlStep extends XmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
runningMessage()
if (_location.isEmpty) {
_location = context.location
@@ -297,6 +298,8 @@ class DefaultXmlStep extends XmlStep {
bindings(name).value.getUnderlyingValue match {
case qn: QNameValue =>
Some(new QName(qn.getPrefix, qn.getNamespaceURI, qn.getLocalName))
+ case u: UntypedAtomicValue =>
+ Some(bindings(name).context.parseQName(u.getStringValue))
case _ => None
}
} else {
@@ -408,7 +411,7 @@ class DefaultXmlStep extends XmlStep {
new XProcMetadata(MediaType.TEXT, props.toMap)
}
- def serialize(context: StaticContext, source: Any, metadata: XProcMetadata, output: OutputStream): Unit = {
+ def serialize(context: MinimalStaticContext, source: Any, metadata: XProcMetadata, output: OutputStream): Unit = {
source match {
case bn: BinaryNode =>
val bytes = new Array[Byte](8192)
@@ -448,7 +451,7 @@ class DefaultXmlStep extends XmlStep {
}
}
- private def serializeJson(context: StaticContext, source: Any, metadata: XProcMetadata, output: OutputStream): Unit = {
+ private def serializeJson(context: MinimalStaticContext, source: Any, metadata: XProcMetadata, output: OutputStream): Unit = {
val expr = new XProcXPathExpression(context, "serialize($map, map {\"method\": \"json\"})")
val bindingsMap = mutable.HashMap.empty[String, Message]
val vmsg = new XdmValueItemMessage(source.asInstanceOf[XdmValue], XProcMetadata.TEXT, context)
diff --git a/src/main/scala/com/xmlcalabash/steps/Delete.scala b/src/main/scala/com/xmlcalabash/steps/Delete.scala
index ce047c1..8e28993 100644
--- a/src/main/scala/com/xmlcalabash/steps/Delete.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Delete.scala
@@ -3,6 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.om.AttributeMap
import net.sf.saxon.s9api.XdmNode
@@ -20,7 +21,7 @@ class Delete() extends DefaultXmlStep with ProcessMatchingNodes {
source_metadata = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
pattern = stringBinding(XProcConstants._match)
matcher = new ProcessMatch(config, this, context)
diff --git a/src/main/scala/com/xmlcalabash/steps/Error.scala b/src/main/scala/com/xmlcalabash/steps/Error.scala
index 7274adb..2c84864 100644
--- a/src/main/scala/com/xmlcalabash/steps/Error.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Error.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.TypeUtils
+import com.xmlcalabash.util.{MinimalStaticContext, TypeUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap, NamespaceMap}
import net.sf.saxon.s9api.{QName, XdmNode}
@@ -22,7 +22,7 @@ class Error extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val code = qnameBinding(_code).get
diff --git a/src/main/scala/com/xmlcalabash/steps/EscapeMarkup.scala b/src/main/scala/com/xmlcalabash/steps/EscapeMarkup.scala
index 4f409e5..d5d8cb8 100644
--- a/src/main/scala/com/xmlcalabash/steps/EscapeMarkup.scala
+++ b/src/main/scala/com/xmlcalabash/steps/EscapeMarkup.scala
@@ -1,11 +1,10 @@
package com.xmlcalabash.steps
import java.io.StringWriter
-
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.S9Api
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api}
import net.sf.saxon.s9api._
class EscapeMarkup() extends DefaultXmlStep {
@@ -21,7 +20,7 @@ class EscapeMarkup() extends DefaultXmlStep {
metadata = meta
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val options = mapBinding(XProcConstants._serialization)
diff --git a/src/main/scala/com/xmlcalabash/steps/Filter.scala b/src/main/scala/com/xmlcalabash/steps/Filter.scala
index 0d8e55f..362f715 100644
--- a/src/main/scala/com/xmlcalabash/steps/Filter.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Filter.scala
@@ -5,6 +5,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{XProcItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XProcXPathExpression, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.XdmNode
import scala.collection.mutable
@@ -26,7 +27,7 @@ class Filter extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
val select = stringBinding(XProcConstants._select)
val expr = new XProcXPathExpression(context, select)
diff --git a/src/main/scala/com/xmlcalabash/steps/Hash.scala b/src/main/scala/com/xmlcalabash/steps/Hash.scala
index 80cd099..936e295 100644
--- a/src/main/scala/com/xmlcalabash/steps/Hash.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Hash.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{HashUtils, TypeUtils}
+import com.xmlcalabash.util.{HashUtils, MinimalStaticContext, TypeUtils}
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.event.ReceiverOption
import net.sf.saxon.om.{AttributeInfo, AttributeMap}
@@ -36,7 +36,7 @@ class Hash() extends DefaultXmlStep with ProcessMatchingNodes {
this.metadata = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val value = stringBinding(_value).getBytes("UTF-8")
diff --git a/src/main/scala/com/xmlcalabash/steps/HttpRequest.scala b/src/main/scala/com/xmlcalabash/steps/HttpRequest.scala
index 09b4fa6..482bca6 100644
--- a/src/main/scala/com/xmlcalabash/steps/HttpRequest.scala
+++ b/src/main/scala/com/xmlcalabash/steps/HttpRequest.scala
@@ -7,7 +7,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XdmValueItemMessage
import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, NameValueBinding, StaticContext, XProcMetadata, XProcXPathExpression, XmlPortSpecification}
-import com.xmlcalabash.util.{InternetProtocolRequest, MediaType}
+import com.xmlcalabash.util.{InternetProtocolRequest, MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmMap, XdmValue}
import java.io.ByteArrayOutputStream
@@ -21,7 +21,7 @@ class HttpRequest() extends DefaultXmlStep {
private val sources = ListBuffer.empty[Any]
private val sourceMeta = ListBuffer.empty[XProcMetadata]
- private var context: StaticContext = _
+ private var context: MinimalStaticContext = _
private var href: URI = _
private var method = ""
private val headers = mutable.HashMap.empty[String,String]
@@ -133,7 +133,7 @@ class HttpRequest() extends DefaultXmlStep {
}
}
- override def run(ctx: StaticContext): Unit = {
+ override def run(ctx: MinimalStaticContext): Unit = {
super.run(ctx)
context = ctx
diff --git a/src/main/scala/com/xmlcalabash/steps/Insert.scala b/src/main/scala/com/xmlcalabash/steps/Insert.scala
index 784050b..c9f1f6b 100644
--- a/src/main/scala/com/xmlcalabash/steps/Insert.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Insert.scala
@@ -4,6 +4,7 @@ import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.om.AttributeMap
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmNode, XdmNodeKind}
@@ -43,7 +44,7 @@ class Insert() extends DefaultXmlStep with ProcessMatchingNodes {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
position = stringBinding(_position)
diff --git a/src/main/scala/com/xmlcalabash/steps/JavaScript.scala b/src/main/scala/com/xmlcalabash/steps/JavaScript.scala
index ea0f007..71742b8 100644
--- a/src/main/scala/com/xmlcalabash/steps/JavaScript.scala
+++ b/src/main/scala/com/xmlcalabash/steps/JavaScript.scala
@@ -4,7 +4,7 @@ import com.jafpl.runtime.RuntimeConfiguration
import com.jafpl.steps.PortCardinality
import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XMLCalabashRuntime, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.TypeUtils
+import com.xmlcalabash.util.{MinimalStaticContext, TypeUtils}
import javax.script.ScriptEngineManager
import net.sf.saxon.s9api.{QName, XdmNode, XdmValue}
@@ -22,11 +22,6 @@ class JavaScript extends DefaultXmlStep {
override def outputSpec: XmlPortSpecification = XmlPortSpecification.ANYRESULT
- override def initialize(config: RuntimeConfiguration): Unit = {
- super.initialize(config)
- typeUtils = new TypeUtils(config.asInstanceOf[XMLCalabashRuntime])
- }
-
override def receive(port: String, item: Any, metadata: XProcMetadata): Unit = {
if (port == "script") {
item match {
@@ -44,8 +39,9 @@ class JavaScript extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
+ typeUtils = new TypeUtils(config, context)
for (key <- parameters.keySet) {
engine.put(key.getLocalName, parameters(key))
diff --git a/src/main/scala/com/xmlcalabash/steps/LabelElements.scala b/src/main/scala/com/xmlcalabash/steps/LabelElements.scala
index 4df6187..23bb835 100644
--- a/src/main/scala/com/xmlcalabash/steps/LabelElements.scala
+++ b/src/main/scala/com/xmlcalabash/steps/LabelElements.scala
@@ -1,13 +1,13 @@
package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.messages.{XdmNodeItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime._
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.event.ReceiverOption
import net.sf.saxon.om.{AttributeInfo, AttributeMap, EmptyAttributeMap, FingerprintedQName, NamespaceMap}
-import net.sf.saxon.s9api.{Axis, QName, XdmAtomicValue, XdmNode, XdmValue}
+import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmNode}
import java.net.URI
import scala.jdk.CollectionConverters.IterableHasAsScala
@@ -18,7 +18,7 @@ class LabelElements() extends DefaultXmlStep with ProcessMatchingNodes {
private val _replace = new QName("replace")
private val p_index = new QName("p", XProcConstants.ns_p, "index")
- private var context: StaticContext = _
+ private var context: MinimalStaticContext = _
private var attribute: QName = _
private var label: String = _
private var labelNamespaceBindings = Map.empty[String, String]
@@ -40,11 +40,11 @@ class LabelElements() extends DefaultXmlStep with ProcessMatchingNodes {
override def receiveBinding(variable: NameValueBinding): Unit = {
super.receiveBinding(variable)
if (variable.name == _label) {
- labelNamespaceBindings = variable.context.nsBindings
+ labelNamespaceBindings = variable.context.inscopeNamespaces
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
attribute = qnameBinding(_attribute).get
diff --git a/src/main/scala/com/xmlcalabash/steps/Load.scala b/src/main/scala/com/xmlcalabash/steps/Load.scala
index f906fb1..6a4799e 100644
--- a/src/main/scala/com/xmlcalabash/steps/Load.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Load.scala
@@ -4,7 +4,7 @@ import java.net.URI
import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{BinaryNode, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmMap, XdmValue}
import scala.collection.mutable
@@ -14,7 +14,7 @@ class Load() extends DefaultXmlStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.ANY
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val href = uriBinding(XProcConstants._href);
diff --git a/src/main/scala/com/xmlcalabash/steps/Markdown.scala b/src/main/scala/com/xmlcalabash/steps/Markdown.scala
index c3274ea..5ba6c28 100644
--- a/src/main/scala/com/xmlcalabash/steps/Markdown.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Markdown.scala
@@ -4,7 +4,7 @@ import java.io.ByteArrayInputStream
import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmNode, XdmValue}
import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer
@@ -31,7 +31,7 @@ class Markdown() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val parser = Parser.builder.build
diff --git a/src/main/scala/com/xmlcalabash/steps/NamespaceDelete.scala b/src/main/scala/com/xmlcalabash/steps/NamespaceDelete.scala
index c3591b6..d4b9a4c 100644
--- a/src/main/scala/com/xmlcalabash/steps/NamespaceDelete.scala
+++ b/src/main/scala/com/xmlcalabash/steps/NamespaceDelete.scala
@@ -1,11 +1,10 @@
package com.xmlcalabash.steps
import java.net.URI
-
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime._
-import com.xmlcalabash.util.S9Api
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api}
import net.sf.saxon.s9api.{Axis, QName, XdmNode, XdmValue}
import scala.collection.mutable
@@ -23,13 +22,13 @@ class NamespaceDelete() extends DefaultXmlStep {
metadata = meta
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
namespaces = mutable.HashSet.empty[String]
val prefixes = bindings(XProcConstants._prefixes).value.getUnderlyingValue.getStringValue.split("\\s+")
for (prefix <- prefixes) {
- val uri = context.nsBindings.get(prefix)
+ val uri = context.inscopeNamespaces.get(prefix)
if (uri.isDefined) {
namespaces.add(uri.get)
} else {
diff --git a/src/main/scala/com/xmlcalabash/steps/NamespaceRename.scala b/src/main/scala/com/xmlcalabash/steps/NamespaceRename.scala
index 2bb123c..fafd282 100644
--- a/src/main/scala/com/xmlcalabash/steps/NamespaceRename.scala
+++ b/src/main/scala/com/xmlcalabash/steps/NamespaceRename.scala
@@ -2,6 +2,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.`type`.{BuiltInAtomicType, Untyped}
import net.sf.saxon.event.ReceiverOption
import net.sf.saxon.om.{AttributeInfo, AttributeMap, FingerprintedQName, NameOfNode, NamespaceMap, NodeInfo}
@@ -29,7 +30,7 @@ class NamespaceRename() extends DefaultXmlStep with ProcessMatchingNodes {
metadata = meta
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
from = stringBinding(_from)
diff --git a/src/main/scala/com/xmlcalabash/steps/OptionValue.scala b/src/main/scala/com/xmlcalabash/steps/OptionValue.scala
index 2bd5eb8..7da0cec 100644
--- a/src/main/scala/com/xmlcalabash/steps/OptionValue.scala
+++ b/src/main/scala/com/xmlcalabash/steps/OptionValue.scala
@@ -2,7 +2,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmNode, XdmValue}
class OptionValue extends DefaultXmlStep {
@@ -15,7 +15,7 @@ class OptionValue extends DefaultXmlStep {
this.value = Some(variable.value)
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
val builder = new SaxonTreeBuilder(config)
diff --git a/src/main/scala/com/xmlcalabash/steps/Pack.scala b/src/main/scala/com/xmlcalabash/steps/Pack.scala
index 76f6bb3..f7aace5 100644
--- a/src/main/scala/com/xmlcalabash/steps/Pack.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Pack.scala
@@ -3,6 +3,7 @@ package com.xmlcalabash.steps
import com.jafpl.steps.PortCardinality
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmNode}
import scala.collection.mutable.ListBuffer
@@ -28,7 +29,7 @@ class Pack() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val wrapName = qnameBinding(_wrapper).get
diff --git a/src/main/scala/com/xmlcalabash/steps/Parameters.scala b/src/main/scala/com/xmlcalabash/steps/Parameters.scala
index 6557ac5..d927f35 100644
--- a/src/main/scala/com/xmlcalabash/steps/Parameters.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Parameters.scala
@@ -2,7 +2,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, TypeUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
import net.sf.saxon.s9api.{QName, XdmValue}
@@ -19,7 +19,7 @@ class Parameters() extends DefaultXmlStep {
}
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
val builder = new SaxonTreeBuilder(config)
diff --git a/src/main/scala/com/xmlcalabash/steps/Producer.scala b/src/main/scala/com/xmlcalabash/steps/Producer.scala
index aefdb46..b825dba 100644
--- a/src/main/scala/com/xmlcalabash/steps/Producer.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Producer.scala
@@ -1,7 +1,7 @@
package com.xmlcalabash.steps
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import scala.collection.mutable
@@ -19,7 +19,7 @@ class Producer() extends DefaultXmlStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.ANYRESULTSEQ
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
for (item <- items) {
diff --git a/src/main/scala/com/xmlcalabash/steps/PropertyExtract.scala b/src/main/scala/com/xmlcalabash/steps/PropertyExtract.scala
index ae6b65e..1d94a46 100644
--- a/src/main/scala/com/xmlcalabash/steps/PropertyExtract.scala
+++ b/src/main/scala/com/xmlcalabash/steps/PropertyExtract.scala
@@ -4,7 +4,7 @@ import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, TypeUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap, NamespaceMap}
import net.sf.saxon.s9api.{XdmAtomicValue, XdmNode}
@@ -22,7 +22,7 @@ class PropertyExtract extends DefaultXmlStep {
meta = Some(metadata)
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
consumer.receive("result", doc.get, meta.get)
diff --git a/src/main/scala/com/xmlcalabash/steps/PropertyMerge.scala b/src/main/scala/com/xmlcalabash/steps/PropertyMerge.scala
index 18dfcfa..1b9a991 100644
--- a/src/main/scala/com/xmlcalabash/steps/PropertyMerge.scala
+++ b/src/main/scala/com/xmlcalabash/steps/PropertyMerge.scala
@@ -3,9 +3,9 @@ package com.xmlcalabash.steps
import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
-import com.xmlcalabash.model.xml.XMLContext
+import com.xmlcalabash.model.xxml.{XMLStaticContext, XStaticContext}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.S9Api
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api}
import net.sf.saxon.s9api.{Axis, QName, XdmAtomicValue, XdmNode, XdmNodeKind, XdmValue}
import scala.collection.mutable
@@ -32,7 +32,7 @@ class PropertyMerge extends DefaultXmlStep {
}
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
propDoc.get match {
@@ -62,7 +62,7 @@ class PropertyMerge extends DefaultXmlStep {
consumer.receive("result", sourceDoc.get, newmeta)
}
- private def extractProperties(context: StaticContext, node: XdmNode): Map[QName,XdmValue] = {
+ private def extractProperties(context: MinimalStaticContext, node: XdmNode): Map[QName,XdmValue] = {
val prop = mutable.HashMap.empty[QName,XdmValue]
val piter = node.axisIterator(Axis.CHILD)
@@ -77,13 +77,8 @@ class PropertyMerge extends DefaultXmlStep {
val vtypestr = Option(pnode.getAttributeValue(XProcConstants.xsi_type))
val vtype = if (vtypestr.isDefined) {
- val ns = S9Api.inScopeNamespaces(pnode)
- val scontext = new XMLContext(config.config)
- scontext.nsBindings = ns
- if (location.isDefined) {
- scontext.location = location.get
- }
- Some(ValueParser.parseQName(vtypestr.get, scontext))
+ val scontext = new XStaticContext(location, S9Api.inScopeNamespaces(pnode))
+ Some(scontext.parseQName(vtypestr.get))
} else {
None
}
@@ -139,12 +134,8 @@ class PropertyMerge extends DefaultXmlStep {
case XProcConstants.xs_anyURI =>
prop.put(name, new XdmAtomicValue(strvalue))
case XProcConstants.xs_QName =>
- val scontext = new XMLContext(config.config)
- scontext.nsBindings = S9Api.inScopeNamespaces(node)
- if (location.isDefined) {
- scontext.location = location.get
- }
- prop.put(name, new XdmAtomicValue(ValueParser.parseQName(strvalue,scontext)))
+ val scontext = new XStaticContext(location, S9Api.inScopeNamespaces(node))
+ prop.put(name, new XdmAtomicValue(scontext.parseQName(strvalue)))
case XProcConstants.xs_notation =>
prop.put(name, new XdmAtomicValue(strvalue))
case XProcConstants.xs_decimal =>
diff --git a/src/main/scala/com/xmlcalabash/steps/Rename.scala b/src/main/scala/com/xmlcalabash/steps/Rename.scala
index 75ec524..9a22e8b 100644
--- a/src/main/scala/com/xmlcalabash/steps/Rename.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Rename.scala
@@ -4,7 +4,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{XdmNodeItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime._
-import com.xmlcalabash.util.TypeUtils
+import com.xmlcalabash.util.{MinimalStaticContext, TypeUtils}
import net.sf.saxon.om.{AttributeInfo, AttributeMap, EmptyAttributeMap, FingerprintedQName}
import net.sf.saxon.s9api.{Axis, QName, XdmAtomicValue, XdmNode}
import net.sf.saxon.value.QNameValue
@@ -14,7 +14,7 @@ import scala.jdk.CollectionConverters.ListHasAsScala
class Rename() extends DefaultXmlStep with ProcessMatchingNodes {
private val _new_name = new QName("new-name")
- private var context: StaticContext = _
+ private var context: MinimalStaticContext = _
private var newName: QName = _
private var pattern: String = _
private var matcher: ProcessMatch = _
@@ -29,7 +29,7 @@ class Rename() extends DefaultXmlStep with ProcessMatchingNodes {
metadata = meta
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
newName = qnameBinding(_new_name).get
diff --git a/src/main/scala/com/xmlcalabash/steps/Replace.scala b/src/main/scala/com/xmlcalabash/steps/Replace.scala
index f44c746..54c4c77 100644
--- a/src/main/scala/com/xmlcalabash/steps/Replace.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Replace.scala
@@ -4,7 +4,7 @@ import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.S9Api
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api}
import net.sf.saxon.om.AttributeMap
import net.sf.saxon.s9api.{Axis, QName, XdmAtomicValue, XdmNode, XdmNodeKind}
@@ -34,7 +34,7 @@ class Replace() extends DefaultXmlStep with ProcessMatchingNodes {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
pattern = stringBinding(XProcConstants._match)
diff --git a/src/main/scala/com/xmlcalabash/steps/SetAttributes.scala b/src/main/scala/com/xmlcalabash/steps/SetAttributes.scala
index c9da1eb..448dfed 100644
--- a/src/main/scala/com/xmlcalabash/steps/SetAttributes.scala
+++ b/src/main/scala/com/xmlcalabash/steps/SetAttributes.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{S9Api, TypeUtils}
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api, TypeUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap, NamespaceMap}
import net.sf.saxon.s9api.{Axis, QName, XdmNode}
@@ -31,7 +31,7 @@ class SetAttributes() extends DefaultXmlStep with ProcessMatchingNodes {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
pattern = stringBinding(XProcConstants._match)
diff --git a/src/main/scala/com/xmlcalabash/steps/SetProperties.scala b/src/main/scala/com/xmlcalabash/steps/SetProperties.scala
index be8ebf8..c2aca25 100644
--- a/src/main/scala/com/xmlcalabash/steps/SetProperties.scala
+++ b/src/main/scala/com/xmlcalabash/steps/SetProperties.scala
@@ -4,8 +4,10 @@ import java.net.URI
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import com.xmlcalabash.util.TypeUtils.castAsXml
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmMap, XdmNode, XdmValue}
+import org.apache.xerces.util.URI.MalformedURIException
import scala.collection.mutable
import scala.jdk.CollectionConverters.MapHasAsScala
@@ -28,7 +30,7 @@ class SetProperties() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val properties = mapBinding(XProcConstants._properties)
@@ -78,12 +80,19 @@ class SetProperties() extends DefaultXmlStep {
var result = source
// The base URI is special; make sure it's not some bogus string
if (newprops.contains(XProcConstants._base_uri)) {
- val uri = if (context.baseURI.isDefined) {
- resolveURI(context.baseURI.get, newprops(XProcConstants._base_uri).toString)
- } else {
- new URI(newprops(XProcConstants._base_uri).toString)
+ val ustr = newprops(XProcConstants._base_uri).toString
+ try {
+ val uri = new URI(ustr)
+ if (!uri.isAbsolute) {
+ throw XProcException.xdInvalidURI(ustr, location)
+ }
+ newprops.put(XProcConstants._base_uri, new XdmAtomicValue(uri))
+ } catch {
+ case _: MalformedURIException =>
+ throw XProcException.xdInvalidURI(ustr, location)
+ case ex: Exception =>
+ throw ex
}
- newprops.put(XProcConstants._base_uri, new XdmAtomicValue(uri))
} else {
if (metadata.properties.contains(XProcConstants._base_uri)) {
// The base URI property has been removed...
diff --git a/src/main/scala/com/xmlcalabash/steps/SplitSequence.scala b/src/main/scala/com/xmlcalabash/steps/SplitSequence.scala
index 12e4c43..703bb9a 100644
--- a/src/main/scala/com/xmlcalabash/steps/SplitSequence.scala
+++ b/src/main/scala/com/xmlcalabash/steps/SplitSequence.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.jafpl.steps.PortCardinality
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{BinaryNode, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.S9Api
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api}
import net.sf.saxon.expr.LastPositionFinder
import net.sf.saxon.om.{FocusIterator, Item, NodeInfo}
import net.sf.saxon.s9api.{QName, XdmItem, XdmMap, XdmNode}
@@ -31,7 +31,7 @@ class SplitSequence() extends DefaultXmlStep {
metas += metadata
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
val initialOnly = bindings(_initial_only).value.getUnderlyingValue.effectiveBooleanValue()
@@ -49,7 +49,7 @@ class SplitSequence() extends DefaultXmlStep {
if (staticContext.baseURI.isDefined) {
compiler.setBaseURI(staticContext.baseURI.get)
}
- for ((pfx, uri) <- bindings(XProcConstants._test).context.nsBindings) {
+ for ((pfx, uri) <- bindings(XProcConstants._test).context.inscopeNamespaces) {
compiler.declareNamespace(pfx, uri)
}
val exec = compiler.compile(testExpr)
diff --git a/src/main/scala/com/xmlcalabash/steps/Store.scala b/src/main/scala/com/xmlcalabash/steps/Store.scala
index ea4e667..859f696 100644
--- a/src/main/scala/com/xmlcalabash/steps/Store.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Store.scala
@@ -2,12 +2,11 @@ package com.xmlcalabash.steps
import java.io.{FileOutputStream, InputStream}
import java.net.URI
-
import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api}
import net.sf.saxon.s9api.{Serializer, XdmNode}
class Store extends DefaultXmlStep {
@@ -26,7 +25,7 @@ class Store extends DefaultXmlStep {
smeta = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val href = if (context.baseURI.isDefined) {
diff --git a/src/main/scala/com/xmlcalabash/steps/StringReplace.scala b/src/main/scala/com/xmlcalabash/steps/StringReplace.scala
index 8306a62..eccc9a1 100644
--- a/src/main/scala/com/xmlcalabash/steps/StringReplace.scala
+++ b/src/main/scala/com/xmlcalabash/steps/StringReplace.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.jafpl.steps.PortCardinality
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{S9Api, TypeUtils}
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api, TypeUtils}
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.event.ReceiverOption
import net.sf.saxon.om.{AttributeInfo, AttributeMap, EmptyAttributeMap}
@@ -18,7 +18,7 @@ class StringReplace() extends DefaultXmlStep with ProcessMatchingNodes {
private var pattern: String = _
private var matcher: ProcessMatch = _
private var replace: String = _
- private var replContext: StaticContext = _
+ private var replContext: MinimalStaticContext = _
override def inputSpec: XmlPortSpecification = new XmlPortSpecification(
Map("source"->PortCardinality.EXACTLY_ONE),
@@ -33,7 +33,7 @@ class StringReplace() extends DefaultXmlStep with ProcessMatchingNodes {
source_metadata = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
pattern = stringBinding(XProcConstants._match)
@@ -52,7 +52,7 @@ class StringReplace() extends DefaultXmlStep with ProcessMatchingNodes {
if (replContext.baseURI.isDefined) {
compiler.setBaseURI(replContext.baseURI.get)
}
- for ((pfx, uri) <- replContext.nsBindings) {
+ for ((pfx, uri) <- replContext.inscopeNamespaces) {
compiler.declareNamespace(pfx, uri)
}
val expr = compiler.compile(replace)
diff --git a/src/main/scala/com/xmlcalabash/steps/Unarchive.scala b/src/main/scala/com/xmlcalabash/steps/Unarchive.scala
index 5d86416..4e17cda 100644
--- a/src/main/scala/com/xmlcalabash/steps/Unarchive.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Unarchive.scala
@@ -7,7 +7,7 @@ import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, URIUtils, Urify}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, URIUtils, Urify}
import net.sf.saxon.s9api.{QName, XdmArray, XdmValue}
import org.apache.commons.compress.archivers.zip.ZipFile
import org.apache.commons.compress.utils.IOUtils
@@ -44,7 +44,7 @@ class Unarchive extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
format = if (qnameBinding(XProcConstants._format).isDefined) {
@@ -98,7 +98,7 @@ class Unarchive extends DefaultXmlStep {
}
}
- private def unzip(context: StaticContext): Unit = {
+ private def unzip(context: MinimalStaticContext): Unit = {
// ZIP requires random access: https://commons.apache.org/proper/commons-compress/zip.html
source match {
case bn: BinaryNode =>
@@ -108,7 +108,7 @@ class Unarchive extends DefaultXmlStep {
}
}
- private def unzipFile(context: StaticContext, zfile: File): Unit = {
+ private def unzipFile(context: MinimalStaticContext, zfile: File): Unit = {
val zipIn = new ZipFile(zfile)
val enum = zipIn.getEntries
while (enum.hasMoreElements) {
diff --git a/src/main/scala/com/xmlcalabash/steps/Uncompress.scala b/src/main/scala/com/xmlcalabash/steps/Uncompress.scala
index 5de9e0a..ecf8d2b 100644
--- a/src/main/scala/com/xmlcalabash/steps/Uncompress.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Uncompress.scala
@@ -6,7 +6,7 @@ import com.xmlcalabash.config.{DocumentRequest, DocumentResponse}
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmValue}
import scala.collection.mutable
@@ -36,7 +36,7 @@ class Uncompress extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
format = qnameBinding(XProcConstants._format)
diff --git a/src/main/scala/com/xmlcalabash/steps/UnescapeMarkup.scala b/src/main/scala/com/xmlcalabash/steps/UnescapeMarkup.scala
index cb77982..3e5a13f 100644
--- a/src/main/scala/com/xmlcalabash/steps/UnescapeMarkup.scala
+++ b/src/main/scala/com/xmlcalabash/steps/UnescapeMarkup.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api}
import net.sf.saxon.om.FingerprintedQName
import net.sf.saxon.s9api._
@@ -24,7 +24,7 @@ class UnescapeMarkup() extends DefaultXmlStep {
metadata = meta
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
if (definedBinding(XProcConstants._namespace)) {
diff --git a/src/main/scala/com/xmlcalabash/steps/Unwrap.scala b/src/main/scala/com/xmlcalabash/steps/Unwrap.scala
index 855c559..3ccac36 100644
--- a/src/main/scala/com/xmlcalabash/steps/Unwrap.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Unwrap.scala
@@ -3,6 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.om.AttributeMap
import net.sf.saxon.s9api.{Axis, XdmNode, XdmNodeKind}
@@ -20,7 +21,7 @@ class Unwrap() extends DefaultXmlStep with ProcessMatchingNodes {
source_metadata = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
pattern = stringBinding(XProcConstants._match)
diff --git a/src/main/scala/com/xmlcalabash/steps/Uuid.scala b/src/main/scala/com/xmlcalabash/steps/Uuid.scala
index 0afc6ac..cfc9c1d 100644
--- a/src/main/scala/com/xmlcalabash/steps/Uuid.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Uuid.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{HashUtils, TypeUtils}
+import com.xmlcalabash.util.{HashUtils, MinimalStaticContext, TypeUtils}
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.event.ReceiverOption
import net.sf.saxon.om.{AttributeInfo, AttributeMap, EmptyAttributeMap}
@@ -26,7 +26,7 @@ class Uuid() extends DefaultXmlStep with ProcessMatchingNodes {
this.metadata = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val version = integerBinding(XProcConstants._version)
diff --git a/src/main/scala/com/xmlcalabash/steps/ValidateWithRNG.scala b/src/main/scala/com/xmlcalabash/steps/ValidateWithRNG.scala
index 1ce7d36..75e1a2d 100644
--- a/src/main/scala/com/xmlcalabash/steps/ValidateWithRNG.scala
+++ b/src/main/scala/com/xmlcalabash/steps/ValidateWithRNG.scala
@@ -1,7 +1,6 @@
package com.xmlcalabash.steps
import java.io.{IOException, StringReader}
-
import com.jafpl.graph.Location
import com.jafpl.steps.PortCardinality
import com.thaiopensource.util.PropertyMapBuilder
@@ -12,7 +11,7 @@ import com.thaiopensource.validate.{SchemaReader, ValidateProperty, ValidationDr
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.runtime.{StaticContext, XProcLocation, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.util.xc.Errors
-import com.xmlcalabash.util.{CachingErrorListener, S9Api}
+import com.xmlcalabash.util.{CachingErrorListener, MinimalStaticContext, S9Api}
import net.sf.saxon.s9api.{QName, XdmNode}
import org.xml.sax.InputSource
@@ -60,7 +59,7 @@ class ValidateWithRNG() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
if (definedBinding(_dtd_id_idref_warnings)) {
@@ -136,7 +135,7 @@ class ValidateWithRNG() extends DefaultXmlStep {
}
}
} else {
- throw XProcException.xcNotSchemaValidRelaxNG(source.getBaseURI.toASCIIString, "Error loading schema", location)
+ throw XProcException.xcNotRelaxNG(source.getBaseURI.toASCIIString, "Error loading schema", location)
}
consumer.receive("report", report.endErrors() , XProcMetadata.XML)
diff --git a/src/main/scala/com/xmlcalabash/steps/ValidateWithSCH.scala b/src/main/scala/com/xmlcalabash/steps/ValidateWithSCH.scala
index acd46d1..260546d 100644
--- a/src/main/scala/com/xmlcalabash/steps/ValidateWithSCH.scala
+++ b/src/main/scala/com/xmlcalabash/steps/ValidateWithSCH.scala
@@ -7,7 +7,7 @@ import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{StaticContext, XProcLocation, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api, SchematronImpl}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, SchematronImpl}
import javax.xml.transform.sax.SAXSource
import javax.xml.transform.{Source, URIResolver}
@@ -54,7 +54,7 @@ class ValidateWithSCH() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
assert_valid = booleanBinding(XProcConstants._assert_valid).getOrElse(assert_valid)
diff --git a/src/main/scala/com/xmlcalabash/steps/ValidateWithXSD.scala b/src/main/scala/com/xmlcalabash/steps/ValidateWithXSD.scala
index 5480905..049088f 100644
--- a/src/main/scala/com/xmlcalabash/steps/ValidateWithXSD.scala
+++ b/src/main/scala/com/xmlcalabash/steps/ValidateWithXSD.scala
@@ -1,14 +1,14 @@
package com.xmlcalabash.steps
import java.net.URI
-
import com.jafpl.steps.PortCardinality
import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.util.xc.Errors
-import com.xmlcalabash.util.{CachingErrorListener, MediaType, S9Api}
+import com.xmlcalabash.util.{CachingErrorListener, MediaType, MinimalStaticContext, S9Api}
+
import javax.xml.transform.sax.SAXSource
import net.sf.saxon.Controller
import net.sf.saxon.`type`.ValidationException
@@ -60,7 +60,7 @@ class ValidateWithXSD() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val manager = Option(config.processor.getSchemaManager)
diff --git a/src/main/scala/com/xmlcalabash/steps/Wrap.scala b/src/main/scala/com/xmlcalabash/steps/Wrap.scala
index 181744d..c74b780 100644
--- a/src/main/scala/com/xmlcalabash/steps/Wrap.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Wrap.scala
@@ -4,7 +4,7 @@ import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{ProcessMatch, ProcessMatchingNodes, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api}
import net.sf.saxon.om.AttributeMap
import net.sf.saxon.s9api.{Axis, QName, XdmItem, XdmNode, XdmNodeKind, XdmValue}
@@ -19,10 +19,10 @@ class Wrap() extends DefaultXmlStep with ProcessMatchingNodes {
private var pattern: String = _
private var matcher: ProcessMatch = _
private var groupAdjacent = Option.empty[String]
- private var groupAdjacentContext = Option.empty[StaticContext]
+ private var groupAdjacentContext = Option.empty[MinimalStaticContext]
private var wrapper: QName = _
private val inGroup = mutable.Stack[Boolean]()
- private var staticContext: StaticContext = _
+ private var staticContext: MinimalStaticContext = _
override def inputSpec: XmlPortSpecification = new XmlPortSpecification(
Map("source"->PortCardinality.EXACTLY_ONE),
@@ -35,7 +35,7 @@ class Wrap() extends DefaultXmlStep with ProcessMatchingNodes {
source_metadata = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
staticContext = context
@@ -161,7 +161,7 @@ class Wrap() extends DefaultXmlStep with ProcessMatchingNodes {
if (staticContext.baseURI.isDefined) {
xcomp.setBaseURI(staticContext.baseURI.get)
}
- for ((pfx,uri) <- groupAdjacentContext.get.nsBindings) {
+ for ((pfx,uri) <- groupAdjacentContext.get.inscopeNamespaces) {
xcomp.declareNamespace(pfx, uri)
}
diff --git a/src/main/scala/com/xmlcalabash/steps/WrapSequence.scala b/src/main/scala/com/xmlcalabash/steps/WrapSequence.scala
index be4ebbd..444540c 100644
--- a/src/main/scala/com/xmlcalabash/steps/WrapSequence.scala
+++ b/src/main/scala/com/xmlcalabash/steps/WrapSequence.scala
@@ -2,13 +2,11 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.SaxonTreeBuilder
-import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.S9Api
+import com.xmlcalabash.runtime.{XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.{MinimalStaticContext, S9Api}
import net.sf.saxon.expr.LastPositionFinder
-import net.sf.saxon.om.{Item, NodeInfo}
-import net.sf.saxon.s9api.{QName, XdmItem, XdmNode, XdmValue}
+import net.sf.saxon.s9api.{QName, XdmNode, XdmValue}
import net.sf.saxon.tree.iter.ManualIterator
-import net.sf.saxon.value.SequenceExtent
import scala.collection.mutable.ListBuffer
@@ -18,7 +16,7 @@ class WrapSequence extends DefaultXmlStep {
private val inputs = ListBuffer.empty[XdmNode]
private var groupAdjacent = Option.empty[String]
- private var groupAdjacentContext = Option.empty[StaticContext]
+ private var groupAdjacentContext = Option.empty[MinimalStaticContext]
private var wrapper: QName = _
private var index = 1
@@ -34,7 +32,7 @@ class WrapSequence extends DefaultXmlStep {
}
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
wrapper = qnameBinding(_wrapper).get
@@ -50,7 +48,7 @@ class WrapSequence extends DefaultXmlStep {
}
}
- def runSimple(staticContext: StaticContext): Unit = {
+ def runSimple(staticContext: MinimalStaticContext): Unit = {
val builder = new SaxonTreeBuilder(config)
builder.startDocument(staticContext.baseURI)
@@ -64,7 +62,7 @@ class WrapSequence extends DefaultXmlStep {
consumer.receive("result", builder.result, XProcMetadata.XML)
}
- def runAdjacent(staticContext: StaticContext): Unit = {
+ def runAdjacent(staticContext: MinimalStaticContext): Unit = {
var inGroup = false
var lastValue: XdmValue = null
var builder: SaxonTreeBuilder = null
@@ -111,7 +109,7 @@ class WrapSequence extends DefaultXmlStep {
private def adjacentValue(node: XdmNode): XdmValue = {
val compiler = config.processor.newXPathCompiler()
compiler.setBaseURI(groupAdjacentContext.get.baseURI.get)
- for ((pfx, uri) <- bindings(_group_adjacent).context.nsBindings) {
+ for ((pfx, uri) <- bindings(_group_adjacent).context.inscopeNamespaces) {
compiler.declareNamespace(pfx, uri)
}
val exec = compiler.compile(groupAdjacent.get)
diff --git a/src/main/scala/com/xmlcalabash/steps/WwwFormUrlDecode.scala b/src/main/scala/com/xmlcalabash/steps/WwwFormUrlDecode.scala
index 3eefdac..5ec4a1d 100644
--- a/src/main/scala/com/xmlcalabash/steps/WwwFormUrlDecode.scala
+++ b/src/main/scala/com/xmlcalabash/steps/WwwFormUrlDecode.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{XdmAtomicValue, XdmMap, XdmValue}
import java.net.URLDecoder
@@ -15,7 +15,7 @@ class WwwFormUrlDecode() extends DefaultXmlStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.JSONRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val value = stringBinding(XProcConstants._value).trim
diff --git a/src/main/scala/com/xmlcalabash/steps/WwwFormUrlEncode.scala b/src/main/scala/com/xmlcalabash/steps/WwwFormUrlEncode.scala
index d3d5e35..a0e8fd1 100644
--- a/src/main/scala/com/xmlcalabash/steps/WwwFormUrlEncode.scala
+++ b/src/main/scala/com/xmlcalabash/steps/WwwFormUrlEncode.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{XdmAtomicValue, XdmMap, XdmValue}
import java.net.URLDecoder
@@ -16,7 +16,7 @@ class WwwFormUrlEncode() extends DefaultXmlStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.TEXTRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val parameters = mapBinding(XProcConstants._parameters)
diff --git a/src/main/scala/com/xmlcalabash/steps/XInclude.scala b/src/main/scala/com/xmlcalabash/steps/XInclude.scala
index a651bbc..938afff 100644
--- a/src/main/scala/com/xmlcalabash/steps/XInclude.scala
+++ b/src/main/scala/com/xmlcalabash/steps/XInclude.scala
@@ -4,6 +4,7 @@ import com.nwalsh.sinclude.exceptions.{MalformedXPointerSchemeException, Unknown
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmNode}
import net.sf.saxon.trans.XPathException
@@ -17,7 +18,7 @@ class XInclude() extends DefaultXmlStep {
private var source: XdmNode = _
private var smeta: XProcMetadata = _
- private var staticContext: StaticContext = _
+ private var staticContext: MinimalStaticContext = _
override def inputSpec: XmlPortSpecification = XmlPortSpecification.MARKUPSOURCE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
@@ -27,7 +28,7 @@ class XInclude() extends DefaultXmlStep {
smeta = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
staticContext = context
diff --git a/src/main/scala/com/xmlcalabash/steps/XQuery.scala b/src/main/scala/com/xmlcalabash/steps/XQuery.scala
index 6eba100..98eeee4 100644
--- a/src/main/scala/com/xmlcalabash/steps/XQuery.scala
+++ b/src/main/scala/com/xmlcalabash/steps/XQuery.scala
@@ -6,7 +6,7 @@ import com.jafpl.steps.PortCardinality
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, S9Api, XProcCollectionFinder}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, XProcCollectionFinder}
import javax.xml.transform.{ErrorListener, TransformerException}
import net.sf.saxon.event.{PipelineConfiguration, Receiver}
@@ -58,7 +58,7 @@ class XQuery extends DefaultXmlStep {
}
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
val pmap = mapBinding(XProcConstants._parameters)
diff --git a/src/main/scala/com/xmlcalabash/steps/Xslt.scala b/src/main/scala/com/xmlcalabash/steps/Xslt.scala
index 79aa58e..d7eb348 100644
--- a/src/main/scala/com/xmlcalabash/steps/Xslt.scala
+++ b/src/main/scala/com/xmlcalabash/steps/Xslt.scala
@@ -5,7 +5,7 @@ import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{MediaType, PipelineEnvironmentOptionString, S9Api, URIUtils, Urify, ValueUtils, XProcCollectionFinder, Xslt10ClassLoader, Xslt10Source}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, PipelineEnvironmentOptionString, S9Api, URIUtils, Urify, ValueUtils, XProcCollectionFinder, Xslt10ClassLoader, Xslt10Source}
import net.sf.saxon.Configuration
import net.sf.saxon.event.{PipelineConfiguration, Receiver}
import net.sf.saxon.expr.XPathContext
@@ -36,7 +36,7 @@ class Xslt extends DefaultXmlStep {
private val inputSequence = ListBuffer.empty[XdmItem]
private val inputMetadata = ListBuffer.empty[XProcMetadata]
- private var staticContext: StaticContext = _
+ private var staticContext: MinimalStaticContext = _
private var globalContextItem = Option.empty[XdmValue]
private var initialMode = Option.empty[QName]
private var templateName = Option.empty[QName]
@@ -82,7 +82,7 @@ class Xslt extends DefaultXmlStep {
}
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
this.staticContext = staticContext
@@ -98,9 +98,6 @@ class Xslt extends DefaultXmlStep {
if (bindings.contains(_global_context_item)) {
globalContextItem = Some(bindings(_global_context_item).value)
- if (globalContextItem.get.size() == 0) {
- globalContextItem = None
- }
}
initialMode = qnameBinding(_initial_mode)
@@ -287,7 +284,9 @@ class Xslt extends DefaultXmlStep {
// FIXME: transformer.getUnderlyingController().setUnparsedTextURIResolver(unparsedTextURIResolver)
if (globalContextItem.isDefined) {
- transformer.setGlobalContextItem(globalContextItem.get.asInstanceOf[XdmItem])
+ if (globalContextItem.get.size() > 0) {
+ transformer.setGlobalContextItem(globalContextItem.get.asInstanceOf[XdmItem])
+ }
}
try {
diff --git a/src/main/scala/com/xmlcalabash/steps/file/DirectoryList.scala b/src/main/scala/com/xmlcalabash/steps/file/DirectoryList.scala
index 1e64902..b3fee4c 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/DirectoryList.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/DirectoryList.scala
@@ -4,7 +4,7 @@ import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
import com.xmlcalabash.util.stores.{DataInfo, FallbackDataStore, FileDataStore}
-import com.xmlcalabash.util.{MediaType, TypeUtils, URIUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils, URIUtils}
import net.sf.saxon.om.{AttributeMap, SingletonAttributeMap}
import net.sf.saxon.s9api.{QName, XdmAtomicValue}
@@ -21,7 +21,7 @@ class DirectoryList() extends DefaultXmlStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val builder = new SaxonTreeBuilder(config)
diff --git a/src/main/scala/com/xmlcalabash/steps/file/FileCopy.scala b/src/main/scala/com/xmlcalabash/steps/file/FileCopy.scala
index d7ddfee..a809db8 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/FileCopy.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/FileCopy.scala
@@ -4,7 +4,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.util.stores.{DataInfo, DataReader, DataWriter}
-import com.xmlcalabash.util.{InternetProtocolRequest, MediaType}
+import com.xmlcalabash.util.{InternetProtocolRequest, MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmAtomicValue}
import java.io.{InputStream, OutputStream}
@@ -23,7 +23,7 @@ class FileCopy() extends FileStep {
private var copyLinks = false
private var copyAttributes = false
- private var staticContext: StaticContext = _
+ private var staticContext: MinimalStaticContext = _
private var href: URI = _
private var target: URI = _
private var exception = Option.empty[Exception]
@@ -33,7 +33,7 @@ class FileCopy() extends FileStep {
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
staticContext = context
diff --git a/src/main/scala/com/xmlcalabash/steps/file/FileCreateTempFile.scala b/src/main/scala/com/xmlcalabash/steps/file/FileCreateTempFile.scala
index 218a1d2..4f37be9 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/FileCreateTempFile.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/FileCreateTempFile.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps.file
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import java.net.URI
import java.nio.file.{Files, Path, Paths, StandardCopyOption}
@@ -15,7 +15,7 @@ class FileCreateTempFile() extends FileStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val href = uriBinding(XProcConstants._href)
diff --git a/src/main/scala/com/xmlcalabash/steps/file/FileDelete.scala b/src/main/scala/com/xmlcalabash/steps/file/FileDelete.scala
index 8d680a8..49c5a90 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/FileDelete.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/FileDelete.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps.file
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{InternetProtocolRequest, MediaType}
+import com.xmlcalabash.util.{InternetProtocolRequest, MediaType, MinimalStaticContext}
import java.io.IOException
import java.net.URI
@@ -16,14 +16,14 @@ class FileDelete() extends FileStep {
private var recursive = false
private var failOnError = true
- private var staticContext: StaticContext = _
+ private var staticContext: MinimalStaticContext = _
private var exception = Option.empty[Exception]
private val failures = ListBuffer.empty[URI]
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
staticContext = context
diff --git a/src/main/scala/com/xmlcalabash/steps/file/FileInfo.scala b/src/main/scala/com/xmlcalabash/steps/file/FileInfo.scala
index 84c0d79..f550071 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/FileInfo.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/FileInfo.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps.file
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.{InternetProtocolRequest, MediaType, TypeUtils, URIUtils}
+import com.xmlcalabash.util.{InternetProtocolRequest, MediaType, MinimalStaticContext, TypeUtils, URIUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
import net.sf.saxon.s9api.QName
@@ -17,13 +17,13 @@ class FileInfo() extends FileStep {
private var href: URI = _
private var failOnError = true
- private var staticContext: StaticContext = _
+ private var staticContext: MinimalStaticContext = _
private var builder: SaxonTreeBuilder = _
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
staticContext = context
diff --git a/src/main/scala/com/xmlcalabash/steps/file/FileMkdir.scala b/src/main/scala/com/xmlcalabash/steps/file/FileMkdir.scala
index ad4f4ce..04afc20 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/FileMkdir.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/FileMkdir.scala
@@ -4,7 +4,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.{InternetProtocolRequest, MediaType, URIUtils}
+import com.xmlcalabash.util.{InternetProtocolRequest, MediaType, MinimalStaticContext, URIUtils}
import java.io.IOException
import java.net.URI
@@ -18,7 +18,7 @@ class FileMkdir() extends FileStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
href = uriBinding(XProcConstants._href).get
diff --git a/src/main/scala/com/xmlcalabash/steps/file/FileMove.scala b/src/main/scala/com/xmlcalabash/steps/file/FileMove.scala
index c7ade19..cea3636 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/FileMove.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/FileMove.scala
@@ -4,7 +4,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.QName
import java.net.URI
@@ -16,7 +16,7 @@ class FileMove() extends FileStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val href = uriBinding(XProcConstants._href).get
diff --git a/src/main/scala/com/xmlcalabash/steps/file/FileTouch.scala b/src/main/scala/com/xmlcalabash/steps/file/FileTouch.scala
index 3a9a9b6..86ee9ba 100644
--- a/src/main/scala/com/xmlcalabash/steps/file/FileTouch.scala
+++ b/src/main/scala/com/xmlcalabash/steps/file/FileTouch.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps.file
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import java.io.FileOutputStream
import java.nio.file.{Files, Path, Paths}
@@ -15,7 +15,7 @@ class FileTouch() extends FileStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val href = uriBinding(XProcConstants._href).get
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/AbstractLoader.scala b/src/main/scala/com/xmlcalabash/steps/internal/AbstractLoader.scala
index 738a9e3..4ea9a98 100644
--- a/src/main/scala/com/xmlcalabash/steps/internal/AbstractLoader.scala
+++ b/src/main/scala/com/xmlcalabash/steps/internal/AbstractLoader.scala
@@ -3,9 +3,10 @@ package com.xmlcalabash.steps.internal
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{AnyItemMessage, XProcItemMessage, XdmNodeItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.runtime.{BinaryNode, DynamicContext, NameValueBinding, StaticContext, XProcExpression, XProcMetadata, XProcXPathExpression}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.{MediaType, S9Api}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api}
import net.sf.saxon.s9api.{QName, XdmItem, XdmMap, XdmNode, XdmValue}
import scala.collection.mutable
@@ -16,11 +17,11 @@ class AbstractLoader() extends DefaultXmlStep {
protected var contextItem = Option.empty[XProcItemMessage]
protected var msgBindings = mutable.HashMap.empty[String, XProcItemMessage]
protected var docProps = Map.empty[QName, XdmValue]
- protected var exprContext: StaticContext = _
+ protected var exprContext: MinimalStaticContext = _
protected var contentType: MediaType = _
override def receive(port: String, item: Any, meta: XProcMetadata): Unit = {
- val context = new StaticContext(config, None)
+ val context = new XStaticContext()
item match {
case node: XdmNode =>
contextItem = Some(new XdmNodeItemMessage(node, meta, context))
@@ -37,7 +38,7 @@ class AbstractLoader() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
if (bindings.contains(XProcConstants._content_type)) {
@@ -45,11 +46,13 @@ class AbstractLoader() extends DefaultXmlStep {
}
// Fake the statics
+ /* FIXME:
for ((name,message) <- exprContext.statics) {
val msg = message.asInstanceOf[XdmValueItemMessage]
val qname = ValueParser.parseClarkName(name)
receiveBinding(new NameValueBinding(qname, msg))
}
+ */
for ((name, binding) <- bindings) {
binding.value match {
@@ -75,9 +78,8 @@ class AbstractLoader() extends DefaultXmlStep {
}
protected def xpathValue(expr: XProcExpression): XdmValue = {
- val dynContext = new DynamicContext()
val eval = config.expressionEvaluator.newInstance()
- val msg = eval.withContext(dynContext) { eval.singletonValue(expr, contextItem.toList, msgBindings.toMap, None) }
+ val msg = eval.singletonValue(expr, contextItem.toList, msgBindings.toMap, None)
msg.item
}
}
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/ContentTypeChecker.scala b/src/main/scala/com/xmlcalabash/steps/internal/ContentTypeChecker.scala
index 3e4e9af..dba4f07 100644
--- a/src/main/scala/com/xmlcalabash/steps/internal/ContentTypeChecker.scala
+++ b/src/main/scala/com/xmlcalabash/steps/internal/ContentTypeChecker.scala
@@ -8,9 +8,10 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.{XdmNodeItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.runtime.params.ContentTypeCheckerParams
import com.xmlcalabash.runtime.{ImplParams, NameValueBinding, StaticContext, XMLCalabashRuntime, XProcDataConsumer, XProcMetadata, XProcXPathExpression, XmlPortSpecification, XmlStep}
-import com.xmlcalabash.util.{MediaType, S9Api, XProcVarValue}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, XProcVarValue}
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmItem, XdmNode, XdmNodeKind, XdmValue}
import org.slf4j.{Logger, LoggerFactory}
@@ -34,9 +35,9 @@ class ContentTypeChecker() extends XmlStep {
private val nodeMeta = mutable.HashMap.empty[XdmNode, XProcMetadata]
private val nodes = ListBuffer.empty[XdmNode]
protected var allowedTypes = List.empty[MediaType]
- protected var errCode = XProcException.err_xd0038
+ protected var errCode: QName = XProcException.err_xd0038
protected var select = Option.empty[String]
- protected var selectContext: StaticContext = _
+ protected var selectContext: XStaticContext = _
protected var portName: String = _
protected var sequence = false
protected var inputPort = false
@@ -136,12 +137,12 @@ class ContentTypeChecker() extends XmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
// FIXME: do whatever logging DefaultXmlStep does.
- for ((name, message) <- selectContext.statics) {
- if (!bindings.contains(name)) {
- bindings.put(name, message)
+ for ((name, message) <- selectContext.inscopeConstants) {
+ if (!bindings.contains(name.getClarkName)) {
+ bindings.put(name.getClarkName, message.constantValue.get)
}
}
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/DocumentLoader.scala b/src/main/scala/com/xmlcalabash/steps/internal/DocumentLoader.scala
index 0d35c4b..e40ac4a 100644
--- a/src/main/scala/com/xmlcalabash/steps/internal/DocumentLoader.scala
+++ b/src/main/scala/com/xmlcalabash/steps/internal/DocumentLoader.scala
@@ -9,7 +9,7 @@ import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
import com.xmlcalabash.runtime.params.DocumentLoaderParams
import com.xmlcalabash.runtime.{BinaryNode, ImplParams, StaticContext, XProcMetadata, XProcXPathExpression, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.{MediaType, S9Api}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api}
import net.sf.saxon.s9api.{QName, SaxonApiException, XdmMap, XdmNode, XdmValue}
import scala.collection.mutable.ListBuffer
@@ -60,7 +60,7 @@ class DocumentLoader() extends AbstractLoader {
// nop, we do it after we've computed the href attribute
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val parts = ListBuffer.empty[String]
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/EmptyLoader.scala b/src/main/scala/com/xmlcalabash/steps/internal/EmptyLoader.scala
index 5f45d35..21306e1 100644
--- a/src/main/scala/com/xmlcalabash/steps/internal/EmptyLoader.scala
+++ b/src/main/scala/com/xmlcalabash/steps/internal/EmptyLoader.scala
@@ -4,6 +4,7 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.runtime.params.EmptyLoaderParams
import com.xmlcalabash.runtime.{ImplParams, StaticContext, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.QName
class EmptyLoader() extends AbstractLoader {
@@ -31,7 +32,7 @@ class EmptyLoader() extends AbstractLoader {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
// Produce nothing.
}
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/InlineExpander.scala b/src/main/scala/com/xmlcalabash/steps/internal/InlineExpander.scala
index 9047a29..4302d89 100644
--- a/src/main/scala/com/xmlcalabash/steps/internal/InlineExpander.scala
+++ b/src/main/scala/com/xmlcalabash/steps/internal/InlineExpander.scala
@@ -5,11 +5,11 @@ import com.jafpl.messages.Message
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.config.DocumentRequest
import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.messages.{XProcItemMessage, XdmNodeItemMessage, XdmValueItemMessage}
+import com.xmlcalabash.messages.{XProcItemMessage, XdmValueItemMessage}
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
-import com.xmlcalabash.model.xml.Inline
-import com.xmlcalabash.runtime.{BinaryNode, DynamicContext, StaticContext, XProcExpression, XProcMetadata, XProcVtExpression, XProcXPathExpression}
-import com.xmlcalabash.util.{MediaType, TypeUtils}
+import com.xmlcalabash.model.xxml.{XInline, XMLStaticContext}
+import com.xmlcalabash.runtime.{BinaryNode, DynamicContext, XMLCalabashRuntime, XProcExpression, XProcMetadata, XProcVtExpression, XProcXPathExpression}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils}
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.event.ReceiverOption
import net.sf.saxon.om.{AttributeInfo, AttributeMap, EmptyAttributeMap, NamespaceMap}
@@ -26,20 +26,32 @@ import scala.jdk.CollectionConverters._
// the case where this is a default binding, it must *not* evaluate its options if the default
// is not used.
-protected[xmlcalabash] class InlineExpander(val config: XMLCalabash, val node: XdmNode, val meta: XProcMetadata, val exprContext: StaticContext, val location: Option[Location]) {
+protected[xmlcalabash] class InlineExpander(val config: XMLCalabash, val node: XdmNode, val meta: XProcMetadata, val exprContext: MinimalStaticContext, val location: Option[Location]) {
+ private var _documentProperties: Map[QName, XdmValue] = Map.empty[QName, XdmValue]
+ private val fq_inline_expand_text = TypeUtils.fqName(XProcConstants._inline_expand_text)
+ private val fq_p_inline_expand_text = TypeUtils.fqName(XProcConstants.p_inline_expand_text)
+ private val _msgBindings = mutable.HashMap.empty[String, XProcItemMessage]
+
var contentType: MediaType = meta.contentType
var encoding: Option[String] = None
var excludeURIs: Set[String] = Set()
- var msgBindings: Map[String, XProcItemMessage] = Map()
var contextItem: Option[XProcItemMessage] = None
- private var _documentProperties: Map[QName, XdmValue] = Map.empty[QName, XdmValue]
+ def msgBindings: Map[String, XProcItemMessage] = _msgBindings.toMap
+ def msgBindings_=(bindings: Map[String, XProcItemMessage]): Unit = {
+ _msgBindings ++= bindings
+ }
- private val fq_inline_expand_text = TypeUtils.fqName(XProcConstants._inline_expand_text)
- private val fq_p_inline_expand_text = TypeUtils.fqName(XProcConstants.p_inline_expand_text)
+ def copyStaticOptionsToBindings(runtime: XMLCalabashRuntime): Unit = {
+ for ((name,value) <- runtime.staticOptions) {
+ if (exprContext.inscopeConstants.contains(name)) {
+ _msgBindings.put(name.getClarkName, value)
+ }
+ }
+ }
- def this(inline: Inline) = {
- this(inline.config, inline.node, new XProcMetadata(inline.contentType), inline.inlineContext, inline.location)
+ def this(inline: XInline) = {
+ this(inline.config, inline.content, new XProcMetadata(inline.contentType), inline.staticContext, inline.location)
encoding = inline.encoding
excludeURIs = inline.excludeURIs
}
@@ -313,9 +325,8 @@ protected[xmlcalabash] class InlineExpander(val config: XMLCalabash, val node: X
}
protected def xpathValue(expr: XProcExpression): XdmValue = {
- val dynContext = new DynamicContext()
val eval = config.expressionEvaluator.newInstance()
- val msg = eval.withContext(dynContext) { eval.singletonValue(expr, contextItem.toList, msgBindings, None) }
+ val msg = eval.singletonValue(expr, contextItem.toList, msgBindings, None)
msg.item
}
}
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/InlineLoader.scala b/src/main/scala/com/xmlcalabash/steps/internal/InlineLoader.scala
index c96b643..e1de878 100644
--- a/src/main/scala/com/xmlcalabash/steps/internal/InlineLoader.scala
+++ b/src/main/scala/com/xmlcalabash/steps/internal/InlineLoader.scala
@@ -9,7 +9,7 @@ import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants
import com.xmlcalabash.runtime.params.InlineLoaderParams
import com.xmlcalabash.runtime.{BinaryNode, ImplParams, StaticContext, XProcMetadata, XProcVtExpression, XProcXPathExpression, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.{MediaType, S9Api, TypeUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, S9Api, TypeUtils}
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.event.ReceiverOption
import net.sf.saxon.om.{AttributeInfo, AttributeMap, EmptyAttributeMap, NamespaceMap}
@@ -30,7 +30,7 @@ import scala.jdk.CollectionConverters._
class InlineLoader() extends AbstractLoader {
private var node: XdmNode = _
private var encoding = Option.empty[String]
- private var exclude_inline_prefixes = Option.empty[String]
+ private var excludeUris = Set.empty[String]
private var expandText = false
private var contextProvided = false
@@ -54,7 +54,7 @@ class InlineLoader() extends AbstractLoader {
content_type = doc.content_type
encoding = doc.encoding
_document_properties = doc.document_properties
- exclude_inline_prefixes = doc.exclude_inline_prefixes
+ excludeUris = doc.exclude_uris
expandText = doc.expand_text
contextProvided = doc.context_provided
exprContext = doc.context
@@ -67,7 +67,7 @@ class InlineLoader() extends AbstractLoader {
// nop, we do it after we've computed the href attribute
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val propContentType = if (docProps.contains(XProcConstants._content_type)) {
@@ -123,14 +123,11 @@ class InlineLoader() extends AbstractLoader {
val expander = new InlineExpander(config.config, node, meta, exprContext, location)
expander.contentType = contentType
expander.encoding = encoding
- expander.excludeURIs = if (exclude_inline_prefixes.isDefined) {
- S9Api.urisForPrefixes(node, exclude_inline_prefixes.get.split("\\s+").toSet)
- } else {
- Set()
- }
+ expander.excludeURIs = excludeUris
expander.msgBindings = msgBindings.toMap
expander.contextItem = contextItem
expander.documentProperties = docProps
+ expander.copyStaticOptionsToBindings(config)
val req = expander.loadDocument(expandText)
val resp = config.documentManager.parse(req)
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/SelectFilter.scala b/src/main/scala/com/xmlcalabash/steps/internal/SelectFilter.scala
index 0a2333c..17a2c99 100644
--- a/src/main/scala/com/xmlcalabash/steps/internal/SelectFilter.scala
+++ b/src/main/scala/com/xmlcalabash/steps/internal/SelectFilter.scala
@@ -11,7 +11,7 @@ import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser}
import com.xmlcalabash.runtime.params.SelectFilterParams
import com.xmlcalabash.runtime.{BinaryNode, ImplParams, StaticContext, XMLCalabashRuntime, XProcDataConsumer, XProcMetadata, XProcXPathExpression, XmlPortSpecification, XmlStep}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.{MediaType, XProcVarValue}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, XProcVarValue}
import net.sf.saxon.s9api.{QName, XdmItem, XdmNode, XdmNodeKind, XdmValue}
import org.slf4j.{Logger, LoggerFactory}
@@ -31,9 +31,8 @@ class SelectFilter() extends DefaultXmlStep {
private val nodeMeta = mutable.HashMap.empty[Any, XProcMetadata]
private val nodes = ListBuffer.empty[Any]
private var select: String = _
- private var selectContext: StaticContext = _
+ private var selectContext: MinimalStaticContext = _
private var port: String = _
- private var ispec: XmlPortSpecification = _
// ==========================================================================
@@ -57,76 +56,44 @@ class SelectFilter() extends DefaultXmlStep {
select = cp.select
selectContext = cp.context
port = cp.port
- ispec = cp.ispec
+ sequence = cp.sequence
case _ => throw XProcException.xiWrongImplParams()
}
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
- msgBindings.clear()
- msgBindings ++= selectContext.statics
+ for ((name, value) <- selectContext.inscopeConstants) {
+ msgBindings.put(name.getClarkName, value.constantValue.get)
+ }
for ((name, binding) <- bindings) {
msgBindings.put(name.getClarkName, new XdmValueItemMessage(binding.value, binding.meta, context))
}
- if (nodes.isEmpty) {
- makeSelection(List())
- } else {
- for (node <- nodes) {
- val metadata = nodeMeta(node)
- val msg = node match {
- case value: XdmNode =>
- new XdmNodeItemMessage(value, metadata, selectContext)
- case value: XdmValue =>
- new XdmValueItemMessage(value, metadata, selectContext)
- case value: BinaryNode =>
- val tree = new SaxonTreeBuilder(config)
- tree.startDocument(metadata.baseURI)
- tree.endDocument()
- new AnyItemMessage(tree.result, value, metadata, selectContext)
- case _ =>
- throw XProcException.xiThisCantHappen(s"Unexpected node type ${node}", location)
- }
- makeSelection(List(msg))
- }
+ val items = ListBuffer.empty[Tuple2[Any, XProcMetadata]]
+ for (node <- nodes) {
+ items += Tuple2(node, nodeMeta(node))
}
- }
- private def makeSelection(context: List[Message]): Unit = {
- val expr = new XProcXPathExpression(selectContext, select, None, None, None)
- val exprEval = config.expressionEvaluator.newInstance()
- val result = exprEval.value(expr, context, msgBindings.toMap, None)
- val iter = result.item.iterator()
- var count = 0
- while (iter.hasNext) {
- val item = iter.next()
- count += 1
+ val xpselector = new XPathSelector(config.config, items.toList, select, context, msgBindings.toMap)
+ val results = xpselector.select()
- if (!ispec.cardinality("source").get.withinBounds(count)) {
- throw XProcException.xdInputSequenceNotAllowed(port, location)
- }
+ if (results.length != 1 && !sequence) {
+ throw XProcException.xdInputSequenceNotAllowed(port, None)
+ }
- item match {
+ for (result <- results) {
+ result match {
case node: XdmNode =>
- if (node.getNodeKind == XdmNodeKind.ATTRIBUTE) {
- throw XProcException.xdInvalidSelection(select, "attribute", location)
- }
- val tree = new SaxonTreeBuilder(config)
- tree.startDocument(node.getBaseURI)
- tree.addSubtree(node)
- tree.endDocument()
- consume(tree.result, "result")
+ consume(node, "result")
+ case value: XdmValue =>
+ consume(value, "result")
case _ =>
- consume(item, "result")
+ throw XProcException.xiThisCantHappen("XPathSelector returned something that wasn't an XdmValue?")
}
}
-
- if (!ispec.cardinality("source").get.withinBounds(count)) {
- throw XProcException.xdInputSequenceNotAllowed(port, location)
- }
}
override def toString: String = {
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/ValueComputation.scala b/src/main/scala/com/xmlcalabash/steps/internal/ValueComputation.scala
new file mode 100644
index 0000000..20f2dc8
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/steps/internal/ValueComputation.scala
@@ -0,0 +1,55 @@
+package com.xmlcalabash.steps.internal
+
+import com.jafpl.graph.{ContainerStart, Node}
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.model.util.XProcConstants.ValueTemplate
+import com.xmlcalabash.model.xxml.{XArtifact, XAtomicStep}
+import com.xmlcalabash.runtime.params.XPathBindingParams
+import com.xmlcalabash.runtime.{XMLCalabashRuntime, XProcVtExpression, XProcXPathExpression}
+import net.sf.saxon.s9api.{QName, SequenceType, XdmAtomicValue}
+
+class ValueComputation private(parentStep: XArtifact, name: QName, collection: Boolean) extends XAtomicStep(parentStep.config, XProcConstants.cx_value_computation) {
+ private var _avt = Option.empty[ValueTemplate]
+ private var _select = Option.empty[String]
+ private var _as = Option.empty[SequenceType]
+ private var _tokens = Option.empty[List[XdmAtomicValue]]
+ staticContext = parentStep.staticContext
+ _synthetic = true
+ parent = parentStep
+
+ def this(parentStep: XArtifact, name: QName, avt: ValueTemplate, collection: Boolean) = {
+ this(parentStep, name, collection)
+ _avt = Some(avt)
+ }
+
+ def this(parentStep: XArtifact, name: QName, select: String, collection: Boolean) = {
+ this(parentStep, name, collection)
+ _select = Some(select)
+ }
+
+ def valueName: QName = name
+ def avt: Option[ValueTemplate] = _avt
+ def select: Option[String] = _select
+
+ def as: Option[SequenceType] = _as
+ protected[xmlcalabash] def as_=(seqType: SequenceType): Unit = {
+ _as = Some(seqType)
+ }
+
+ def allowedValues: Option[List[XdmAtomicValue]] = _tokens
+ protected[xmlcalabash] def allowedValues_=(values: List[XdmAtomicValue]): Unit = {
+ _tokens = Some(values)
+ }
+
+ override def graphNodes(runtime: XMLCalabashRuntime, parent: Node): Unit = {
+ val init = if (_avt.isDefined) {
+ new XProcVtExpression(staticContext, _avt.get, true)
+ } else {
+ val params = new XPathBindingParams(staticContext.inscopeConstantValues, collection)
+ new XProcXPathExpression(staticContext, _select.getOrElse("()"), as, _tokens, Some(params))
+ }
+
+ val cnode = runtime.node(ancestorContainer.get).asInstanceOf[ContainerStart]
+ runtime.addNode(this, cnode.addOption(name.getClarkName, init))
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/steps/internal/XPathSelector.scala b/src/main/scala/com/xmlcalabash/steps/internal/XPathSelector.scala
new file mode 100644
index 0000000..7bf067e
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/steps/internal/XPathSelector.scala
@@ -0,0 +1,73 @@
+package com.xmlcalabash.steps.internal
+
+import com.jafpl.messages.Message
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.{AnyItemMessage, XdmNodeItemMessage, XdmValueItemMessage}
+import com.xmlcalabash.model.util.SaxonTreeBuilder
+import com.xmlcalabash.runtime.{BinaryNode, XProcMetadata, XProcXPathExpression}
+import com.xmlcalabash.util.MinimalStaticContext
+import net.sf.saxon.s9api.{XdmNode, XdmNodeKind, XdmValue}
+
+import scala.collection.mutable.ListBuffer
+
+class XPathSelector(config: XMLCalabash,
+ items: List[Tuple2[Any, XProcMetadata]],
+ select: String,
+ context: MinimalStaticContext,
+ bindings: Map[String,Message]) {
+ private val results = ListBuffer.empty[Any]
+
+ def select(): List[Any] = {
+ if (items.isEmpty) {
+ makeSelection(List())
+ } else {
+ for (item <- items) {
+ val node = item._1
+ val metadata = item._2
+ val msg = node match {
+ case value: XdmNode =>
+ new XdmNodeItemMessage(value, metadata, context)
+ case value: XdmValue =>
+ new XdmValueItemMessage(value, metadata, context)
+ case value: BinaryNode =>
+ val tree = new SaxonTreeBuilder(config)
+ tree.startDocument(metadata.baseURI)
+ tree.endDocument()
+ new AnyItemMessage(tree.result, value, metadata, context)
+ case _ =>
+ throw XProcException.xiThisCantHappen(s"Unexpected node type ${node}", context.location)
+ }
+ makeSelection(List(msg))
+ }
+ }
+
+ results.toList
+ }
+
+ private def makeSelection(contextItem: List[Message]): Unit = {
+ val expr = new XProcXPathExpression(context, select, None, None, None)
+ val exprEval = config.expressionEvaluator.newInstance()
+ val result = exprEval.value(expr, contextItem, bindings, None)
+ val iter = result.item.iterator()
+ var count = 0
+ while (iter.hasNext) {
+ val item = iter.next()
+ count += 1
+
+ item match {
+ case node: XdmNode =>
+ if (node.getNodeKind == XdmNodeKind.ATTRIBUTE) {
+ throw XProcException.xdInvalidSelection(select, "attribute", context.location)
+ }
+ val tree = new SaxonTreeBuilder(config)
+ tree.startDocument(node.getBaseURI)
+ tree.addSubtree(node)
+ tree.endDocument()
+ results += tree.result
+ case _ =>
+ results += item
+ }
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/steps/json/Join.scala b/src/main/scala/com/xmlcalabash/steps/json/Join.scala
index 4efc8c4..1ff1c22 100644
--- a/src/main/scala/com/xmlcalabash/steps/json/Join.scala
+++ b/src/main/scala/com/xmlcalabash/steps/json/Join.scala
@@ -3,6 +3,7 @@ package com.xmlcalabash.steps.json
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmArray, XdmItem, XdmValue}
import scala.collection.mutable.ListBuffer
@@ -25,7 +26,7 @@ class Join extends DefaultXmlStep {
}
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
val depth = bindings(_flatten_to_depth).value.getUnderlyingValue.getStringValue
diff --git a/src/main/scala/com/xmlcalabash/steps/json/Merge.scala b/src/main/scala/com/xmlcalabash/steps/json/Merge.scala
index 1b16768..83733b7 100644
--- a/src/main/scala/com/xmlcalabash/steps/json/Merge.scala
+++ b/src/main/scala/com/xmlcalabash/steps/json/Merge.scala
@@ -4,6 +4,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmItem, XdmMap, XdmNode, XdmValue}
import scala.collection.mutable.ListBuffer
@@ -30,7 +31,7 @@ class Merge extends DefaultXmlStep {
}
}
- override def run(staticContext: StaticContext): Unit = {
+ override def run(staticContext: MinimalStaticContext): Unit = {
super.run(staticContext)
duplicates = bindings(_duplicates).value.getUnderlyingValue.getStringValue
@@ -51,7 +52,7 @@ class Merge extends DefaultXmlStep {
if (staticContext.baseURI.isDefined) {
compiler.setBaseURI(staticContext.baseURI.get)
}
- for ((pfx, uri) <- staticContext.nsBindings) {
+ for ((pfx, uri) <- staticContext.inscopeNamespaces) {
compiler.declareNamespace(pfx, uri)
}
compiler.declareVariable(p_index)
diff --git a/src/main/scala/com/xmlcalabash/steps/os/OsExec.scala b/src/main/scala/com/xmlcalabash/steps/os/OsExec.scala
index c1eb22f..702344e 100644
--- a/src/main/scala/com/xmlcalabash/steps/os/OsExec.scala
+++ b/src/main/scala/com/xmlcalabash/steps/os/OsExec.scala
@@ -6,7 +6,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, SaxonApiException, XdmNode}
import java.io.{ByteArrayInputStream, File, IOException, InputStream, InputStreamReader}
@@ -47,7 +47,7 @@ class OsExec extends DefaultXmlStep {
sourceMeta = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
command = stringBinding(_command)
diff --git a/src/main/scala/com/xmlcalabash/steps/os/OsInfo.scala b/src/main/scala/com/xmlcalabash/steps/os/OsInfo.scala
index 122302c..6ec4025 100644
--- a/src/main/scala/com/xmlcalabash/steps/os/OsInfo.scala
+++ b/src/main/scala/com/xmlcalabash/steps/os/OsInfo.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps.os
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.{MediaType, TypeUtils}
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext, TypeUtils}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
import net.sf.saxon.s9api.QName
@@ -26,7 +26,7 @@ class OsInfo extends DefaultXmlStep {
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
var amap: AttributeMap = EmptyAttributeMap.getInstance()
diff --git a/src/main/scala/com/xmlcalabash/steps/text/Count.scala b/src/main/scala/com/xmlcalabash/steps/text/Count.scala
index 525a4fe..a052a6d 100644
--- a/src/main/scala/com/xmlcalabash/steps/text/Count.scala
+++ b/src/main/scala/com/xmlcalabash/steps/text/Count.scala
@@ -2,13 +2,13 @@ package com.xmlcalabash.steps.text
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
class Count() extends TextLines {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.TEXTSOURCE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.XMLRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val builder = new SaxonTreeBuilder(config)
diff --git a/src/main/scala/com/xmlcalabash/steps/text/Head.scala b/src/main/scala/com/xmlcalabash/steps/text/Head.scala
index a583aa2..c82c9d1 100644
--- a/src/main/scala/com/xmlcalabash/steps/text/Head.scala
+++ b/src/main/scala/com/xmlcalabash/steps/text/Head.scala
@@ -1,7 +1,7 @@
package com.xmlcalabash.steps.text
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmAtomicValue}
import scala.collection.mutable.ListBuffer
@@ -12,7 +12,7 @@ class Head() extends TextLines {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.TEXTSOURCE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.TEXTRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val count = integerBinding(_count).get
diff --git a/src/main/scala/com/xmlcalabash/steps/text/Join.scala b/src/main/scala/com/xmlcalabash/steps/text/Join.scala
index b49bd1a..666b61c 100644
--- a/src/main/scala/com/xmlcalabash/steps/text/Join.scala
+++ b/src/main/scala/com/xmlcalabash/steps/text/Join.scala
@@ -4,7 +4,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmNode}
import scala.collection.mutable.ListBuffer
@@ -26,7 +26,7 @@ class Join() extends DefaultXmlStep {
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val separator = stringBinding(_separator)
diff --git a/src/main/scala/com/xmlcalabash/steps/text/Replace.scala b/src/main/scala/com/xmlcalabash/steps/text/Replace.scala
index e810ee4..9a31982 100644
--- a/src/main/scala/com/xmlcalabash/steps/text/Replace.scala
+++ b/src/main/scala/com/xmlcalabash/steps/text/Replace.scala
@@ -5,6 +5,7 @@ import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XdmNodeItemMessage
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XProcXPathExpression, XmlPortSpecification}
import com.xmlcalabash.steps.DefaultXmlStep
+import com.xmlcalabash.util.MinimalStaticContext
import net.sf.saxon.s9api.{QName, XdmNode}
class Replace() extends DefaultXmlStep {
@@ -26,7 +27,7 @@ class Replace() extends DefaultXmlStep {
meta = metadata
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val pattern = stringBinding(_pattern).replace("'", "''")
diff --git a/src/main/scala/com/xmlcalabash/steps/text/Sort.scala b/src/main/scala/com/xmlcalabash/steps/text/Sort.scala
index f9536b8..3361408 100644
--- a/src/main/scala/com/xmlcalabash/steps/text/Sort.scala
+++ b/src/main/scala/com/xmlcalabash/steps/text/Sort.scala
@@ -3,7 +3,7 @@ package com.xmlcalabash.steps.text
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
import com.xmlcalabash.runtime.{NameValueBinding, StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import com.xmlcalabash.util.xc.XsltStylesheet
import javax.xml.transform.{ErrorListener, TransformerException}
@@ -31,14 +31,14 @@ class Sort() extends TextLines {
override def receiveBinding(variable: NameValueBinding): Unit = {
super.receiveBinding(variable)
if (variable.name == _sort_key) {
- keyNamespaceBindings = variable.context.nsBindings
+ keyNamespaceBindings = variable.context.inscopeNamespaces
}
}
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
- val xslbuilder = new XsltStylesheet(config, context.nsBindings, List(), "2.0")
+ val xslbuilder = new XsltStylesheet(config, context.inscopeNamespaces, List(), "2.0")
xslbuilder.startVariable("lines", "element()*")
for (line <- lines) {
diff --git a/src/main/scala/com/xmlcalabash/steps/text/Tail.scala b/src/main/scala/com/xmlcalabash/steps/text/Tail.scala
index 06198e4..bc7aa8f 100644
--- a/src/main/scala/com/xmlcalabash/steps/text/Tail.scala
+++ b/src/main/scala/com/xmlcalabash/steps/text/Tail.scala
@@ -1,7 +1,7 @@
package com.xmlcalabash.steps.text
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmAtomicValue}
import scala.collection.mutable.ListBuffer
@@ -12,7 +12,7 @@ class Tail() extends TextLines {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.TEXTSOURCE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.TEXTRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
super.run(context)
val count = integerBinding(_count).get
diff --git a/src/main/scala/com/xmlcalabash/testing/TestRunner.scala b/src/main/scala/com/xmlcalabash/testing/TestRunner.scala
index 2246189..8eb7fa5 100644
--- a/src/main/scala/com/xmlcalabash/testing/TestRunner.scala
+++ b/src/main/scala/com/xmlcalabash/testing/TestRunner.scala
@@ -5,11 +5,11 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.TestException
import com.xmlcalabash.messages.XdmNodeItemMessage
import com.xmlcalabash.model.util.{SaxonTreeBuilder, ValueParser, XProcConstants}
-import com.xmlcalabash.model.xml.XMLContext
+import com.xmlcalabash.model.xxml.{XMLStaticContext, XStaticContext}
import com.xmlcalabash.runtime.{SaxonExpressionEvaluator, StaticContext, XProcLocation, XProcMetadata, XProcXPathExpression}
import com.xmlcalabash.util.{MediaType, S9Api, TypeUtils, URIUtils, Urify}
import net.sf.saxon.om.{AttributeMap, EmptyAttributeMap}
-import net.sf.saxon.s9api.{Axis, Processor, QName, XdmNode, XdmNodeKind, XdmValue}
+import net.sf.saxon.s9api.{Axis, ItemType, Processor, QName, XdmAtomicValue, XdmNode, XdmNodeKind, XdmValue}
import org.slf4j.{Logger, LoggerFactory}
import org.xml.sax.InputSource
@@ -59,6 +59,7 @@ class TestRunner(processor: Processor, online: Boolean, regex: Option[String], t
private val t_schematron = new QName(tsns, "schematron")
private val t_input = new QName(tsns, "input")
private val t_option = new QName(tsns, "option")
+ private val _as = new QName("", "as")
private val _src = new QName("", "src")
private val _port = new QName("", "port")
private val _name = new QName("", "name")
@@ -560,12 +561,6 @@ class TestRunner(processor: Processor, online: Boolean, regex: Option[String], t
var urifyFeature = Option.empty[String]
val features = node.getAttributeValue(_features)
if (features != null) {
- if (features.contains("lazy-eval")) {
- val result = new TestResult(true) // skipped counts as a pass...
- result.baseURI = node.getBaseURI
- result.skipped = "The 'lazy-eval' feature is not supported"
- return result
- }
if (features.contains("xslt-1")) {
xmlcalabash.args.config(XProcConstants.cc_xslt10_classpath, Urify.urify("src/test/resources/saxon-6.5.5.jar"))
}
@@ -775,10 +770,13 @@ class TestRunner(processor: Processor, online: Boolean, regex: Option[String], t
}
}
+ /*
if (!result.passed && result.exception.isDefined) {
result.exception.get.printStackTrace()
}
+ */
+
result
}
}
@@ -814,13 +812,19 @@ class TestRunner(processor: Processor, online: Boolean, regex: Option[String], t
val src = node.getAttributeValue(_src)
if ((src == null) && children.isEmpty) {
- val scontext = new XMLContext(emptyConfig, Some(node.getBaseURI), S9Api.inScopeNamespaces(node), Some(new XProcLocation(node)))
- val value = node.getAttributeValue(_select)
+ val scontext = new XStaticContext(new XProcLocation(node), S9Api.inScopeNamespaces(node))
+ val select = node.getAttributeValue(_select)
val contextItem = inlineDocument(node)
val message = new XdmNodeItemMessage(contextItem.get, new XProcMetadata(MediaType.XML), scontext)
val eval = emptyConfig.expressionEvaluator.newInstance()
- val result = eval.singletonValue(new XProcXPathExpression(scontext, value), List(message), Map.empty[String,Message], None)
- Some(result.item)
+ val result = eval.singletonValue(new XProcXPathExpression(scontext, select), List(message), Map.empty[String,Message], None)
+
+ var value = result.item
+ if (node.getAttributeValue(_as) == null) {
+ value = TypeUtils.castAtomicAs(value.asInstanceOf[XdmAtomicValue], ItemType.UNTYPED_ATOMIC, scontext)
+ }
+
+ Some(value)
} else {
loadResource(node)
}
diff --git a/src/main/scala/com/xmlcalabash/util/ArgBundle.scala b/src/main/scala/com/xmlcalabash/util/ArgBundle.scala
index 82118fa..f7e648d 100644
--- a/src/main/scala/com/xmlcalabash/util/ArgBundle.scala
+++ b/src/main/scala/com/xmlcalabash/util/ArgBundle.scala
@@ -1,18 +1,13 @@
package com.xmlcalabash.util
-import com.jafpl.messages.Message
import com.jafpl.steps.DataConsumer
-import com.xmlcalabash.config.{DocumentRequest, XMLCalabashDebugOptions}
+import com.xmlcalabash.config.XMLCalabashDebugOptions
import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.messages.XdmValueItemMessage
-import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
-import com.xmlcalabash.model.xml.XMLContext
-import com.xmlcalabash.runtime.{XProcMetadata, XProcXPathExpression}
-import net.sf.saxon.lib.NamespaceConstant
-import net.sf.saxon.s9api.{Axis, ItemTypeFactory, Processor, QName, XdmAtomicValue, XdmNode, XdmNodeKind, XdmValue}
+import com.xmlcalabash.model.util.XProcConstants
+import net.sf.saxon.s9api.{Axis, Processor, QName, XdmNode, XdmNodeKind, XdmValue}
import org.slf4j.{Logger, LoggerFactory}
-import java.io.File
+import java.io.{BufferedReader, File, InputStreamReader}
import java.net.URI
import java.util.Properties
import scala.collection.mutable
@@ -544,61 +539,64 @@ class ArgBundle() {
}
}
- def loadProperties(): Unit = {
- val uriEnum = this.getClass.getClassLoader.getResources("com.xmlcalabash.properties")
+ def loadSettings(): Unit = {
+ val NSPattern = "namespace\\s+(.+)$".r
+ val FPattern = "function\\s+(.+):(.+)$".r
+ val SPattern = "step\\s+(.+):(.+)$".r
+
+ // These started out as .properties files, but it turned out that didn't (conveniently) work
+ val uriEnum = this.getClass.getClassLoader.getResources("com.xmlcalabash.settings")
while (uriEnum.hasMoreElements) {
val url = uriEnum.nextElement()
- logger.debug(s"Loading properties: $url")
+ logger.debug(s"Loading settings: $url")
val conn = url.openConnection()
val stream = conn.getInputStream
- val props = new Properties()
- props.load(stream)
val nsmap = mutable.HashMap.empty[String,String]
- val NSPattern = "namespace\\s+(.+)$".r
- val FPattern = "function\\s+(.+):(.+)$".r
- val SPattern = "step\\s+(.+):(.+)$".r
-
- // Properties are unordered so find the namespace bindings
- var propIter = props.stringPropertyNames().iterator()
- while (propIter.hasNext) {
- val name = propIter.next()
- val value = props.get(name).asInstanceOf[String]
- value match {
- case NSPattern(uri) =>
- if (nsmap.contains(name)) {
- throw new RuntimeException("Cannot redefine namespace bindings in property file")
- }
- nsmap.put(name, uri)
- case _ => ()
- }
- }
- // Now parse the step and function declarations
- propIter = props.stringPropertyNames().iterator()
- while (propIter.hasNext) {
- val name = propIter.next()
- val value = props.get(name).asInstanceOf[String]
- value match {
- case NSPattern(uri) => ()
- case FPattern(pfx,local) =>
- if (nsmap.contains(pfx)) {
- val qname = new QName(pfx, nsmap(pfx), local)
- _parameters += new PipelineFunctionImplementation(qname.getEQName, value, name)
- } else {
- logger.debug(s"No namespace binding for $pfx, ignoring: $name=$value")
- }
- case SPattern(pfx,local) =>
- if (nsmap.contains(pfx)) {
- val qname = new QName(pfx, nsmap(pfx), local)
- _parameters += new PipelineStepImplementation(qname.getEQName, value, name)
- } else {
- logger.debug(s"No namespace binding for $pfx, ignoring: $name=$value")
+ val reader = new BufferedReader(new InputStreamReader(stream))
+ var line = reader.readLine()
+ while (line != null) {
+ var pos = line.indexOf("#")
+ if (pos >= 0) {
+ line = line.substring(0, pos)
+ }
+ line = line.trim()
+ if (line != "") {
+ pos = line.indexOf("=")
+ if (pos <= 0) {
+ logger.warn(s"Ignoring unparsable setting: ${line}")
+ } else {
+ val name = line.substring(0, pos).trim
+ val value = line.substring(pos+1).trim
+ name match {
+ case NSPattern(uri) =>
+ if (nsmap.contains(name)) {
+ throw new RuntimeException("Cannot redefine namespace bindings in property file")
+ }
+ nsmap.put(value, uri)
+ case FPattern(pfx,local) =>
+ if (nsmap.contains(pfx)) {
+ val qname = new QName(pfx, nsmap(pfx), local)
+ _parameters += new PipelineFunctionImplementation(qname.getEQName, name, value)
+ } else {
+ logger.debug(s"No namespace binding for $pfx, ignoring: $name=$value")
+ }
+ case SPattern(pfx,local) =>
+ if (nsmap.contains(pfx)) {
+ val qname = new QName(pfx, nsmap(pfx), local)
+ _parameters += new PipelineStepImplementation(qname.getEQName, name, value)
+ } else {
+ logger.debug(s"No namespace binding for $pfx, ignoring: $name=$value")
+ }
+ case _ =>
+ logger.debug(s"Unparseable property, ignoring: $name=$value")
}
- case _ =>
- logger.debug(s"Unparseable property, ignoring: $name=$value")
+ }
}
+
+ line = reader.readLine()
}
}
}
diff --git a/src/main/scala/com/xmlcalabash/util/DefaultDocumentManager.scala b/src/main/scala/com/xmlcalabash/util/DefaultDocumentManager.scala
index 07959af..9014127 100644
--- a/src/main/scala/com/xmlcalabash/util/DefaultDocumentManager.scala
+++ b/src/main/scala/com/xmlcalabash/util/DefaultDocumentManager.scala
@@ -10,8 +10,7 @@ import com.xmlcalabash.messages.XdmValueItemMessage
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{BinaryNode, StaticContext, XProcMetadata, XProcXPathExpression}
import com.xmlcalabash.util.xc.Errors
-import net.sf.saxon.Configuration
-import net.sf.saxon.lib.{AugmentedSource, ErrorReporter, ParseOptions}
+import net.sf.saxon.lib.{AugmentedSource, ParseOptions}
import net.sf.saxon.s9api.{QName, SaxonApiException, XdmAtomicValue, XdmNode, XdmValue}
import net.sf.saxon.trans.XPathException
import nu.validator.htmlparser.common.XmlViolationPolicy
@@ -21,7 +20,6 @@ import org.apache.http.client.utils.DateUtils
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.ByteArrayBuffer
import org.slf4j.{Logger, LoggerFactory}
-import org.xml.sax.helpers.XMLReaderFactory
import org.xml.sax.{InputSource, SAXException}
import java.io.{File, FileInputStream, FileNotFoundException, IOException, InputStream, UnsupportedEncodingException}
diff --git a/src/main/scala/com/xmlcalabash/util/DefaultErrorExplanation.scala b/src/main/scala/com/xmlcalabash/util/DefaultErrorExplanation.scala
index e0eede5..561e746 100644
--- a/src/main/scala/com/xmlcalabash/util/DefaultErrorExplanation.scala
+++ b/src/main/scala/com/xmlcalabash/util/DefaultErrorExplanation.scala
@@ -138,7 +138,7 @@ class DefaultErrorExplanation() extends ErrorExplanation {
}
override def explanation(code: QName, variant: Int, details: List[Any]): String = {
- var message = template(code, variant, details.length).explanation
+ val message = template(code, variant, details.length).explanation
substitute(message, details)
}
diff --git a/src/main/scala/com/xmlcalabash/util/DefaultXMLCalabashConfigurer.scala b/src/main/scala/com/xmlcalabash/util/DefaultXMLCalabashConfigurer.scala
index 378b9d3..7839b5f 100644
--- a/src/main/scala/com/xmlcalabash/util/DefaultXMLCalabashConfigurer.scala
+++ b/src/main/scala/com/xmlcalabash/util/DefaultXMLCalabashConfigurer.scala
@@ -19,7 +19,7 @@ class DefaultXMLCalabashConfigurer() extends XMLCalabashConfigurer {
override def configure(input: List[PipelineParameter]): List[PipelineParameter] = {
val args = new ArgBundle()
- args.loadProperties()
+ args.loadSettings()
val searchProp = ListBuffer.empty[PipelineParameter] ++ input
val propConfig = Option(System.getProperty("com.xmlcalabash.configFile"))
diff --git a/src/main/scala/com/xmlcalabash/util/InternetProtocolRequest.scala b/src/main/scala/com/xmlcalabash/util/InternetProtocolRequest.scala
index 1d4a5c6..938852b 100644
--- a/src/main/scala/com/xmlcalabash/util/InternetProtocolRequest.scala
+++ b/src/main/scala/com/xmlcalabash/util/InternetProtocolRequest.scala
@@ -4,6 +4,7 @@ import com.jafpl.graph.Location
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.runtime.{StaticContext, XMLCalabashRuntime, XProcMetadata, XProcXPathExpression}
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmMap, XdmValue}
import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
@@ -27,7 +28,7 @@ import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.jdk.CollectionConverters.{ListHasAsScala, SeqHasAsJava}
-class InternetProtocolRequest(val config: XMLCalabash, val context: StaticContext, val uri: URI) {
+class InternetProtocolRequest(val config: XMLCalabash, val context: MinimalStaticContext, val uri: URI) {
private val _expires = new QName("", "expires")
private val _date = new QName("", "date")
private val _content_disposition = new QName("", "content-disposition")
@@ -53,11 +54,11 @@ class InternetProtocolRequest(val config: XMLCalabash, val context: StaticContex
private var _followRedirectCount = -1
private var _sendBodyAnyway = false
- def this(config: XMLCalabashRuntime, context: StaticContext, uri: URI) =
+ def this(config: XMLCalabashRuntime, context: MinimalStaticContext, uri: URI) =
this(config.config, context, uri)
def this(config: XMLCalabash, uri: URI) =
- this(config, new StaticContext(config), uri)
+ this(config, new XStaticContext(), uri)
def location: Option[Location] = _location
def location_=(loc: Location): Unit = {
diff --git a/src/main/scala/com/xmlcalabash/util/MediaType.scala b/src/main/scala/com/xmlcalabash/util/MediaType.scala
index 07d2043..132fb34 100644
--- a/src/main/scala/com/xmlcalabash/util/MediaType.scala
+++ b/src/main/scala/com/xmlcalabash/util/MediaType.scala
@@ -10,6 +10,7 @@ import scala.collection.mutable.ListBuffer
// N.B. This class accepts "*" for type and subtype because it's used for matching
object MediaType {
+ val ANY = new MediaType("*", "*")
val OCTET_STREAM = new MediaType("application", "octet-stream")
val TEXT = new MediaType("text", "plain")
val XML = new MediaType("application", "xml")
@@ -21,18 +22,18 @@ object MediaType {
val MULTIPART = new MediaType("multipart", "*")
val MULTIPART_MIXED = new MediaType("multipart", "mixed")
- val MATCH_XML: Array[MediaType] = Array(
+ val MATCH_XML: List[MediaType] = List(
MediaType.parse("application/xml"),
MediaType.parse("text/xml"),
MediaType.parse("*/*+xml"),
MediaType.parse("-application/xhtml+xml"))
- val MATCH_HTML: Array[MediaType] = Array(
+ val MATCH_HTML: List[MediaType] = List(
MediaType.parse("text/html"),
MediaType.parse("application/xhtml+xml")
)
- val MATCH_TEXT: Array[MediaType] = Array(
+ val MATCH_TEXT: List[MediaType] = List(
MediaType.parse("text/*"),
MediaType.parse("application/relax-ng-compact-syntax"),
MediaType.parse("application/xquery"),
@@ -41,16 +42,16 @@ object MediaType {
MediaType.parse("-text/xml")
)
- val MATCH_JSON: Array[MediaType] = Array(
+ val MATCH_JSON: List[MediaType] = List(
MediaType.parse("application/json"),
MediaType.parse("*/*+json")
)
- val MATCH_YAML: Array[MediaType] = Array(
+ val MATCH_YAML: List[MediaType] = List(
MediaType.parse("application/vnd.yaml")
)
- val MATCH_ANY: Array[MediaType] = Array(
+ val MATCH_ANY: List[MediaType] = List(
MediaType.parse("*/*")
)
@@ -142,21 +143,41 @@ object MediaType {
}
def parseList(ctypes: String): ListBuffer[MediaType] = {
+ val fullyQualifiedExt = "^((-?)([^/\\s]+)/([^\\s;+]+)(\\+[^/\\s;]+)?(;\\s*([^\\s]+))?)\\s*(.*)$".r
+ val xmlShortcut = "^xml\\s+(.*)$".r
+ val htmlShortcut = "^html\\s+(.*)$".r
+ val textShortcut = "^text\\s+(.*)$".r
+ val jsonShortcut = "^json\\s+(.*)$".r
+ val anyShortcut = "^any\\s+(.*)$".r
+
val contentTypes = ListBuffer.empty[MediaType]
- for (ctype <- ctypes.split("\\s+")) {
- ctype match {
- case "xml" => contentTypes ++= MATCH_XML
- case "html" => contentTypes ++= MATCH_HTML
- case "text" => contentTypes ++= MATCH_TEXT
- case "json" => contentTypes ++= MATCH_JSON
- case "any" => contentTypes ++= MATCH_ANY
- case _ =>
- if (ctype.indexOf("/") <= 0) {
- throw XProcException.xsUnrecognizedContentTypeShortcut(ctype, None)
- }
+
+ var typelist = ctypes + " "
+ while (typelist.trim != "") {
+ typelist match {
+ case fullyQualifiedExt(ctype, minus, mediatype, mediasubtype, ext, semi, params, rest) =>
contentTypes += MediaType.parse(ctype)
+ typelist = rest
+ case xmlShortcut(rest) =>
+ contentTypes ++= MATCH_XML
+ typelist = rest
+ case htmlShortcut(rest) =>
+ contentTypes ++= MATCH_HTML
+ typelist = rest
+ case textShortcut(rest) =>
+ contentTypes ++= MATCH_TEXT
+ typelist = rest
+ case jsonShortcut(rest) =>
+ contentTypes ++= MATCH_JSON
+ typelist = rest
+ case anyShortcut(rest) =>
+ contentTypes ++= MATCH_ANY
+ typelist = rest
+ case _ =>
+ throw XProcException.xsUnrecognizedContentTypeShortcut(typelist, None)
}
}
+
contentTypes
}
}
@@ -283,7 +304,7 @@ class MediaType(val mediaType: String, val mediaSubtype: String, val suffix: Opt
xmlContentType || htmlContentType
}
- def matchingMediaType(mtypes: Array[MediaType]): Option[MediaType] = {
+ def matchingMediaType(mtypes: List[MediaType]): Option[MediaType] = {
var matching = Option.empty[MediaType]
for (mtype <- mtypes) {
//println(s"$this $mtype ${matches(mtype)}")
diff --git a/src/main/scala/com/xmlcalabash/util/MinimalStaticContext.scala b/src/main/scala/com/xmlcalabash/util/MinimalStaticContext.scala
new file mode 100644
index 0000000..d25497a
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/util/MinimalStaticContext.scala
@@ -0,0 +1,275 @@
+package com.xmlcalabash.util
+
+import com.jafpl.graph.Location
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.model.xxml.{XArtifact, XNameBinding, XOption}
+import net.sf.saxon.s9api.{ItemType, ItemTypeFactory, OccurrenceIndicator, QName, SaxonApiException, SequenceType, XdmAtomicValue, XdmNode}
+
+import java.net.URI
+
+abstract class MinimalStaticContext() {
+ def baseURI: Option[URI]
+
+ def location: Option[Location]
+
+ def inscopeNamespaces: Map[String, String]
+
+ def inscopeConstants: Map[QName, XNameBinding]
+
+ def parseClarkName(name: String): QName = {
+ parseClarkName(name, None)
+ }
+
+ def parseClarkName(name: String, prefix: String): QName = {
+ parseClarkName(name, Some(prefix))
+ }
+
+ private def parseClarkName(name: String, pfx: Option[String]): QName = {
+ // FIXME: Better error handling for ClarkName parsing
+ if (name.startsWith("{")) {
+ val pos = name.indexOf("}")
+ val uri = name.substring(1, pos)
+ val local = name.substring(pos + 1)
+ if (pfx.isDefined) {
+ new QName(pfx.get, uri, local)
+ } else {
+ new QName(uri, local)
+ }
+ } else {
+ new QName("", name)
+ }
+ }
+
+ def parseQName(name: String): QName = {
+ parseQName(Some(name)).get
+ }
+
+ def parseQName(optname: Option[String]): Option[QName] = {
+ if (optname.isDefined) {
+ val name = optname.get
+
+ if (name.startsWith("Q{")) {
+ val pos = name.lastIndexOf("}")
+ if (pos < 0) {
+ throw XProcException.xdCannotResolveQName(name, None)
+ }
+ val uri = name.substring(2, pos)
+ val local = parseNCName(name.substring(pos + 1))
+ Some(new QName(uri, local))
+ } else if (name.contains(":")) {
+ val pos = name.indexOf(":")
+ val pfx = parseNCName(name.substring(0, pos))
+ val local = parseNCName(name.substring(pos + 1))
+ val uri = inscopeNamespaces.get(pfx)
+ if (uri.isEmpty) {
+ throw XProcException.xdCannotResolveQName(name, None)
+ }
+ Some(new QName(pfx, uri.get, local))
+ } else {
+ Some(new QName("", parseNCName(name)))
+ }
+ } else {
+ None
+ }
+ }
+
+ def parseNCName(name: String): String = {
+ parseNCName(Some(name)).get
+ }
+
+ def parseNCName(name: Option[String]): Option[String] = {
+ if (name.isDefined) {
+ try {
+ val ncname = castAtomicAs(new XdmAtomicValue(name.get), ItemType.NCNAME)
+ Some(ncname.getStringValue)
+ } catch {
+ case _: SaxonApiException =>
+ throw XProcException.xsBadTypeValue(name.get, "NCName", None)
+ case e: Exception =>
+ throw e
+ }
+ } else {
+ None
+ }
+ }
+
+ def parseSequenceType(seqType: Option[String], typeFactory: ItemTypeFactory): Option[SequenceType] = {
+ if (seqType.isDefined) {
+ Some(parseSequenceType(seqType.get, typeFactory))
+ } else {
+ None
+ }
+ }
+
+ def parseSequenceType(seqType: String, typeFactory: ItemTypeFactory): SequenceType = {
+ // XPathParser.parseSequenceType returns a type.SequenceType.
+ // I need an s9api.SequenceType. Michael Kay confirms there's no
+ // easy way to convert between them. I'm rolling my own until such time
+ // as there's a better way. If it has to stay this way, I should call
+ // the actual XPath 3.1 parser for it, but I'm just going to hack my
+ // way through it for now.
+ val parensre = "^\\((.*)\\)$".r
+ val stypere = "^([^*+?()]+)\\s*([*+?])?$".r
+ val mtypere = "^map\\s*\\((.*)\\)\\s*([*+?])?$".r
+ val atypere = "^array\\s*\\((.*)\\)\\s*([*+?])?$".r
+ val ftypere = "^function\\s*\\((.*)\\)\\s*([*+?])?$".r
+ val itemre = "^item\\s*\\(\\s*\\)\\s*([*+?])?$".r
+ seqType.trim() match {
+ case parensre(body) =>
+ parseSequenceType(body, typeFactory)
+ case stypere(typename, cardchar) =>
+ // xs:sometype?
+ try {
+ val itype = TypeUtils.simpleType(parseQName(typename))
+ SequenceType.makeSequenceType(itype, cardinality(cardchar))
+ } catch {
+ case ex: XProcException =>
+ if (ex.code == XProcException.err_xd0036) {
+ throw XProcException.xsInvalidSequenceType(typename, "Expected QName", None)
+ }
+ throw ex
+ case ex: Throwable =>
+ throw ex
+ }
+ case mtypere(mbody, cardchar) =>
+ if (mbody.trim == "*") {
+ return SequenceType.makeSequenceType(ItemType.ANY_MAP, cardinality(cardchar))
+ }
+ val tuplere = "^([^,]+),(.*)$".r
+ mbody match {
+ case tuplere(keyseqtype, itemseqtype) =>
+ val keytype = TypeUtils.simpleType(parseQName(keyseqtype))
+ val itemtype = parseSequenceType(itemseqtype, typeFactory)
+ SequenceType.makeSequenceType(typeFactory.getMapType(keytype, itemtype), cardinality(cardchar))
+ case _ =>
+ throw new RuntimeException(s"Unexpected map syntax: map($mbody)")
+ }
+ case atypere(abody, cardchar) =>
+ if (abody.trim == "*") {
+ return SequenceType.makeSequenceType(ItemType.ANY_ARRAY, cardinality(cardchar))
+ }
+ val itemtype = parseSequenceType(abody, typeFactory)
+ SequenceType.makeSequenceType(typeFactory.getArrayType(itemtype), cardinality(cardchar))
+ case ftypere(params, cardchar) =>
+ SequenceType.makeSequenceType(ItemType.ANY_FUNCTION, cardinality(cardchar))
+ case itemre(cardchar) =>
+ SequenceType.makeSequenceType(ItemType.ANY_ITEM, cardinality(cardchar))
+ case _ =>
+ throw new RuntimeException(s"Unexpected sequence type: $seqType")
+ }
+ }
+
+ def parseFakeMapSequenceType(seqType: String, typeFactory: ItemTypeFactory): SequenceType = {
+ // This is just like parseSequenceType except that it lies about the type of maps
+ val mtypere = "^map\\s*\\((.*)\\)\\s*([*+?])?$".r
+ seqType.trim() match {
+ case mtypere(mbody, cardchar) =>
+ // I actually only care about the case where the keys are QNames
+ if (mbody.trim == "*") {
+ return SequenceType.makeSequenceType(ItemType.ANY_MAP, cardinality(cardchar))
+ }
+ val tuplere = "^([^,]+),(.*)$".r
+ mbody match {
+ case tuplere(keyseqtype, itemseqtype) =>
+ val keytype = ItemType.ANY_ATOMIC_VALUE
+ val itemtype = parseSequenceType(itemseqtype, typeFactory)
+ SequenceType.makeSequenceType(typeFactory.getMapType(keytype, itemtype), cardinality(cardchar))
+ case _ =>
+ throw new RuntimeException(s"Unexpected map syntax: map($mbody)")
+ }
+ case _ =>
+ parseSequenceType(seqType, typeFactory)
+ }
+ }
+
+ private def cardinality(card: String): OccurrenceIndicator = {
+ if (card == null) {
+ OccurrenceIndicator.ONE
+ } else {
+ card match {
+ case "*" => OccurrenceIndicator.ZERO_OR_MORE
+ case "?" => OccurrenceIndicator.ZERO_OR_ONE
+ case "+" => OccurrenceIndicator.ONE_OR_MORE
+ case _ =>
+ throw new RuntimeException(s"Unexpected cardinality $card")
+ }
+ }
+ }
+
+ def parseBoolean(value: Option[String]): Option[Boolean] = {
+ if (value.isDefined) {
+ if (value.get == "true" || value.get == "false") {
+ Some(value.get == "true")
+ } else {
+ throw XProcException.xsBadTypeValue(value.get, "boolean", None)
+ }
+ } else {
+ None
+ }
+ }
+
+ def parseSingleContentType(ctype: Option[String]): Option[MediaType] = {
+ if (ctype.isDefined) {
+ MediaType.parse(ctype)
+ } else {
+ None
+ }
+ }
+
+ def parseContentTypes(ctypes: Option[String]): List[MediaType] = {
+ if (ctypes.isDefined) {
+ try {
+ MediaType.parseList(ctypes.get).toList
+ } catch {
+ case ex: XProcException =>
+ if (ex.code == XProcException.err_xc0070) {
+ // Map to the static error...
+ throw XProcException.xsUnrecognizedContentTypeShortcut(ex.details.head.toString, ex.location)
+ } else {
+ throw ex
+ }
+ }
+ } else {
+ List.empty[MediaType]
+ }
+ }
+
+ def castAtomicAs(value: XdmAtomicValue, seqType: Option[SequenceType]): XdmAtomicValue = {
+ if (seqType.isEmpty) {
+ return value
+ }
+
+ castAtomicAs(value, seqType.get.getItemType)
+ }
+
+ def castAtomicAs(value: XdmAtomicValue, xsdtype: ItemType): XdmAtomicValue = {
+ if ((xsdtype == ItemType.UNTYPED_ATOMIC) || (xsdtype == ItemType.STRING || (xsdtype == ItemType.ANY_ITEM))) {
+ return value
+ }
+
+ if (xsdtype == ItemType.QNAME) {
+ val qnamev = value.getPrimitiveTypeName match {
+ case XProcConstants.xs_string => new XdmAtomicValue(parseQName(value.getStringValue))
+ case XProcConstants.xs_untypedAtomic => new XdmAtomicValue(parseQName(value.getStringValue))
+ case XProcConstants.xs_QName => value
+ case _ =>
+ throw new RuntimeException(s"Don't know how to convert $value to an xs:QName")
+ }
+ return qnamev
+ }
+
+ try {
+ new XdmAtomicValue(value.getStringValue, xsdtype)
+ } catch {
+ case sae: SaxonApiException =>
+ if (sae.getMessage.contains("Invalid URI")) {
+ throw XProcException.xdInvalidURI(value.getStringValue, location)
+ } else {
+ throw XProcException.xdBadType(value.getStringValue, xsdtype.toString, location)
+ }
+ case ex: Exception =>
+ throw(ex)
+ }
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/util/PipelineParameter.scala b/src/main/scala/com/xmlcalabash/util/PipelineParameter.scala
index cf75649..d98a1aa 100644
--- a/src/main/scala/com/xmlcalabash/util/PipelineParameter.scala
+++ b/src/main/scala/com/xmlcalabash/util/PipelineParameter.scala
@@ -1,6 +1,7 @@
package com.xmlcalabash.util
import com.jafpl.steps.DataConsumer
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.runtime.StaticContext
import net.sf.saxon.s9api.{QName, XdmNode, XdmValue}
import org.slf4j.LoggerFactory
@@ -118,7 +119,7 @@ class PipelineExpressionOption(eqname: String, val expression: String) extends P
override def toString = s"${eqname} = XPath: ${expression}"
}
-class PipelineOptionValue(val context: StaticContext, val value: XdmValue) extends PipelineParameter {}
+class PipelineOptionValue(val context: XStaticContext, val value: XdmValue) extends PipelineParameter {}
class PipelineSystemProperty(val name: String, val value: String) extends PipelineParameter {
override def toString = s"${name} = ${value}"
diff --git a/src/main/scala/com/xmlcalabash/util/S9Api.scala b/src/main/scala/com/xmlcalabash/util/S9Api.scala
index b7c0207..2b092dc 100644
--- a/src/main/scala/com/xmlcalabash/util/S9Api.scala
+++ b/src/main/scala/com/xmlcalabash/util/S9Api.scala
@@ -198,7 +198,7 @@ object S9Api {
tree.result
}
- def forceQNameKeys(inputMap: MapItem, context: StaticContext): XdmMap = {
+ def forceQNameKeys(inputMap: MapItem, context: MinimalStaticContext): XdmMap = {
var map = new XdmMap()
val iter = inputMap.keyValuePairs().iterator()
@@ -206,7 +206,7 @@ object S9Api {
val pair = iter.next()
pair.key.getItemType match {
case BuiltInAtomicType.STRING =>
- val qname = ValueParser.parseQName(pair.key.getStringValue, context)
+ val qname = context.parseQName(pair.key.getStringValue)
map = map.put(new XdmAtomicValue(qname), XdmValue.wrap(pair.value))
case BuiltInAtomicType.QNAME =>
val qvalue = pair.key.asInstanceOf[QNameValue]
@@ -315,7 +315,11 @@ object S9Api {
}
if (!found) {
- throw new RuntimeException("No binding for prefix: " + pfx)
+ if (pfx == "#default") {
+ throw XProcException.xsBadExcludeInlinePrefixesDefault()
+ } else {
+ throw XProcException.xsBadExcludeInlinePrefixes(pfx)
+ }
}
}
diff --git a/src/main/scala/com/xmlcalabash/util/TvtExpander.scala b/src/main/scala/com/xmlcalabash/util/TvtExpander.scala
index fb0f161..5ea40fb 100644
--- a/src/main/scala/com/xmlcalabash/util/TvtExpander.scala
+++ b/src/main/scala/com/xmlcalabash/util/TvtExpander.scala
@@ -5,6 +5,7 @@ import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XProcItemMessage
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
+import com.xmlcalabash.model.xxml.XStaticContext
import com.xmlcalabash.runtime.{StaticContext, XProcVtExpression}
import net.sf.saxon.`type`.BuiltInAtomicType
import net.sf.saxon.event.ReceiverOption
@@ -16,7 +17,7 @@ import scala.jdk.CollectionConverters.ListHasAsScala
class TvtExpander(config: XMLCalabash,
contextItem: Option[XProcItemMessage],
- exprContext: StaticContext,
+ exprContext: XStaticContext,
msgBindings: Map[String, XProcItemMessage],
location: Option[Location]) {
private val excludeURIs = mutable.HashSet.empty[String]
diff --git a/src/main/scala/com/xmlcalabash/util/TypeUtils.scala b/src/main/scala/com/xmlcalabash/util/TypeUtils.scala
index 8878a53..758ae18 100644
--- a/src/main/scala/com/xmlcalabash/util/TypeUtils.scala
+++ b/src/main/scala/com/xmlcalabash/util/TypeUtils.scala
@@ -1,18 +1,21 @@
package com.xmlcalabash.util
import com.xmlcalabash.XMLCalabash
-
-import java.util
import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.util.{ValueParser, XProcConstants}
-import com.xmlcalabash.parsers.SequenceBuilder
-import com.xmlcalabash.runtime.{StaticContext, XMLCalabashRuntime}
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.runtime.XMLCalabashRuntime
import jdk.nashorn.api.scripting.ScriptObjectMirror
-import net.sf.saxon.`type`.BuiltInAtomicType
+import net.sf.saxon.`type`.{BuiltInAtomicType, TypeHierarchy, ValidationException}
import net.sf.saxon.event.ReceiverOption
+import net.sf.saxon.expr.parser.{Loc, RoleDiagnostic}
import net.sf.saxon.om.{AttributeInfo, FingerprintedQName}
import net.sf.saxon.s9api._
+import net.sf.saxon.trans.XPathException
+import net.sf.saxon.value.StringValue
+import org.slf4j.{Logger, LoggerFactory}
+import java.net.URI
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.jdk.CollectionConverters.{CollectionHasAsScala, IterableHasAsJava, MapHasAsScala}
@@ -62,14 +65,14 @@ object TypeUtils {
case xarr: XdmArray =>
val list = ListBuffer.empty[Any]
var idx = 0
- for (idx <- 0 until xarr.arrayLength()) {
+ for (idx <- 0 until xarr.arrayLength()) {
val value = xarr.get(idx)
list += castAsJava(value)
}
list.toArray
case xmap: XdmMap =>
val map = xmap.asMap()
- val jmap = mutable.HashMap.empty[Any,Any]
+ val jmap = mutable.HashMap.empty[Any, Any]
for (key <- map.asScala.keySet) {
val value = map.asScala(key)
jmap.put(castAsJava(key), castAsJava(value))
@@ -92,14 +95,14 @@ object TypeUtils {
case xarr: XdmArray =>
val list = ListBuffer.empty[Any]
var idx = 0
- for (idx <- 0 until xarr.arrayLength()) {
+ for (idx <- 0 until xarr.arrayLength()) {
val value = xarr.get(idx)
list += castAsScala(value)
}
list.toArray
case xmap: XdmMap =>
val map = xmap.asMap()
- val jmap = mutable.HashMap.empty[Any,Any]
+ val jmap = mutable.HashMap.empty[Any, Any]
for (key <- map.asScala.keySet) {
val value = map.asScala(key)
jmap.put(castAsScala(key), castAsScala(value))
@@ -134,7 +137,7 @@ object TypeUtils {
MediaType.parse(s"application/vnd.xmlcalabash.$t+xml")
}
- def castAtomicAs(value: XdmAtomicValue, seqType: Option[SequenceType], context: StaticContext): XdmAtomicValue = {
+ def castAtomicAs(value: XdmAtomicValue, seqType: Option[SequenceType], context: MinimalStaticContext): XdmAtomicValue = {
if (seqType.isEmpty) {
return value
}
@@ -142,15 +145,17 @@ object TypeUtils {
castAtomicAs(value, seqType.get.getItemType, context)
}
- def castAtomicAs(value: XdmAtomicValue, xsdtype: ItemType, context: StaticContext): XdmAtomicValue = {
+ def castAtomicAs(value: XdmAtomicValue, xsdtype: ItemType, context: MinimalStaticContext): XdmAtomicValue = {
+ /*
if ((xsdtype == ItemType.UNTYPED_ATOMIC) || (xsdtype == ItemType.STRING || (xsdtype == ItemType.ANY_ITEM))) {
return value
}
+ */
if (xsdtype == ItemType.QNAME) {
val qnamev = value.getPrimitiveTypeName match {
- case XProcConstants.xs_string => new XdmAtomicValue(ValueParser.parseQName(value.getStringValue, context))
- case XProcConstants.xs_untypedAtomic => new XdmAtomicValue(ValueParser.parseQName(value.getStringValue, context))
+ case XProcConstants.xs_string => new XdmAtomicValue(context.parseQName(value.getStringValue))
+ case XProcConstants.xs_untypedAtomic => new XdmAtomicValue(context.parseQName(value.getStringValue))
case XProcConstants.xs_QName => value
case _ =>
throw new RuntimeException(s"Don't know how to convert $value to an xs:QName")
@@ -174,53 +179,78 @@ object TypeUtils {
throw XProcException.xdBadType(value.getStringValue, xsdtype.toString, location)
}
case ex: Exception =>
- throw(ex)
+ throw (ex)
+ }
+ }
+
+ def simpleType(qname: QName): ItemType = {
+ qname match {
+ case XProcConstants.xs_anyURI => ItemType.ANY_URI
+ case XProcConstants.xs_base64Binary => ItemType.BASE64_BINARY
+ case XProcConstants.xs_boolean => ItemType.BOOLEAN
+ case XProcConstants.xs_byte => ItemType.BYTE
+ case XProcConstants.xs_date => ItemType.DATE
+ case XProcConstants.xs_dateTime => ItemType.DATE_TIME
+ case XProcConstants.xs_dateTimeStamp => ItemType.DATE_TIME_STAMP
+ case XProcConstants.xs_dayTimeDuration => ItemType.DAY_TIME_DURATION
+ case XProcConstants.xs_decimal => ItemType.DECIMAL
+ case XProcConstants.xs_double => ItemType.DOUBLE
+ case XProcConstants.xs_duration => ItemType.DURATION
+ case XProcConstants.xs_ENTITY => ItemType.ENTITY
+ case XProcConstants.xs_float => ItemType.FLOAT
+ case XProcConstants.xs_gDay => ItemType.G_DAY
+ case XProcConstants.xs_gMonth => ItemType.G_MONTH
+ case XProcConstants.xs_gMonthDay => ItemType.G_MONTH_DAY
+ case XProcConstants.xs_gYear => ItemType.G_YEAR
+ case XProcConstants.xs_gYearMonth => ItemType.G_YEAR_MONTH
+ case XProcConstants.xs_hexBinary => ItemType.HEX_BINARY
+ case XProcConstants.xs_ID => ItemType.ID
+ case XProcConstants.xs_IDREF => ItemType.IDREF
+ case XProcConstants.xs_int => ItemType.INT
+ case XProcConstants.xs_integer => ItemType.INTEGER
+ case XProcConstants.xs_language => ItemType.LANGUAGE
+ case XProcConstants.xs_long => ItemType.LONG
+ case XProcConstants.xs_name => ItemType.NAME
+ case XProcConstants.xs_NCName => ItemType.NCNAME
+ case XProcConstants.xs_negativeInteger => ItemType.NEGATIVE_INTEGER
+ case XProcConstants.xs_NMTOKEN => ItemType.NMTOKEN
+ case XProcConstants.xs_nonNegativeInteger => ItemType.NON_NEGATIVE_INTEGER
+ case XProcConstants.xs_nonPositiveInteger => ItemType.NON_POSITIVE_INTEGER
+ case XProcConstants.xs_normalizedString => ItemType.NORMALIZED_STRING
+ case XProcConstants.xs_notation => ItemType.NOTATION
+ case XProcConstants.xs_positiveInteger => ItemType.POSITIVE_INTEGER
+ case XProcConstants.xs_QName => ItemType.QNAME
+ case XProcConstants.xs_short => ItemType.SHORT
+ case XProcConstants.xs_string => ItemType.STRING
+ case XProcConstants.xs_time => ItemType.TIME
+ case XProcConstants.xs_token => ItemType.TOKEN
+ case XProcConstants.xs_unsignedByte => ItemType.UNSIGNED_BYTE
+ case XProcConstants.xs_unsignedInt => ItemType.UNSIGNED_INT
+ case XProcConstants.xs_unsignedLong => ItemType.UNSIGNED_LONG
+ case XProcConstants.xs_unsignedShort => ItemType.UNSIGNED_SHORT
+ case XProcConstants.xs_untypedAtomic => ItemType.UNTYPED_ATOMIC
+ case XProcConstants.xs_yearMonthDuration => ItemType.YEAR_MONTH_DURATION
+ case XProcConstants.xs_anyAtomicType => ItemType.ANY_ATOMIC_VALUE
+ case _ =>
+ throw XProcException.xsInvalidSequenceType(qname.getEQName, "Unknown type", None)
}
}
}
-class TypeUtils(val processor: Processor, val context: StaticContext) {
+class TypeUtils(val processor: Processor, val context: MinimalStaticContext) {
+ protected val logger: Logger = LoggerFactory.getLogger(this.getClass)
private val err_XD0045 = new QName(XProcConstants.ns_err, "XD0045")
- def this(config: XMLCalabash, context: StaticContext) = {
+ def this(config: XMLCalabash, context: MinimalStaticContext) = {
this(config.processor, context)
}
- def this(config: XMLCalabashRuntime, context: StaticContext) = {
+
+ def this(config: XMLCalabashRuntime, context: MinimalStaticContext) = {
this(config.processor, context)
}
- def this(config: XMLCalabash) = {
- this(config.processor, new StaticContext(config))
- }
- def this(config: XMLCalabashRuntime) = {
- this(config.processor, new StaticContext(config))
- }
val typeFactory = new ItemTypeFactory(processor)
-
- // This was added experimentally to handle lists in literal values for include-filter and exclude-filter.
- // It was subsequently decided that literal values shouldn't be lists, so this is no longer being used.
- // I'm leaving it around for the time being (19 Aug 2018) in case it turns out to be useful somewhere
- // else.
- def castSequenceAs(value: XdmAtomicValue, xsdtype: Option[QName], occurrence: String, context: StaticContext): XdmValue = {
- // Today, we only need to handle a sequence of strings
- if (xsdtype.isEmpty || xsdtype.get != XProcConstants.xs_string) {
- throw new IllegalArgumentException("Only lists of strings are supported")
- }
-
- val builder = new SequenceBuilder()
- val list = builder.parse(value.getStringValue)
- val alist = new util.ArrayList[XdmAtomicValue]
-
- val itype = typeFactory.getAtomicType(XProcConstants.xs_string)
- for (item <- list) {
- if (item.as != XProcConstants.xs_string) {
- throw new IllegalArgumentException("Only lists of strings are supported")
- }
- alist.add(new XdmAtomicValue(item.item, itype))
- }
-
- new XdmValue(alist)
- }
+ var types = Option.empty[TypeHierarchy]
def parseSequenceType(seqType: Option[String]): Option[SequenceType] = {
if (seqType.isDefined) {
@@ -242,7 +272,7 @@ class TypeUtils(val processor: Processor, val context: StaticContext) {
val mtypere = "^map\\s*\\((.*)\\)\\s*([*+?])?$".r
val atypere = "^array\\s*\\((.*)\\)\\s*([*+?])?$".r
val ftypere = "^function\\s*\\((.*)\\)\\s*([*+?])?$".r
- val itemre = "^item\\s*\\(\\s*\\)\\s*([*+?])?$".r
+ val itemre = "^item\\s*\\(\\s*\\)\\s*([*+?])?$".r
seqType.trim() match {
case parensre(body) =>
parseSequenceType(body)
@@ -266,7 +296,7 @@ class TypeUtils(val processor: Processor, val context: StaticContext) {
}
val tuplere = "^([^,]+),(.*)$".r
mbody match {
- case tuplere(keyseqtype,itemseqtype) =>
+ case tuplere(keyseqtype, itemseqtype) =>
val keytype = simpleType(keyseqtype)
val itemtype = parseSequenceType(itemseqtype)
SequenceType.makeSequenceType(typeFactory.getMapType(keytype, itemtype), cardinality(cardchar))
@@ -299,7 +329,7 @@ class TypeUtils(val processor: Processor, val context: StaticContext) {
}
val tuplere = "^([^,]+),(.*)$".r
mbody match {
- case tuplere(keyseqtype,itemseqtype) =>
+ case tuplere(keyseqtype, itemseqtype) =>
val keytype = ItemType.ANY_ATOMIC_VALUE
val itemtype = parseSequenceType(itemseqtype)
SequenceType.makeSequenceType(typeFactory.getMapType(keytype, itemtype), cardinality(cardchar))
@@ -312,57 +342,7 @@ class TypeUtils(val processor: Processor, val context: StaticContext) {
}
private def simpleType(typename: String): ItemType = {
- val qname = ValueParser.parseQName(typename, context)
- qname match {
- case XProcConstants.xs_anyURI => ItemType.ANY_URI
- case XProcConstants.xs_base64Binary => ItemType.BASE64_BINARY
- case XProcConstants.xs_boolean => ItemType.BOOLEAN
- case XProcConstants.xs_byte => ItemType.BYTE
- case XProcConstants.xs_date => ItemType.DATE
- case XProcConstants.xs_dateTime => ItemType.DATE_TIME
- case XProcConstants.xs_dateTimeStamp => ItemType.DATE_TIME_STAMP
- case XProcConstants.xs_dayTimeDuration => ItemType.DAY_TIME_DURATION
- case XProcConstants.xs_decimal => ItemType.DECIMAL
- case XProcConstants.xs_double => ItemType.DOUBLE
- case XProcConstants.xs_duration => ItemType.DURATION
- case XProcConstants.xs_ENTITY => ItemType.ENTITY
- case XProcConstants.xs_float => ItemType.FLOAT
- case XProcConstants.xs_gDay => ItemType.G_DAY
- case XProcConstants.xs_gMonth => ItemType.G_MONTH
- case XProcConstants.xs_gMonthDay => ItemType.G_MONTH_DAY
- case XProcConstants.xs_gYear => ItemType.G_YEAR
- case XProcConstants.xs_gYearMonth => ItemType.G_YEAR_MONTH
- case XProcConstants.xs_hexBinary => ItemType.HEX_BINARY
- case XProcConstants.xs_ID => ItemType.ID
- case XProcConstants.xs_IDREF => ItemType.IDREF
- case XProcConstants.xs_int => ItemType.INT
- case XProcConstants.xs_integer => ItemType.INTEGER
- case XProcConstants.xs_language => ItemType.LANGUAGE
- case XProcConstants.xs_long => ItemType.LONG
- case XProcConstants.xs_name => ItemType.NAME
- case XProcConstants.xs_NCName => ItemType.NCNAME
- case XProcConstants.xs_negativeInteger => ItemType.NEGATIVE_INTEGER
- case XProcConstants.xs_NMTOKEN => ItemType.NMTOKEN
- case XProcConstants.xs_nonNegativeInteger => ItemType.NON_NEGATIVE_INTEGER
- case XProcConstants.xs_nonPositiveInteger => ItemType.NON_POSITIVE_INTEGER
- case XProcConstants.xs_normalizedString => ItemType.NORMALIZED_STRING
- case XProcConstants.xs_notation => ItemType.NOTATION
- case XProcConstants.xs_positiveInteger => ItemType.POSITIVE_INTEGER
- case XProcConstants.xs_QName => ItemType.QNAME
- case XProcConstants.xs_short => ItemType.SHORT
- case XProcConstants.xs_string => ItemType.STRING
- case XProcConstants.xs_time => ItemType.TIME
- case XProcConstants.xs_token => ItemType.TOKEN
- case XProcConstants.xs_unsignedByte => ItemType.UNSIGNED_BYTE
- case XProcConstants.xs_unsignedInt => ItemType.UNSIGNED_INT
- case XProcConstants.xs_unsignedLong => ItemType.UNSIGNED_LONG
- case XProcConstants.xs_unsignedShort => ItemType.UNSIGNED_SHORT
- case XProcConstants.xs_untypedAtomic => ItemType.UNTYPED_ATOMIC
- case XProcConstants.xs_yearMonthDuration => ItemType.YEAR_MONTH_DURATION
- case XProcConstants.xs_anyAtomicType => ItemType.ANY_ATOMIC_VALUE
- case _ =>
- throw XProcException.xsInvalidSequenceType(qname.getClarkName, "Unknown type", context.location)
- }
+ TypeUtils.simpleType(context.parseQName(typename))
}
private def cardinality(card: String): OccurrenceIndicator = {
@@ -412,4 +392,68 @@ class TypeUtils(val processor: Processor, val context: StaticContext) {
val itype = typeFactory.getAtomicType(dtype)
new XdmAtomicValue(value, itype)
}
-}
+
+ def checkType(name: QName, valueMsg: XdmValueItemMessage, seqType: Option[SequenceType], tokenList: Option[List[XdmAtomicValue]]): Unit = {
+ convertType(name, valueMsg, seqType, tokenList)
+ }
+
+ def convertType(name: QName, valueMsg: XdmValueItemMessage, seqType: Option[SequenceType], tokenList: Option[List[XdmAtomicValue]]): XdmValueItemMessage = {
+ if (types.isEmpty) {
+ types = Some(processor.getUnderlyingConfiguration.getTypeHierarchy)
+ }
+
+ var value = valueMsg.item
+ if (seqType.isDefined) {
+ // Special cases
+ if (Option(seqType.get.getItemType).isDefined) {
+ seqType.get.getItemType match {
+ case ItemType.QNAME =>
+ value.getUnderlyingValue match {
+ case _: StringValue =>
+ val msg = new XdmValueItemMessage(new XdmAtomicValue(valueMsg.context.parseQName(value.getUnderlyingValue.getStringValue)), valueMsg.metadata, valueMsg.context)
+ value = msg.item
+ case _ =>
+ ()
+ }
+ case ItemType.ANY_URI =>
+ value.getUnderlyingValue match {
+ case _: StringValue =>
+ val msg = new XdmValueItemMessage(new XdmAtomicValue(new URI(value.getUnderlyingValue.getStringValue)), valueMsg.metadata, valueMsg.context)
+ value = msg.item
+ case _ =>
+ ()
+ }
+ case _ =>
+ ()
+ }
+ }
+
+ try {
+ val diag = new RoleDiagnostic(RoleDiagnostic.OPTION, name.toString, 0)
+ val convseq = types.get.applyFunctionConversionRules(value.getUnderlyingValue, seqType.get.getUnderlyingSequenceType, diag, Loc.NONE)
+ value = XdmValue.wrap(convseq)
+ } catch {
+ case ex: ValidationException =>
+ logger.debug(ex.getMessage)
+ throw XProcException.xdBadType(name, value.getUnderlyingValue.toString, seqType.get.toString, None)
+ case ex: XPathException =>
+ logger.debug(ex.getMessage)
+ throw XProcException.xdBadType(name, value.getUnderlyingValue.toString, seqType.get.toString, None)
+ case ex: Exception =>
+ logger.debug(ex.getMessage)
+ throw ex
+ }
+ }
+
+ if (tokenList.isDefined) {
+ if (value ne XdmEmptySequence.getInstance()) {
+ val matched = tokenList.get find { _ == value }
+ if (matched.isEmpty) {
+ throw XProcException.xdBadValue(value.toString, None)
+ }
+ }
+ }
+
+ new XdmValueItemMessage(value, valueMsg.metadata, valueMsg.context)
+ }
+}
\ No newline at end of file
diff --git a/src/main/scala/com/xmlcalabash/util/URIUtils.scala b/src/main/scala/com/xmlcalabash/util/URIUtils.scala
index 7035269..3b343f2 100644
--- a/src/main/scala/com/xmlcalabash/util/URIUtils.scala
+++ b/src/main/scala/com/xmlcalabash/util/URIUtils.scala
@@ -60,7 +60,7 @@ object URIUtils {
// don't double-escape %-escaped chars!
// FIXME: This should be more general, but Windows seems to be the only problem
- // and I'm oto lazy to look up how to dyanmically escape "\"
+ // and I'm to lazy to look up how to dyanmically escape "\"
val filesep = System.getProperty("file.separator")
val adjsrc = if ("\\" == filesep) {
@@ -70,7 +70,7 @@ object URIUtils {
}
var encoded = ""
- val bytes = src.getBytes("UTF-8")
+ val bytes = adjsrc.getBytes("UTF-8")
for (ch <- bytes) {
if (okChars.indexOf(ch) >= 0) {
encoded += ch.toChar
diff --git a/src/main/scala/com/xmlcalabash/util/Urify.scala b/src/main/scala/com/xmlcalabash/util/Urify.scala
index f8c7149..13362c0 100644
--- a/src/main/scala/com/xmlcalabash/util/Urify.scala
+++ b/src/main/scala/com/xmlcalabash/util/Urify.scala
@@ -44,6 +44,10 @@ object Urify {
urify(filestr, Some(basedir))
}
+ def urify(filestr: String, basedir: URI): String = {
+ urify(filestr, Some(basedir.toString))
+ }
+
def urify(filestr: String, basedir: Option[String]): String = {
val filepath = new Urify(filestr, basedir)
if (!filepath.hierarchical || (filepath.scheme.isDefined && filepath.absolute)) {
diff --git a/src/main/scala/com/xmlcalabash/util/VoidLocation.scala b/src/main/scala/com/xmlcalabash/util/VoidLocation.scala
index f037e60..1f9361c 100644
--- a/src/main/scala/com/xmlcalabash/util/VoidLocation.scala
+++ b/src/main/scala/com/xmlcalabash/util/VoidLocation.scala
@@ -9,7 +9,7 @@ object VoidLocation {
def instance(): VoidLocation = loc
}
-class VoidLocation extends Location {
+class VoidLocation private extends Location {
override def getSystemId: String = null
override def getPublicId: String = null
diff --git a/src/main/scala/com/xmlcalabash/util/XProcVarValue.scala b/src/main/scala/com/xmlcalabash/util/XProcVarValue.scala
index b999298..143d05d 100644
--- a/src/main/scala/com/xmlcalabash/util/XProcVarValue.scala
+++ b/src/main/scala/com/xmlcalabash/util/XProcVarValue.scala
@@ -1,9 +1,8 @@
package com.xmlcalabash.util
-import com.xmlcalabash.runtime.StaticContext
import net.sf.saxon.s9api.XdmValue
-class XProcVarValue(val value: XdmValue, val context: StaticContext) {
+class XProcVarValue(val value: XdmValue, val context: MinimalStaticContext) {
def getStringValue: String = {
val iter = value.iterator()
var s = ""
diff --git a/src/main/scala/com/xmlcalabash/util/XdmLocation.scala b/src/main/scala/com/xmlcalabash/util/XdmLocation.scala
new file mode 100644
index 0000000..e264608
--- /dev/null
+++ b/src/main/scala/com/xmlcalabash/util/XdmLocation.scala
@@ -0,0 +1,125 @@
+package com.xmlcalabash.util
+
+import com.jafpl.graph.Location
+import com.xmlcalabash.exceptions.XProcException
+import net.sf.saxon.s9api.{Axis, XdmNode, XdmNodeKind}
+
+import java.net.{URI, URISyntaxException}
+import scala.xml.SAXParseException
+
+object XdmLocation {
+ // Can't use a constructor for this because node.getBaseURI might fail
+ def from(node: XdmNode): Option[XdmLocation] = {
+ val uristr = node.getUnderlyingNode.getBaseURI
+ if (Option(uristr).isEmpty) {
+ None
+ } else {
+ try {
+ val buri = new URI(uristr)
+ Some(new XdmLocation(node))
+ } catch {
+ case _: URISyntaxException =>
+ throw XProcException.xdInvalidURI(uristr, None)
+ case ex: Exception =>
+ throw ex
+ }
+ }
+ }
+}
+
+class XdmLocation private(private val puri: String, private val pline: Int, private val pcol: Int) extends Location {
+ private var _uri: Option[String] = Some(puri)
+ private var _line = pline
+ private var _col = pcol
+
+ private def this(node: XdmNode) = {
+ this(Urify.urify(node.getBaseURI.toString), node.getLineNumber, node.getColumnNumber)
+
+ // Special case: if there's a preceding sibling PI that contains the original
+ // location, decode that and use it.
+ var pi = Option.empty[XdmNode]
+ var found = false
+ val iter = node.axisIterator(Axis.PRECEDING_SIBLING)
+ while (iter.hasNext) {
+ val pnode = iter.next()
+ if (!found) {
+ pnode.getNodeKind match {
+ case XdmNodeKind.TEXT =>
+ if (pnode.getStringValue.trim != "") {
+ found = true
+ }
+ case XdmNodeKind.PROCESSING_INSTRUCTION =>
+ if (pnode.getNodeName.getLocalName == "_xmlcalabash") {
+ pi = Some(pnode)
+ found = true
+ }
+ case _ => found = true
+ }
+ }
+ }
+
+ if (found && pi.isDefined) {
+ val str = pi.get.getStringValue
+ val uripatn = ".*uri=\"([^\"]+)\".*".r
+ val linepatn = ".*line=\"(\\d+)\".*".r
+ val colpatn = ".*column=\"(\\d+)\".*".r
+
+ str match {
+ case uripatn(uri) =>
+ _uri = Some(uri)
+ case _ =>
+ _uri = None
+ }
+
+ str match {
+ case linepatn(line) =>
+ _line = line.toInt
+ case _ =>
+ _line = -1
+ }
+
+ str match {
+ case colpatn(col) =>
+ _col = col.toInt
+ case _ =>
+ _col = -1
+ }
+ }
+ }
+
+ def this(ex: SAXParseException) = {
+ this(ex.getSystemId, ex.getLineNumber, ex.getColumnNumber)
+ }
+
+ override def uri: Option[String] = _uri
+
+ override def line: Option[Long] = {
+ if (_line > 0) {
+ Some(_line.toLong)
+ } else {
+ None
+ }
+ }
+
+ override def column: Option[Long] = {
+ if (_col > 0) {
+ Some(_col.toLong)
+ } else {
+ None
+ }
+ }
+
+ override def toString: String = {
+ var str = ""
+ if (_uri.isDefined) {
+ str += _uri.get
+ }
+ if (line.isDefined) {
+ str += ":" + line.get.toString
+ }
+ if (column.isDefined) {
+ str += ":" + column.get.toString
+ }
+ str
+ }
+}
diff --git a/src/main/scala/com/xmlcalabash/util/XmlItemComparator.scala b/src/main/scala/com/xmlcalabash/util/XmlItemComparator.scala
index 439fc54..26c9983 100644
--- a/src/main/scala/com/xmlcalabash/util/XmlItemComparator.scala
+++ b/src/main/scala/com/xmlcalabash/util/XmlItemComparator.scala
@@ -5,13 +5,13 @@ import com.jafpl.util.ItemComparator
import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
import com.xmlcalabash.messages.XdmValueItemMessage
-import com.xmlcalabash.model.xml.ForUntil
+import com.xmlcalabash.model.xxml.{XForUntil, XStaticContext}
import com.xmlcalabash.runtime.{DynamicContext, StaticContext, XProcMetadata, XProcXPathExpression}
import net.sf.saxon.s9api.{XdmNode, XdmValue}
import scala.collection.mutable
-class XmlItemComparator(config: XMLCalabash, comparator: String, maxIterations: Long, art: ForUntil) extends ItemComparator {
+class XmlItemComparator(config: XMLCalabash, comparator: String, maxIterations: Long, art: XForUntil) extends ItemComparator {
private var count = 0L
override def areTheSame(a: Any, b: Any): Boolean = {
@@ -38,7 +38,7 @@ class XmlItemComparator(config: XMLCalabash, comparator: String, maxIterations:
}
private def sameNode(anode: XdmNode, bnode: XdmNode): Boolean = {
- val context = new StaticContext(config)
+ val context = new XStaticContext()
val expr = new XProcXPathExpression(context, comparator)
val bindingsMap = mutable.HashMap.empty[String, Message]
val amsg = new XdmValueItemMessage(anode, XProcMetadata.XML, context)
@@ -47,7 +47,7 @@ class XmlItemComparator(config: XMLCalabash, comparator: String, maxIterations:
bindingsMap.put("{}b", bmsg)
var same = false
- val dynamicContext = new DynamicContext(Some(art))
+ val dynamicContext = new DynamicContext(art)
DynamicContext.withContext(dynamicContext) {
val exeval = config.expressionEvaluator.newInstance()
same = exeval.booleanValue(expr, List(amsg), bindingsMap.toMap, None)
diff --git a/src/main/scala/com/xmlcalabash/util/XmlItemTester.scala b/src/main/scala/com/xmlcalabash/util/XmlItemTester.scala
index c96d048..86fc669 100644
--- a/src/main/scala/com/xmlcalabash/util/XmlItemTester.scala
+++ b/src/main/scala/com/xmlcalabash/util/XmlItemTester.scala
@@ -2,12 +2,11 @@ package com.xmlcalabash.util
import com.jafpl.messages.Message
import com.jafpl.util.ItemTester
-import com.xmlcalabash.XMLCalabash
import com.xmlcalabash.exceptions.XProcException
-import com.xmlcalabash.model.xml.ForWhile
-import com.xmlcalabash.runtime.{DynamicContext, StaticContext, XProcXPathExpression}
+import com.xmlcalabash.model.xxml.{XForWhile, XStaticContext}
+import com.xmlcalabash.runtime.{DynamicContext, SaxonExpressionEvaluator, XMLCalabashRuntime, XProcXPathExpression}
-class XmlItemTester(config: XMLCalabash, comparator: String, maxIterations: Long, art: ForWhile) extends ItemTester {
+class XmlItemTester(runtime: XMLCalabashRuntime, comparator: String, maxIterations: Long, art: XForWhile) extends ItemTester {
private var count = 0L
override def test(item: List[Message], bindings: Map[String, Message]): Boolean = {
@@ -16,13 +15,12 @@ class XmlItemTester(config: XMLCalabash, comparator: String, maxIterations: Long
}
count += 1
- val context = new StaticContext(config)
- val expr = new XProcXPathExpression(context, comparator)
+ val expr = new XProcXPathExpression(art.staticContext, comparator)
var pass = false
- val dynamicContext = new DynamicContext(Some(art))
+ val dynamicContext = new DynamicContext(runtime, art)
DynamicContext.withContext(dynamicContext) {
- val exeval = config.expressionEvaluator.newInstance()
+ val exeval = runtime.expressionEvaluator.newInstance()
pass = exeval.booleanValue(expr, item, bindings, None)
}
diff --git a/src/test/resources/com.xmlcalabash.properties b/src/test/resources/com.xmlcalabash.properties
deleted file mode 100644
index e026d2a..0000000
--- a/src/test/resources/com.xmlcalabash.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-cx = namespace http://xmlcalabash.com/ns/extensions
-com.xmlcalabash.steps.StepOutputTest = step cx:step-output-test
diff --git a/src/test/resources/com.xmlcalabash.settings b/src/test/resources/com.xmlcalabash.settings
new file mode 100644
index 0000000..feaf5f3
--- /dev/null
+++ b/src/test/resources/com.xmlcalabash.settings
@@ -0,0 +1,2 @@
+namespace http://xmlcalabash.com/ns/extensions = cx
+step cx:step-output-test = com.xmlcalabash.steps.StepOutputTest
diff --git a/src/test/resources/extension-tests.txt b/src/test/resources/extension-tests.txt
index 2c255ee..a7e4aae 100644
--- a/src/test/resources/extension-tests.txt
+++ b/src/test/resources/extension-tests.txt
@@ -1,6 +1,3 @@
-collection-attr-001.xml
-collection-attr-002.xml
-collection-attr-003.xml
cx-base64-encode.xml
cx-loop-001.xml
cx-loop-002.xml
diff --git a/src/test/resources/extension-tests/tests/collection-attr-001.xml b/src/test/resources/extension-tests/tests/collection-attr-001.xml
deleted file mode 100644
index 5357104..0000000
--- a/src/test/resources/extension-tests/tests/collection-attr-001.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The pipeline root is not c:result
-
-
- The result is not 2
-
-
-
-
-
-
diff --git a/src/test/resources/extension-tests/tests/collection-attr-002.xml b/src/test/resources/extension-tests/tests/collection-attr-002.xml
deleted file mode 100644
index 9211822..0000000
--- a/src/test/resources/extension-tests/tests/collection-attr-002.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/test/resources/extension-tests/tests/collection-attr-003.xml b/src/test/resources/extension-tests/tests/collection-attr-003.xml
deleted file mode 100644
index 97649e6..0000000
--- a/src/test/resources/extension-tests/tests/collection-attr-003.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- fail
-
-
-
-
- fail
-
-
-
-
-
-
diff --git a/src/test/scala/com/xmlcalabash/drivers/ParserTest.scala b/src/test/scala/com/xmlcalabash/drivers/ParserTest.scala
new file mode 100644
index 0000000..1a0958d
--- /dev/null
+++ b/src/test/scala/com/xmlcalabash/drivers/ParserTest.scala
@@ -0,0 +1,75 @@
+package com.xmlcalabash.drivers
+
+import com.xmlcalabash.XMLCalabash
+import com.xmlcalabash.config.DocumentRequest
+import com.xmlcalabash.exceptions.XProcException
+import com.xmlcalabash.messages.XdmValueItemMessage
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.model.xxml.{XOption, XParser}
+import com.xmlcalabash.runtime.{StaticContext, XProcMetadata}
+import com.xmlcalabash.testing.TestRunner
+import com.xmlcalabash.util.{DefaultErrorExplanation, MediaType, S9Api}
+import net.sf.saxon.s9api.{Processor, QName, Serializer, XdmAtomicValue, XdmDestination, XdmValue}
+import org.slf4j.LoggerFactory
+
+import java.io.{BufferedReader, File, FileReader, PrintWriter}
+import java.net.URI
+import java.nio.file.{Files, Paths}
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+import scala.jdk.javaapi.CollectionConverters.asJava
+
+object ParserTest extends App {
+ private val config: XMLCalabash = XMLCalabash.newInstance()
+ private val explainer = new DefaultErrorExplanation()
+
+ config.args.config(XProcConstants.cc_graph.getEQName, "pipeline")
+ config.args.config(XProcConstants.cc_graph.getEQName, "graph")
+ config.args.config(XProcConstants.cc_graph.getEQName, "graph-open")
+ config.args.option("limit", 10)
+
+ config.configure()
+
+ val xparser = new XParser(config)
+ try {
+ //val decl = xparser.loadDeclareStep(new URI("file:///Users/ndw/Projects/xproc/gradle-plugin/examples/json/countdown.xpl"))
+ val decl = xparser.loadDeclareStep(new URI("file:///Users/ndw/Projects/xproc/meerschaum/pipe.xpl"))
+ if (decl.exceptions.isEmpty) {
+ println("SUCCESS")
+ println(decl.dump)
+
+ val runtime = decl.runtime()
+ //runtime.run()
+ //println(decl.dump)
+ } else {
+ println(s"FAIL: ${decl.exceptions.length}")
+ var count = 0
+ for (ex <- decl.exceptions) {
+ count += 1
+ ex match {
+ case xp: XProcException =>
+ println(s"${count}: ${xp.location.getOrElse("")} ${explain(ex)}")
+ case _ =>
+ println(s"${count}: ${explain(ex)}")
+ }
+ }
+ }
+ } catch {
+ case ex: Exception =>
+ println(explain(ex))
+ var count = 0
+ for (ex <- xparser.exceptions) {
+ count += 1
+ println(s"${count}: ${explain(ex)}")
+ }
+ }
+
+ private def explain(ex: Exception): String = {
+ ex match {
+ case xp: XProcException =>
+ explainer.message(xp.code, xp.variant, xp.details)
+ case _ =>
+ ex.getMessage
+ }
+ }
+}
diff --git a/src/test/scala/com/xmlcalabash/steps/StepOutputTest.scala b/src/test/scala/com/xmlcalabash/steps/StepOutputTest.scala
index 2073211..13d27a1 100644
--- a/src/test/scala/com/xmlcalabash/steps/StepOutputTest.scala
+++ b/src/test/scala/com/xmlcalabash/steps/StepOutputTest.scala
@@ -1,14 +1,11 @@
package com.xmlcalabash.steps
-import java.io.ByteArrayInputStream
-
-import com.xmlcalabash.model.xml.XMLContext
import com.xmlcalabash.model.util.{SaxonTreeBuilder, XProcConstants}
import com.xmlcalabash.runtime.{StaticContext, XProcMetadata, XmlPortSpecification}
-import com.xmlcalabash.util.MediaType
-import com.xmlcalabash.util.TypeUtils.castAsXml
+import com.xmlcalabash.util.{MediaType, MinimalStaticContext}
import net.sf.saxon.s9api.{QName, XdmAtomicValue, XdmMap, XdmValue}
-import net.sf.saxon.value.AtomicValue
+
+import java.io.ByteArrayInputStream
class StepOutputTest() extends DefaultXmlStep {
private val _content_type = XProcConstants._content_type
@@ -17,7 +14,7 @@ class StepOutputTest() extends DefaultXmlStep {
override def inputSpec: XmlPortSpecification = XmlPortSpecification.NONE
override def outputSpec: XmlPortSpecification = XmlPortSpecification.ANYRESULT
- override def run(context: StaticContext): Unit = {
+ override def run(context: MinimalStaticContext): Unit = {
val contentType = MediaType.parse(stringBinding(_content_type))
val resultType = stringBinding(_result_type)
val metadata = new XProcMetadata(contentType, Map.empty[QName,XdmValue])
diff --git a/src/test/scala/com/xmlcalabash/test/XStaticContextSpec.scala b/src/test/scala/com/xmlcalabash/test/XStaticContextSpec.scala
new file mode 100644
index 0000000..7390a34
--- /dev/null
+++ b/src/test/scala/com/xmlcalabash/test/XStaticContextSpec.scala
@@ -0,0 +1,107 @@
+package com.xmlcalabash.test
+
+import com.jafpl.graph.Location
+import com.xmlcalabash.model.util.XProcConstants
+import com.xmlcalabash.model.xxml.XMLStaticContext
+import net.sf.saxon.s9api.QName
+import org.scalatest.flatspec.AnyFlatSpec
+
+import scala.collection.mutable
+
+object VoidLocation extends Location {
+ override def uri: Option[String] = None
+ override def line: Option[Long] = None
+ override def column: Option[Long] = None
+}
+
+class XStaticContextSpec extends AnyFlatSpec {
+ private val nsmap = mutable.HashMap.empty[String,String]
+
+ nsmap.put("p", XProcConstants.ns_p)
+ nsmap.put("cx", XProcConstants.ns_cx)
+
+ private val staticContext = new XMLStaticContext(new QName("", "irrelevant"), VoidLocation, nsmap.toMap)
+
+ "p:foo " should "be a valid QName" in {
+ val qname = staticContext.parseQName("p:foo")
+ assert(qname.getPrefix == "p")
+ assert(qname.getNamespaceURI == XProcConstants.ns_p)
+ assert(qname.getLocalName == "foo")
+ }
+
+ "foo " should "be a valid QName" in {
+ val qname = staticContext.parseQName("foo")
+ assert(qname.getPrefix == "")
+ assert(qname.getNamespaceURI == "")
+ assert(qname.getLocalName == "foo")
+ }
+
+ "Q{http://example.com/}foo " should "be a valid QName" in {
+ val qname = staticContext.parseQName("Q{http://example.com/}foo")
+ assert(qname.getPrefix == "")
+ assert(qname.getNamespaceURI == "http://example.com/")
+ assert(qname.getLocalName == "foo")
+ }
+
+ ":foo " should " throw an exception" in {
+ try {
+ staticContext.parseQName(":foo")
+ fail()
+ } catch {
+ case _: Exception =>
+ ()
+ }
+ }
+
+ "xyz:foo " should " throw an exception" in {
+ try {
+ staticContext.parseQName("xyz:foo")
+ fail()
+ } catch {
+ case _: Exception =>
+ ()
+ }
+ }
+
+ "Q{foo " should " throw an exception" in {
+ try {
+ staticContext.parseQName("Q{foo")
+ fail()
+ } catch {
+ case _: Exception =>
+ ()
+ }
+ }
+
+ "3 " should " throw an exception" in {
+ try {
+ staticContext.parseQName("3")
+ fail()
+ } catch {
+ case _: Exception =>
+ ()
+ }
+ }
+
+ "p:3 " should " throw an exception" in {
+ try {
+ staticContext.parseQName("p:3")
+ fail()
+ } catch {
+ case _: Exception =>
+ ()
+ }
+ }
+
+ "p:foo:bar " should " throw an exception" in {
+ try {
+ staticContext.parseQName("p:foo:bar")
+ fail()
+ } catch {
+ case _: Exception =>
+ ()
+ }
+ }
+
+
+}