diff --git a/src/adap-b01/names/Name.ts b/src/adap-b01/names/Name.ts index 1717decf..c830dd7b 100644 --- a/src/adap-b01/names/Name.ts +++ b/src/adap-b01/names/Name.ts @@ -20,7 +20,7 @@ export class Name { /** Expects that all Name components are properly masked */ constructor(other: string[], delimiter?: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } /** @@ -29,7 +29,7 @@ export class Name { * Users can vary the delimiter character to be used */ public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } /** @@ -38,35 +38,35 @@ export class Name { * The control characters in the data string are the default characters */ public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getComponent(i: number): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } /** Expects that new Name component c is properly masked */ public setComponent(i: number, c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } /** Returns number of components in Name instance */ public getNoComponents(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } /** Expects that new Name component c is properly masked */ public insert(i: number, c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } /** Expects that new Name component c is properly masked */ public append(c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public remove(i: number): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b02/files1/File.ts b/src/adap-b02/files1/File.ts index c7e26a03..7eaef4b2 100644 --- a/src/adap-b02/files1/File.ts +++ b/src/adap-b02/files1/File.ts @@ -1,29 +1,44 @@ export class File { public isOpen(): boolean { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } public isClosed(): boolean { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } + public open(): void { + this.assertIsClosedFile(); + throw new Error("incomplete example code"); + } + public read(): Object[] { this.assertIsOpenFile(); - throw new Error("no implementation"); + throw new Error("incomplete example code"); + } + + public write(data: Object[]): void { + this.assertIsOpenFile(); + throw new Error("incomplete example code"); } + public close(): void { + this.assertIsOpenFile(); + throw new Error("incomplete example code"); + } + public delete(): void { this.assertIsClosedFile(); - throw new Error("no implementation"); + throw new Error("incomplete example code"); } protected assertIsOpenFile(): void { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } protected assertIsClosedFile(): void { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } } \ No newline at end of file diff --git a/src/adap-b02/files2/File.ts b/src/adap-b02/files2/File.ts index ee64fbec..a46e2a72 100644 --- a/src/adap-b02/files2/File.ts +++ b/src/adap-b02/files2/File.ts @@ -4,8 +4,10 @@ export interface File { isOpen(): boolean; isClosed(): boolean; + open(): void; read(): any[]; write(data: any[]): void; + close(): void; delete(): void; } \ No newline at end of file diff --git a/src/adap-b02/files2/ObjFile.ts b/src/adap-b02/files2/ObjFile.ts index f4ec4326..0a1c09d7 100644 --- a/src/adap-b02/files2/ObjFile.ts +++ b/src/adap-b02/files2/ObjFile.ts @@ -10,34 +10,44 @@ export class ObjFile implements File { } public isOpen(): boolean { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } public isClosed(): boolean { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } + public open(): void { + this.assertIsClosedFile(); + throw new Error("incomplete example code"); + } + public read(): Object[] { this.assertIsOpenFile(); - throw new Error("no implementation"); + throw new Error("incomplete example code"); } public write(data: Object[]): void { this.assertIsOpenFile(); - throw new Error("no implementation"); + throw new Error("incomplete example code"); } + public close(): void { + this.assertIsOpenFile(); + throw new Error("incomplete example code"); + } + public delete(): void { this.assertIsClosedFile(); - throw new Error("no implementation"); + throw new Error("incomplete example code"); } protected assertIsOpenFile(): void { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } protected assertIsClosedFile(): void { - throw new Error("no implementation"); + throw new Error("incomplete example code"); } } \ No newline at end of file diff --git a/src/adap-b02/names/StringArrayName.ts b/src/adap-b02/names/StringArrayName.ts index 9195ed77..3d216645 100644 --- a/src/adap-b02/names/StringArrayName.ts +++ b/src/adap-b02/names/StringArrayName.ts @@ -7,51 +7,51 @@ export class StringArrayName implements Name { protected components: string[] = []; constructor(other: string[], delimiter?: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getNoComponents(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getComponent(i: number): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public setComponent(i: number, c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public insert(i: number, c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public append(c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public remove(i: number): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b02/names/StringName.ts b/src/adap-b02/names/StringName.ts index a2e4ec5c..1009a702 100644 --- a/src/adap-b02/names/StringName.ts +++ b/src/adap-b02/names/StringName.ts @@ -8,51 +8,51 @@ export class StringName implements Name { protected noComponents: number = 0; constructor(other: string, delimiter?: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getNoComponents(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getComponent(x: number): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public setComponent(n: number, c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public insert(n: number, c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public append(c: string): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public remove(n: number): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b03/names/AbstractName.ts b/src/adap-b03/names/AbstractName.ts index b54de984..971f7127 100644 --- a/src/adap-b03/names/AbstractName.ts +++ b/src/adap-b03/names/AbstractName.ts @@ -6,39 +6,39 @@ export abstract class AbstractName implements Name { protected delimiter: string = DEFAULT_DELIMITER; constructor(delimiter: string = DEFAULT_DELIMITER) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public clone(): Name { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public toString(): string { - throw new Error("needs implementation"); + return this.asDataString(); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEqual(other: Name): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getHashCode(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } abstract getNoComponents(): number; @@ -51,7 +51,7 @@ export abstract class AbstractName implements Name { abstract remove(i: number): void; public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b03/names/StringArrayName.ts b/src/adap-b03/names/StringArrayName.ts index 3d27cf00..da67f37c 100644 --- a/src/adap-b03/names/StringArrayName.ts +++ b/src/adap-b03/names/StringArrayName.ts @@ -8,66 +8,62 @@ export class StringArrayName extends AbstractName { constructor(other: string[], delimiter?: string) { super(); - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public clone(): Name { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); - } - - public toString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEqual(other: Name): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getHashCode(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getNoComponents(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getComponent(i: number): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public setComponent(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public insert(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public append(c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public remove(i: number) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b03/names/StringName.ts b/src/adap-b03/names/StringName.ts index 5a8db508..8049e4af 100644 --- a/src/adap-b03/names/StringName.ts +++ b/src/adap-b03/names/StringName.ts @@ -9,67 +9,63 @@ export class StringName extends AbstractName { constructor(other: string, delimiter?: string) { super(); - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public clone(): Name { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); - } - - public toString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEqual(other: Name): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getHashCode(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getNoComponents(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getComponent(i: number): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public setComponent(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public insert(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public append(c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public remove(i: number) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b04/common/AssertionDispatcher.ts b/src/adap-b04/common/AssertionDispatcher.ts new file mode 100644 index 00000000..bfe4fcda --- /dev/null +++ b/src/adap-b04/common/AssertionDispatcher.ts @@ -0,0 +1,32 @@ +import { IllegalArgumentException } from "./IllegalArgumentException"; +import { InvalidStateException } from "./InvalidStateException"; +import { MethodFailedException } from "./MethodFailedException"; + +export enum ExceptionType { + PRECONDITION = 0, + CLASS_INVARIANT = 1, + POSTCONDITION = 2 +}; + +export class AssertionDispatcher { + + protected static DISPATCHER = new AssertionDispatcher(); + + public static dispatch(key: ExceptionType, condition: boolean, message?: string) { + this.DISPATCHER.doDispatch(key, condition, message); + } + + protected methods: { [key in ExceptionType]: (c: boolean, m?: string) => void }; + + constructor() { + this.methods = { + [ExceptionType.PRECONDITION]: IllegalArgumentException.assertCondition, + [ExceptionType.CLASS_INVARIANT]: InvalidStateException.assertCondition, + [ExceptionType.POSTCONDITION]: MethodFailedException.assertCondition + } + } + + protected doDispatch(key: ExceptionType, condition: boolean, message?: string) { + this.methods[key](condition, message); + } +} \ No newline at end of file diff --git a/src/adap-b04/common/Exception.ts b/src/adap-b04/common/Exception.ts index 93cf6d7c..3e2cd15d 100644 --- a/src/adap-b04/common/Exception.ts +++ b/src/adap-b04/common/Exception.ts @@ -1,14 +1,41 @@ +import { InvalidStateException } from "./InvalidStateException"; + /** * Root class for exceptions in ADAP examples */ export abstract class Exception extends Error { + protected trigger: Exception | null = null; + static isNullOrUndefined(o: Object | null) { return (o == undefined) || (o == null); } - constructor(m: string) { + constructor(m: string, t?: Exception) { super(m); + + if (t != undefined) { + this.trigger = t; + } + } + + public hasTrigger(): boolean { + return this.trigger != null; + } + + public getTrigger(): Exception { + this.assertHasTrigger(); // should be InvalidStateException + return this.trigger as Exception; + } + + protected assertHasTrigger(): void { + if (!this.hasTrigger()) { + throw new (class extends Exception { + constructor(t: Exception) { + super("exception had no trigger", t); + } + })(this); + } } } \ No newline at end of file diff --git a/src/adap-b04/common/IllegalArgumentException.ts b/src/adap-b04/common/IllegalArgumentException.ts index ea42eb2c..bf7b2990 100644 --- a/src/adap-b04/common/IllegalArgumentException.ts +++ b/src/adap-b04/common/IllegalArgumentException.ts @@ -1,4 +1,5 @@ import { Exception } from "./Exception"; +import { InvalidStateException } from "./InvalidStateException"; /** * An IllegalArgumentException signals an invalid argument. @@ -6,16 +7,21 @@ import { Exception } from "./Exception"; */ export class IllegalArgumentException extends Exception { - static assertIsNotNullOrUndefined(o: Object | null, exMsg: string = "null or undefined"): void { - this.assertCondition(!this.isNullOrUndefined(o), exMsg); + static assertIsNotNullOrUndefined(o: Object | null, m: string = "null or undefined", t?: Exception): void { + this.assertCondition(!this.isNullOrUndefined(o), m); } - static assertCondition(cond: boolean, exMsg: string): void { - if (!cond) throw new IllegalArgumentException(exMsg); + static assertCondition(c: boolean, m: string = "illegal argument", t?: Exception): void { + if (!c) throw new IllegalArgumentException(m, t); } - constructor(m: string) { - super(m); + constructor(m: string, t?: Exception) { + super(m, t); } - + + public getTrigger(): Exception { + InvalidStateException.assertCondition(this.hasTrigger()); + return super.getTrigger(); + } + } diff --git a/src/adap-b04/common/InvalidStateException.ts b/src/adap-b04/common/InvalidStateException.ts index 106741c9..58dae00d 100644 --- a/src/adap-b04/common/InvalidStateException.ts +++ b/src/adap-b04/common/InvalidStateException.ts @@ -6,16 +6,16 @@ import { Exception } from "./Exception"; */ export class InvalidStateException extends Exception { - static assertIsNotNullOrUndefined(o: Object | null, exMsg: string = "null or undefined"): void { - this.assertCondition(!this.isNullOrUndefined(o), exMsg); + static assertIsNotNullOrUndefined(o: Object | null, m: string = "null or undefined", t?: Exception): void { + this.assertCondition(!this.isNullOrUndefined(o), m, t); } - static assertCondition(cond: boolean, exMsg: string): void { - if (!cond) throw new InvalidStateException(exMsg); + static assertCondition(c: boolean, m: string = "invalid state", t?: Exception): void { + if (!c) throw new InvalidStateException(m, t); } - constructor(m: string) { - super(m); + constructor(m: string, t?: Exception) { + super(m, t); } } diff --git a/src/adap-b04/common/MethodFailedException.ts b/src/adap-b04/common/MethodFailedException.ts new file mode 100644 index 00000000..e365a5f0 --- /dev/null +++ b/src/adap-b04/common/MethodFailedException.ts @@ -0,0 +1,21 @@ +import { Exception } from "./Exception"; + +/** + * A MethodFailedException signals that the method failed to provide its service. + * In other words, a postcondition failed. + */ +export class MethodFailedException extends Exception { + + static assertIsNotNullOrUndefined(o: Object | null, m: string = "null or undefined", t?: Exception): void { + this.assertCondition(!this.isNullOrUndefined(o), m, t); + } + + static assertCondition(c: boolean, m: string = "method failed", t?: Exception): void { + if (!c) throw new MethodFailedException(m, t); + } + + constructor(m: string, t?: Exception) { + super(m, t); + } + +} diff --git a/src/adap-b04/common/MethodFailureException.ts b/src/adap-b04/common/MethodFailureException.ts deleted file mode 100644 index 275a5f56..00000000 --- a/src/adap-b04/common/MethodFailureException.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Exception } from "./Exception"; - -/** - * A MethodFailureException signals that the method failed to provide its service. - * In other words, a postcondition failed. - */ -export class MethodFailureException extends Exception { - - static assertIsNotNullOrUndefined(o: Object | null, exMsg: string = "null or undefined"): void { - this.assertCondition(!this.isNullOrUndefined(o), exMsg); - } - - static assertCondition(cond: boolean, exMsg: string): void { - if (!cond) throw new MethodFailureException(exMsg); - } - - constructor(m: string) { - super(m); - } - -} diff --git a/src/adap-b04/coordinates/AbstractCoordinate.ts b/src/adap-b04/coordinates/AbstractCoordinate.ts index d17c36ab..ecb4a56a 100644 --- a/src/adap-b04/coordinates/AbstractCoordinate.ts +++ b/src/adap-b04/coordinates/AbstractCoordinate.ts @@ -1,4 +1,7 @@ +import { ExceptionType } from "../common/AssertionDispatcher"; +import { AssertionDispatcher } from "../common/AssertionDispatcher"; import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { MethodFailedException } from "../common/MethodFailedException"; import { Coordinate } from "./Coordinate"; export abstract class AbstractCoordinate implements Coordinate { @@ -14,7 +17,7 @@ export abstract class AbstractCoordinate implements Coordinate { } public isEqual(other: Coordinate): boolean { - this.assertIsNotNullOrUndefined(other); + this.assertIsNotNullOrUndefinedAsPrecondition(other); return (this.doGetX() == other.getX()) && (this.doGetY() == other.getY()); } @@ -39,7 +42,7 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetX(): number; public setX(x: number): void { - this.assertIsNotNullOrUndefined(x); + this.assertIsNotNullOrUndefinedAsPrecondition(x); this.doSetX(x); } @@ -53,7 +56,7 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetY(): number; public setY(y: number): void { - this.assertIsNotNullOrUndefined(y); + this.assertIsNotNullOrUndefinedAsPrecondition(y); this.doSetY(y); } @@ -61,7 +64,7 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doSetY(y: number): void; public calcStraightLineDistance(other: Coordinate): number { - this.assertIsNotNullOrUndefined(other); + this.assertIsNotNullOrUndefinedAsPrecondition(other); let deltaX: number = Math.abs(other.getX() - this.doGetX()); let deltaY: number = Math.abs(other.getY() - this.doGetY()); @@ -75,7 +78,8 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetR(): number; public setR(r: number): void { - this.assertIsNotNullOrUndefined(r); + this.assertIsNotNullOrUndefinedAsPrecondition(r); + this.assertIsValidRAsPrecondition(r); this.doSetR(r); } @@ -88,17 +92,26 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetPhi(): number; + /** + * Example code to demonstrate design by contract + * @param phi Angle of vector + */ public setPhi(phi: number): void { - this.assertIsNotNullOrUndefined(phi); - this.assertIsValidPhi(phi); + this.assertIsNotNullOrUndefinedAsPrecondition(phi); + this.assertIsValidPhiAsPrecondition(phi); this.doSetPhi(phi); + + const newPhi: number = this.doGetPhi(); + this.assertIsValidPhiAsClassInvariant(newPhi); + + MethodFailedException.assertCondition(newPhi == phi); } protected abstract doSetPhi(phi: number): void; public calcGreatCircleDistance(other: Coordinate): number { - this.assertIsNotNullOrUndefined(other); + this.assertIsNotNullOrUndefinedAsPrecondition(other); let lowerR = Math.min(this.getR(), other.getR()); let deltaPhi = Math.abs(other.getPhi() - this.getPhi()); @@ -106,7 +119,7 @@ export abstract class AbstractCoordinate implements Coordinate { } public multiplyWith(other: Coordinate): void { - this.assertIsNotNullOrUndefined(other); + this.assertIsNotNullOrUndefinedAsPrecondition(other); let newR = this.getR() * other.getR(); let newPhi = this.getPhi() + other.getPhi(); @@ -114,19 +127,44 @@ export abstract class AbstractCoordinate implements Coordinate { this.setPhi(newPhi); } - protected assertIsNotNullOrUndefined(other: Object): void { + protected assertIsNotNullOrUndefinedAsPrecondition(other: Object): void { + this.assertIsNotNullOrUndefined(other, ExceptionType.PRECONDITION); + } + + protected assertIsNotNullOrUndefined(other: Object, et: ExceptionType): void { let condition: boolean = !IllegalArgumentException.isNullOrUndefined(other); - IllegalArgumentException.assertCondition(condition, "null or undefined argument"); + AssertionDispatcher.dispatch(et, condition, "null or undefined value"); + } + + protected assertIsValidRAsPrecondition(r: number): void { + this.assertIsValidR(r, ExceptionType.PRECONDITION); + } + + protected assertIsValidR(r: number, et: ExceptionType): void { + let condition: boolean = (r >= 0); + AssertionDispatcher.dispatch(et, condition, "invalid r value"); + } + + protected assertIsValidPhiAsPrecondition(phi: number): void { + this.assertIsValidPhi(phi, ExceptionType.PRECONDITION); } - protected assertIsValidPhi(phi: number): void { + protected assertIsValidPhiAsClassInvariant(phi: number): void { + this.assertIsValidPhi(phi, ExceptionType.CLASS_INVARIANT); + } + + protected assertIsValidPhi(phi: number, et: ExceptionType): void { let condition: boolean = (phi < 0) || (phi >= 2*Math.PI); - IllegalArgumentException.assertCondition(condition, "invalid phi value"); + AssertionDispatcher.dispatch(et, condition, "invalid phi value"); + } + + protected assertIsValidDelCharAsPrecondition(d: string): void { + this.assertIsValidDelChar(d, ExceptionType.PRECONDITION); } - protected assertIsValidDelChar(d: string) { + protected assertIsValidDelChar(d: string, et: ExceptionType): void { let condition: boolean = (d.length == 1); - IllegalArgumentException.assertCondition(condition, "invalid delimiter character"); + AssertionDispatcher.dispatch(et, condition, "invalid delimiter character"); } } \ No newline at end of file diff --git a/src/adap-b04/coordinates/Coordinate.ts b/src/adap-b04/coordinates/Coordinate.ts index 2879c1e1..c7eba49e 100644 --- a/src/adap-b04/coordinates/Coordinate.ts +++ b/src/adap-b04/coordinates/Coordinate.ts @@ -30,7 +30,7 @@ export interface Coordinate extends Cloneable, Equality { /** * Gets and sets r and phi in a two-dimensional polar coordinate system - * Expects that 0 <= phi < 2*Math.PI + * Expects that r >= 0 and 0 <= phi < 2*Math.PI */ getR(): number; setR(r: number): void; diff --git a/src/adap-b04/files/Directory.ts b/src/adap-b04/files/Directory.ts index b1179030..3ff937f7 100644 --- a/src/adap-b04/files/Directory.ts +++ b/src/adap-b04/files/Directory.ts @@ -2,7 +2,7 @@ import { Node } from "./Node"; export class Directory extends Node { - protected childNodes: Set = new Set(); + protected childNodes: Set = new Set(); constructor(bn: string, pn: Directory) { super(bn, pn); diff --git a/src/adap-b04/files/File.ts b/src/adap-b04/files/File.ts index 0e7aa84a..f59bdd51 100644 --- a/src/adap-b04/files/File.ts +++ b/src/adap-b04/files/File.ts @@ -1,5 +1,6 @@ import { Node } from "./Node"; import { Directory } from "./Directory"; +import { MethodFailedException } from "../common/MethodFailedException"; enum FileState { OPEN, @@ -19,6 +20,11 @@ export class File extends Node { // do something } + public read(noBytes: number): Int8Array { + // read something + return new Int8Array(); + } + public close(): void { // do something } diff --git a/src/adap-b04/files/Node.ts b/src/adap-b04/files/Node.ts index aa09c999..af26b451 100644 --- a/src/adap-b04/files/Node.ts +++ b/src/adap-b04/files/Node.ts @@ -8,12 +8,19 @@ export class Node { constructor(bn: string, pn: Directory) { this.doSetBaseName(bn); + this.parentNode = pn; // why oh why do I have to set this + this.initialize(pn); + } + + protected initialize(pn: Directory): void { this.parentNode = pn; + this.parentNode.add(this); } public move(to: Directory): void { this.parentNode.remove(this); to.add(this); + this.parentNode = to; } public getFullName(): Name { @@ -38,7 +45,7 @@ export class Node { this.baseName = bn; } - public getParentNode(): Node { + public getParentNode(): Directory { return this.parentNode; } diff --git a/src/adap-b04/files/RootNode.ts b/src/adap-b04/files/RootNode.ts index cff22b8d..996b1b69 100644 --- a/src/adap-b04/files/RootNode.ts +++ b/src/adap-b04/files/RootNode.ts @@ -12,6 +12,9 @@ export class RootNode extends Directory { constructor() { super("", new Object as Directory); + } + + protected initialize(pn: Directory): void { this.parentNode = this; } diff --git a/src/adap-b04/names/AbstractName.ts b/src/adap-b04/names/AbstractName.ts index b54de984..971f7127 100644 --- a/src/adap-b04/names/AbstractName.ts +++ b/src/adap-b04/names/AbstractName.ts @@ -6,39 +6,39 @@ export abstract class AbstractName implements Name { protected delimiter: string = DEFAULT_DELIMITER; constructor(delimiter: string = DEFAULT_DELIMITER) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public clone(): Name { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public toString(): string { - throw new Error("needs implementation"); + return this.asDataString(); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEqual(other: Name): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getHashCode(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } abstract getNoComponents(): number; @@ -51,7 +51,7 @@ export abstract class AbstractName implements Name { abstract remove(i: number): void; public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b04/names/StringArrayName.ts b/src/adap-b04/names/StringArrayName.ts index 3d27cf00..da67f37c 100644 --- a/src/adap-b04/names/StringArrayName.ts +++ b/src/adap-b04/names/StringArrayName.ts @@ -8,66 +8,62 @@ export class StringArrayName extends AbstractName { constructor(other: string[], delimiter?: string) { super(); - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public clone(): Name { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); - } - - public toString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEqual(other: Name): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getHashCode(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getNoComponents(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getComponent(i: number): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public setComponent(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public insert(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public append(c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public remove(i: number) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b04/names/StringName.ts b/src/adap-b04/names/StringName.ts index 5a8db508..8049e4af 100644 --- a/src/adap-b04/names/StringName.ts +++ b/src/adap-b04/names/StringName.ts @@ -9,67 +9,63 @@ export class StringName extends AbstractName { constructor(other: string, delimiter?: string) { super(); - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public clone(): Name { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asString(delimiter: string = this.delimiter): string { - throw new Error("needs implementation"); - } - - public toString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public asDataString(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEqual(other: Name): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getHashCode(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public isEmpty(): boolean { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getDelimiterCharacter(): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getNoComponents(): number { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public getComponent(i: number): string { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public setComponent(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public insert(i: number, c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public append(c: string) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public remove(i: number) { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } public concat(other: Name): void { - throw new Error("needs implementation"); + throw new Error("needs implementation or deletion"); } } \ No newline at end of file diff --git a/src/adap-b05/common/AssertionDispatcher.ts b/src/adap-b05/common/AssertionDispatcher.ts new file mode 100644 index 00000000..bfe4fcda --- /dev/null +++ b/src/adap-b05/common/AssertionDispatcher.ts @@ -0,0 +1,32 @@ +import { IllegalArgumentException } from "./IllegalArgumentException"; +import { InvalidStateException } from "./InvalidStateException"; +import { MethodFailedException } from "./MethodFailedException"; + +export enum ExceptionType { + PRECONDITION = 0, + CLASS_INVARIANT = 1, + POSTCONDITION = 2 +}; + +export class AssertionDispatcher { + + protected static DISPATCHER = new AssertionDispatcher(); + + public static dispatch(key: ExceptionType, condition: boolean, message?: string) { + this.DISPATCHER.doDispatch(key, condition, message); + } + + protected methods: { [key in ExceptionType]: (c: boolean, m?: string) => void }; + + constructor() { + this.methods = { + [ExceptionType.PRECONDITION]: IllegalArgumentException.assertCondition, + [ExceptionType.CLASS_INVARIANT]: InvalidStateException.assertCondition, + [ExceptionType.POSTCONDITION]: MethodFailedException.assertCondition + } + } + + protected doDispatch(key: ExceptionType, condition: boolean, message?: string) { + this.methods[key](condition, message); + } +} \ No newline at end of file diff --git a/src/adap-b05/common/Cloneable.ts b/src/adap-b05/common/Cloneable.ts index c34600e1..768202f3 100644 --- a/src/adap-b05/common/Cloneable.ts +++ b/src/adap-b05/common/Cloneable.ts @@ -3,6 +3,6 @@ export interface Cloneable { /** * Returns shallow copy (clone) of this object */ - clone(): Object; // @todo Use object - + clone(): Object; // @todo Clarify use of Objects vs object + } \ No newline at end of file diff --git a/src/adap-b05/common/Equality.ts b/src/adap-b05/common/Equality.ts index 5428e269..87e986bd 100644 --- a/src/adap-b05/common/Equality.ts +++ b/src/adap-b05/common/Equality.ts @@ -4,10 +4,11 @@ export interface Equality { * Returns true if other object is of equal value to this one * @param other Object to compare with */ - isEqual(other: Object): boolean; // @todo How to ensure this works with vitest and Object.is? + isEqual(other: Object): boolean; /** * Returns hashcode for this object, respecting equality contract */ getHashCode(): number; + } diff --git a/src/adap-b05/common/Exception.ts b/src/adap-b05/common/Exception.ts index 93cf6d7c..3e2cd15d 100644 --- a/src/adap-b05/common/Exception.ts +++ b/src/adap-b05/common/Exception.ts @@ -1,14 +1,41 @@ +import { InvalidStateException } from "./InvalidStateException"; + /** * Root class for exceptions in ADAP examples */ export abstract class Exception extends Error { + protected trigger: Exception | null = null; + static isNullOrUndefined(o: Object | null) { return (o == undefined) || (o == null); } - constructor(m: string) { + constructor(m: string, t?: Exception) { super(m); + + if (t != undefined) { + this.trigger = t; + } + } + + public hasTrigger(): boolean { + return this.trigger != null; + } + + public getTrigger(): Exception { + this.assertHasTrigger(); // should be InvalidStateException + return this.trigger as Exception; + } + + protected assertHasTrigger(): void { + if (!this.hasTrigger()) { + throw new (class extends Exception { + constructor(t: Exception) { + super("exception had no trigger", t); + } + })(this); + } } } \ No newline at end of file diff --git a/src/adap-b05/common/IllegalArgumentException.ts b/src/adap-b05/common/IllegalArgumentException.ts index a347b864..bf7b2990 100644 --- a/src/adap-b05/common/IllegalArgumentException.ts +++ b/src/adap-b05/common/IllegalArgumentException.ts @@ -1,4 +1,5 @@ import { Exception } from "./Exception"; +import { InvalidStateException } from "./InvalidStateException"; /** * An IllegalArgumentException signals an invalid argument. @@ -6,16 +7,21 @@ import { Exception } from "./Exception"; */ export class IllegalArgumentException extends Exception { - static assertIsNotNullOrUndefined(o: Object | null, exMsg: string = "null or undefined"): void { - this.assertCondition(this.isNullOrUndefined(o), exMsg); + static assertIsNotNullOrUndefined(o: Object | null, m: string = "null or undefined", t?: Exception): void { + this.assertCondition(!this.isNullOrUndefined(o), m); } - static assertCondition(cond: boolean, exMsg: string): void { - if (!cond) throw new IllegalArgumentException(exMsg); + static assertCondition(c: boolean, m: string = "illegal argument", t?: Exception): void { + if (!c) throw new IllegalArgumentException(m, t); } - constructor(m: string) { - super(m); + constructor(m: string, t?: Exception) { + super(m, t); } - + + public getTrigger(): Exception { + InvalidStateException.assertCondition(this.hasTrigger()); + return super.getTrigger(); + } + } diff --git a/src/adap-b05/common/InvalidStateException.ts b/src/adap-b05/common/InvalidStateException.ts index e4139ce7..58dae00d 100644 --- a/src/adap-b05/common/InvalidStateException.ts +++ b/src/adap-b05/common/InvalidStateException.ts @@ -6,16 +6,16 @@ import { Exception } from "./Exception"; */ export class InvalidStateException extends Exception { - static assertNotNullOrUndefined(o: Object | null, exMsg: string = "null or undefined"): void { - this.assertCondition(this.isNullOrUndefined(o), exMsg); + static assertIsNotNullOrUndefined(o: Object | null, m: string = "null or undefined", t?: Exception): void { + this.assertCondition(!this.isNullOrUndefined(o), m, t); } - static assertCondition(cond: boolean, exMsg: string): void { - if (!cond) throw new InvalidStateException(exMsg); + static assertCondition(c: boolean, m: string = "invalid state", t?: Exception): void { + if (!c) throw new InvalidStateException(m, t); } - constructor(m: string) { - super(m); + constructor(m: string, t?: Exception) { + super(m, t); } } diff --git a/src/adap-b05/common/MethodFailedException.ts b/src/adap-b05/common/MethodFailedException.ts index 2e4228e4..e365a5f0 100644 --- a/src/adap-b05/common/MethodFailedException.ts +++ b/src/adap-b05/common/MethodFailedException.ts @@ -6,16 +6,16 @@ import { Exception } from "./Exception"; */ export class MethodFailedException extends Exception { - static assertNotNullOrUndefined(o: Object | null, exMsg: string = "null or undefined"): void { - this.assertCondition(this.isNullOrUndefined(o), exMsg); + static assertIsNotNullOrUndefined(o: Object | null, m: string = "null or undefined", t?: Exception): void { + this.assertCondition(!this.isNullOrUndefined(o), m, t); } - static assertCondition(cond: boolean, exMsg: string): void { - if (!cond) throw new MethodFailedException(exMsg); + static assertCondition(c: boolean, m: string = "method failed", t?: Exception): void { + if (!c) throw new MethodFailedException(m, t); } - constructor(m: string) { - super(m); + constructor(m: string, t?: Exception) { + super(m, t); } } diff --git a/src/adap-b05/common/Printable.ts b/src/adap-b05/common/Printable.ts index 4dd378c8..604c07b0 100644 --- a/src/adap-b05/common/Printable.ts +++ b/src/adap-b05/common/Printable.ts @@ -1,14 +1,27 @@ +export const DEFAULT_DELIMITER: string = '.'; +export const ESCAPE_CHARACTER = '\\'; + export interface Printable { - /** - * Returns human-readable representation of this object - * Expects that delimiter is a single character + /** + * Returns a human-readable representation of the Name instance using user-set control characters + * Control characters are not escaped (creating a human-readable string) + * Users can vary the delimiter character to be used + * The delimiter character must be a single character */ - asString(delChar?: string): string; + asString(delimiter?: string): string; - /** - * Returns machine-readable representation of this object - */ - asDataString(): string; + /** + * Returns a machine-readable representation of Name instance using default control characters + * Machine-readable means that from a data string, a Name can be parsed back in + * The control characters in the data string are the default characters + * Different fields of the object are separated by the delimiter character + */ + asDataString(): string; + + /** + * Returns delimiter char; must be a single character + */ + getDelimiterCharacter(): string; } \ No newline at end of file diff --git a/src/adap-b05/common/ServiceFailureException.ts b/src/adap-b05/common/ServiceFailureException.ts new file mode 100644 index 00000000..cdda39dc --- /dev/null +++ b/src/adap-b05/common/ServiceFailureException.ts @@ -0,0 +1,17 @@ +import { Exception } from "./Exception"; + +/** + * A ServiceFailureException signals that a service failed to provide its service. + * ServiceFailureExceptions must be checked for by the client after the service call. + */ +export class ServiceFailureException extends Exception { + + static assertCondition(c: boolean, m: string = "service failed", t?: Exception): void { + if (!c) throw new ServiceFailureException(m, t); + } + + constructor(m: string, t?: Exception) { + super(m, t); + } + +} diff --git a/src/adap-b05/errors1/Errors.ts b/src/adap-b05/errors1/Errors.ts new file mode 100644 index 00000000..afc590cb --- /dev/null +++ b/src/adap-b05/errors1/Errors.ts @@ -0,0 +1,33 @@ +/** + * Unclear purpose of file, keeping it for now (not used) + */ + +enum FS_ERROR_TYPES { + FILE_NOT_FOUND, + READ_ERROR, + WRITE_ERROR, + CORRUPT_DATA +} + +export type CorruptDataError = { + id: number; + type: FS_ERROR_TYPES; + source: Object; + message: string; +} + +export class FileNotFoundException extends Error { + protected id: number; + protected source: Object | null; + + constructor(id: number, source?: Object, msg?: string) { + super(msg); + this.id = id; + if(source != undefined) { + this.source = source; + } else { + this.source = null; + } + } + +} \ No newline at end of file diff --git a/src/adap-b05/files/BuggyFile.ts b/src/adap-b05/files/BuggyFile.ts new file mode 100644 index 00000000..d6648f69 --- /dev/null +++ b/src/adap-b05/files/BuggyFile.ts @@ -0,0 +1,19 @@ +import { File } from "./File"; +import { Directory } from "./Directory"; + +export class BuggyFile extends File { + + constructor(baseName: string, parent: Directory) { + super(baseName, parent); + } + + /** + * Fault injection for homework + * @returns base name, here always "" + */ + protected doGetBaseName(): string { + this.baseName = ""; + return super.doGetBaseName(); + } + +} diff --git a/src/adap-b05/files/Directory.ts b/src/adap-b05/files/Directory.ts new file mode 100644 index 00000000..3ff937f7 --- /dev/null +++ b/src/adap-b05/files/Directory.ts @@ -0,0 +1,19 @@ +import { Node } from "./Node"; + +export class Directory extends Node { + + protected childNodes: Set = new Set(); + + constructor(bn: string, pn: Directory) { + super(bn, pn); + } + + public add(cn: Node): void { + this.childNodes.add(cn); + } + + public remove(cn: Node): void { + this.childNodes.delete(cn); // Yikes! Should have been called remove + } + +} \ No newline at end of file diff --git a/src/adap-b05/files/File.ts b/src/adap-b05/files/File.ts new file mode 100644 index 00000000..03da75e0 --- /dev/null +++ b/src/adap-b05/files/File.ts @@ -0,0 +1,54 @@ +import { Node } from "./Node"; +import { Directory } from "./Directory"; +import { MethodFailedException } from "../common/MethodFailedException"; + +enum FileState { + OPEN, + CLOSED, + DELETED +}; + +export class File extends Node { + + protected state: FileState = FileState.CLOSED; + + constructor(baseName: string, parent: Directory) { + super(baseName, parent); + } + + public open(): void { + // do something + } + + public read(noBytes: number): Int8Array { + let result: Int8Array = new Int8Array(noBytes); + // do something + + let tries: number = 0; + for (let i: number = 0; i < noBytes; i++) { + try { + result[i] = this.readNextByte(); + } catch(ex) { + tries++; + if (ex instanceof MethodFailedException) { + // Oh no! What @todo?! + } + } + } + + return result; + } + + protected readNextByte(): number { + return 0; // @todo + } + + public close(): void { + // do something + } + + protected doGetFileState(): FileState { + return this.state; + } + +} \ No newline at end of file diff --git a/src/adap-b05/files/Link.ts b/src/adap-b05/files/Link.ts new file mode 100644 index 00000000..e240325f --- /dev/null +++ b/src/adap-b05/files/Link.ts @@ -0,0 +1,38 @@ +import { Node } from "./Node"; +import { Directory } from "./Directory"; + +export class Link extends Node { + + protected targetNode: Node | null = null; + + constructor(bn: string, pn: Directory, tn?: Node) { + super(bn, pn); + + if (tn != undefined) { + this.targetNode = tn; + } + } + + public getTargetNode(): Node | null { + return this.targetNode; + } + + public setTargetNode(target: Node): void { + this.targetNode = target; + } + + public getBaseName(): string { + const target = this.ensureTargetNode(this.targetNode); + return target.getBaseName(); + } + + public rename(bn: string): void { + const target = this.ensureTargetNode(this.targetNode); + target.rename(bn); + } + + protected ensureTargetNode(target: Node | null): Node { + const result: Node = this.targetNode as Node; + return result; + } +} \ No newline at end of file diff --git a/src/adap-b05/files/Node.ts b/src/adap-b05/files/Node.ts new file mode 100644 index 00000000..02d32229 --- /dev/null +++ b/src/adap-b05/files/Node.ts @@ -0,0 +1,74 @@ +import { ExceptionType, AssertionDispatcher } from "../common/AssertionDispatcher"; +import { IllegalArgumentException } from "../common/IllegalArgumentException"; +import { InvalidStateException } from "../common/InvalidStateException"; + +import { Name } from "../names/Name"; +import { Directory } from "./Directory"; + +export class Node { + + protected baseName: string = ""; + protected parentNode: Directory; + + constructor(bn: string, pn: Directory) { + this.doSetBaseName(bn); + this.parentNode = pn; // why oh why do I have to set this + this.initialize(pn); + } + + protected initialize(pn: Directory): void { + this.parentNode = pn; + this.parentNode.add(this); + } + + public move(to: Directory): void { + this.parentNode.remove(this); + to.add(this); + this.parentNode = to; + } + + public getFullName(): Name { + const result: Name = this.parentNode.getFullName(); + result.append(this.getBaseName()); + return result; + } + + public getBaseName(): string { + return this.doGetBaseName(); + } + + protected doGetBaseName(): string { + return this.baseName; + } + + public rename(bn: string): void { + this.doSetBaseName(bn); + } + + protected doSetBaseName(bn: string): void { + this.baseName = bn; + } + + public getParentNode(): Directory { + return this.parentNode; + } + + /** + * Returns all nodes in the tree that match bn + * @param bn basename of node being searched for + */ + public findNodes(bn: string): Set { + throw new Error("needs implementation or deletion"); + } + + protected assertClassInvariants(): void { + const bn: string = this.doGetBaseName(); + this.assertIsValidBaseName(bn, ExceptionType.CLASS_INVARIANT); + } + + protected assertIsValidBaseName(bn: string, et: ExceptionType): void { + const condition: boolean = (bn != ""); + AssertionDispatcher.dispatch(et, condition, "invalid base name"); + } + +} diff --git a/src/adap-b05/files/RootNode.ts b/src/adap-b05/files/RootNode.ts new file mode 100644 index 00000000..f671dbc3 --- /dev/null +++ b/src/adap-b05/files/RootNode.ts @@ -0,0 +1,40 @@ +import { ExceptionType, AssertionDispatcher } from "../common/AssertionDispatcher"; + +import { Name } from "../names/Name"; +import { StringName } from "../names/StringName"; +import { Directory } from "./Directory"; + +export class RootNode extends Directory { + + protected static ROOT_NODE: RootNode = new RootNode(); + + public static getRootNode() { + return this.ROOT_NODE; + } + + constructor() { + super("", new Object as Directory); + } + + protected initialize(pn: Directory): void { + this.parentNode = this; + } + + public getFullName(): Name { + return new StringName("", '/'); + } + + public move(to: Directory): void { + // null operation + } + + protected doSetBaseName(bn: string): void { + // null operation + } + + protected assertIsValidBaseName(bn: string, et: ExceptionType): void { + const condition: boolean = (bn == ""); // Root must have "" as base name + AssertionDispatcher.dispatch(et, condition, "invalid base name"); + } + +} \ No newline at end of file diff --git a/src/adap-b05/names/AbstractName.ts b/src/adap-b05/names/AbstractName.ts new file mode 100644 index 00000000..971f7127 --- /dev/null +++ b/src/adap-b05/names/AbstractName.ts @@ -0,0 +1,57 @@ +import { DEFAULT_DELIMITER, ESCAPE_CHARACTER } from "../common/Printable"; +import { Name } from "./Name"; + +export abstract class AbstractName implements Name { + + protected delimiter: string = DEFAULT_DELIMITER; + + constructor(delimiter: string = DEFAULT_DELIMITER) { + throw new Error("needs implementation or deletion"); + } + + public clone(): Name { + throw new Error("needs implementation or deletion"); + } + + public asString(delimiter: string = this.delimiter): string { + throw new Error("needs implementation or deletion"); + } + + public toString(): string { + return this.asDataString(); + } + + public asDataString(): string { + throw new Error("needs implementation or deletion"); + } + + public isEqual(other: Name): boolean { + throw new Error("needs implementation or deletion"); + } + + public getHashCode(): number { + throw new Error("needs implementation or deletion"); + } + + public isEmpty(): boolean { + throw new Error("needs implementation or deletion"); + } + + public getDelimiterCharacter(): string { + throw new Error("needs implementation or deletion"); + } + + abstract getNoComponents(): number; + + abstract getComponent(i: number): string; + abstract setComponent(i: number, c: string): void; + + abstract insert(i: number, c: string): void; + abstract append(c: string): void; + abstract remove(i: number): void; + + public concat(other: Name): void { + throw new Error("needs implementation or deletion"); + } + +} \ No newline at end of file diff --git a/src/adap-b05/names/Name.ts b/src/adap-b05/names/Name.ts new file mode 100644 index 00000000..e5767a29 --- /dev/null +++ b/src/adap-b05/names/Name.ts @@ -0,0 +1,44 @@ +import { Equality } from "../common/Equality"; +import { Cloneable } from "../common/Cloneable"; +import { Printable } from "../common/Printable"; + +/** + * A name is a sequence of string components separated by a delimiter character. + * Special characters within the string may need masking, if they are to appear verbatim. + * There are only two special characters, the delimiter character and the escape character. + * The escape character can't be set, the delimiter character can. + * + * Homogenous name examples + * + * "oss.cs.fau.de" is a name with four name components and the delimiter character '.'. + * "///" is a name with four empty components and the delimiter character '/'. + * "Oh\.\.\." is a name with one component, if the delimiter character is '.'. + */ +export interface Name extends Cloneable, Printable, Equality { + + /** + * Returns true, if number of components == 0; else false + */ + isEmpty(): boolean; + + /** + * Returns number of components in Name instance + */ + getNoComponents(): number; + + getComponent(i: number): string; + + /** Expects that new Name component c is properly masked */ + setComponent(i: number, c: string): void; + + /** Expects that new Name component c is properly masked */ + insert(i: number, c: string): void; + + /** Expects that new Name component c is properly masked */ + append(c: string): void; + + remove(i: number): void; + + concat(other: Name): void; + +} \ No newline at end of file diff --git a/src/adap-b05/names/StringArrayName.ts b/src/adap-b05/names/StringArrayName.ts new file mode 100644 index 00000000..da67f37c --- /dev/null +++ b/src/adap-b05/names/StringArrayName.ts @@ -0,0 +1,69 @@ +import { DEFAULT_DELIMITER, ESCAPE_CHARACTER } from "../common/Printable"; +import { Name } from "./Name"; +import { AbstractName } from "./AbstractName"; + +export class StringArrayName extends AbstractName { + + protected components: string[] = []; + + constructor(other: string[], delimiter?: string) { + super(); + throw new Error("needs implementation or deletion"); + } + + public clone(): Name { + throw new Error("needs implementation or deletion"); + } + + public asString(delimiter: string = this.delimiter): string { + throw new Error("needs implementation or deletion"); + } + + public asDataString(): string { + throw new Error("needs implementation or deletion"); + } + + public isEqual(other: Name): boolean { + throw new Error("needs implementation or deletion"); + } + + public getHashCode(): number { + throw new Error("needs implementation or deletion"); + } + + public isEmpty(): boolean { + throw new Error("needs implementation or deletion"); + } + + public getDelimiterCharacter(): string { + throw new Error("needs implementation or deletion"); + } + + public getNoComponents(): number { + throw new Error("needs implementation or deletion"); + } + + public getComponent(i: number): string { + throw new Error("needs implementation or deletion"); + } + + public setComponent(i: number, c: string) { + throw new Error("needs implementation or deletion"); + } + + public insert(i: number, c: string) { + throw new Error("needs implementation or deletion"); + } + + public append(c: string) { + throw new Error("needs implementation or deletion"); + } + + public remove(i: number) { + throw new Error("needs implementation or deletion"); + } + + public concat(other: Name): void { + throw new Error("needs implementation or deletion"); + } +} \ No newline at end of file diff --git a/src/adap-b05/names/StringName.ts b/src/adap-b05/names/StringName.ts new file mode 100644 index 00000000..8049e4af --- /dev/null +++ b/src/adap-b05/names/StringName.ts @@ -0,0 +1,71 @@ +import { DEFAULT_DELIMITER, ESCAPE_CHARACTER } from "../common/Printable"; +import { Name } from "./Name"; +import { AbstractName } from "./AbstractName"; + +export class StringName extends AbstractName { + + protected name: string = ""; + protected noComponents: number = 0; + + constructor(other: string, delimiter?: string) { + super(); + throw new Error("needs implementation or deletion"); + } + + public clone(): Name { + throw new Error("needs implementation or deletion"); + } + + public asString(delimiter: string = this.delimiter): string { + throw new Error("needs implementation or deletion"); + } + + public asDataString(): string { + throw new Error("needs implementation or deletion"); + } + + public isEqual(other: Name): boolean { + throw new Error("needs implementation or deletion"); + } + + public getHashCode(): number { + throw new Error("needs implementation or deletion"); + } + + public isEmpty(): boolean { + throw new Error("needs implementation or deletion"); + } + + public getDelimiterCharacter(): string { + throw new Error("needs implementation or deletion"); + } + + public getNoComponents(): number { + throw new Error("needs implementation or deletion"); + } + + public getComponent(i: number): string { + throw new Error("needs implementation or deletion"); + } + + public setComponent(i: number, c: string) { + throw new Error("needs implementation or deletion"); + } + + public insert(i: number, c: string) { + throw new Error("needs implementation or deletion"); + } + + public append(c: string) { + throw new Error("needs implementation or deletion"); + } + + public remove(i: number) { + throw new Error("needs implementation or deletion"); + } + + public concat(other: Name): void { + throw new Error("needs implementation or deletion"); + } + +} \ No newline at end of file diff --git a/test/adap-b04/common/Exceptions.test.ts b/test/adap-b04/common/Exceptions.test.ts index b49aaab3..b7d3a26b 100644 --- a/test/adap-b04/common/Exceptions.test.ts +++ b/test/adap-b04/common/Exceptions.test.ts @@ -1,20 +1,20 @@ import { describe, it, expect } from "vitest"; import { IllegalArgumentException } from "../../../src/adap-b04/common/IllegalArgumentException"; -import { MethodFailureException } from "../../../src/adap-b04/common/MethodFailureException"; +import { MethodFailedException } from "../../../src/adap-b04/common/MethodFailedException"; import { InvalidStateException } from "../../../src/adap-b04/common/InvalidStateException"; describe("Asserting not null or undefined", () => { it("test asserIsNotNullOrUndefined", async () => { - const exMsg: string = "null or undefined"; + const m: string = "null or undefined"; IllegalArgumentException.assertIsNotNullOrUndefined("hurray!"); - expect(() => IllegalArgumentException.assertIsNotNullOrUndefined(null)).toThrow(new IllegalArgumentException(exMsg)); + expect(() => IllegalArgumentException.assertIsNotNullOrUndefined(null)).toThrow(new IllegalArgumentException(m)); - MethodFailureException.assertIsNotNullOrUndefined("hurray!"); - expect(() => MethodFailureException.assertIsNotNullOrUndefined(null)).toThrow(new MethodFailureException(exMsg)); + MethodFailedException.assertIsNotNullOrUndefined("hurray!"); + expect(() => MethodFailedException.assertIsNotNullOrUndefined(null)).toThrow(new MethodFailedException(m)); InvalidStateException.assertIsNotNullOrUndefined("hurray!"); - expect(() => InvalidStateException.assertIsNotNullOrUndefined(null)).toThrow(new InvalidStateException(exMsg)); + expect(() => InvalidStateException.assertIsNotNullOrUndefined(null)).toThrow(new InvalidStateException(m)); }); }); diff --git a/test/adap-b04/files-private/Files.private.test.ts b/test/adap-b04/files-private/Files.private.test.ts new file mode 100644 index 00000000..7200d202 --- /dev/null +++ b/test/adap-b04/files-private/Files.private.test.ts @@ -0,0 +1,19 @@ +import { describe, it, expect } from "vitest"; + +import { StringName } from "../../../src/adap-b04/names-solution/StringName"; +import { StringArrayName } from "../../../src/adap-b04/names-solution/StringArrayName"; + +import { File } from "../../../src/adap-b04/files-solution/File"; +import { Directory } from "../../../src/adap-b04/files-solution/Directory"; +import { RootNode } from "../../../src/adap-b04/files-solution/RootNode"; + +describe("Basic structure test", () => { + it("test construction", () => { + let rn: RootNode = RootNode.getRootNode(); + let dir1: Directory = new Directory("usr", rn); + let dir2: Directory = new Directory("bin", dir1); + let ed: File = new File("vi", dir2); + expect(ed.getFullName() == new StringArrayName(["usr", "bin", "vi"], '/')); + expect(ed.getFullName().isEqual(new StringName("/usr/bin/vi", '/'))); + }); +});