abstract class Layer extends AnyRef
This is the primary API class for writing layers.
All layers are derived from this class.
This class is used directly as a base class to define transforming layers.
To simplify the
definition of checksum layers, a specialized sub-class
org.apache.daffodil.runtime1.layers.api.ChecksumLayer
is also available.
Many of the requirements for layers, such as the naming conventions for layer variables,
are described here, but they apply equally to checksum layers.
Derived classes will be dynamically loaded by Java's Service Provider Interface (SPI) system.
The names of concrete classes derived from Layer are listed in
a metadata resource file
named for this class (that is, the file name is the fully-qualified class name of this class:
resources/META-INF/services/org.apache.daffodil.runtime1.layers.api.Layer
).
This file contains lines where each line contains one fully qualified class name of a derived
layer class.
More than one line in the file denotes that the Jar file contains the definitions of more than one derived layer
class.
This file is incorporated in the compiled jar file for the derived Layer
class
so that the class path can be searched and Jars containing layer classes can be dynamically loaded.
The SPI creates an instance the class by calling a default (no-arg) constructor, which should be the only constructor.
Instances of derived layer classes can be stateful. They are private to threads, and each time a layer is encountered during parse/unparse, an instance is created for that situation.
Layer instances should not share mutable state (such as via singleton objects).
About Layer Variables
Layer logic may read and write DFDL variables. These variables are associated with the layer implementation class by using Java/Scala reflection to find matches (case-sensitive) between the names of DFDL variables and method names and method arguments of the layer's Java/Scala code. Hence, the layer's DFDL variables must have names suitable for use as Java/Scala identifiers.
The layer namespace is used only for its layer. All DFDL variables defined in that namespace are either used to pass parameters to the layer code, or receive results (such as a checksum) back from the layer code. This is enforced. If a layer namespace contains a DFDL variable and there is no corresponding usage of that variable name in the layer code (following the conventions below), then it is a Schema Definition Error when the layer code is loaded.
A layer that does not define any DFDL variables does
not have to define a DFDL schema that defines the layer's target
namespace, but any layer that uses DFDL variables *must* define a
schema with the layer's namespace as its target namespace, and with the
variables declared in it (using dfdl:defineVariable
).
Every DFDL Variable in the layer's target namespace is used either at the start of the layer algorithm as a parameter to the layer or at the end of the layer algorithm where it is assigned a return value (such as a checksum or flag) from the layer.
Variables being read must have values before being read by the layer, and this is true for both parsing and unparsing. This happens when the Daffodil processor begins parsing/unparsing the layered sequence. When unparsing, variables being read cannot be forward-referencing to parts of the DFDL infoset that have not yet been unparsed.
A layer that wants to read parameters declares a special setter named
setLayerVariableParameters
which has arguments where each has a name and type that match a corresponding
dfdl:defineVariable
in the layer's target namespace.
For example, if the layer logic has a DFDL variable for a parameter named direction
of type xs:string
and another DFDL variable for a parameter named wordCount
of type xs:unsignedShort
then the derived Layer class must have a setLayerVariableParameters
with
arguments corresponding in name and type to these two variables.
This setter will be called passing the value of the variables immediately after the layer
instance is constructed.
The arguments to setLayerVariableParameters
can be in any order:
void setLayerVariableParameters(String direction, int wordCount) { // usually this setter will assign the values to data members this.direction = direction; this.wordCount = wordCount; }Beside initializing local members, this setter is also an initializer for the layer class instance. Any exception thrown becomes a Schema Definition Error.
If there are no parameter variables, then this setter, with no arguments, can be used purely for initialization.
A DFDL variable used to return a result from a layer must be undefined, since variables in DFDL are single-assignment.
Usually this means the use of the layer must be surrounded by a dfdl:newVariableInstance
annotation which creates a new instance of the layer result variable, over a limited scope
of use.
The variable is assigned by the layer, and it is then available for reading by the DFDL schema until
the end of the dfdl:newVariableInstance
scope.
To return a value into a DFDL variable, the layer implementation defines a special recognizable
getter method.
The name of the getter is formed from prefixing the DFDL variable name with the string
"getLayerVariableResult_
".
The return type of the getter must correspond to the type of the variable.
For example, a result value getter for a DFDL variable named total
of type xs:unsignedShort
would be:
int getLayerVariableResult_total() {
// returns the value created by the layer's algorithm.
// commonly this returns the value of a data member.
return this.total;
}
Layers could have multiple result variables, but a single variable is most common, generally for returning checksums.
See the ChecksumLayer
class, which is designed to facilitate creation of checksum/CRC/hash/digest layers.The Java types to use for the setter arguments and getter return types correspond to the DFDL variable types according to this table:
DFDL Schema Type | Java Type |
---|---|
xs:byte | byte |
xs:short | short |
xs:int | int |
xs:long | long |
xs:integer | java.math.BigInteger |
xs:decimal | java.math.BigDecimal |
xs:unsignedInt | long |
xs:unsignedByte | short |
xs:unsignedShort | int |
xs:unsignedLong | java.math.BigInteger |
xs:nonNegativeInteger | java.math.BigInteger |
xs:double | double |
xs:float | float |
xs:hexBinary | byte[] |
xs:anyURI | java.net.URI |
xs:boolean | boolean |
xs:dateTime | com.ibm.icu.util.ICUCalendar |
xs:date | com.ibm.icu.util.ICUCalendar |
xs:time | com.ibm.icu.util.ICUCalendar |
Layer Algorithm
The rest of the Layer class implements the layer decode/encode logic.
The actual algorithm of the layer is not implemented in methods of the derived layer class, but rather is
implemented in the layer's input decoder and output encoder.
These extend the java.io.InputStream
and java.io.OutputStream
base classes to actually handle the
data.
Every layer must implement the #wrapLayerInput
and #wrapLayerOutput
methods, which provide the input decoder and output encoder instances to the Daffodil layer framework.
When parsing/unparsing, the derived Layer class itself is concerned with setup and tear-down of the
layer's input decoder and output encoder, with providing access to/from DFDL variables,
and with reporting errors effectively.
Layer Exception Handling
The method setProcessingErrorException
allows the layer to specify that if the layer throws specific
exceptions or runtime exceptions that they are converted into processing errors.
This eliminates most need for layer code to contain try-catches.
For example:
setProcessingErrorException(IOException.class);
informs the DFDL processor that an IOException thrown from the layer is to be treated as a processing error.Unhandled exceptions thrown by the layer code are treated as fatal errors.
- Alphabetic
- By Inheritance
- Layer
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Instance Constructors
- new Layer(localName: String, targetNamespace: String)
Abstract Value Members
-
abstract
def
wrapLayerInput(jis: InputStream): InputStream
Wraps a layer input decoder around an input stream.
Wraps a layer input decoder around an input stream.
This input decoder does the real parser-time work of the layer. It implements
java.io.InputStream
, by reading data from the given argument input stream, and decoding it.Any exception thrown from this wrap method becomes a Schema Definition Error (fatal).
The input decoder generally uses a reference to this layer instance to report errors.
To have the Daffodil layer framework convert uncaught exceptions thrown by the input decoder into processing errors automatically, call the
#setProcessingErrorException(Class)
. Other kinds of input decoder processing errors can be signaled by calling the#processingError(String)
or#processingError(Throwable)
methods. Input decoder schema definition errors are more rare, but if needed the#runtimeSchemaDefinitionError(String)
or#runtimeSchemaDefinitionError(Throwable)
methods may be called.- jis
The input stream to be wrapped.
- returns
An input stream with the layer's input decoder wrapped around it.
-
abstract
def
wrapLayerOutput(jos: OutputStream): OutputStream
Wraps a layer output encoder around an output stream.
Wraps a layer output encoder around an output stream.
The output encoder does the real unparse-time work of the layer. It implements
java.io.OutputStream
by accepting data via the usual output stream write calls, encoding this data, and writing the encoded data to the argument output stream.Any exception thrown from this wrap method becomes a Schema Definition Error (fatal).
The output encoder generally uses a reference to this layer to report errors.
To have the Daffodil layer framework convert uncaught exceptions thrown by the output encoder into processing errors automatically, call the
#setProcessingErrorException(Class)
. Other kinds of output encoder processing errors can be signaled by calling the#processingError(String)
or#processingError(Throwable)
methods. Output encoder schema definition errors are more rare, but if needed the#runtimeSchemaDefinitionError(String)
or#runtimeSchemaDefinitionError(Throwable)
methods may be called.- jos
The output stream to be wrapped.
- returns
An output stream with the layer wrapped around it.
Concrete Value Members
-
final
def
!=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
final
def
##(): Int
- Definition Classes
- AnyRef → Any
-
final
def
==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
final
def
asInstanceOf[T0]: T0
- Definition Classes
- Any
-
def
clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( ... ) @native()
-
final
def
eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
def
equals(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
def
finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( classOf[java.lang.Throwable] )
-
final
def
getClass(): Class[_]
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
-
final
def
getLayerRuntime(): LayerRuntime
For framework use.
For framework use.
Called by the execution framework to obtain the context for reporting errors.
-
final
def
getProcessingErrorExceptions(): List[Class[_ <: Exception]]
For framework use.
-
def
hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
-
final
def
isInstanceOf[T0]: Boolean
- Definition Classes
- Any
-
final
def
localName(): String
For framework use.
-
final
def
name(): String
For framework use.
For framework use.
This method and the string it returns are required by the SPI loader.
- returns
A unique indentifier for the kind of layer. Contains both local and namespace components of the layer's complete name.
-
final
def
namespace(): String
For framework use.
-
final
def
ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
final
def
notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
-
final
def
notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
-
def
processingError(cause: Throwable): Unit
Use to report a processing error.
Use to report a processing error.
When parsing a processing error can cause backtracking so that the parse can often recover from the error.
When unparsing a processing error is fatal.
- cause
a throwable object that describes the error
-
def
processingError(msg: String): Unit
Use to report a processing error.
Use to report a processing error.
When parsing a processing error can cause backtracking so that the parse can often recover from the error.
When unparsing a processing error is fatal.
- msg
describes the error
-
def
runtimeSchemaDefinitionError(cause: Throwable): Unit
Use to report a runtime schema definition error.
Use to report a runtime schema definition error.
This indicates that the layer is unable to work meaningfully because of the way it is configured. The schema itself is not well defined due to the way the layer is configured.
This error type is always fatal whether parsing or unparsing. See
for discussion of situations where a Runtime Schema Definition Error is appropriate.
- cause
a throwable object that describes the error
-
def
runtimeSchemaDefinitionError(msg: String): Unit
Use to report a runtime schema definition error.
Use to report a runtime schema definition error.
This indicates that the layer is unable to work meaningfully because of the way it is configured. That is, the schema itself is not well defined due to the way the layer is configured.
For example suppose a layer had a DFDL variable for a parameter that is supposed to be an integer between 1 and 10, and this parameter is generally provided as a constant value. If the provided parameter variable value is 0, which is not meaningful, then a Runtime Schema Definition Error is the right error to invoke.
This error type is always fatal whether parsing or unparsing.
- msg
describes the error
-
final
def
setLayerRuntime(lr: LayerRuntime): Unit
For framework use.
For framework use.
Called by the execution framework to give the context for reporting errors.
- lr
runtime data structure used by the framework
-
final
def
setProcessingErrorException(e: Class[_ <: Exception]): Unit
Use to add an exception class to the list of exceptions that will be automatically converted into processing errors.
Use to add an exception class to the list of exceptions that will be automatically converted into processing errors.
The purpose of this is to allow one to use java/scala libraries that may throw exceptions when encountering bad data. Such exceptions should be translated into processing errors, which will allow the parser to backtrack and try other alternatives which may work for that data. By calling this method the layer framework implements the try-catch logic to capture these exception types and convert them into processing errors. This avoids a great deal of try-catch logic that would otherwise be required in layer methods.
When considering whether a thrown Exception is to be converted to a processing error RuntimeException classes are handled separately from Exception classes. Hence calling
setProcessingErrorException(Exception.class);
will NOT cause all RuntimeExceptions to also be converted into processing errors. It will, however, cause all classes derived from Exception that are NOT RuntimeExceptions to be caught and converted into Processing Errors.- e
the exception class to be added to the list of processing error exceptions
-
final
def
synchronized[T0](arg0: ⇒ T0): T0
- Definition Classes
- AnyRef
-
def
toString(): String
- Definition Classes
- AnyRef → Any
-
final
def
wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... ) @native()
This is the documentation for the Apache Daffodil Scala API.
Package structure
org.apache.daffodil.sapi - Provides the classes necessary to compile DFDL schemas, parse and unparse files using the compiled objects, and retrieve results and parsing diagnostics
org.apache.daffodil.udf - Provides the classes necessary to create User Defined Functions to extend the DFDL expression language
org.apache.daffodil.runtime1.layers.api - Provides the classes necessary to create custom Layer extensions to DFDL.