From a4fa371366bda93c51c0eb19917cf6af8ca96913 Mon Sep 17 00:00:00 2001 From: Michael Edgar Date: Fri, 29 Dec 2023 12:57:36 -0500 Subject: [PATCH] Add missing JavaDoc comments in package `io.xlate.edi.stream` (#424) Signed-off-by: Michael Edgar --- .../xlate/edi/internal/ThrowingRunnable.java | 32 +- .../io/xlate/edi/stream/EDIInputFactory.java | 23 ++ .../io/xlate/edi/stream/EDINamespaces.java | 13 + .../io/xlate/edi/stream/EDIOutputFactory.java | 12 + .../xlate/edi/stream/EDIStreamConstants.java | 44 +++ .../io/xlate/edi/stream/EDIStreamEvent.java | 79 +++++ .../xlate/edi/stream/EDIStreamException.java | 18 ++ .../io/xlate/edi/stream/EDIStreamReader.java | 11 +- .../edi/stream/EDIStreamValidationError.java | 205 ++++++++++++- .../io/xlate/edi/stream/EDIStreamWriter.java | 288 +++++++++++++++++- .../edi/stream/EDIValidationException.java | 58 ++++ .../io/xlate/edi/stream/PropertySupport.java | 14 + 12 files changed, 786 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/xlate/edi/internal/ThrowingRunnable.java b/src/main/java/io/xlate/edi/internal/ThrowingRunnable.java index 104fc792..3dc91342 100644 --- a/src/main/java/io/xlate/edi/internal/ThrowingRunnable.java +++ b/src/main/java/io/xlate/edi/internal/ThrowingRunnable.java @@ -5,18 +5,46 @@ /** * A {@link Runnable} that can throw exceptions/errors. Internal use only. * + * @param + * the type of exception that may be thrown + * * @since 1.24 */ public interface ThrowingRunnable { + /** + * Run any code provided by an implementation, including code that may throw + * an exception. + * + * @throws T + * any exception thrown by the implementation of this method + */ void run() throws T; - @SuppressWarnings("unchecked") + /** + * Execute the provided task and apply the given exceptionWrapper function + * on any exception thrown by it. + * + * @param + * the type of exception that may be thrown + * @param + * the type of exception that this method may throw, wrapping any + * thrown by task + * @param task + * runnable to execute that may thrown an exception T + * @param exceptionWrapper + * wrapper function to wrap/convert an exception T to an + * exception E + * @throws E + * exception thrown when task throws an exception T + */ static void run(ThrowingRunnable task, Function exceptionWrapper) throws E { try { task.run(); } catch (Exception e) { - throw exceptionWrapper.apply((T) e); + @SuppressWarnings("unchecked") + T thrown = (T) e; + throw exceptionWrapper.apply(thrown); } } } diff --git a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java index f074c429..1ef7dd7a 100644 --- a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java +++ b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java @@ -22,8 +22,25 @@ import io.xlate.edi.schema.Schema; +/** + * Defines an abstract implementation of a factory for getting EDIStreamReaders, + * JSON parsers (wrapping an existing EDIStreamReader), and XMLStreamReaders + * (wrapping an existing EDIStreamReader). An EDIInputErrorReporter may also be + * configured via the factory for use with each new EDIStreamReader created. + */ public abstract class EDIInputFactory extends PropertySupport { + /** + * When set to false, control structures, segments, elements, and codes will + * not be validated unless a user-provided control schema has been set using + * {@link EDIStreamReader#setControlSchema(Schema)}. + * + * When set to true AND no user-provided control schema has been set, the + * reader will attempt to find a known control schema for the detected EDI + * dialect and version to be used for control structure validation. + * + * Default value: true + */ public static final String EDI_VALIDATE_CONTROL_STRUCTURE = "io.xlate.edi.stream.EDI_VALIDATE_CONTROL_STRUCTURE"; /** @@ -138,6 +155,12 @@ public static EDIInputFactory newFactory() { return new io.xlate.edi.internal.stream.StaEDIInputFactory(); } + /** + * Default constructor + */ + protected EDIInputFactory() { + } + /** * Creates a new {@link EDIStreamReader} using the given {@link InputStream} * (with default encoding). diff --git a/src/main/java/io/xlate/edi/stream/EDINamespaces.java b/src/main/java/io/xlate/edi/stream/EDINamespaces.java index e31e12cf..1b5ff7f3 100644 --- a/src/main/java/io/xlate/edi/stream/EDINamespaces.java +++ b/src/main/java/io/xlate/edi/stream/EDINamespaces.java @@ -3,7 +3,15 @@ import java.util.Arrays; import java.util.List; +/** + * Defines the constant values of namespaces for each of the XML elements + * generated by the XMLStreamReader (created via + * {@link EDIInputFactory#createXMLStreamReader(EDIStreamReader)} and consumed + * by the XMLStreamWriter (created via + * {@link EDIOutputFactory#createXMLStreamWriter(EDIStreamWriter)}. + */ public class EDINamespaces { + private EDINamespaces() { } @@ -30,6 +38,11 @@ private EDINamespaces() { */ public static final String ELEMENTS = "urn:xlate.io:staedi:names:elements"; + /** + * Obtain a list of all namespace constants declared by {@linkplain EDINamespaces}. + * + * @return the list of all namespace constants + */ public static final List all() { return Arrays.asList(LOOPS, SEGMENTS, COMPOSITES, ELEMENTS); } diff --git a/src/main/java/io/xlate/edi/stream/EDIOutputFactory.java b/src/main/java/io/xlate/edi/stream/EDIOutputFactory.java index b1b1635a..3c815d3c 100644 --- a/src/main/java/io/xlate/edi/stream/EDIOutputFactory.java +++ b/src/main/java/io/xlate/edi/stream/EDIOutputFactory.java @@ -19,6 +19,12 @@ import javax.xml.stream.XMLStreamWriter; +/** + * Defines an abstract implementation of a factory for getting EDIStreamWriters + * and XMLStreamWriters (wrapping an existing EDIStreamWriter). An EDIOutputErrorReporter + * may also be configured via the factory for use with each new EDIStreamWriter + * created. + */ public abstract class EDIOutputFactory extends PropertySupport { /** @@ -67,6 +73,12 @@ public static EDIOutputFactory newFactory() { return new io.xlate.edi.internal.stream.StaEDIOutputFactory(); } + /** + * Default constructor + */ + protected EDIOutputFactory() { + } + /** * Create a new EDIStreamWriter that writes to a stream * diff --git a/src/main/java/io/xlate/edi/stream/EDIStreamConstants.java b/src/main/java/io/xlate/edi/stream/EDIStreamConstants.java index 7b209151..9255bf45 100644 --- a/src/main/java/io/xlate/edi/stream/EDIStreamConstants.java +++ b/src/main/java/io/xlate/edi/stream/EDIStreamConstants.java @@ -15,8 +15,17 @@ ******************************************************************************/ package io.xlate.edi.stream; +/** + * Collection of constant values identifying the several EDI standards and the + * delimiters used in processing EDI data streams. + */ public interface EDIStreamConstants { + /** + * Defines the constant values possibly returned by + * {@link EDIStreamReader#getStandard()} and + * {@link EDIStreamWriter#getStandard()}. + */ public static class Standards { private Standards() { } @@ -39,15 +48,50 @@ private Standards() { public static final String X12 = "X12"; } + /** + * Defines the constant values of EDI delimiters present in the maps + * returned by + * {@link EDIStreamReader#getDelimiters()}/{@link EDIStreamWriter#getDelimiters()} + * and accepted as properties for output via + * {@link EDIOutputFactory#setProperty(String, Object)}. + */ public static class Delimiters { private Delimiters() { } + /** + * Key for the delimiter used to terminate/end a segment. + */ public static final String SEGMENT = "io.xlate.edi.stream.delim.segment"; + + /** + * Key for the delimiter used to terminate/end a simple or composite + * data element. + */ public static final String DATA_ELEMENT = "io.xlate.edi.stream.delim.dataElement"; + + /** + * Key for the delimiter used to terminate/end a component of a + * composite element. + */ public static final String COMPONENT_ELEMENT = "io.xlate.edi.stream.delim.componentElement"; + + /** + * Key for the delimiter used to terminate/end a repeating data element. + */ public static final String REPETITION = "io.xlate.edi.stream.delim.repetition"; + + /** + * Key for the character used as the decimal point for non-integer + * numeric element types. + */ public static final String DECIMAL = "io.xlate.edi.stream.delim.decimal"; + + /** + * Key for the character used as a release character, allowing the next + * character in the EDI stream to be treated as data element text rather + * than a delimiter. + */ public static final String RELEASE = "io.xlate.edi.stream.delim.release"; } } diff --git a/src/main/java/io/xlate/edi/stream/EDIStreamEvent.java b/src/main/java/io/xlate/edi/stream/EDIStreamEvent.java index 701103d2..dce44a55 100644 --- a/src/main/java/io/xlate/edi/stream/EDIStreamEvent.java +++ b/src/main/java/io/xlate/edi/stream/EDIStreamEvent.java @@ -1,29 +1,101 @@ package io.xlate.edi.stream; +/** + * Enumeration of stream event types that may be encountered by the + * EDIStreamReader. + * + * @see EDIStreamReader#next() + * @see EDIStreamReader#nextTag() + * @see EDIStreamReader#getEventType() + */ public enum EDIStreamEvent { + + /** + * Event for a simple element or component element (within a composite). + */ ELEMENT_DATA, + + /** + * Event for binary element data + * + * @see EDIStreamReader#getBinaryData() + */ ELEMENT_DATA_BINARY, + /** + * Event for the start of a composite element. + */ START_COMPOSITE, + + /** + * Event for the end of a composite element. + */ END_COMPOSITE, + /** + * Event for the start of a segment. + */ START_SEGMENT, + + /** + * Event for the end of a segment. + */ END_SEGMENT, + /** + * Event for the start of an interchange. + */ START_INTERCHANGE, + + /** + * Event for the end of an interchange. + */ END_INTERCHANGE, + /** + * Event for the start of a functional group. + */ START_GROUP, + + /** + * Event for the end of a functional group. + */ END_GROUP, + /** + * Event for the start of a transaction. + */ START_TRANSACTION, + + /** + * Event for the end of a transaction. + */ END_TRANSACTION, + /** + * Event for the start of a data loop (logical grouping of segments). + */ START_LOOP, + + /** + * Event for the end of a data loop (logical grouping of segments). + */ END_LOOP, + /** + * Event for an error relating to a segment + */ SEGMENT_ERROR(true), + + /** + * Event for an error relating to the data received in an element. + */ ELEMENT_DATA_ERROR(true), + + /** + * Event for an error relating to the an occurrence of an element. E.g. a + * missing required element. + */ ELEMENT_OCCURRENCE_ERROR(true); private final boolean error; @@ -36,6 +108,13 @@ private EDIStreamEvent() { this(false); } + /** + * Indicates whether a particular EDIStreamEvent represents a validation + * error. + * + * @return true when the event represents a validation error, otherwise + * false. + */ public boolean isError() { return error; } diff --git a/src/main/java/io/xlate/edi/stream/EDIStreamException.java b/src/main/java/io/xlate/edi/stream/EDIStreamException.java index d4ea71bb..0e34fb60 100644 --- a/src/main/java/io/xlate/edi/stream/EDIStreamException.java +++ b/src/main/java/io/xlate/edi/stream/EDIStreamException.java @@ -15,12 +15,30 @@ ******************************************************************************/ package io.xlate.edi.stream; +/** + * Checked exception that may be thrown by EDIInputFactory, EDIStreamReader, + * EDIOutputFactory, and EDIStreamWriter in the processing of EDI data. + */ public class EDIStreamException extends Exception { private static final long serialVersionUID = -1232370584780899896L; + /** + * {@linkplain Location} of the exception + */ protected final transient Location location; + /** + * Build a readable message that includes a detail message together with the + * location of the exception + * + * @param message + * detail message for the exception + * @param location + * location of the exception + * @return concatenation of the detail message and location if the message + * does not already contain the location + */ protected static String buildMessage(String message, Location location) { String locationString = location.toString(); if (message.contains(locationString)) { diff --git a/src/main/java/io/xlate/edi/stream/EDIStreamReader.java b/src/main/java/io/xlate/edi/stream/EDIStreamReader.java index 433e00cb..841b9c91 100644 --- a/src/main/java/io/xlate/edi/stream/EDIStreamReader.java +++ b/src/main/java/io/xlate/edi/stream/EDIStreamReader.java @@ -26,6 +26,15 @@ import io.xlate.edi.schema.Schema; import io.xlate.edi.schema.implementation.EDITypeImplementation; +/** + * The EDIStreamReader interface allows forward, read-only access to EDI. It is + * designed to be the lowest level and most efficient way to read EDI data. + * + *

+ * The EDIStreamReader is designed to iterate over EDI using {@link #next()} and + * {@link #hasNext()}. The data can be accessed using methods such as + * {@link #getEventType()} and {@link #getText()}. + */ public interface EDIStreamReader extends Closeable, EDIStreamConstants { /** @@ -51,7 +60,7 @@ public interface EDIStreamReader extends Closeable, EDIStreamConstants { /** * Get next parsing event * - * @return the integer code corresponding to the current parse event + * @return the event type corresponding to the current parse event * @throws NoSuchElementException * if this is called when hasNext() returns false * @throws EDIStreamException diff --git a/src/main/java/io/xlate/edi/stream/EDIStreamValidationError.java b/src/main/java/io/xlate/edi/stream/EDIStreamValidationError.java index ef3c8489..f4a133b1 100644 --- a/src/main/java/io/xlate/edi/stream/EDIStreamValidationError.java +++ b/src/main/java/io/xlate/edi/stream/EDIStreamValidationError.java @@ -1,48 +1,235 @@ package io.xlate.edi.stream; +/** + * Enumeration of validation error types that may be encountered by the + * EDIStreamReader or EDIStreamWriter. + * + * @see EDIStreamReader#getErrorType() + * @see EDIInputErrorReporter#report(EDIStreamValidationError, EDIStreamReader) + * @see EDIOutputErrorReporter#report(EDIStreamValidationError, EDIStreamWriter, + * Location, CharSequence, io.xlate.edi.schema.EDIReference) + */ public enum EDIStreamValidationError { + /** + * No error + */ NONE(null), + /** + * An unrecognized (undefined) segment was encountered in the data stream. + * + * @deprecated Not used by StAEDI - reserved for future use. + */ UNRECOGNIZED_SEGMENT_ID(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment was received in an unexpected position in the data stream. This + * error may occur when a segment is encountered in a loop for which it is + * not defined. + */ UNEXPECTED_SEGMENT(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment was defined (via a schema) with a minimum number of occurrences, + * but the number of occurrences encountered in the data stream does not + * meet the requirement. + */ MANDATORY_SEGMENT_MISSING(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Loop encountered in the data stream has more occurrences than allowed by + * the configured schema. + */ LOOP_OCCURS_OVER_MAXIMUM_TIMES(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment encountered in the data stream has more occurrences than allowed + * by the configured schema. + */ SEGMENT_EXCEEDS_MAXIMUM_USE(EDIStreamEvent.SEGMENT_ERROR), + + /** + * An unrecognized (undefined) segment was encountered in the data stream. + */ SEGMENT_NOT_IN_DEFINED_TRANSACTION_SET(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment was encountered out of order. This will occur if the segment is + * valid within the current data loop, otherwise the error will be + * {@link #UNEXPECTED_SEGMENT}. + */ SEGMENT_NOT_IN_PROPER_SEQUENCE(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment contains data element errors. + * + * @deprecated Not used by StAEDI - reserved for future use. + */ SEGMENT_HAS_DATA_ELEMENT_ERRORS(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment is defined as unused (maximum use is zero) in the schema but was + * encountered in the data stream. + */ IMPLEMENTATION_UNUSED_SEGMENT_PRESENT(EDIStreamEvent.SEGMENT_ERROR), - // Not yet supported: IMPLEMENTATION_DEPENDENT_SEGMENT_MISSING(EDIStreamEvent.SEGMENT_ERROR), + + /** + * @deprecated Not used by StAEDI - reserved for future use. + */ + IMPLEMENTATION_DEPENDENT_SEGMENT_MISSING(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Loop is defined with a minimum number of occurrences in the + * implementation schema but too few were encountered in the data stream. + */ IMPLEMENTATION_LOOP_OCCURS_UNDER_MINIMUM_TIMES(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment is defined with a minimum number of occurrences in the + * implementation schema but too few were encountered in the data stream. + */ IMPLEMENTATION_SEGMENT_BELOW_MINIMUM_USE(EDIStreamEvent.SEGMENT_ERROR), - // Not yet supported: IMPLEMENTATION_DEPENDENT_UNUSED_SEGMENT_PRESENT(EDIStreamEvent.SEGMENT_ERROR), + + /** + * @deprecated Not used by StAEDI - reserved for future use. + */ + IMPLEMENTATION_DEPENDENT_UNUSED_SEGMENT_PRESENT(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment is configured as conditionally required (relative to other + * segments in the loop) in the schema but was not present in the data + * stream. + */ CONDITIONAL_REQUIRED_SEGMENT_MISSING(EDIStreamEvent.SEGMENT_ERROR), + + /** + * Segment is configured as conditionally excluded (relative to other + * segments in the loop) in the schema but was present in the data stream. + */ SEGMENT_EXCLUSION_CONDITION_VIOLATED(EDIStreamEvent.SEGMENT_ERROR), + /** + * Element was defined (via a schema) with a minimum number of occurrences + * but the number of occurrences encountered in the data stream does not + * meet the requirement. + */ REQUIRED_DATA_ELEMENT_MISSING(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element is configured as conditionally required (relative to other + * elements in the segment) in the schema but was not present in the data + * stream. + */ CONDITIONAL_REQUIRED_DATA_ELEMENT_MISSING(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element is configured as conditionally required (relative to other + * elements in the segment) in the schema but was not present in the data + * stream. + */ TOO_MANY_DATA_ELEMENTS(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element is configured as conditionally excluded (relative to other + * elements in the segment) in the schema but was present in the data + * stream. + */ EXCLUSION_CONDITION_VIOLATED(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element repeats more times than allowed by the configured schema. + */ TOO_MANY_REPETITIONS(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element contains more components than defined by the configured schema. + */ TOO_MANY_COMPONENTS(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), - // Not yet supported: IMPLEMENTATION_DEPENDENT_DATA_ELEMENT_MISSING(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * @deprecated Not used by StAEDI - reserved for future use. + */ + IMPLEMENTATION_DEPENDENT_DATA_ELEMENT_MISSING(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element is defined as unused (maximum use is zero) in the schema but was + * encountered in the data stream. + */ IMPLEMENTATION_UNUSED_DATA_ELEMENT_PRESENT(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element is defined with a minimum number of occurrences in the + * implementation schema but too few were encountered in the data stream. + */ IMPLEMENTATION_TOO_FEW_REPETITIONS(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), - // Not yet supported: IMPLEMENTATION_DEPENDENT_UNUSED_DATA_ELEMENT_PRESENT(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + /** + * @deprecated Not used by StAEDI - reserved for future use. + */ + IMPLEMENTATION_DEPENDENT_UNUSED_DATA_ELEMENT_PRESENT(EDIStreamEvent.ELEMENT_OCCURRENCE_ERROR), + + /** + * Element length is less than the minimum length required by the configured + * schema. + */ DATA_ELEMENT_TOO_SHORT(EDIStreamEvent.ELEMENT_DATA_ERROR), + + /** + * Element length is greater than the maximum length allowed by the + * configured schema. + */ DATA_ELEMENT_TOO_LONG(EDIStreamEvent.ELEMENT_DATA_ERROR), + + /** + * Element contains invalid character data. + */ INVALID_CHARACTER_DATA(EDIStreamEvent.ELEMENT_DATA_ERROR), + + /** + * Element contains a value that is not present in the set of values + * configured in the schema. + */ INVALID_CODE_VALUE(EDIStreamEvent.ELEMENT_DATA_ERROR), + + /** + * Element is defined with type + * {@linkplain io.xlate.edi.schema.EDISimpleType.Base#DATE} but the data + * encountered does not match formatted as a date. + */ INVALID_DATE(EDIStreamEvent.ELEMENT_DATA_ERROR), + + /** + * Element is defined with type + * {@linkplain io.xlate.edi.schema.EDISimpleType.Base#TIME} but the data + * encountered does not match formatted as a time. + */ INVALID_TIME(EDIStreamEvent.ELEMENT_DATA_ERROR), + + /** + * Element contains a value that is not present in the set of values + * configured in the implementation schema. + */ IMPLEMENTATION_INVALID_CODE_VALUE(EDIStreamEvent.ELEMENT_DATA_ERROR), + + /** + * @deprecated Not used by StAEDI - reserved for future use. + */ IMPLEMENTATION_PATTERN_MATCH_FAILURE(EDIStreamEvent.ELEMENT_DATA_ERROR), // Control number and counter validation errors + + /** + * Control number/reference in the trailer segment does not match the value + * encountered in the header. + */ CONTROL_REFERENCE_MISMATCH(EDIStreamEvent.ELEMENT_DATA_ERROR), - CONTROL_COUNT_DOES_NOT_MATCH_ACTUAL_COUNT(EDIStreamEvent.ELEMENT_DATA_ERROR) - ; + + /** + * Control count (e.g. count of segments, transactions, or functional + * groups) encountered in the trailer does not match the actual count in the + * stream. + */ + CONTROL_COUNT_DOES_NOT_MATCH_ACTUAL_COUNT(EDIStreamEvent.ELEMENT_DATA_ERROR); private EDIStreamEvent category; @@ -50,6 +237,12 @@ private EDIStreamValidationError(EDIStreamEvent category) { this.category = category; } + /** + * Provides the category of the validation error. The category is one of the + * EDIStreamEvents where {@linkplain EDIStreamEvent#isError()} is true. + * + * @return the EDIStreamEvent category of the validation error. + */ public EDIStreamEvent getCategory() { return category; } diff --git a/src/main/java/io/xlate/edi/stream/EDIStreamWriter.java b/src/main/java/io/xlate/edi/stream/EDIStreamWriter.java index c92bbe80..0db7f061 100644 --- a/src/main/java/io/xlate/edi/stream/EDIStreamWriter.java +++ b/src/main/java/io/xlate/edi/stream/EDIStreamWriter.java @@ -21,6 +21,12 @@ import io.xlate.edi.schema.Schema; +/** + * The EDIStreamWriter interface specifies how to write EDI. Each method depends + * on the internal state of the writer and a client application must ensure that + * the methods are called in the proper sequence. For example, element data may + * not be written prior to starting an interchange and a segment. + */ public interface EDIStreamWriter extends AutoCloseable { /** @@ -132,7 +138,8 @@ public interface EDIStreamWriter extends AutoCloseable { String getStandard(); /** - * Retrieve a read-only map of delimiters in use for the stream being written. + * Retrieve a read-only map of delimiters in use for the stream being + * written. * * @return The value of the property * @throws IllegalStateException @@ -157,43 +164,320 @@ public interface EDIStreamWriter extends AutoCloseable { */ EDIStreamWriter startInterchange() throws EDIStreamException; + /** + * Completes an interchange and returns the writer to its initial state. Any + * data pending output will be {@linkplain #flush() flushed}. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is in a state writing a segment, element, + * composite + */ EDIStreamWriter endInterchange() throws EDIStreamException; + /** + * Begin a new segment with the given name and write the tag to the + * underlying output. + * + * @param name + * name of the segment (i.e. the segment tag) + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state to begin a segment + */ EDIStreamWriter writeStartSegment(String name) throws EDIStreamException; + /** + * Complete a segment by writing the segment terminator to the underlying + * output. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state to end a segment + */ EDIStreamWriter writeEndSegment() throws EDIStreamException; + /** + * Start a new element, composite or simple. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when a segment has not been started with + * {@link #writeStartSegment(String)} + */ EDIStreamWriter writeStartElement() throws EDIStreamException; + /** + * Start a new element for binary data. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the a segment has not been started with + * {@link #writeStartSegment(String)} + */ EDIStreamWriter writeStartElementBinary() throws EDIStreamException; + /** + * Complete an element. A delimiter will not be written immediately. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in the state of writing an element + */ EDIStreamWriter endElement() throws EDIStreamException; + /** + * Write an element repeat delimiter/separator to the output stream. + * Following this method being called, the writer will be in a state to + * accept element data using {@link #writeElementData(CharSequence)} or + * {@link #writeElementData(char[], int, int)}. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing element data. A + * segment must have already been started. + */ EDIStreamWriter writeRepeatElement() throws EDIStreamException; + /** + * Start a component of a composite element. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when an element has not been started with + * {@link #writeStartElement()} + */ EDIStreamWriter startComponent() throws EDIStreamException; + /** + * Complete a component of a composite element. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in the state of writing an component + * element + */ EDIStreamWriter endComponent() throws EDIStreamException; + /** + * Write an empty simple element. + *

+ * Shorthand for calling {@link #writeStartElement()} immediately followed + * by {@link #endElement()}. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing simple element + * data + */ EDIStreamWriter writeEmptyElement() throws EDIStreamException; + /** + * Begin an element, write text data from the given CharSequence to the + * output, and end the element. + *

+ * Shorthand for calling {@link #writeStartElement()}, + * {@link #writeElementData(CharSequence)}, and {@link #endElement()}, in + * that order. + * + * @param text + * CharSequence containing element's full text data + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing simple element + * data + */ EDIStreamWriter writeElement(CharSequence text) throws EDIStreamException; + /** + * Begin an element, write text data from the given char array to the + * output, and end the element. Data will be read from the offset given by + * start (inclusive) to the offset given by end (exclusive). + *

+ * Shorthand for calling {@link #writeStartElement()}, + * {@link #writeElementData(char[], int, int)}, and {@link #endElement()}, + * in that order. + * + * @param text + * char array containing element's full text data + * @param start + * the start index, inclusive + * @param end + * the end index, exclusive + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing simple element + * data + */ EDIStreamWriter writeElement(char[] text, int start, int end) throws EDIStreamException; + /** + * Write an empty component + *

+ * Shorthand for calling {@link #startComponent()} immediately followed by + * {@link #endComponent()}. + * + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing component + * element data + */ EDIStreamWriter writeEmptyComponent() throws EDIStreamException; + /** + * Begin a component element, write text data from the given CharSequence to + * the output, and end the element. + *

+ * Shorthand for calling {@link #startComponent()}, + * {@link #writeElementData(CharSequence)}, and {@link #endComponent()}, in + * that order. + * + * @param text + * CharSequence containing component's full text data + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing component + * element data + */ EDIStreamWriter writeComponent(CharSequence text) throws EDIStreamException; + /** + * Begin a component element, write text data from the given char array to + * the output, and end the element. Data will be read from the offset given + * by start (inclusive) to the offset given by end (exclusive). + *

+ * Shorthand for calling {@link #startComponent()}, + * {@link #writeElementData(char[], int, int)}, and {@link #endComponent()}, + * in that order. + * + * @param text + * char array containing component's full text data + * @param start + * the start index, inclusive + * @param end + * the end index, exclusive + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing component + * element data + */ EDIStreamWriter writeComponent(char[] text, int start, int end) throws EDIStreamException; + /** + * Write text data from the given CharSequence to the output. + * + * @param text + * CharSequence containing element text data + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing element data. + * See {@linkplain #writeStartElement()} + */ EDIStreamWriter writeElementData(CharSequence text) throws EDIStreamException; + /** + * Write text data from the given char array to the output. Data will be + * read from the offset given by start (inclusive) to the offset given by + * end (exclusive). + * + * @param text + * char array containing element text data + * @param start + * the start index, inclusive + * @param end + * the end index, exclusive + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing element data. + * See {@linkplain #writeStartElement()} + */ EDIStreamWriter writeElementData(char[] text, int start, int end) throws EDIStreamException; + /** + * Write binary data from the given InputStream to the output. The stream + * will be read fully, until the byte returned by + * {@linkplain InputStream#read()} is {@code -1}. Any data pending output + * will first be {@linkplain #flush() flushed}. + * + * @param stream + * InputStream containing binary data to be consumed by the + * reader and written to the underlying output + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing binary element + * data. See {@linkplain #writeStartElementBinary()} + */ EDIStreamWriter writeBinaryData(InputStream stream) throws EDIStreamException; - EDIStreamWriter writeBinaryData(byte[] text, int start, int end) throws EDIStreamException; + /** + * Write binary data from the given byte array to the output. Data will be + * read from the offset given by start (inclusive) to the offset given by + * end (exclusive). Any data pending output will first be + * {@linkplain #flush() flushed}. + * + * @param binary + * byte array containing binary data + * @param start + * the start index, inclusive + * @param end + * the end index, exclusive + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing binary element + * data. See {@linkplain #writeStartElementBinary()} + */ + EDIStreamWriter writeBinaryData(byte[] binary, int start, int end) throws EDIStreamException; + /** + * Write binary data from the given buffer to the output. Any data pending + * output will first be {@linkplain #flush() flushed}. + * + * @param buffer + * data buffer containing binary data + * @return this EDI stream writer + * @throws EDIStreamException + * if an error occurs + * @throws IllegalStateException + * when the writer is not in a state for writing binary element + * data. See {@linkplain #writeStartElementBinary()} + */ EDIStreamWriter writeBinaryData(ByteBuffer buffer) throws EDIStreamException; } diff --git a/src/main/java/io/xlate/edi/stream/EDIValidationException.java b/src/main/java/io/xlate/edi/stream/EDIValidationException.java index 7563ae29..a9d60d4f 100644 --- a/src/main/java/io/xlate/edi/stream/EDIValidationException.java +++ b/src/main/java/io/xlate/edi/stream/EDIValidationException.java @@ -1,17 +1,55 @@ package io.xlate.edi.stream; +/** + * Runtime exception that may occur when reading or writing EDI any the EDI + * error event would not otherwise be handled by an application. For example, + * when writing EDI and no {@linkplain EDIOutputErrorReporter} is in use. + */ public class EDIValidationException extends RuntimeException { private static final long serialVersionUID = 5811097042070037687L; + /** + * The stream event associated with the error, one of the events for which + * {@linkplain EDIStreamEvent#isError()} is true. + */ protected final EDIStreamEvent event; + + /** + * The actual {@linkplain EDIStreamValidationError error} of this exception. + */ protected final EDIStreamValidationError error; + + /** + * {@linkplain Location} of the exception + */ protected final transient Location location; + + /** + * The data from the data stream (input for a reader or output for a writer) + * associated with the validation error. + */ protected final transient CharSequence data; + /** + * The next exception when more than one validation error occurred in + * processing the stream. + */ @SuppressWarnings("java:S1165") // Intentionally allow field to be set after instantiation private EDIValidationException nextException; + /** + * Construct an EDIValidationException with the given data elements. + * + * @param event + * stream event (required) + * @param error + * validation error (required) + * @param location + * location of the validation error + * @param data + * data associated with the validation error + */ public EDIValidationException(EDIStreamEvent event, EDIStreamValidationError error, Location location, @@ -23,18 +61,38 @@ public EDIValidationException(EDIStreamEvent event, this.data = data; } + /** + * Get the stream event associated with the error + * + * @return event associated with the error + */ public EDIStreamEvent getEvent() { return event; } + /** + * Get the type of validation error + * + * @return type of validation error + */ public EDIStreamValidationError getError() { return error; } + /** + * Get the location of the validation error + * + * @return location of the validation error + */ public Location getLocation() { return location; } + /** + * Get the data associated with the validation error + * + * @return character data associated with the validation error + */ public CharSequence getData() { return data; } diff --git a/src/main/java/io/xlate/edi/stream/PropertySupport.java b/src/main/java/io/xlate/edi/stream/PropertySupport.java index 33e571b2..736210eb 100644 --- a/src/main/java/io/xlate/edi/stream/PropertySupport.java +++ b/src/main/java/io/xlate/edi/stream/PropertySupport.java @@ -20,11 +20,25 @@ import java.util.Map; import java.util.Set; +/** + * Abstract parent class for factories that support setting, retrieving, and checking + * whether properties are supported. + */ public abstract class PropertySupport { + /** + * Set of property keys/names supported by the subclass. + */ protected final Set supportedProperties; + + /** + * Map of properties actually configured by the subclass. + */ protected final Map properties; + /** + * Default constructor, initialize fields to empty collections. + */ protected PropertySupport() { this.supportedProperties = new HashSet<>(); this.properties = new HashMap<>();