UNPKG

x12

Version:
320 lines (235 loc) 12.4 kB
'use strict'; import { ArgumentNullError, ParserError } from './Errors'; import { Range, Position } from './Positioning'; import { X12Diagnostic, X12DiagnosticLevel } from './X12Diagnostic'; import { X12Interchange } from './X12Interchange'; import { X12FunctionalGroup } from './X12FunctionalGroup'; import { X12Transaction } from './X12Transaction'; import { X12Segment } from './X12Segment'; import { X12Element } from './X12Element'; const DOCUMENT_MIN_LENGTH: number = 113; // ISA = 106, IEA > 7 const SEGMENT_TERMINATOR_POS: number = 105; const ELEMENT_DELIMITER_POS: number = 3; const INTERCHANGE_CACHE_SIZE: number = 10; export class X12Parser { constructor(private _strict: boolean) { this.diagnostics = new Array<X12Diagnostic>(); } diagnostics: X12Diagnostic[]; parseX12(edi: string): X12Interchange { if (!edi) { throw new ArgumentNullError('edi'); } this.diagnostics.splice(0); if (edi.length < DOCUMENT_MIN_LENGTH) { let errorMessage = `X12 Standard: Document is too short. Document must be at least ${DOCUMENT_MIN_LENGTH} characters long to be well-formed X12.`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, new Range(0, 0, 0, edi.length - 1))); } let segmentTerminator = edi.charAt(SEGMENT_TERMINATOR_POS); let elementDelimiter = edi.charAt(ELEMENT_DELIMITER_POS); if (edi.charAt(103) !== elementDelimiter) { let errorMessage = 'X12 Standard: The ISA segment is not the correct length (106 characters, including segment terminator).'; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, new Range(0, 0, 0, 2))); } let interchange = new X12Interchange(segmentTerminator, elementDelimiter); let group: X12FunctionalGroup; let transaction: X12Transaction; let segments = this._parseSegments(edi, segmentTerminator, elementDelimiter); segments.forEach((seg) => { if (seg.tag == 'ISA') { this._processISA(interchange, seg); } else if (seg.tag == 'IEA') { this._processIEA(interchange, seg); } else if (seg.tag == 'GS') { group = new X12FunctionalGroup(); this._processGS(group, seg); interchange.functionalGroups.push(group); } else if (seg.tag == 'GE') { if (!group) { let errorMessage = 'X12 Standard: Missing GS segment!'; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, seg.range)); } this._processGE(group, seg); group = null; } else if (seg.tag == 'ST') { if (!group) { let errorMessage = `X12 Standard: ${seg.tag} segment cannot appear outside of a functional group.`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, seg.range)); } transaction = new X12Transaction(); this._processST(transaction, seg); group.transactions.push(transaction); } else if (seg.tag == 'SE') { if (!group) { let errorMessage = `X12 Standard: ${seg.tag} segment cannot appear outside of a functional group.`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, seg.range)); } if (!transaction) { let errorMessage = 'X12 Standard: Missing ST segment!'; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, seg.range)); } this._processSE(transaction, seg); transaction = null; } else { if (!group) { let errorMessage = `X12 Standard: ${seg.tag} segment cannot appear outside of a functional group.`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, seg.range)); } if (!transaction) { let errorMessage = `X12 Standard: ${seg.tag} segment cannot appear outside of a transaction.`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, seg.range)); } else { transaction.segments.push(seg); } } }); return interchange; } private _parseSegments(edi: string, segmentTerminator: string, elementDelimiter: string): X12Segment[] { let segments = new Array<X12Segment>(); let tagged = false; let currentSegment: X12Segment; let currentElement: X12Element; currentSegment = new X12Segment(); for (let i = 0, l = 0, c = 0; i < edi.length; i++) { // segment not yet named and not whitespace or delimiter - begin naming segment if (!tagged && (edi[i].search(/\s/) == -1) && (edi[i] !== elementDelimiter) && (edi[i] !== segmentTerminator)) { currentSegment.tag += edi[i]; if (!currentSegment.range.start) { currentSegment.range.start = new Position(l, c); } } // trailing line breaks - consume them and increment line number else if (!tagged && (edi[i].search(/\s/) > -1)) { if (edi[i] == '\n') { l++; c = -1; } } // segment tag/name is completed - mark as tagged else if (!tagged && (edi[i] == elementDelimiter)) { tagged = true; currentElement = new X12Element(); currentElement.range.start = new Position(l, c); } // segment terminator else if (edi[i] == segmentTerminator) { currentElement.range.end = new Position(l, (c - 1)); currentSegment.elements.push(currentElement); currentSegment.range.end = new Position(l, c); segments.push(currentSegment); currentSegment = new X12Segment(); tagged = false; if (segmentTerminator === '\n') { l++; c = -1; } } // element delimiter else if (tagged && (edi[i] == elementDelimiter)) { currentElement.range.end = new Position(l, (c - 1)); currentSegment.elements.push(currentElement); currentElement = new X12Element(); currentElement.range.start = new Position(l, c + 1); } // element data else { currentElement.value += edi[i]; } c++; } return segments; } private _processISA(interchange: X12Interchange, segment: X12Segment): void { interchange.header = segment; } private _processIEA(interchange: X12Interchange, segment: X12Segment): void { interchange.trailer = segment; if (parseInt(segment.valueOf(1)) !== interchange.functionalGroups.length) { let errorMessage = `X12 Standard: The value in IEA01 (${segment.valueOf(1)}) does not match the number of GS segments in the interchange (${interchange.functionalGroups.length}).`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, segment.elements[0].range)); } if (segment.valueOf(2) !== interchange.header.valueOf(13)) { let errorMessage = `X12 Standard: The value in IEA02 (${segment.valueOf(2)}) does not match the value in ISA13 (${interchange.header.valueOf(13)}).`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, segment.elements[1].range)); } } private _processGS(group: X12FunctionalGroup, segment: X12Segment): void { group.header = segment; } private _processGE(group: X12FunctionalGroup, segment: X12Segment): void { group.trailer = segment; if (parseInt(segment.valueOf(1)) !== group.transactions.length) { let errorMessage = `X12 Standard: The value in GE01 (${segment.valueOf(1)}) does not match the number of ST segments in the functional group (${group.transactions.length}).`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, segment.elements[0].range)); } if (segment.valueOf(2) !== group.header.valueOf(6)) { let errorMessage = `X12 Standard: The value in GE02 (${segment.valueOf(2)}) does not match the value in GS06 (${group.header.valueOf(6)}).`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, segment.elements[1].range)); } } private _processST(transaction: X12Transaction, segment: X12Segment): void { transaction.header = segment; } private _processSE(transaction: X12Transaction, segment: X12Segment): void { transaction.trailer = segment; let expectedNumberOfSegments = (transaction.segments.length + 2); if (parseInt(segment.valueOf(1)) !== expectedNumberOfSegments) { let errorMessage = `X12 Standard: The value in SE01 (${segment.valueOf(1)}) does not match the number of segments in the transaction (${expectedNumberOfSegments}).`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, segment.elements[0].range)); } if (segment.valueOf(2) !== transaction.header.valueOf(2)) { let errorMessage = `X12 Standard: The value in SE02 (${segment.valueOf(2)}) does not match the value in ST02 (${transaction.header.valueOf(2)}).`; if (this._strict) { throw new ParserError(errorMessage); } this.diagnostics.push(new X12Diagnostic(X12DiagnosticLevel.Error, errorMessage, segment.elements[1].range)); } } }