blob: f72225d56f26d3335f4c9fc37ab084e2c423b83a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.daffodil.sapi
import java.io.File
import java.net.URI
import java.nio.channels.Channels
import java.nio.channels.ReadableByteChannel
import java.nio.channels.WritableByteChannel
import org.apache.daffodil.api.DFDL.{ DaffodilUnhandledSAXException => SDaffodilUnhandledSAXException }
import org.apache.daffodil.api.DFDL.{ DaffodilUnparseErrorSAXException => SDaffodilUnparseErrorSAXException }
import org.apache.daffodil.api.URISchemaSource
import org.apache.daffodil.api.Validator
import org.apache.daffodil.api.{ DataLocation => SDataLocation }
import org.apache.daffodil.api.{ Diagnostic => SDiagnostic }
import org.apache.daffodil.api.{ LocationInSchemaFile => SLocationInSchemaFile }
import org.apache.daffodil.api.{ WithDiagnostics => SWithDiagnostics }
import org.apache.daffodil.compiler.{ Compiler => SCompiler }
import org.apache.daffodil.compiler.{ InvalidParserException => SInvalidParserException }
import org.apache.daffodil.compiler.{ ProcessorFactory => SProcessorFactory }
import org.apache.daffodil.debugger.Debugger
import org.apache.daffodil.debugger.{ InteractiveDebugger => SInteractiveDebugger }
import org.apache.daffodil.debugger.{ TraceDebuggerRunner => STraceDebuggerRunner }
import org.apache.daffodil.dsom.ExpressionCompilers
import org.apache.daffodil.dsom.walker.RootView
import org.apache.daffodil.externalvars.{ Binding, ExternalVariablesLoader }
import org.apache.daffodil.processors.{ DaffodilParseXMLReader => SDaffodilParseXMLReader }
import org.apache.daffodil.processors.{ DaffodilUnparseContentHandler => SDaffodilUnparseContentHandler }
import org.apache.daffodil.processors.{ DataProcessor => SDataProcessor }
import org.apache.daffodil.processors.{ ExternalVariableException => SExternalVariableException }
import org.apache.daffodil.processors.{ InvalidUsageException => SInvalidUsageException }
import org.apache.daffodil.processors.{ ParseResult => SParseResult }
import org.apache.daffodil.processors.{ UnparseResult => SUnparseResult }
import org.apache.daffodil.sapi.ValidationMode.ValidationMode
import org.apache.daffodil.sapi.debugger._
import org.apache.daffodil.sapi.infoset._
import org.apache.daffodil.sapi.io.InputSourceDataInputStream
import org.apache.daffodil.sapi.logger._
import org.apache.daffodil.sapi.packageprivate._
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe._
import org.apache.daffodil.util.MaybeULong
import org.apache.daffodil.xml.NS
import org.apache.daffodil.xml.XMLUtils
import org.apache.daffodil.xml.DFDLCatalogResolver
private class Daffodil private {
// Having this empty but private companion class removes the constructor from
// Scaladocs, as well as prevents the creation of a Daffodil class,
// forcing one to use the static methods on the Daffodil object
}
/**
* Factory object to create a [[Compiler]] and set global configurations
*/
object Daffodil {
/** Create a new object used to compiled DFDL schemas */
def compiler(): Compiler = {
new Daffodil // silence warning about unused private constructor above.
new Compiler(SCompiler())
}
/** Set the LogWriter to use to capture logging messages from Daffodil */
@deprecated("Use Log4j for logging", "3.2.0")
def setLogWriter(lw: LogWriter): Unit = {}
/** Set the maximum logging level */
@deprecated("Use Log4j for logging", "3.2.0")
def setLoggingLevel(lvl: LogLevel.Value): Unit = {}
}
/**
* Validation modes for validating the resulting infoset against the DFDL schema
*/
object ValidationMode extends Enumeration {
type ValidationMode = Value
val Off = Value(10)
val Limited = Value(20)
val Full = Value(30)
case class Custom(v: Validator) extends ValidationMode {
val id: Int = 100
}
}
/**
* Compile DFDL schemas into [[ProcessorFactory]]'s or reload saved parsers into [[DataProcessor]]'s.
*/
class Compiler private[sapi] (private var sCompiler: SCompiler) {
private def copy(sCompiler: SCompiler = sCompiler) = new Compiler(sCompiler)
/**
* Compile DFDL schema file into a [[ProcessorFactory]]
*
* To allow jar-file packaging, (where schema files might be part of a jar),
* it is recommended to use [[Compiler.compileSource]] instead.
*
* @param schemaFile DFDL schema file used to create a [[ProcessorFactory]].
* @param optRootName Option for name of root element, or None to choose automatically from first element of schema.
* Defaults to None.
* @param optRootNamespace Option for string of namespace of the root element, or None to infer automatically when
* unambiguous. Pass Some("") (empty string) for No Namespace. Defaults to None.
* @return [[ProcessorFactory]] used to create [[DataProcessor]](s). Must check [[ProcessorFactory.isError]] before using it.
*/
@throws(classOf[java.io.IOException])
def compileFile(schemaFile: File,
optRootName: Option[String] = None,
optRootNamespace: Option[String] = None): ProcessorFactory = {
val pf = sCompiler.compileFile(schemaFile, optRootName, optRootNamespace)
pf.isError
new ProcessorFactory(pf)
}
/**
* Compile DFDL schema source into a [[ProcessorFactory]]
*
* @param uri URI of DFDL schema file used to create a [[ProcessorFactory]].
* @param optRootName Option for name of root element, or None to choose automatically from first
* element of schema. Defaults to None.
* @param optRootNamespace Option for string of namespace of the root element, or None to infer
* automatically when unambiguous. Pass Some("") (empty string) for No Namespace.
* Defaults to None.
* @return [[ProcessorFactory]] used to create [[DataProcessor]](s). Must check [[ProcessorFactory.isError]] before using it.
*/
@throws(classOf[java.io.IOException])
def compileSource(uri: URI,
optRootName: Option[String] = None,
optRootNamespace: Option[String] = None): ProcessorFactory = {
val source = URISchemaSource(uri)
val pf = sCompiler.compileSource(source, optRootName, optRootNamespace)
new ProcessorFactory(pf.asInstanceOf[SProcessorFactory])
}
/**
* Reload a saved parser from a file
*
* To allow jar-file packaging, (where the savedParser might be part of a jar),
* it is recommended to use the other version of [[Compiler.reload(savedParser:java\.nio\.channels\.ReadableByteChannel)* Compiler.reload]] where the argument is
* a java.nio.channels.ReadableByteChannel for a saved parser.
*
* @param savedParser file of a saved parser, created with [[DataProcessor.save]]
* @return [[DataProcessor]] used to parse data. Must check [[DataProcessor.isError]] before using it.
* @throws InvalidParserException if the file is not a valid saved parser.
*/
def reload(savedParser: File): DataProcessor = {
try {
new DataProcessor(sCompiler.reload(savedParser).asInstanceOf[SDataProcessor])
} catch {
case ex: SInvalidParserException => throw new InvalidParserException(ex)
}
}
/**
* Reload a saved parser from a java.nio.channels.ReadableByteChannel
*
* @param savedParser java.nio.channels.ReadableByteChannel of a saved parser, created with [[DataProcessor.save]]
* @return [[DataProcessor]] used to parse data. Must check [[DataProcessor.isError]] before using it.
* @throws InvalidParserException if the file is not a valid saved parser.
*/
def reload(savedParser: ReadableByteChannel): DataProcessor = {
try {
new DataProcessor(sCompiler.reload(savedParser).asInstanceOf[SDataProcessor])
} catch {
case ex: SInvalidParserException => throw new InvalidParserException(ex)
}
}
/**
* Specify a global element to be the root of DFDL Schema to start parsing
*
* @param name name of the root node
* @param namespace namespace of the root node. Set to empty string to specify
* no namespace. Set to to NULL to figure out the namespace.
*/
@deprecated("Pass arguments to compileSource, or compileFile.", "2.6.0")
def setDistinguishedRootNode(name: String, namespace: String): Unit =
sCompiler = sCompiler.withDistinguishedRootNode(name, namespace)
/**
* Set the value of a DFDL variable
*
* @param name name of the variable
* @param namespace namespace of the variable. Set to empty string to specify
* no namespace. Set to to NULL to figure out the namespace.
* @param value value to so the variable to
*/
@deprecated("Use DataProcessor.setExternalVariables", "2.6.0")
def setExternalDFDLVariable(name: String, namespace: String, value: String): Unit = {
val bindings = Seq(Binding(name, Some(NS(namespace)), value))
sCompiler = sCompiler.withExternalDFDLVariablesImpl(bindings)
}
/**
* Set the value of multiple DFDL variables
*
* @param extVarsMap a may of key/value pairs, where the key is the variable
* name, and the value is the value of the variable. The key
* may be preceded by a string of the form "{namespace}" to
* define a namespace for the variable. If preceded with "{}",
* then no namespace is used. With not preceded by "{namespace}",
* then Daffodil will figure out the namespace.
*/
@deprecated("Use DataProcessor.setExternalVariables", "2.6.0")
def setExternalDFDLVariables(extVarsMap: Map[String, String]): Unit = {
val extVars = ExternalVariablesLoader.mapToBindings(extVarsMap)
sCompiler = sCompiler.withExternalDFDLVariablesImpl(extVars)
}
/**
* Read external variables from a Daffodil configuration file
*
* @see <a target="_blank" href='https://daffodil.apache.org/configuration/'>Daffodil Configuration File</a> - Daffodil configuration file format
*
* @param extVarsFile file to read DFDL variables from.
*/
@deprecated("Use DataProcessor.setExternalVariables", "2.6.0")
def setExternalDFDLVariables(extVarsFile: File): Unit = {
val extVars = ExternalVariablesLoader.fileToBindings(extVarsFile)
sCompiler = sCompiler.withExternalDFDLVariablesImpl(extVars)
}
/**
* Enable/disable DFDL validation of resulting infoset with the DFDL schema
*
* @param value true to enable validation, false to disabled
*/
@deprecated("Do not use this method. DFDL schema validation should be performed.", "2.6.0")
def setValidateDFDLSchemas(value: Boolean): Unit = {
sCompiler = sCompiler.withValidateDFDLSchemas(value)
}
/**
* Set a Daffodil tunable parameter
*
* @see <a target="_blank" href='https://daffodil.apache.org/configuration/#tunable-parameters'>Tunable Parameters</a> - list of tunables names of default values
*
* @param tunable name of the tunable parameter to set.
* @param value value of the tunable parameter to set
*/
@deprecated("Use withTunable.", "2.6.0")
def setTunable(tunable: String, value: String): Unit = {
sCompiler = sCompiler.withTunable(tunable, value)
}
/**
* Return a new [[Compiler]] with a specific Daffodil tunable parameter
*
* @see <a target="_blank" href='https://daffodil.apache.org/configuration/#tunable-parameters'>Tunable Parameters</a> - list of tunables names of default values
*
* @param tunable name of the tunable parameter to set.
* @param value value of the tunable parameter to set
*/
def withTunable(tunable: String, value: String): Compiler = {
copy(sCompiler = sCompiler.withTunable(tunable, value))
}
/**
* Set the value of multiple tunable parameters
*
* @see <a target="_blank" href='https://daffodil.apache.org/configuration/#tunable-parameters'>Tunable Parameters</a> - list of tunables names of default values
*
* @param tunables a map of key/value pairs, where the key is the tunable name and the value is the value to set it to
*/
@deprecated("Use withTunables.", "2.6.0")
def setTunables(tunables: Map[String, String]): Unit = {
sCompiler = sCompiler.withTunables(tunables.toMap)
}
/**
* Return a new [[Compiler]] with multiple tunable parameters
*
* @see <a target="_blank" href='https://daffodil.apache.org/configuration/#tunable-parameters'>Tunable Parameters</a> - list of tunables names of default values
*
* @param tunables a map of key/value pairs, where the key is the tunable name and the value is the value to set it to
*/
def withTunables(tunables: Map[String, String]): Compiler = {
copy(sCompiler = sCompiler.withTunables(tunables.toMap))
}
}
/**
* Factory to create [[DataProcessor]]'s, used for parsing data
*/
class ProcessorFactory private[sapi] (private var pf: SProcessorFactory)
extends WithDiagnostics(pf) {
private def copy(pf: SProcessorFactory = pf) = new ProcessorFactory(pf)
/**
* Specify a global element to be the root of DFDL Schema to start parsing
*
* @param name name of the root node
* @param namespace namespace of the root node. Set to empty string to specify
* no namespace. Set to to NULL to figure out the namespace.
*/
@deprecated("Use withDistinguishedRootNode.", "2.6.0")
def setDistinguishedRootNode(name: String, namespace: String): Unit =
pf = pf.withDistinguishedRootNode(name, namespace)
/**
* Get a new [[ProcessorFactory]] having a global element specified as the root of DFDL Schema to start parsing.
*
* @param name name of the root node
* @param namespace namespace of the root node. Set to empty string to specify
* no namespace. Set to to NULL to figure out the namespace.
*/
def withDistinguishedRootNode(name: String, namespace: String): ProcessorFactory =
copy(pf = pf.withDistinguishedRootNode(name, namespace))
/**
* Create a [[DataProcessor]]
*
* @param path path to an element to use as the parsing root, relative to the distinguished root node. Currently, must be set to "/"
* @return [[DataProcessor]] used to parse data. Must check [[DataProcessor.isError]] before using it.
*/
def onPath(path: String) = {
val dp = pf.onPath(path).asInstanceOf[SDataProcessor]
new DataProcessor(dp)
}
/**
* Exposes the RootView object corresponding to this ProcessorFactory. This can
* be used to start a walk using the walkFromRoot method in a DSOM Walker.
*/
object experimental {
val rootView: RootView = pf.rootView
}
}
/**
* Abstract class that adds diagnostic information to classes that extend it.
*
* When a function returns a class that extend this, one should call
* [[WithDiagnostics.isError]] on that class before performing any further
* actions. If an error exists, any use of that class, aside from those
* functions in [[WithDiagnostics]], is invalid and will result in an
* Exception.
*/
abstract class WithDiagnostics private[sapi] (wd: SWithDiagnostics)
extends Serializable {
/**
* Determine if any errors occurred in the creation of the parent object.
*
* @return true if it represents an error, false otherwise
*/
def isError() = wd.isError
/**
* Determine if this object can be used in any future parse activities
*
* @return true it is safe to proceed, false otherwise
*/
@deprecated("Use !isError() to determine if it is safe to proceed","2.0.0")
def canProceed() = !isError
/**
* Get the list of [[Diagnostic]]'s created during the construction of the parent object
*
* @return list of [[Diagnostic]]'s. May contain errors or warnings, and so may be non-empty even if [[WithDiagnostics.isError]] is false.
*/
def getDiagnostics: Seq[Diagnostic] = wd.getDiagnostics.map { new Diagnostic(_) }
}
/**
* Class containing diagnostic information
*/
class Diagnostic private[sapi] (d: SDiagnostic) {
/**
* Get the diagnostic message
*
* @return diagnostic message in string form
*/
def getMessage(): String = d.getMessage()
override def toString() = d.toString
/**
* Get data location information relevant to this diagnostic object.
*
* For example, this might be a file name, and position within the file.
*
* @return list of [[DataLocation]]'s related to this diagnostic
*/
def getDataLocations: Seq[DataLocation] = d.getDataLocations.map { new DataLocation(_) }
/**
* Get schema location information relevant to this diagnostic object.
*
* For example, this might be a file name of a schema, and position within the schema file.
*
* @return list of [[LocationInSchemaFile]]'s related to this diagnostic.
*/
def getLocationsInSchemaFiles: Seq[LocationInSchemaFile] =
d.getLocationsInSchemaFiles.map { new LocationInSchemaFile(_) }
/**
* Determine if a diagnostic object represents an error or something less serious.
*
* @return true if it represents an error, false otherwise
*/
def isError = d.isError
/**
* Get the cause of that cause this diagnostic
*
* @return the exception that caused the diagnostic
*/
def getSomeCause: Throwable = d.getSomeCause.get
/**
* Get the message that caused this diagnostic
*
* @return the message that caused the diagnostic
*/
def getSomeMessage: String = d.getSomeMessage.get
}
/**
* Information related to a location in data
*/
class DataLocation private[sapi] (dl: SDataLocation) {
override def toString() = dl.toString
/**
* Determine if we're positioned at the end of data.
*
* Blocks until either one byte of data can be read, or end-of-data
* is encountered.
*
* It is generally not advised to use this on network TCP data streams
* as it will block waiting for the sender of data to provide more data
* or close the stream.
*
* @return boolean indicating whether we are known to be positioned at
* the end of data.
*/
@deprecated("Use comparison of bitPos1b() with expected position instead.", "3.1.0")
def isAtEnd() = dl.isAtEnd
/**
* Get the position of the data, in bits, using 1-based indexing
*/
def bitPos1b() = dl.bitPos1b
/**
* Get the position of the data, in bytes, using 1-based indexing
*/
def bytePos1b() = dl.bytePos1b
}
/**
* Information related to locations in DFDL schema files
*/
class LocationInSchemaFile private[sapi] (lsf: SLocationInSchemaFile) {
/**
* Get the description of the location file, for example, containing file and line number information
*/
override def toString() = lsf.locationDescription
}
/**
* Compiled version of a DFDL Schema, used to parse data and get the DFDL infoset
*/
class DataProcessor private[sapi] (private var dp: SDataProcessor)
extends WithDiagnostics(dp)
with Serializable {
private def copy(dp: SDataProcessor = dp) = new DataProcessor(dp)
/**
* Enable/disable debugging.
*
* Before enabling, [[DataProcessor#withDebugger]] or [[DataProcessor#withDebuggerRunner]] must be called with a
* non-null debugger.
*
* @param flag true to enable debugging, false to disabled
*/
@deprecated("Use withDebugging.", "2.6.0")
def setDebugging(flag: Boolean): Unit = {
dp = dp.withDebugging(flag)
}
/**
* Obtain a new [[DataProcessor]] instance with debugging enabled or disabled.
*
* Before enabling, [[DataProcessor#withDebugger]] or [[DataProcessor#withDebuggerRunner]] must be called to obtain
* a [[DataProcessor]] with a non-null debugger.
*
* @param flag true to enable debugging, false to disabled
*/
def withDebugging(flag: Boolean): DataProcessor = {
copy(dp = dp.withDebugging(flag))
}
/**
* Set the debugger runner
*
* @param dr debugger runner
*/
@deprecated("Use withDebuggerRunner.", "2.6.0")
def setDebugger(dr: DebuggerRunner): Unit = {
val debugger = newDebugger(dr)
dp = dp.withDebugger(debugger)
}
/**
* Obtain a new [[DataProcessor]] with a specified debugger runner.
*
* @param dr debugger runner
*/
def withDebuggerRunner(dr: DebuggerRunner): DataProcessor = {
val debugger = newDebugger(dr)
copy(dp = dp.withDebugger(debugger))
}
/**
* Obtain a new [[DataProcessor]] with a specified debugger.
*
* @param dbg debugger
*/
def withDebugger(dbg: Debugger): DataProcessor = {
copy(dp = dp.withDebugger(dbg))
}
private def newDebugger(dr: DebuggerRunner) = {
val runner = dr match {
case tdr: TraceDebuggerRunner => new STraceDebuggerRunner()
case dr: DebuggerRunner => new JavaInteractiveDebuggerRunner(dr)
case null => null
}
val debugger = if (runner != null) {
new SInteractiveDebugger(runner, ExpressionCompilers)
} else {
null
}
debugger
}
/**
* Set validation mode
*
* @param mode mode to control validation
* @throws InvalidUsageException if mode is not a valid ValidateMode value
*/
@deprecated("Use withValidationMode.", "2.6.0")
@throws(classOf[InvalidUsageException])
def setValidationMode(mode: ValidationMode): Unit = {
try { dp = dp.withValidationMode(ValidationConversions.modeToScala(mode)) }
catch { case e: SInvalidUsageException => throw new InvalidUsageException(e) }
}
/**
* Obtain a new [[DataProcessor]] having a specific validation mode
*
* @param mode mode to control validation
* @throws InvalidUsageException if mode is not a valid ValidateMode value
*/
@throws(classOf[InvalidUsageException])
def withValidationMode(mode: ValidationMode): DataProcessor = {
try { copy(dp = dp.withValidationMode(ValidationConversions.modeToScala(mode))) }
catch { case e: SInvalidUsageException => throw new InvalidUsageException(e) }
}
def withValidator(validator: Validator): DataProcessor = withValidationMode(ValidationMode.Custom(validator))
/**
* Read external variables from a Daffodil configuration file
*
* @see <a target="_blank" href='https://daffodil.apache.org/configuration/'>Daffodil Configuration File</a> - Daffodil configuration file format
*
* @param extVars file to read DFDL variables from.
* @throws ExternalVariableException if an error occurs while setting an external variable
*/
@deprecated("Use withExternalVariables.", "2.6.0")
@throws(classOf[ExternalVariableException])
def setExternalVariables(extVars: File): Unit = {
//$COVERAGE-OFF$
try { dp = dp.withExternalVariables(extVars) }
catch { case e: SExternalVariableException => throw new ExternalVariableException(e.getMessage) }
//$COVERAGE-ON$
}
/**
* Obtain a new [[DataProcessor]] with external variables read from a Daffodil configuration file
*
* @see <a target="_blank" href='https://daffodil.apache.org/configuration/'>Daffodil Configuration File</a> - Daffodil configuration file format
*
* @param extVars file to read DFDL variables from.
* @throws ExternalVariableException if an error occurs while setting an external variable
*/
@throws(classOf[ExternalVariableException])
def withExternalVariables(extVars: File): DataProcessor = {
try { copy(dp = dp.withExternalVariables(extVars)) }
catch { case e: SExternalVariableException => throw new ExternalVariableException(e.getMessage) }
}
/**
* Set the value of multiple DFDL variables
*
* @param extVars a map of key/value pairs, where the key is the variable
* name, and the value is the value of the variable. The key
* may be preceded by a string of the form "{namespace}" to
* define a namespace for the variable. If preceded with "{}",
* then no namespace is used. If not preceded by anything,
* then Daffodil will figure out the namespace.
* @throws ExternalVariableException if an error occurs while setting an external variable
*/
@deprecated("Use withExternalVariables.", "2.6.0")
@throws(classOf[ExternalVariableException])
def setExternalVariables(extVars: Map[String, String]) = {
//$COVERAGE-OFF$
try { dp = dp.withExternalVariables(extVars) }
catch { case e: SExternalVariableException => throw new ExternalVariableException(e.getMessage) }
//$COVERAGE-ON$
}
/**
* Obtain a new [[DataProcessor]] with multiple DFDL variables set.
*
* @param extVars a map of key/value pairs, where the key is the variable
* name, and the value is the value of the variable. The key
* may be preceded by a string of the form "{namespace}" to
* define a namespace for the variable. If preceded with "{}",
* then no namespace is used. If not preceded by anything,
* then Daffodil will figure out the namespace.
* @throws ExternalVariableException if an error occurs while setting an external variable
*/
@throws(classOf[ExternalVariableException])
def withExternalVariables(extVars: Map[String, String]): DataProcessor = {
try {copy(dp = dp.withExternalVariables(extVars)) }
catch { case e: SExternalVariableException => throw new ExternalVariableException(e.getMessage) }
}
/**
* Save the DataProcessor
*
* The resulting output can be reloaded by [[Compiler.reload(savedParser:java\.nio\.channels\.ReadableByteChannel)* Compiler.reload]].
* @param output the byte channel to write the [[DataProcessor]] to. Note that external variable settings are not saved.
*/
def save(output: WritableByteChannel): Unit = dp.save(output)
/**
* Obtain a new [[DaffodilParseXMLReader]] from the current [[DataProcessor]].
*/
def newXMLReaderInstance(): DaffodilParseXMLReader =
new DaffodilParseXMLReader(xmlrdr = dp.newXMLReaderInstance.asInstanceOf[SDaffodilParseXMLReader])
/**
* Obtain a new [[DaffodilUnparseContentHandler]] from the current [[DataProcessor]].
*/
def newContentHandlerInstance(output: WritableByteChannel): DaffodilUnparseContentHandler =
new DaffodilUnparseContentHandler(sContentHandler =
dp.newContentHandlerInstance(output).asInstanceOf[SDaffodilUnparseContentHandler])
/**
* Parse input data with a specified length
*
* @param input data to be parsed
* @param lengthLimitInBits the length of the input data in bits, or -1 if no limit
* @return an object which contains the result, and/or diagnostic information.
*/
@deprecated("Use parse(InputSourceDataInputStream, InfosetOutputter) to parse the data and get the infoset representation from the InfosetOutputter instead of ParseResult.result()","2.2.0")
def parse(input: ReadableByteChannel, lengthLimitInBits: Long): ParseResult = {
val is = Channels.newInputStream(input)
val dis = new InputSourceDataInputStream(is)
if (lengthLimitInBits > 0) {
dis.dis.setBitLimit0b(MaybeULong(lengthLimitInBits))
}
val output = new ScalaXMLInfosetOutputter()
val pr = dp.parse(dis.dis, output).asInstanceOf[SParseResult]
new ParseResult(pr, Maybe(output))
}
/**
* Parse input data without specifying a length
*
* @param input data to be parsed
* @return an object which contains the result, and/or diagnostic information.
*/
@deprecated("Use parse(InputSourceDataInputStream, InfosetOutputter) to parse the data and get the infoset representation from the InfosetOutputter instead of ParseResult.result()","2.2.0")
def parse(input: ReadableByteChannel): ParseResult = parse(input, -1)
/**
* Parse input data with a specified length
*
* @param input data to be parsed
* @param output the InfosetOutputter that will be used to output the infoset
* @param lengthLimitInBits the length of the input data in bits, or -1 if no limit
* @return an object which contains the result, and/or diagnostic information.
*/
@deprecated("Use parse(InputSourceDataInputStream, InfosetOutputter) to parse the data and get the infoset representation from the InfosetOutputter instead of ParseResult.result()","2.2.0")
def parse(input: ReadableByteChannel, output: InfosetOutputter, lengthLimitInBits: Long): ParseResult = {
val is = Channels.newInputStream(input)
val dis = new InputSourceDataInputStream(is)
if (lengthLimitInBits > 0) {
dis.dis.setBitLimit0b(MaybeULong(lengthLimitInBits))
}
val pr = dp.parse(dis.dis, output).asInstanceOf[SParseResult]
new ParseResult(pr, Nope)
}
/**
* Parse input data without specifying a length
*
* Use this when you don't know how big the data is.
*
* @param input data to be parsed
* @param output the InfosetOutputter that will be used to output the infoset
* @return an object which contains the result, and/or diagnostic information.
*/
@deprecated("Use parse(InputSourceDataInputStream, InfosetOutputter) to parse the data and get the infoset representation from the InfosetOutputter instead of ParseResult.result()","2.2.0")
def parse(input: ReadableByteChannel, output: InfosetOutputter): ParseResult = parse(input, output, -1)
/**
* Parse input data from an InputSourceDataInputStream and output the infoset to an InfosetOutputter
*
*
* @param input data to be parsed
* @param output the InfosetOutputter that will be used to output the infoset
* @return an object which contains the result, and/or diagnostic information.
*/
def parse(input: InputSourceDataInputStream, output: InfosetOutputter): ParseResult = {
val pr = dp.parse(input.dis, output).asInstanceOf[SParseResult]
new ParseResult(pr, Nope)
}
/**
* Unparse an InfosetInputter
*
* @param input the infoset inputter to use for unparsing
* @param output the byte channel to write the data to
* @return an object with contains diagnostic information
*/
def unparse(input: InfosetInputter, output: WritableByteChannel): UnparseResult = {
val ur = dp.unparse(input, output).asInstanceOf[SUnparseResult]
new UnparseResult(ur)
}
/**
* Unparse a scala.xml.Node infoset
*
* @param output the byte channel to write the data to
* @param infoset the infoset to unparse, as a scala xml Node
* @return an object with contains the result and/or diagnostic information
*/
@deprecated("Use unparse(InfosetInputter, WritableByteChannel)", "2.0.0")
def unparse(output: WritableByteChannel, infoset: scala.xml.Node): UnparseResult = {
val input = new ScalaXMLInfosetInputter(infoset)
unparse(input, output)
}
}
/**
* Result of calling [[DataProcessor.parse(input:org\.apache\.daffodil* DataProcessor.parse]], containing
* any diagnostic information, and the final data location
*/
class ParseResult private[sapi] (pr: SParseResult, deprecatedOutput: Maybe[ScalaXMLInfosetOutputter])
extends WithDiagnostics(pr) {
/**
* Get the resulting infoset as a scala.xml.Node
*
* @throws InvalidUsageException if you call this when isError is true
* because in that case there is no result document.
*
* @return a scala.xml.Node representing the DFDL infoset for the parsed data
*/
@deprecated("ParseResult carrying the infoset representation is deprecated. Intead, use parse(ReadableByteChannel, InfosetInputter) to parse the data and get the infoset representation from the InfosetOutputter","2.0.0")
@throws(classOf[InvalidUsageException])
def result(): scala.xml.Node = {
// When this result function is removed due to deprecation, we should also
// remove the deprecatedOutput parameter to the ParseResult constructor
if (deprecatedOutput.isDefined) {
deprecatedOutput.get.getResult()
} else {
val ex = new org.apache.daffodil.processors.InvalidUsageException(
"When passing an InfosetOutputter to parse(), you must get the infoset result from the InfosetOutputter instead of the ParseResult.")
throw new InvalidUsageException(ex)
}
}
/**
* Get the [[DataLocation]] where the parse completed
*
* @return the data location where the parse completed
*/
def location(): DataLocation = new DataLocation(pr.resultState.currentLocation)
/**
* Determine if any processing errors occurred. isError() will always return
* true if this returns true.
*
* @return true if any processing errors occurred, false otherwise.
*/
def isProcessingError(): Boolean = pr.isProcessingError
/**
* Determine if all validation checks passed based on the validation mode of
* the DataProcessor. If validation mode is Off, this will always return
* false. This is only meaningful when isProcessingError() is false.
* isError() will always return true if this return true.
*
* @return true if any validation errors occurred, false otherwise.
*/
def isValidationError(): Boolean = pr.isValidationError
}
/**
* Result of calling [[DataProcessor.unparse(input* DataProcessor.unparse]],
* containing diagnostic information
*/
class UnparseResult private[sapi] (ur: SUnparseResult)
extends WithDiagnostics(ur) {
}
/**
* This exception will be thrown as a result of attempting to reload a saved parser
* that is invalid (not a parser file, corrupt, etc.) or
* is not in the GZIP format.
*/
class InvalidParserException(cause: org.apache.daffodil.compiler.InvalidParserException) extends Exception(cause.getMessage(), cause.getCause())
/**
* This exception will be thrown as a result of an invalid usage of the Daffodil API
*/
class InvalidUsageException(cause: org.apache.daffodil.processors.InvalidUsageException) extends Exception(cause.getMessage(), cause.getCause())
/**
* This exception will be thrown if an error occurs when setting an external variable. Example of errors include:
* - Ambiguity in variable to set
* - Variable definition not found in a schema
* - Variable value does not have a valid type with regards to the variable type
* - Variable cannot be set externally
*/
class ExternalVariableException private[sapi] (message: String) extends Exception(message)
/**
* This exception will be thrown when unparseResult.isError returns true during a SAX Unparse
*/
class DaffodilUnparseErrorSAXException private[sapi] (exception: SDaffodilUnparseErrorSAXException)
extends org.xml.sax.SAXException(exception.getMessage)
/**
* This exception will be thrown when an unexpected error occurs during the SAX unparse
*/
class DaffodilUnhandledSAXException private[sapi] (exception: SDaffodilUnhandledSAXException)
extends org.xml.sax.SAXException(exception.getMessage, new Exception(exception.getCause))
/**
* The full URIs needed for setting/getting properties for the [[DaffodilParseXMLReader]]
*/
object DaffodilParseXMLReader {
/**
* Property name to get the [[ParseResult]] from the [[DaffodilParseXMLReader]]. This property is read only.
*/
val DAFFODIL_SAX_URN_PARSERESULT: String = XMLUtils.DAFFODIL_SAX_URN_PARSERESULT
/**
* Property name to get/set blob directory as String from the [[DaffodilParseXMLReader]]
*/
val DAFFODIL_SAX_URN_BLOBDIRECTORY: String = XMLUtils.DAFFODIL_SAX_URN_BLOBDIRECTORY
/**
* Property name to get/set blob prefix as String from the [[DaffodilParseXMLReader]]
*/
val DAFFODIL_SAX_URN_BLOBPREFIX: String = XMLUtils.DAFFODIL_SAX_URN_BLOBPREFIX
/**
* Property name to get/set blob suffix as String from the [[DaffodilParseXMLReader]]
*/
val DAFFODIL_SAX_URN_BLOBSUFFIX: String = XMLUtils.DAFFODIL_SAX_URN_BLOBSUFFIX
}
/**
* SAX Method of parsing schema and getting the DFDL Infoset via designated
* org.xml.sax.ContentHandler, based on the org.xml.sax.XMLReader interface
*/
class DaffodilParseXMLReader private[sapi] (xmlrdr: SDaffodilParseXMLReader) extends org.xml.sax.XMLReader {
/**
* Get the value of the feature flag
* @param name feature flag whose value is to be retrieved
* @return value of the feature flag
*/
override def getFeature(name: String): Boolean = xmlrdr.getFeature(name)
/**
* Set the value of the feature flag
* @param name feature flag to be set
* @param value value to be assigned to feature flag
*/
override def setFeature(name: String, value: Boolean): Unit = xmlrdr.setFeature(name, value)
/**
* Get the value of the property
* @param name property whose value is to be retrieved
* @return value of the property
*/
override def getProperty(name: String): AnyRef = {
val res = xmlrdr.getProperty(name)
if (name == DaffodilParseXMLReader.DAFFODIL_SAX_URN_PARSERESULT) {
val pr = new ParseResult(res.asInstanceOf[SParseResult], Nope)
pr
} else {
res
}
}
/**
* Set the value of the property
* @param name property whose value is to be set
* @param value value to be assigned to the property
*/
override def setProperty(name: String, value: AnyRef): Unit = xmlrdr.setProperty(name, value)
/**
* Register an entity resolver
* @param resolver entity resolver to be registered
*/
override def setEntityResolver(resolver: org.xml.sax.EntityResolver): Unit =
xmlrdr.setEntityResolver(resolver)
/**
* Return the registered entity resolver
* @return registered entity resolver or null
*/
override def getEntityResolver: org.xml.sax.EntityResolver = xmlrdr.getEntityResolver
/**
* Register a DTD Handler
* @param handler DTD Handler to be registered
*/
override def setDTDHandler(handler: org.xml.sax.DTDHandler): Unit = xmlrdr.setDTDHandler(handler)
/**
* Retrieve registered DTD Handler
* @return registered DTD Handler or null
*/
override def getDTDHandler: org.xml.sax.DTDHandler = xmlrdr.getDTDHandler
/**
* Register a content handler
* @param handler content handler to be registered
*/
override def setContentHandler(handler: org.xml.sax.ContentHandler): Unit = xmlrdr.setContentHandler(handler)
/**
* Retrieve registered content handler
* @return registered content handler or null
*/
override def getContentHandler: org.xml.sax.ContentHandler = xmlrdr.getContentHandler
/**
* Register an error handler
* @param handler error handler to be registered
*/
override def setErrorHandler(handler: org.xml.sax.ErrorHandler): Unit = xmlrdr.setErrorHandler(handler)
/**
* Retrieve registered error handler
* @return registered error handler or null
*/
override def getErrorHandler: org.xml.sax.ErrorHandler = xmlrdr.getErrorHandler
/**
* Parse input data from an InputSource. Infoset can be retrieved via the registered
* contentHandler and diagnostics via the registered errorHandler
* @param input data to be parsed
*/
override def parse(input: org.xml.sax.InputSource): Unit = xmlrdr.parse(input)
/**
* Parse data from a system identifier/URI. This method is not supported by the API.
* @param systemId URI for data to be parsed
*/
override def parse(systemId: String): Unit = xmlrdr.parse(systemId)
/**
* Parse input data from an InputSourceDataInputStream. Infoset can retrieved via the registered
* contentHandler and diagnostics via the registered errorHandler
* @param isdis data to be parsed
*/
def parse(isdis: InputSourceDataInputStream): Unit = xmlrdr.parse(isdis.dis)
/**
* Parse input data from an InputStream. Infoset can retrieved via the registered contentHandler
* and diagnostics via the registered errorHandler
* @param stream data to be parsed
*/
def parse(stream: java.io.InputStream): Unit = xmlrdr.parse(stream)
/**
* Parse input data from an array of bytes. Infoset can retrieved via the registered
* contentHandler and diagnostics via the registered errorHandler
* @param arr data to be parsed
*/
def parse(arr: Array[Byte]): Unit = xmlrdr.parse(arr)
}
/**
* Accepts SAX callback events from any SAX XMLReader for unparsing
*/
class DaffodilUnparseContentHandler private[sapi] (sContentHandler: SDaffodilUnparseContentHandler)
extends org.xml.sax.ContentHandler {
private val contentHandler: org.xml.sax.ContentHandler = sContentHandler
/**
* Returns the result of the SAX unparse containing diagnostic information. In the case of an
* DaffodilUnhandledSAXException, this will return null.
*/
def getUnparseResult: UnparseResult = {
val ur = sContentHandler.getUnparseResult.asInstanceOf[SUnparseResult]
if (ur == null) null
else new UnparseResult(ur)
}
override def setDocumentLocator(locator: org.xml.sax.Locator): Unit =
contentHandler.setDocumentLocator(locator)
override def startDocument(): Unit =
try {
contentHandler.startDocument()
} catch {
case e: SDaffodilUnparseErrorSAXException => throw new DaffodilUnparseErrorSAXException(e)
case e: SDaffodilUnhandledSAXException => throw new DaffodilUnhandledSAXException(e)
}
override def endDocument(): Unit =
try {
contentHandler.endDocument()
} catch {
case e: SDaffodilUnparseErrorSAXException => throw new DaffodilUnparseErrorSAXException(e)
case e: SDaffodilUnhandledSAXException => throw new DaffodilUnhandledSAXException(e)
}
override def startPrefixMapping(prefix: String, uri: String): Unit =
contentHandler.startPrefixMapping(prefix, uri)
override def endPrefixMapping(prefix: String): Unit =
contentHandler.endPrefixMapping(prefix)
override def startElement(uri: String, localName: String, qName: String, atts: org.xml.sax.Attributes): Unit =
try {
contentHandler.startElement(uri, localName, qName, atts)
} catch {
case e: SDaffodilUnparseErrorSAXException => throw new DaffodilUnparseErrorSAXException(e)
case e: SDaffodilUnhandledSAXException => throw new DaffodilUnhandledSAXException(e)
}
override def endElement(uri: String, localName: String, qName: String): Unit =
try {
contentHandler.endElement(uri, localName, qName)
} catch {
case e: SDaffodilUnparseErrorSAXException => throw new DaffodilUnparseErrorSAXException(e)
case e: SDaffodilUnhandledSAXException => throw new DaffodilUnhandledSAXException(e)
}
override def characters(ch: Array[Char], start: Int, length: Int): Unit =
contentHandler.characters(ch, start, length)
override def ignorableWhitespace(ch: Array[Char], start: Int, length: Int): Unit =
contentHandler.ignorableWhitespace(ch, start, length)
override def processingInstruction(target: String, data: String): Unit =
contentHandler.processingInstruction(target, data)
override def skippedEntity(name: String): Unit =
contentHandler.skippedEntity(name)
}
/**
* Returns the EntityResolver used by Daffodil to resolve import/include
* schemaLocations.
*
* The entity resolver attempts to resolve namespaces and systemId's in the
* following order:
*
* 1. Use an org.apache.xml.resolver.Catalog/CatalogManager. By default the
* Catalog only includes the daffodil-built-in-catalog.xml, but additional
* catalogs can be added by putting CatalogManager.properties on the
* classpath when daffodil is run.
*
* 2. If not resolved in step 1, schemaLocations are resolved relative to the
* importing schema URI, which could either be a file on the filesystem or in
* a jar on the classpath.
*
* The DFDLCatalogResolver isn't thread safe, but it also is expensive and stateful,
* so we use ThreadLocal to only create one instance per thread.
*/
object DaffodilXMLEntityResolver {
def getEntityResolver: org.xml.sax.EntityResolver = DFDLCatalogResolver.get
def getXMLEntityResolver: org.apache.xerces.xni.parser.XMLEntityResolver = DFDLCatalogResolver.get
def getLSResourceResolver: org.w3c.dom.ls.LSResourceResolver = DFDLCatalogResolver.get
}