UNPKG

typesxml

Version:

Open source XML library written in TypeScript

274 lines 9.97 kB
"use strict"; /******************************************************************************* * Copyright (c) 2023-2026 Maxprograms. * * This program and the accompanying materials * are made available under the terms of the Eclipse License 1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-v10.html * * Contributors: * Maxprograms - initial API and implementation *******************************************************************************/ Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonNodeReader = void 0; const NeedMoreDataError_js_1 = require("../NeedMoreDataError.js"); class JsonNodeReader { tokenizer; sequenceStarted = false; sequenceEnded = false; expectComma = false; pendingTrailingValidation = false; constructor(tokenizer) { this.tokenizer = tokenizer; } readNextEvent() { if (this.sequenceEnded) { if (this.pendingTrailingValidation) { const trailing = this.tokenizer.peekToken(); if (trailing.type !== 'eof') { throw new Error('Unexpected trailing content after JSON event stream'); } this.pendingTrailingValidation = false; } return undefined; } this.ensureSequenceStarted(); if (this.expectComma) { const nextToken = this.tokenizer.peekToken(); if (nextToken.type === 'bracketClose') { this.consumeAndFinish(); return undefined; } this.consumeDelimiter('comma'); this.expectComma = false; } const lookAhead = this.tokenizer.peekToken(); if (lookAhead.type === 'bracketClose') { this.consumeAndFinish(); return undefined; } const value = this.parseValue(); if (value === null || typeof value !== 'object' || Array.isArray(value)) { throw new Error('JSON event entries must be objects'); } const payload = value; const event = this.toEvent(payload); this.expectComma = true; return event; } ensureSequenceStarted() { if (this.sequenceStarted) { return; } const token = this.tokenizer.nextToken(); if (token.type !== 'bracketOpen') { throw new Error('JSON event stream must start with an array'); } this.sequenceStarted = true; } consumeAndFinish() { const endToken = this.tokenizer.nextToken(); if (endToken.type !== 'bracketClose') { throw new Error('Malformed JSON event stream: expected closing bracket'); } this.sequenceEnded = true; try { const trailing = this.tokenizer.peekToken(); if (trailing.type !== 'eof') { throw new Error('Unexpected trailing content after JSON event stream'); } this.pendingTrailingValidation = false; } catch (error) { if (error instanceof NeedMoreDataError_js_1.NeedMoreDataError) { this.pendingTrailingValidation = true; return; } throw error; } } consumeDelimiter(expected) { const token = this.tokenizer.nextToken(); if (token.type !== expected) { throw new Error('Malformed JSON event stream: expected comma delimiter'); } } parseValue() { const token = this.tokenizer.nextToken(); switch (token.type) { case 'string': return token.value; case 'number': return token.value; case 'boolean': return token.value; case 'null': return null; case 'braceOpen': return this.parseObject(); case 'bracketOpen': return this.parseArray(); case 'eof': throw new Error('Unexpected end of JSON stream while parsing value'); default: throw new Error('Unexpected token while parsing value'); } } parseObject() { const result = {}; const next = this.tokenizer.peekToken(); if (next.type === 'braceClose') { this.tokenizer.nextToken(); return result; } while (true) { const keyToken = this.tokenizer.nextToken(); if (keyToken.type !== 'string') { throw new Error('Object keys must be strings'); } const colon = this.tokenizer.nextToken(); if (colon.type !== 'colon') { throw new Error('Expected colon after object key'); } const value = this.parseValue(); result[keyToken.value] = value; const delimiter = this.tokenizer.nextToken(); if (delimiter.type === 'braceClose') { break; } if (delimiter.type !== 'comma') { throw new Error('Expected comma between object members'); } } return result; } parseArray() { const result = []; const next = this.tokenizer.peekToken(); if (next.type === 'bracketClose') { this.tokenizer.nextToken(); return result; } while (true) { result.push(this.parseValue()); const delimiter = this.tokenizer.nextToken(); if (delimiter.type === 'bracketClose') { break; } if (delimiter.type !== 'comma') { throw new Error('Expected comma between array items'); } } return result; } toEvent(payload) { const type = this.requireString(payload, 'type'); switch (type) { case 'startDocument': return { type: 'startDocument' }; case 'endDocument': return { type: 'endDocument' }; case 'xmlDeclaration': return { type: 'xmlDeclaration', version: this.requireString(payload, 'version'), encoding: this.requireString(payload, 'encoding'), standalone: this.optionalString(payload, 'standalone') }; case 'startElement': return { type: 'startElement', name: this.requireString(payload, 'name'), attributes: this.readAttributes(payload.attributes) }; case 'endElement': return { type: 'endElement', name: this.requireString(payload, 'name') }; case 'characters': return { type: 'characters', value: this.requireString(payload, 'value') }; case 'ignorableWhitespace': return { type: 'ignorableWhitespace', value: this.requireString(payload, 'value') }; case 'comment': return { type: 'comment', value: this.requireString(payload, 'value') }; case 'processingInstruction': return { type: 'processingInstruction', target: this.requireString(payload, 'target'), data: this.requireString(payload, 'data') }; case 'startCDATA': return { type: 'startCDATA' }; case 'endCDATA': return { type: 'endCDATA' }; case 'startDTD': return { type: 'startDTD', name: this.requireString(payload, 'name'), publicId: this.optionalString(payload, 'publicId') ?? '', systemId: this.optionalString(payload, 'systemId') ?? '' }; case 'internalSubset': return { type: 'internalSubset', declaration: this.requireString(payload, 'declaration') }; case 'endDTD': return { type: 'endDTD' }; case 'skippedEntity': return { type: 'skippedEntity', name: this.requireString(payload, 'name') }; default: throw new Error('Unsupported JSON event type: ' + type); } } readAttributes(source) { if (source === undefined) { return []; } if (!Array.isArray(source)) { throw new Error('Attributes must be an array'); } return source.map((item) => { if (item === null || typeof item !== 'object') { throw new Error('Attribute entries must be objects'); } const record = item; const name = this.requireString(record, 'name'); const value = this.requireString(record, 'value'); return { name, value }; }); } requireString(record, key) { const value = record[key]; if (typeof value === 'string') { return value; } throw new Error('Expected string value for property "' + key + '"'); } optionalString(record, key) { const value = record[key]; if (value === undefined) { return undefined; } if (typeof value === 'string') { return value; } throw new Error('Expected string value for property "' + key + '"'); } } exports.JsonNodeReader = JsonNodeReader; //# sourceMappingURL=JsonNodeReader.js.map