import AssertionError from "../error/AssertionError"; import EtudeError from "../error/EtudeError"; import EtudeParser from "../util/EtudeParser"; import Exceptional from "../util/Exceptional"; export default class Accidental { public static size: number = 0; private static _values: Accidental[] = []; public static TRIPLE_FLAT = new Accidental("bbb", -3); public static DOUBLE_FLAT = new Accidental("bb", -2); public static FLAT = new Accidental("b", -1); public static NATURAL = new Accidental("n", 0); public static SHARP = new Accidental("#", 1); public static DOUBLE_SHARP = new Accidental("x", 2); public static TRIPLE_SHARP = new Accidental("#x", 3); private constructor(private symbol: string, private offset: number) { ++Accidental.size; Accidental._values.push(this); } public static values(): Accidental[] { return Accidental._values.slice(); } public ordinal(): number { return Accidental._values.indexOf(this); } public static valueOf(accidentalString: string): Accidental { const accidental = Accidental[accidentalString]; if (accidental instanceof Accidental) { return accidental; } throw EtudeError.forInvalid(Accidental, accidentalString); } public static isValid(value: number | string): boolean { if (typeof (value) === "number") { const offset = value as number; return Accidental.TRIPLE_FLAT.getOffset() <= offset && offset <= Accidental.TRIPLE_SHARP.getOffset(); } else if (typeof (value) === "string") { const accidentalString = value as string; return Accidental .values() .map(a => a.toString()) .some(a => accidentalString.localeCompare(a) === 0); } else { throw new AssertionError("Should not have reached else statement."); } } public static fromOffset(offset: number): Exceptional { return Exceptional .of(offset) .filter(o => Accidental.isValid(o), EtudeError.forInvalid(Accidental, offset, "out of range")) .map(i => Accidental.values()[i - Accidental.TRIPLE_FLAT.getOffset()]); } public getOffset(): number { return this.offset; } public static fromString(accidentalString: string): Exceptional { return EtudeParser .of(accidentalString) .filter(o => o != null, EtudeError.forNull(Accidental)) .parse(s => { const value = Accidental .values() .find(a => s.localeCompare(a.toString()) === 0); return Exceptional .ofNullable(value) .withException(EtudeError.forInvalid(Accidental, s)) }) .get(a => a[0] as Accidental); } public toString(): string { return this.symbol; } }