All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.xlate.edi.internal.stream.StaEDIStreamReader Maven / Gradle / Ivy

There is a newer version: 1.25.2
Show newest version
/*******************************************************************************
 * Copyright 2017 xlate.io LLC, http://www.xlate.io
 *
 * Licensed 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 io.xlate.edi.internal.stream;

import java.io.IOException;
import java.io.InputStream;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import io.xlate.edi.internal.schema.SchemaUtils;
import io.xlate.edi.internal.stream.tokenization.Dialect;
import io.xlate.edi.internal.stream.tokenization.Lexer;
import io.xlate.edi.internal.stream.tokenization.ProxyEventHandler;
import io.xlate.edi.schema.EDIReference;
import io.xlate.edi.schema.EDISchemaException;
import io.xlate.edi.schema.Schema;
import io.xlate.edi.stream.EDIInputErrorReporter;
import io.xlate.edi.stream.EDIInputFactory;
import io.xlate.edi.stream.EDIStreamEvent;
import io.xlate.edi.stream.EDIStreamException;
import io.xlate.edi.stream.EDIStreamReader;
import io.xlate.edi.stream.EDIStreamValidationError;
import io.xlate.edi.stream.Location;

public class StaEDIStreamReader implements EDIStreamReader {

    private static final Logger LOGGER = Logger.getLogger(StaEDIStreamReader.class.getName());

    private Schema controlSchema;
    private final Map properties;
    private final EDIInputErrorReporter reporter;
    private final StaEDIStreamLocation location = new StaEDIStreamLocation();
    private final ProxyEventHandler proxy;
    private final Lexer lexer;

    private boolean complete = false;
    private boolean closed = false;

    public StaEDIStreamReader(
            InputStream stream,
            Charset charset,
            Schema schema,
            Map properties,
            EDIInputErrorReporter reporter) {

        this.controlSchema = schema;
        this.properties = new HashMap<>(properties);
        this.reporter = reporter;
        this.proxy = new ProxyEventHandler(location, this.controlSchema);
        this.lexer = new Lexer(stream, charset, proxy, location, ignoreExtraneousCharacters());
    }

    private void ensureOpen() {
        if (closed) {
            throw new IllegalStateException("Reader is closed");
        }
    }

    private void ensureIncomplete() {
        if (complete) {
            throw new NoSuchElementException("Reader is complete");
        }
    }

    void ensureVersionAvailable(Function versionSupplier, String versionType) {
        if (lexer.getDialect() == null || versionSupplier.apply(lexer.getDialect()) == null) {
            throw new IllegalStateException(versionType + " not accessible");
        }
    }

    private CharBuffer getBuffer() {
        return proxy.getCharacters();
    }

    @Override
    public Object getProperty(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Name must not be null");
        }
        return properties.get(name);
    }

    @Override
    public Map getDelimiters() {
        Dialect dialect = lexer.getDialect();

        if (dialect == null) {
            throw new IllegalStateException("getDelimiters must be called "
                    + "within an interchange");
        }

        Map delimiters = new HashMap<>(5);
        delimiters.put(Delimiters.SEGMENT, dialect.getSegmentTerminator());
        delimiters.put(Delimiters.DATA_ELEMENT, dialect.getDataElementSeparator());
        delimiters.put(Delimiters.COMPONENT_ELEMENT, dialect.getComponentElementSeparator());
        delimiters.put(Delimiters.DECIMAL, dialect.getDecimalMark());

        if (dialect.getRepetitionSeparator() != '\0') {
            delimiters.put(Delimiters.REPETITION, dialect.getRepetitionSeparator());
        }

        if (dialect.getReleaseIndicator() != '\0') {
            delimiters.put(Delimiters.RELEASE, dialect.getReleaseIndicator());
        }

        return Collections.unmodifiableMap(delimiters);
    }

    private EDIStreamEvent nextEvent() throws EDIStreamException {
        ensureOpen();
        ensureIncomplete();

        if (EDIStreamEvent.START_INTERCHANGE == proxy.getEvent() && useInternalControlSchema()) {
            try {
                LOGGER.finer(() -> "Setting control schema: " + getStandard() + ", " + getVersion());
                setControlSchema(SchemaUtils.getControlSchema(getStandard(), getVersion()));
                LOGGER.finer(() -> "Done setting control schema: " + getStandard() + ", " + getVersion());
            } catch (EDISchemaException e) {
                LOGGER.log(Level.WARNING,
                           String.format("Exception loading controlSchema for standard %s, version %s: %s",
                                         getStandard(),
                                         Arrays.stream(getVersion()).map(Object::toString)
                                               .collect(Collectors.joining(", ")),
                                         e.getMessage()),
                           e);
            }
        }

        if (!proxy.nextEvent()) {
            proxy.resetEvents();

            try {
                lexer.parse();
            } catch (IOException e) {
                Location where = getLocation();
                throw new EDIStreamException("Error parsing input", where, e);
            }
        }

        final EDIStreamEvent event = proxy.getEvent();

        LOGGER.finer(() -> "EDI event: " + event);

        if (event == EDIStreamEvent.END_INTERCHANGE) {
            complete = true;
        }

        if (event == EDIStreamEvent.ELEMENT_DATA && proxy.isBinaryElementLength()) {
            try {
                this.setBinaryDataLength(Long.parseLong(getText()));
            } catch (NumberFormatException e) {
                throw new EDIStreamException("Failed to parse binary element length", location, e);
            }
        }

        return event;
    }

    @Override
    public EDIStreamEvent next() throws EDIStreamException {
        EDIStreamEvent event = null;
        boolean eventFound = false;

        do {
            event = nextEvent();

            if (this.reporter != null && event.isError()) {
                reporter.report(getErrorType(), this);
            } else {
                eventFound = true;
            }
        } while (!complete && !eventFound);

        return event;
    }

    @Override
    public EDIStreamEvent nextTag() throws EDIStreamException {
        EDIStreamEvent event = null;
        boolean tagFound = false;

        do {
            event = next();
            switch (event) {
            case START_GROUP:
            case START_TRANSACTION:
            case START_LOOP:
            case START_SEGMENT:
                tagFound = true;
                break;
            default:
                break;
            }
        } while (!complete && !tagFound);

        if (!tagFound) {
            throw new NoSuchElementException("No additional tags in stream");
        }

        return event;
    }

    @Override
    public boolean hasNext() throws EDIStreamException {
        ensureOpen();
        return !complete;
    }

    @Override
    public void close() throws IOException {
        this.closed = true;
        // Do not close the stream
    }

    @Override
    public EDIStreamEvent getEventType() {
        ensureOpen();
        return proxy.getEvent();
    }

    @Override
    public String getStandard() {
        if (lexer.getDialect() == null) {
            throw new IllegalStateException("standard not accessible");
        }

        return lexer.getDialect().getStandard();
    }

    @Override
    public String[] getVersion() {
        ensureVersionAvailable(Dialect::getVersion, "version");
        String[] version = lexer.getDialect().getVersion();
        return Arrays.copyOf(version, version.length);
    }

    @Override
    public String[] getTransactionVersion() {
        ensureVersionAvailable(Dialect::getTransactionVersion, "transaction version");
        String[] version = lexer.getDialect().getTransactionVersion();
        return Arrays.copyOf(version, version.length);
    }

    @Override
    public String getTransactionVersionString() {
        ensureVersionAvailable(Dialect::getTransactionVersion, "transaction version");
        return lexer.getDialect().getTransactionVersionString();
    }

    @Override
    public Schema getControlSchema() {
        return this.controlSchema;
    }

    @Override
    public void setControlSchema(Schema schema) {
        if (getEventType() != EDIStreamEvent.START_INTERCHANGE) {
            throw new IllegalStateException("control schema set after interchange start");
        }

        if (this.controlSchema != null) {
            throw new IllegalStateException("control schema already set");
        }

        this.controlSchema = schema;
        proxy.setControlSchema(schema, validateControlCodeValues());
    }

    @Override
    public Schema getTransactionSchema() {
        return proxy.getTransactionSchema();
    }

    @Override
    public void setTransactionSchema(Schema schema) {
        if (proxy.isTransactionSchemaAllowed()) {
            proxy.setTransactionSchema(schema);
        } else {
            throw new IllegalStateException("Transaction schema can only be set during transaction start");
        }
    }

    @Override
    public String getReferenceCode() {
        return proxy.getReferenceCode();
    }

    @Override
    public EDIStreamValidationError getErrorType() {
        switch (getEventType()) {
        case ELEMENT_DATA_ERROR:
        case ELEMENT_OCCURRENCE_ERROR:
        case SEGMENT_ERROR:
            return proxy.getErrorType();
        default:
            throw new IllegalStateException("not a valid error state");
        }
    }

    private void checkTextState() {
        EDIStreamEvent event = getEventType();

        switch (event) {
        case START_GROUP:
        case START_TRANSACTION:
        case START_LOOP:
        case START_SEGMENT:
        case END_GROUP:
        case END_TRANSACTION:
        case END_LOOP:
        case END_SEGMENT:
        case ELEMENT_DATA:
        case ELEMENT_DATA_ERROR:
        case ELEMENT_OCCURRENCE_ERROR:
        case SEGMENT_ERROR:
            break;
        default:
            throw new IllegalStateException("not a valid text state [" + event + ']');
        }
    }

    @Override
    public String getText() {
        ensureOpen();
        checkTextState();
        final CharBuffer buffer = getBuffer();

        return buffer.toString();
    }

    @Override
    public char[] getTextCharacters() {
        ensureOpen();
        checkTextState();
        final CharBuffer buffer = getBuffer();

        return Arrays.copyOf(buffer.array(), buffer.length());
    }

    @Override
    public int getTextCharacters(int sourceStart,
                                 char[] target,
                                 int targetStart,
                                 int length) {

        ensureOpen();
        checkTextState();

        if (target == null) {
            throw new NullPointerException("Null target array");
        }
        if (targetStart < 0) {
            throw new IndexOutOfBoundsException("targetStart < 0");
        }
        if (targetStart > target.length) {
            throw new IndexOutOfBoundsException("targetStart > target.length");
        }
        if (length < 0) {
            throw new IndexOutOfBoundsException("length < 0");
        }
        if (length > target.length) {
            throw new IndexOutOfBoundsException("length (" + length + ") > target.length (" + target.length + ")");
        }

        final CharBuffer buffer = getBuffer();
        final char[] contents = buffer.array();
        final int count = buffer.remaining();

        if (sourceStart < 0) {
            throw new IndexOutOfBoundsException("sourceStart < 0");
        }

        if (sourceStart > count) {
            throw new IndexOutOfBoundsException("sourceStart > source length");
        }

        int toCopy = Math.min(count - sourceStart, length);
        System.arraycopy(contents, sourceStart, target, targetStart, toCopy);

        return toCopy;
    }

    @Override
    public int getTextStart() {
        ensureOpen();
        checkTextState();
        final CharBuffer buffer = getBuffer();

        return buffer.position();
    }

    @Override
    public int getTextLength() {
        ensureOpen();
        checkTextState();
        final CharBuffer buffer = getBuffer();

        return buffer.limit();
    }

    @Override
    public Location getLocation() {
        ensureOpen();
        return proxy.getLocation();
    }

    @Override
    public void setBinaryDataLength(long length) throws EDIStreamException {
        ensureOpen();

        switch (getEventType()) {
        case START_SEGMENT:
        case ELEMENT_DATA:
        case END_COMPOSITE:
            break;
        default:
            throw new IllegalStateException();
        }

        lexer.setBinaryLength(length);
    }

    @Override
    public InputStream getBinaryData() {
        ensureOpen();

        if (getEventType() != EDIStreamEvent.ELEMENT_DATA_BINARY) {
            throw new IllegalStateException();
        }

        return proxy.getBinary();
    }

    @Override
    public EDIReference getSchemaTypeReference() {
        return proxy.getSchemaTypeReference();
    }

    /**************************************************************************/

    boolean validateControlCodeValues() {
        return getBooleanProperty(EDIInputFactory.EDI_VALIDATE_CONTROL_CODE_VALUES, true);
    }

    boolean useInternalControlSchema() {
        if (this.controlSchema != null) {
            return false;
        }

        return getBooleanProperty(EDIInputFactory.EDI_VALIDATE_CONTROL_STRUCTURE, true);
    }

    boolean ignoreExtraneousCharacters() {
        return getBooleanProperty(EDIInputFactory.EDI_IGNORE_EXTRANEOUS_CHARACTERS, false);
    }

    boolean getBooleanProperty(String propertyName, boolean defaultValue) {
        Object property = properties.get(propertyName);

        if (property == null) {
            return defaultValue;
        }

        return Boolean.parseBoolean(property.toString());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy