'use strict' ; import { QuerySyntaxError } from './Errors' ; import { X12Parser } from './X12Parser' ; import { X12Interchange } from './X12Interchange' ; import { X12FunctionalGroup } from './X12FunctionalGroup' ; import { X12Transaction } from './X12Transaction' ; import { X12Segment } from './X12Segment' ; import { X12Element } from './X12Element' ; export class X12QueryEngine { description param { X12Parser | boolean } parser constructor ( parser = true ) { this . _parser = typeof parser === 'boolean' ? new X12Parser ( parser ) : parser ; } private readonly _parser : X12Parser ; private readonly _forEachPattern : RegExp = /FOREACH\([A-Z0-9]{2,3}\)=>.+/g ; private readonly _concatPattern : RegExp = /CONCAT\(.+,.+\)=>.+/g ; description param { string | X12Interchange } rawEdi param { string } reference param { string } defaultValue returns { X12QueryResult [ ] } query ( rawEdi , reference , defaultValue = null ) { const interchange = typeof rawEdi === 'string' ? ( this . _parser . parse ( rawEdi ) as X12Interchange ) : rawEdi ; const forEachMatch = reference . match ( this . _forEachPattern ) ; if ( forEachMatch !== null ) { reference = this . _evaluateForEachQueryPart ( forEachMatch [ 0 ] ) ; } const concathMatch = reference . match ( this . _concatPattern ) ; let concat ; if ( concathMatch !== null ) { concat = this . _evaluateConcatQueryPart ( interchange , concathMatch [ 0 ] ) ; reference = concat . query ; } const hlPathMatch = reference . match ( /HL\+(\w\+?)+[+-]/g ) ; const segPathMatch = reference . match ( /((?<!\+)[A-Z0-9]{2,3}-)+/g ) ; const elmRefMatch = reference . match ( /[A-Z0-9]{2,3}[0-9]{2}[^[]?/g ) ; const qualMatch = reference . match ( /:[A-Z0-9]{2,3}[0-9]{2,}\[["'][^[\]"']+["']\]/g ) ; const results = new Array < X12QueryResult > ( ) ; for ( let i = 0 ; i < interchange . functionalGroups . length ; i ++ ) { const group = interchange . functionalGroups [ i ] ; for ( let j = 0 ; j < group . transactions . length ; j ++ ) { const txn = group . transactions [ j ] ; let segments = txn . segments ; if ( hlPathMatch !== null ) { segments = this . _evaluateHLQueryPart ( txn , hlPathMatch [ 0 ] ) ; } if ( segPathMatch !== null ) { segments = this . _evaluateSegmentPathQueryPart ( segments , segPathMatch [ 0 ] ) ; } if ( elmRefMatch === null ) { throw new QuerySyntaxError ( 'Element reference queries must contain an element reference!' ) ; } const txnResults = this . _evaluateElementReferenceQueryPart ( interchange , group , txn , [ ] . concat ( segments , [ interchange . header , group . header , txn . header , txn . trailer , group . trailer , interchange . trailer ] ) , elmRefMatch [ 0 ] , qualMatch , defaultValue ) ; txnResults . forEach ( res => { if ( concat !== undefined ) { res . value = `${ concat . value }${ concat . separator }${ res . value }` ; } results . push ( res ) ; } ) ; } } return results ; } description param { string | X12Interchange } rawEdi param { string } reference param { string } defaultValue returns { X12QueryResult } querySingle ( rawEdi , reference , defaultValue = null ) { const results = this . query ( rawEdi , reference ) ; if ( reference . match ( this . _forEachPattern ) !== null ) { const values = results . map ( result => result . value ) ; if ( values . length !== 0 ) { results [ 0 ] . value = null ; results [ 0 ] . values = values ; } } return results . length === 0 ? null : results [ 0 ] ; } private _getMacroParts ( macroQuery ) { const macroPart = macroQuery . substr ( 0 , macroQuery . indexOf ( '=>' ) ) ; const queryPart = macroQuery . substr ( macroQuery . indexOf ( '=>' ) + 2 ) ; const parameters = macroPart . substr ( macroPart . indexOf ( '(' ) + 1 , macroPart . length - macroPart . indexOf ( '(' ) - 2 ) ; return { macroPart , queryPart , parameters } ; } private _evaluateForEachQueryPart ( forEachSegment ) { const { queryPart , parameters } = this . _getMacroParts ( forEachSegment ) ; return `${ parameters }-${ queryPart }` ; } private _evaluateConcatQueryPart ( interchange , concatSegment ) { const { queryPart , parameters } = this . _getMacroParts ( concatSegment ) ; let value = '' ; const expandedParams = parameters . split ( ',' ) ; if ( expandedParams . length === 3 ) { expandedParams [ 1 ] = ',' ; } const result = this . querySingle ( interchange , expandedParams [ 0 ] ) ; if ( result !== null ) { if ( result . value !== null && result . value !== undefined ) { value = result . value ; } else if ( Array . isArray ( result . values ) ) { value = result . values . join ( expandedParams [ 1 ] ) ; } } return { value , separator : expandedParams [ 1 ] , query : queryPart } ; } private _evaluateHLQueryPart ( transaction , hlPath ) { let qualified = false ; const pathParts = hlPath . replace ( '-' , '' ) . split ( '+' ) . filter ( ( value , index , array ) => { return value !== 'HL' && value !== '' && value !== null ; } ) ; const matches = new Array < X12Segment > ( ) ; let lastParentIndex = - 1 ; for ( let i = 0 , j = 0 ; i < transaction . segments . length ; i ++ ) { const segment = transaction . segments [ i ] ; if ( qualified && segment . tag === 'HL' ) { const parentIndex = parseInt ( segment . valueOf ( 2 , '-1' ) ) ; if ( parentIndex !== lastParentIndex ) { j = 0 ; qualified = false ; } } if ( ! qualified && transaction . segments [ i ] . tag === 'HL' && transaction . segments [ i ] . valueOf ( 3 ) === pathParts [ j ] ) { lastParentIndex = parseInt ( segment . valueOf ( 2 , '-1' ) ) ; j ++ ; if ( j === pathParts . length ) { qualified = true ; } } if ( qualified ) { matches . push ( transaction . segments [ i ] ) ; } } return matches ; } private _evaluateSegmentPathQueryPart ( segments , segmentPath ) { let qualified = false ; const pathParts = segmentPath . split ( '-' ) . filter ( ( value , index , array ) => { return ! ! value ; } ) ; const matches = new Array < X12Segment > ( ) ; for ( let i = 0 , j = 0 ; i < segments . length ; i ++ ) { if ( qualified && ( segments [ i ] . tag === 'HL' || pathParts . indexOf ( segments [ i ] . tag ) > - 1 ) ) { j = 0 ; qualified = false ; } if ( ! qualified && segments [ i ] . tag === pathParts [ j ] ) { j ++ ; if ( j === pathParts . length ) { qualified = true ; } } if ( qualified ) { matches . push ( segments [ i ] ) ; } } return matches ; } private _evaluateElementReferenceQueryPart ( interchange , functionalGroup , transaction , segments , elementReference , qualifiers , defaultValue = null ) { const reference = elementReference . replace ( ':' , '' ) ; const tag = reference . substr ( 0 , reference . length - 2 ) ; const pos = reference . substr ( reference . length - 2 , 2 ) ; const posint = parseInt ( pos ) ; const results = new Array < X12QueryResult > ( ) ; for ( let i = 0 ; i < segments . length ; i ++ ) { const segment = segments [ i ] ; if ( segment === null || segment === undefined ) { continue ; } if ( segment . tag !== tag ) { continue ; } const value = segment . valueOf ( posint , defaultValue ) ; if ( value !== null && this . _testQualifiers ( transaction , segment , qualifiers ) ) { results . push ( new X12QueryResult ( interchange , functionalGroup , transaction , segment , segment . elements [ posint - 1 ] , value ) ) ; } } return results ; } private _testQualifiers ( transaction , segment , qualifiers ) { if ( qualifiers === undefined || qualifiers === null ) { return true ; } for ( let i = 0 ; i < qualifiers . length ; i ++ ) { const qualifier = qualifiers [ i ] . substr ( 1 ) ; const elementReference = qualifier . substring ( 0 , qualifier . indexOf ( '[' ) ) ; const elementValue = qualifier . substring ( qualifier . indexOf ( '[' ) + 2 , qualifier . lastIndexOf ( ']' ) - 1 ) ; const tag = elementReference . substr ( 0 , elementReference . length - 2 ) ; const pos = elementReference . substr ( elementReference . length - 2 , 2 ) ; const posint = parseInt ( pos ) ; for ( let j = transaction . segments . indexOf ( segment ) ; j > - 1 ; j -- ) { const seg = transaction . segments [ j ] ; const value = seg . valueOf ( posint ) ; if ( seg . tag === tag && seg . tag === segment . tag && value !== elementValue ) { return false ; } else if ( seg . tag === tag && value === elementValue ) { break ; } if ( j === 0 ) { return false ; } } } return true ; } } description typedef X12QueryResult property { X12Interchange } interchange property { X12FunctionalGroup } functionalGroup property { X12Transaction } transaction property { X12Segment } segment property { X12Element } element property { string } value property { Array < string | string [ ] > } values export class X12QueryResult { constructor ( interchange ? , functionalGroup ? , transaction ? , segment ? , element ? , value ? ) { this . interchange = interchange ; this . functionalGroup = functionalGroup ; this . transaction = transaction ; this . segment = segment ; this . element = element ; this . value = value === null || value === undefined ? element . value : value ; this . values = new Array < string | string [ ] > ( ) ; } interchange : X12Interchange ; functionalGroup : X12FunctionalGroup ; transaction : X12Transaction ; segment : X12Segment ; element : X12Element ; value : string ; values : Array < string | string [ ] > ; }