scriptable-testlab
Version:
A lightweight, efficient tool designed to manage and update scripts for Scriptable.
187 lines (161 loc) • 4.61 kB
text/typescript
import {AbsXMLParser} from 'scriptable-abstract';
interface XMLParserState {
string: string;
didStartDocument: () => void;
didEndDocument: () => void;
didStartElement: (name: string, attributes: {[key: string]: string}) => void;
didEndElement: (name: string) => void;
foundCharacters: (str: string) => void;
parseErrorOccurred: (error: string) => void;
}
/**
* Mock implementation of Scriptable's XMLParser
* Used to parse XML documents
*/
export class MockXMLParser extends AbsXMLParser<XMLParserState> {
constructor(string: string) {
super({
string,
didStartDocument: () => {},
didEndDocument: () => {},
didStartElement: () => {},
didEndElement: () => {},
foundCharacters: () => {},
parseErrorOccurred: () => {},
});
}
/**
* Text to parse
*/
get string(): string {
return this.state.string;
}
/**
* Sets the text to parse
*/
set string(value: string) {
this.setState({string: value});
}
/**
* Function called when parsing starts
*/
get didStartDocument(): () => void {
return this.state.didStartDocument;
}
/**
* Sets the function called when parsing starts
*/
set didStartDocument(value: () => void) {
this.setState({didStartDocument: value});
}
/**
* Function called when parsing ends
*/
get didEndDocument(): () => void {
return this.state.didEndDocument;
}
/**
* Sets the function called when parsing ends
*/
set didEndDocument(value: () => void) {
this.setState({didEndDocument: value});
}
/**
* Function called when an element starts
*/
get didStartElement(): (name: string, attributes: {[key: string]: string}) => void {
return this.state.didStartElement;
}
/**
* Sets the function called when an element starts
*/
set didStartElement(value: (name: string, attributes: {[key: string]: string}) => void) {
this.setState({didStartElement: value});
}
/**
* Function called when an element ends
*/
get didEndElement(): (name: string) => void {
return this.state.didEndElement;
}
/**
* Sets the function called when an element ends
*/
set didEndElement(value: (name: string) => void) {
this.setState({didEndElement: value});
}
/**
* Function called when characters are found
*/
get foundCharacters(): (str: string) => void {
return this.state.foundCharacters;
}
/**
* Sets the function called when characters are found
*/
set foundCharacters(value: (str: string) => void) {
this.setState({foundCharacters: value});
}
/**
* Function called when a parse error occurs
*/
get parseErrorOccurred(): (error: string) => void {
return this.state.parseErrorOccurred;
}
/**
* Sets the function called when a parse error occurs
*/
set parseErrorOccurred(value: (error: string) => void) {
this.setState({parseErrorOccurred: value});
}
/**
* Parses the XML document
* @returns true if parsing was successful, false otherwise
*/
parse(): boolean {
try {
// Call lifecycle methods in sequence
this.didStartDocument();
// Simple XML parsing simulation using regex
const xmlString = this.string;
// Basic validation for well-formed XML
if (!xmlString.match(/^<[^>]+>.*<\/[^>]+>$/s)) {
this.parseErrorOccurred('Invalid XML structure');
return false;
}
const tagRegex = /<(\/?)([\w-]+)((?:\s+[\w-]+="[^"]*")*)\s*\/?>/g;
const textRegex = />([^<]+)</g;
let match;
// Parse tags
while ((match = tagRegex.exec(xmlString)) !== null) {
const [, isClosing, tagName, attributesStr] = match;
if (!isClosing) {
// Opening tag
const attributes: {[key: string]: string} = {};
const attrRegex = /(\w+)="([^"]*)"/g;
let attrMatch;
while ((attrMatch = attrRegex.exec(attributesStr)) !== null) {
const [, name, value] = attrMatch;
attributes[name] = value;
}
this.didStartElement(tagName, attributes);
} else {
// Closing tag
this.didEndElement(tagName);
}
}
// Parse text content
while ((match = textRegex.exec(xmlString)) !== null) {
const [, text] = match;
if (text.trim()) {
this.foundCharacters(text.trim());
}
}
this.didEndDocument();
return true;
} catch (error) {
this.parseErrorOccurred(error instanceof Error ? error.message : String(error));
return false;
}
}
}