import { SchemaJsonPattern } from "./schema-json-pattern";
export class PatternExtractor {
protected streamCache: string = '';
protected state: 'handling' | 'normal' = 'normal';
protected onNormalWrite: (value: string) => void;
protected onHandledWrite: (value: string) => void;
protected keepFlag: false | 'handling' | 'normal' = false;
protected regExpMap: Record<'start' | 'end', Record<'full' | 'partial', RegExp>>;
constructor(config: {
onNormalWrite: (value: string) => void;
onHandledWrite: (value: string) => void;
keepFlag?: false | 'handling' | 'normal';
regExpMap?: Record<string, Record<'full' | 'partial', RegExp>>;
}) {
this.onNormalWrite = config.onNormalWrite;
this.onHandledWrite = config.onHandledWrite;
this.keepFlag = config.keepFlag ?? this.keepFlag;
this.regExpMap = config.regExpMap ?? new SchemaJsonPattern().regExpMap;
}
public setState(state: 'handling' | 'normal') {
this.state = state;
}
public reset() {
this.streamCache = '';
this.state = 'normal';
}
protected updateNormalStream(value: string) {
if (value.length > 0) {
this.onNormalWrite?.(value);
}
}
protected updateHandledStream(value: string) {
if (value.length > 0) {
this.onHandledWrite?.(value);
}
}
protected containsFlag(str: string, flag: RegExp, offset: number = 0): { result: boolean, index: number, length: number } {
const match = str.substring(offset).match(flag);
if (match && match.index !== undefined) return { result: true, index: offset + match.index, length: match[0].length };
return { result: false, index: -1, length: 0 };
}
protected getNextState() {
return this.state === 'normal' ? 'handling' : 'normal';
}
protected getLookingState() {
return this.state === 'normal' ? 'start' : 'end';
}
protected updateStream(value: string) {
if (this.state === 'normal') {
this.updateNormalStream(value);
} else {
this.updateHandledStream(value);
}
}
protected updateStreamWithFlag(value: string) {
if (this.keepFlag === this.state) {
this.updateStream(value);
}
}
public handleContent(content: string) {
let offset = 0;
const value = this.streamCache + content;
while (offset < value.length) {
offset = this.handleValue(value, offset);
}
return this.streamCache;
}
protected handleValue(value: string, prevOffset: number = 0) {
let offset = prevOffset;
const lookingState = this.getLookingState();
const { result, index, length } = this.containsFlag(value, this.regExpMap[lookingState].full, offset);
if (result) {
this.updateStream(value.substring(offset, index));
const flagString = value.substring(index, index + length);
this.updateStreamWithFlag(flagString);
this.state = this.getNextState();
this.updateStreamWithFlag(flagString);
offset = index + length;
this.streamCache = '';
} else {
const { result, index, length } = this.containsFlag(value, this.regExpMap[lookingState].partial, offset);
if (result) {
this.updateStream(value.substring(offset, index));
offset = index + length;
this.streamCache = value.substring(index, value.length);
} else {
this.updateStream(value.substring(offset, value.length));
offset = value.length;
this.streamCache = '';
}
}
return offset;
}
}