This is an automated email from the ASF dual-hosted git repository.

mbeckerle pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/main by this push:
     new 72cee7cc8 Updated javadoc for layering.
72cee7cc8 is described below

commit 72cee7cc8c503ed67c17ac9baddb2039c0a52156
Author: Michael Beckerle <[email protected]>
AuthorDate: Tue May 28 15:26:18 2024 -0400

    Updated javadoc for layering.
    
    DAFFODIL-2569
---
 .../apache/daffodil/layers/runtime1/GZipLayer.java |   5 +-
 .../runtime1/layers/api/ChecksumLayer.java         |  84 +++---
 .../apache/daffodil/runtime1/layers/api/Layer.java | 314 ++++++++++++++++++---
 .../daffodil/runtime1/layers/api/package-info.java |  70 +++++
 daffodil-sapi/root-doc.txt                         |   1 +
 5 files changed, 399 insertions(+), 75 deletions(-)

diff --git 
a/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/GZipLayer.java
 
b/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/GZipLayer.java
index 09b3b5096..0b8d03c45 100644
--- 
a/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/GZipLayer.java
+++ 
b/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/GZipLayer.java
@@ -70,12 +70,13 @@ public final class GZipLayer extends Layer {
 
 /**
  * Prior to Java 16, the java.util.zip.GZIPOutputStream wrote a value of zero 
for
- * the OS field in the header (byte index 9). In Java 16, this was changed to a
+ * the OS field in the header (byte index 9).
+ * In Java 16, this was changed to a
  * value of 255 to better abide by the GZIP specification. Unfortunately, this
  * means unparsed data using a GZIP layer might have a single byte difference,
  * depending on the Java version used. This can lead to inconsistent behavior 
of
  * test failures that expect a certain byte value.
- *
+ * <p>
  * To resolve this issue, we create this GZIPFixedOutputStream. This should 
wrap
  * the underlying OutputStream and be passed as the OutputStream to the
  * GZIPOutputStream. When the GZIPOutputStream writes the 9th byte to this
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/ChecksumLayer.java
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/ChecksumLayer.java
index ed8a0feb9..c89c739cc 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/ChecksumLayer.java
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/ChecksumLayer.java
@@ -21,49 +21,63 @@ import 
org.apache.daffodil.runtime1.layers.ChecksumLayerBase;
 import java.nio.ByteBuffer;
 
 /**
- * A checksum layer computes some sort of integer value from a region of the 
data stream. The term checksum is
- * used generically here to subsume all sorts of CRCs, check digits, data 
hash, and digest calculations.
+ * A checksum layer computes a numeric value from a region of the data stream.
  * <p>
- * This base is suitable only for checksums computed over small sections of 
data, not large data streams or whole large
- * files. The entire region of data the checksum is being computed over, will 
be pulled into a byte buffer in memory.
+ * The term checksum is used generically here to subsume all sorts of CRCs, 
check digits, data hash, and
+ * digest calculations.
  * <p>
- * The resulting checksum is the return value of the compute method.
+ * This abstract base is suitable only for checksums computed over small 
sections of data.
+ * It is not for large data streams or whole large files.
+ * The entire region of data the checksum is being computed over will be 
pulled into a byte buffer in memory.
  * <p>
- * This is delivered into a DFDL variable for use by the DFDL schema. This 
variable can have any name
- * such as 'crc', 'digest', or 'dataHash'.
+ * The resulting checksum is the return value of the {@link #compute} method.
  * <p>
- * The derived implementation class must also define a getter method based on 
the name of the DFDL variable which
- * will be assigned with the checksum value. For example if the checksum is 
actually a specific digest/hash calculation
- * and the DFDL variable is named 'digest', then this getter must be defined:
- * <p>
- *     int getLayerVariableResult_digest() { return getChecksum() }
+ * This result is delivered into a DFDL variable for use by the DFDL schema.
+ * This DFDL variable can have any name such as 'crc', 'digest', or 'dataHash'.
  * <p>
- * This will be called automatically to retrieve the integer value that was 
returned from the `compute` method, and
- * the DFDL variable 'digest' will be assigned that value.
+ * The derived implementation class must also define a getter method based on 
the name of the DFDL variable which
+ * will be assigned with the checksum value.
+ * For example if the checksum is actually a specific digest/hash calculation 
and the DFDL variable is named
+ * {@code digest}, then this getter must be defined:
+ * <pre>{@code
+ *     int getLayerVariableResult_digest() {
+ *       return this.digest; // usually returns a data member
+ *     }
+ * }
+ * </pre>
+ * This will be called automatically to retrieve the integer value that was 
returned from the {@code compute} method,
+ * and the DFDL variable named {@code digest} will be assigned that value.
  * <p>
  * The derived class implementing a checksum layer must call
- * <p>
- *     setLength(len) // sets the length in bytes
- * <p>
+ * <pre>{@code
+ *     setLength(len); // sets the length in bytes
+ * }
+ * </pre>
  * to specify the length of the data region in bytes. Normally this would be 
called from the layer's implementation of
- * the
- * <p>
- *     void setLayerVariableParameters(...) { }
- * <p>
- * method, which, if defined, is called with arguments populated from DFDL 
variables with the same name (and compatible type)
- * defined in a DFDL schema with the Layer's target namespace. So, for example 
if a checksum layer needs to
- * receive a parameter from a DFDL variable named "layerEncoding", the setter 
would be:
- * <p>
- *     void setLayerVariableParameters(String layerEncoding) {
- *         this.layerEncoding = layerEncoding;
+ * the {@code setLayerVariableParameters} method:
+ * <pre>{@code
+ *     void setLayerVariableParameters(...) {
+ *         ...
+ *         setLength(len); // len is a constant,
+ *                         // or is computed from a parameter variable
+ *         ...
  *     }
+ * }</pre>
+ * See the documentation of the {@link Layer} class for a description of how 
DFDL variables are passed to the arguments
+ * of the {@code setLayerVariableParameters} method.
  * <p>
- * 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.
+ * See {@link Layer} for more details about layers generally as most of its 
documentation is
+ * relevant to this derived abstract base class as well.
+ * </p>
  */
 public abstract class ChecksumLayer extends ChecksumLayerBase {
 
+  /**
+   * Base class constructor
+   * @param layerName the name of the layer
+   * @param layerTargetNamespace the URI that is the target namespace of the 
layer
+   * @throws IllegalArgumentException if arguments are null or do not obey 
required syntax.
+   */
   public ChecksumLayer(String layerName, String layerTargetNamespace) {
     super(layerName, layerTargetNamespace);
   }
@@ -73,11 +87,11 @@ public abstract class ChecksumLayer extends 
ChecksumLayerBase {
    *
    * @param isUnparse true if the direction is unparsing. Used because in some 
cases the computed checksum must
    *                  be written into the byte buffer in a specific location.
-   * @param byteBuffer the bytes over which the checksum is to be computed. 
This can be modified, (for example so as
-   *                   to embed the computed checksum in the middle of the 
data somewhere) and the resulting
-   *                   bytes become the data that is written when unparsing.
-   *                   If the bytes in this buffer are modified by the compute 
method, those modified bytes are what
-   *                   the parsing will parse from, and the unparsing will 
output.
+   * @param byteBuffer the bytes over which the checksum is to be computed.
+   *                   This byte buffer can be modified, (for example so as to 
embed the computed checksum in the
+   *                   middle of the data somewhere).
+   *                   The resulting modified bytes become the data that is 
read by the DFDL parsing and written
+   *                   when unparsing.
    * @return the checksum value as an Int (32-bit signed integer)
    */
   public abstract int compute(
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/Layer.java
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/Layer.java
index 2561bdde1..395d20def 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/Layer.java
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/Layer.java
@@ -27,51 +27,235 @@ import java.util.List;
 /**
  * This is the primary API class for writing layers.
  * <p>
- * All layers are derived from this class, and must have no-args default 
constructors.
+ * All layers are derived from this class.
  * <p>
- * Derived classes will be dynamically loaded by Java's SPI system.
- * The names of concrete classes derived from Layer are listed in a 
resources/META-INF/services file
- * so that they can be found and dynamically loaded.
+ * This class is used directly as a base class to define <i>transforming</i> 
layers.
+ * To simplify the
+ * definition of <i>checksum</i> layers, a specialized sub-class
+ * {@link 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.
+ * <p>
+ * 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:
+ * {@code 
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 {@code 
Layer} class
+ * so that the class path can be searched and Jars containing layer classes 
can be dynamically loaded.
  * <p>
  * The SPI creates an instance the class by calling a default (no-arg) 
constructor, which should be
  * the only constructor.
  * <p>
- * Instances of derived layer classes can be stateful. They are private to 
threads, and each time a layer
+ * 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.
  * <p>
- * Layer instances should not share mutable state (such as via singleton 
objects)
+ * Layer instances should not share mutable state (such as via singleton 
objects).
  * <p>
- * The rest of the Layer class implements the
- * layer decode/encode logic, which is done as part of deriving one's Layer 
class from the
- * Layer base class.
+ * <h2>About Layer Variables</h2>
  * <p>
- * About variables: Layer logic may read and write DFDL 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.
  * <p>
- * Every DFDL Variable in the layer's targetNamespace is used either at the 
start of the
- * layer algorithm as a parameter to the layer or at the end of the layer 
algorithm it is
- * assigned as a return value (such as a checksum) from the layer.
+ * 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.
  * <p>
- * Variables being written must be undefined, since variables in DFDL are 
single-assignment.
+ * 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 {@code dfdl:defineVariable}).
+ * </p>
+ * 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.
  * <p>
- * Variables being read must be defined before being read by the layer, and 
this is true for both
- * parsing and unparsing. When unparsing, variables being read cannot be 
forward-referencing to parts
+ * 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.
  * <p>
- * A layer that wants to read parameters declares special setter named 
'setLayerVariableParameters'
- * which has args such that each has a name and type that match a corresponding
- * dfdl:defineVariable in the layer's namespace.
+ * A layer that wants to read parameters declares a special setter named
+ * {@code setLayerVariableParameters}
+ * which has arguments where each has a name and type that match a 
corresponding
+ * {@code dfdl:defineVariable} in the layer's target namespace.
+ * <p>
+ * For example, if the layer logic has a DFDL variable for a parameter named 
{@code direction}
+ * of type {@code xs:string} and another DFDL variable for a parameter named 
{@code wordCount}
+ * of type {@code xs:unsignedShort}
+ * then the derived Layer class must have a {@code 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 {@code setLayerVariableParameters} can be in any order:
+ * <pre>
+ *     void setLayerVariableParameters(String direction, int wordCount) {
+ *         // usually this setter will assign the values to data members
+ *         this.direction = direction;
+ *         this.wordCount = wordCount;
+ *     }
+ * </pre>
+ * Beside initializing local members, this setter is also an initializer for 
the layer class instance.
+ * Any exception thrown becomes a Schema Definition Error.
+ * <p>
+ * If there are no parameter variables, then this setter, with no arguments, 
can be used purely for initialization.
  * <p>
- * A layer that wants to return a value after the layer algorithm completes 
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 match the 
type of the variable.
+ * 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 {@code 
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 {@code dfdl:newVariableInstance} scope.
  * <p>
- * For example, a result value getter for a DFDL variable named 'checksum' of 
type xs:unsignedShort would be:
+ * 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
+ * "{@code getLayerVariableResult_}".
+ * The return type of the getter must correspond to the type of the variable.
+ * <p>
+ * For example, a result value getter for a DFDL variable named {@code total} 
of type {@code xs:unsignedShort} would be:
  * <pre>
- *      int getLayerVariableResult_checksum() {
- *          // returns the value created by the checksum algorithm.
+ * {@code
+ *      int getLayerVariableResult_total() {
+ *          // returns the value created by the layer's algorithm.
+ *          // commonly this returns the value of a data member.
+ *          return this.total;
  *      }
+ * }
  * </pre>
+ * Layers could have multiple result variables, but a single variable is most 
common, generally for returning checksums.
+ * See the {@link ChecksumLayer} class, which is designed to facilitate 
creation of checksum/CRC/hash/digest layers.
+ * <p>
+ * The Java types to use for the setter arguments and getter return types 
correspond to the DFDL variable types
+ * according to this table:
+ * <table border="1">
+ *     <tr>
+ *         <th>DFDL Schema Type</th>
+ *         <th>Java Type</th>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:byte</td>
+ *         <td>byte</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:short</td>
+ *         <td>short</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:int</td>
+ *         <td>int</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:long</td>
+ *         <td>long</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:integer</td>
+ *         <td>java.math.BigInteger</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:decimal</td>
+ *         <td>java.math.BigDecimal</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:unsignedInt</td>
+ *         <td>long</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:unsignedByte</td>
+ *         <td>short</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:unsignedShort</td>
+ *         <td>int</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:unsignedLong</td>
+ *         <td>java.math.BigInteger</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:nonNegativeInteger</td>
+ *         <td>java.math.BigInteger</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:double</td>
+ *         <td>double</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:float</td>
+ *         <td>float</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:hexBinary</td>
+ *         <td>byte[]</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:anyURI</td>
+ *         <td>java.net.URI</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:boolean</td>
+ *         <td>boolean</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:dateTime</td>
+ *         <td>com.ibm.icu.util.ICUCalendar</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:date</td>
+ *         <td>com.ibm.icu.util.ICUCalendar</td>
+ *     </tr>
+ *     <tr>
+ *         <td>xs:time</td>
+ *         <td>com.ibm.icu.util.ICUCalendar</td>
+ *     </tr>
+ *     <caption style="caption-side:bottom"></caption>
+ * </table>
+ * <p>
+ * <h2>Layer Algorithm</h2>
+ * <p>
+ * The rest of the Layer class implements the layer decode/encode logic.
+ * <p>
+ * The actual algorithm of the layer is not implemented in methods of the 
derived layer class, but rather is
+ * implemented in the layer's <i>input decoder</i> and <i>output encoder</i>.
+ * These extend the {@code java.io.InputStream} and {@code 
java.io.OutputStream} base classes to actually handle the
+ * data.
  * <p>
+ * Every layer must implement the {@link #wrapLayerInput} and {@link 
#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.
+ * <p>
+ * <h3> Layer Exception Handling </h3>
+ * The method {@code 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:
+ * <pre>
+ * {@code
+ *     setProcessingErrorException(IOException.class);
+ * }
+ * </pre>
+ * informs the DFDL processor that an IOException thrown from the layer is to 
be treated as a processing error.
+ * <p>
+ * Unhandled exceptions thrown by the layer code are treated as fatal errors.
  */
 public abstract class Layer {
 
@@ -80,11 +264,10 @@ public abstract class Layer {
 
   private LayerRuntime layerRuntime;
 
-  /**
-   * Constructs a new Layer object with the given layer name and namespace.
+  /** Constructs a new Layer object with the given layer name and namespace 
URI.
    *
    * @param localName      the local NCName of the layer. Must be usable as a 
Java identifier.
-   * @param targetNamespace the namespace of the layer. Must obey URI syntax.
+   * @param targetNamespace the namespace URI of the layer. Must obey URI 
syntax.
    * @throws IllegalArgumentException if arguments are null or do not obey 
required syntax.
    */
   public Layer(String localName, String targetNamespace) {
@@ -96,23 +279,34 @@ public abstract class Layer {
     this.targetNamespace = targetNamespace;
   }
 
-  /** The spiName of the Layer class.
+  /** For framework use.
    * <p>
    * This method and the string it returns are required by the SPI loader.
    * @return A unique indentifier for the kind of layer. Contains both local 
and namespace components of the layer's complete name.
    */
   public final String name() { return LayerUtils.spiName(localName, 
targetNamespace); }
 
+  /** For framework use. */
   public final String localName() { return this.localName; }
+
+  /** For framework use. */
   public final String namespace() { return this.targetNamespace; }
 
   /**
+   * For framework use.
+   * <p>
    * Called by the execution framework to give the context for reporting 
errors.
    * @param lr runtime data structure used by the framework
    */
   public final void setLayerRuntime(LayerRuntime lr) {
     this.layerRuntime = lr;
   }
+
+  /**
+   * For framework use.
+   * <p>
+   * Called by the execution framework to obtain the context for reporting 
errors.
+   */
   public final LayerRuntime getLayerRuntime() { return layerRuntime; }
 
   /**
@@ -140,11 +334,16 @@ public abstract class Layer {
    * Use to report a runtime schema definition error.
    * <p>
    * 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
+   * 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.
    * <p>
-   * This error type is always fatal whether parsing or unparsing.
+   * 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.
    * <p>
+   * This error type is always fatal whether parsing or unparsing.
    * @param msg describes the error
    */
   public void runtimeSchemaDefinitionError(String msg) { 
layerRuntime.runtimeSchemaDefinitionError(msg); }
@@ -157,22 +356,54 @@ public abstract class Layer {
    * the way the layer is configured.
    * <p>
    * This error type is always fatal whether parsing or unparsing.
-   * <p>
+   * See {@link #runtimeSchemaDefinitionError(String) } for discussion of 
situations where
+   *  a Runtime Schema Definition Error is appropriate.
    * @param cause a throwable object that describes the error
    */
   public void runtimeSchemaDefinitionError(Throwable cause) { 
layerRuntime.runtimeSchemaDefinitionError(cause); }
 
   /**
-   * Wraps a layer input interpreter around an input stream, using the 
provided LayerRuntimeFoo for runtime information and stateful services.
-   *
+   * Wraps a layer <i>input decoder</i> around an input stream.
+   * <p>
+   * This input decoder does the real parser-time work of the layer.
+   * It implements {@code java.io.InputStream}, by
+   * reading data from the given argument input stream, and decoding it.
+   * <p>
+   * Any exception thrown from this wrap method becomes a Schema Definition 
Error (fatal).
+   * <p>
+   * The input decoder generally uses a reference to this layer instance to 
report errors.
+   * <p>
+   * To have the Daffodil layer framework convert uncaught exceptions
+   * thrown by the input decoder into processing errors automatically,
+   * call the {@link #setProcessingErrorException(Class)}.
+   * Other kinds of input decoder processing errors can be signaled by calling 
the {@link #processingError(String)} or
+   * {@link #processingError(Throwable)} methods.
+   * Input decoder schema definition errors are more rare, but if needed the 
{@link #runtimeSchemaDefinitionError(String)} or
+   * {@link #runtimeSchemaDefinitionError(Throwable)} methods may be called.
    * @param jis The input stream to be wrapped.
-   * @return An input stream with the layer wrapped around it.
+   * @return An input stream with the layer's input decoder wrapped around it.
    */
   public abstract InputStream wrapLayerInput(InputStream jis) throws Exception;
 
   /**
-   * Wraps a layer output interpreter around an output stream, using the 
provided LayerRuntimeFoo for runtime information and stateful services.
-   *
+   * Wraps a layer <i>output encoder</i> around an output stream.
+   * <p>
+   * The output encoder does the real unparse-time work of the layer.
+   * It implements {@code 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.
+   * <p>
+   * Any exception thrown from this wrap method becomes a Schema Definition 
Error (fatal).
+   * <p>
+   * The output encoder generally uses a reference to this layer to report 
errors.
+   * <p>
+   * To have the Daffodil layer framework convert uncaught exceptions
+   * thrown by the output encoder into processing errors automatically,
+   * call the {@link #setProcessingErrorException(Class)}.
+   * Other kinds of output encoder processing errors can be signaled by 
calling the {@link #processingError(String)} or
+   * {@link #processingError(Throwable)} methods.
+   * Output encoder schema definition errors are more rare, but if needed the 
{@link #runtimeSchemaDefinitionError(String)} or
+   * {@link #runtimeSchemaDefinitionError(Throwable)} methods may be called.
    * @param jos The output stream to be wrapped.
    * @return An output stream with the layer wrapped around it.
    */
@@ -181,13 +412,17 @@ public abstract class Layer {
   private ArrayList<Class<? extends Exception>> peExceptions = new 
ArrayList<>();
 
   /**
-   * Adds an exception class to the list of exceptions that will be 
automatically converted
+   * Use to add an exception class to the list of exceptions that will be 
automatically converted
    * into processing errors.
    * <p>
    * 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.
    * <p>
    * When considering whether a thrown Exception is to be converted to a 
processing error
    * RuntimeException classes are handled separately from Exception classes.
@@ -205,6 +440,9 @@ public abstract class Layer {
     peExceptions.add(e);
   }
 
+  /**
+   * For framework use.
+   */
   public final List<Class<? extends Exception>> getProcessingErrorExceptions() 
{
     return peExceptions;
   }
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/package-info.java
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/package-info.java
new file mode 100644
index 000000000..8fe397633
--- /dev/null
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/layers/api/package-info.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+/**
+ * <h2>Daffodil Runtime1 Layers API Package</h2>
+ *
+ * This package provides base classes for creating Layers, which provide a 
means of
+ * algorithmic operations on the data stream that cannot be expressed using
+ * regular DFDL properties.
+ * <p>
+ * There are two kinds of layers:
+ * <ul><li>transforming layers - such as a base64 decoder/encoder for parts of 
a textual format</li>
+ * <li>checksum layers - such as computing CRC32 over a message header</li>
+ * </ul>
+ * Both run an algorithm over a part of the data stream.
+ * <p>
+ * Layers are implemented in Scala or Java as small (usually) Jar files that 
are dynamically loaded from the CLASSPATH in
+ * response to a DFDL schema that uses that layer via the {@code dfdlx:layer} 
property.
+ * <p>
+ * See the <a href="http://daffodil.apache.org/layers";>Layer Usage 
Documentation</a> for how a layer is
+ * used from a Daffodil DFDL schema.
+ * <p>
+ * This API documentation is focused on the programming required to create a 
new layer implementation
+ * as a custom plug-in for Daffodil.
+ * <p>
+ * {@link org.apache.daffodil.runtime1.layers.api.Layer} is the general 
abstract base class
+ * used to define any layer, but most commonly it is used for transforming 
layers.
+ * <p>
+ * {@link org.apache.daffodil.runtime1.layers.api.ChecksumLayer} is an 
abstract base class
+ * derived from {@link org.apache.daffodil.runtime1.layers.api.Layer}, and 
further specialized for defining
+ * checksum layers.
+ *
+ * <h3> About Testing </h3>
+ *
+ * The Daffodil test code base includes tests for many ways that
+ * an API user can goof up the definition of a layer class.
+ * For example there are tests for calling {@code processingError}, {@code 
runtimeSchemaDefinitionError}, and throwing
+ * an {@code Exception} from every place a custom-defined Layer could cause 
these.
+ * Processing errors cause the parser to backtrack in all sensible cases.
+ * That is to say that if a layer is parsing data, and the data it encounters 
is not a match for that layer, then
+ * a properly written layer will issue a processing error, and the parser will 
backtrack, allowing the format to try
+ * other alternatives for parsing that data.
+ *
+ * <h3>Compatibility with Daffodil 3.7.0 and prior versions of Apache 
Daffodil</h3>
+ *
+ * This new Layer API is entirely incompatible with schemas or layer code
+ * from Daffodil 3.7.0 and all prior versions of Daffodil.
+ * <p>
+ * The layer feature was just an experimental feature in earlier versions of 
Daffodil, so we reserved
+ * the right to change it, and for Daffodil 3.8.0 it has changed radically 
based on what we learned from the
+ * earlier experimentation.
+ * <p>
+ * It is our intention that this Layer API (introduced in Daffodil 3.8.0) will 
prove to be stable
+ * and supportable long term.
+ */
+package org.apache.daffodil.runtime1.layers.api;
diff --git a/daffodil-sapi/root-doc.txt b/daffodil-sapi/root-doc.txt
index 29613045c..c7a39e189 100644
--- a/daffodil-sapi/root-doc.txt
+++ b/daffodil-sapi/root-doc.txt
@@ -6,3 +6,4 @@ This is the documentation for the Apache Daffodil Scala API.
 
 [[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.

Reply via email to