diff --git a/src/adap-b06/common/Cloneable.ts b/src/adap-b06/common/Cloneable.ts index c34600e1..768202f3 100644 --- a/src/adap-b06/common/Cloneable.ts +++ b/src/adap-b06/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-b06/common/Equality.ts b/src/adap-b06/common/Equality.ts index 5428e269..87e986bd 100644 --- a/src/adap-b06/common/Equality.ts +++ b/src/adap-b06/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-b06/common/Exception.ts b/src/adap-b06/common/Exception.ts index 93cf6d7c..07f9bae6 100644 --- a/src/adap-b06/common/Exception.ts +++ b/src/adap-b06/common/Exception.ts @@ -3,12 +3,23 @@ */ export abstract class Exception extends Error { - static isNullOrUndefined(o: Object | null) { - return (o == undefined) || (o == null); - } + protected trigger: Exception | null = 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 { + // @todo check if trigger is null + return this.trigger as Exception; } } \ No newline at end of file diff --git a/src/adap-b06/common/IllegalArgumentException.ts b/src/adap-b06/common/IllegalArgumentException.ts index a347b864..e9a8881c 100644 --- a/src/adap-b06/common/IllegalArgumentException.ts +++ b/src/adap-b06/common/IllegalArgumentException.ts @@ -1,4 +1,5 @@ import { Exception } from "./Exception"; +import { InvalidStateException } from "./InvalidStateException"; /** * An IllegalArgumentException signals an invalid argument. @@ -6,16 +7,12 @@ 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); + public static assert(c: boolean, m: string = "illegal argument", t?: Exception): void { + if (!c) throw new IllegalArgumentException(m, t); } - static assertCondition(cond: boolean, exMsg: string): void { - if (!cond) throw new IllegalArgumentException(exMsg); + constructor(m: string, t?: Exception) { + super(m, t); } - - constructor(m: string) { - super(m); - } - + } diff --git a/src/adap-b06/common/InvalidStateException.ts b/src/adap-b06/common/InvalidStateException.ts index e4139ce7..bfad4338 100644 --- a/src/adap-b06/common/InvalidStateException.ts +++ b/src/adap-b06/common/InvalidStateException.ts @@ -5,17 +5,13 @@ import { Exception } from "./Exception"; * In other words, a class invariant failed. */ export class InvalidStateException extends Exception { - - static assertNotNullOrUndefined(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 InvalidStateException(exMsg); + + public static assert(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-b06/common/MethodFailedException.ts b/src/adap-b06/common/MethodFailedException.ts index 2e4228e4..365a693c 100644 --- a/src/adap-b06/common/MethodFailedException.ts +++ b/src/adap-b06/common/MethodFailedException.ts @@ -5,17 +5,13 @@ import { Exception } from "./Exception"; * In other words, a postcondition failed. */ export class MethodFailedException extends Exception { - - static assertNotNullOrUndefined(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 MethodFailedException(exMsg); + + public static assert(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-b06/common/Printable.ts b/src/adap-b06/common/Printable.ts index 4dd378c8..604c07b0 100644 --- a/src/adap-b06/common/Printable.ts +++ b/src/adap-b06/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-b06/common/ServiceFailureException.ts b/src/adap-b06/common/ServiceFailureException.ts new file mode 100644 index 00000000..1ce5fdd7 --- /dev/null +++ b/src/adap-b06/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 { + + public static assert(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-b06/coordinates/AbstractCoordinate.ts b/src/adap-b06/coordinates/AbstractCoordinate.ts index d8ac9845..3478d639 100644 --- a/src/adap-b06/coordinates/AbstractCoordinate.ts +++ b/src/adap-b06/coordinates/AbstractCoordinate.ts @@ -1,12 +1,17 @@ import { IllegalArgumentException } from "../common/IllegalArgumentException"; + import { Coordinate } from "./Coordinate"; -import { PolarCoordinate } from "./PolarCoordinate"; export abstract class AbstractCoordinate implements Coordinate { - public asString(delChar: string) { - this.assertIsValidDelChar(delChar); + public clone(): Coordinate { + return this.doCreate(this.doGetX(), this.doGetY()); + } + + protected abstract doCreate(x: number, y: number): Coordinate; + public asString(delChar: string) { + IllegalArgumentException.assert(this.isValidDelChar(delChar)); return this.doGetX() + delChar + this.doGetY(); } @@ -14,31 +19,23 @@ export abstract class AbstractCoordinate implements Coordinate { return this.asDataString(); } - public asDataString(): string { - return this.asString("#"); - } + public abstract asDataString(): string; public isEqual(other: Coordinate): boolean { - this.assertIsNotNullOrUndefined(other); - return (this.doGetX() == other.getX()) && (this.doGetY() == other.getY()); } public getHashCode(): number { let hashCode: number = 0; const s: string = this.asDataString(); - for (let i = 0; i < s.length; i++) { - let c = s.charCodeAt(i); + for (let i: number = 0; i < s.length; i++) { + let c: number = s.charCodeAt(i); hashCode = (hashCode << 5) - hashCode + c; hashCode |= 0; } return hashCode; } - public clone(): Coordinate { - return { ...this }; - } - abstract getOrigin(): Coordinate; public getX(): number { @@ -48,8 +45,6 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetX(): number; public setX(x: number): Coordinate { - this.assertIsNotNullOrUndefined(x); - return this.doSetX(x); } @@ -62,16 +57,12 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetY(): number; public setY(y: number): Coordinate { - this.assertIsNotNullOrUndefined(y); - return this.doSetY(y); } protected abstract doSetY(y: number): Coordinate; public calcStraightLineDistance(other: Coordinate): number { - this.assertIsNotNullOrUndefined(other); - let deltaX: number = Math.abs(other.getX() - this.doGetX()); let deltaY: number = Math.abs(other.getY() - this.doGetY()); return Math.hypot(deltaX, deltaY); @@ -84,8 +75,6 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetR(): number; public setR(r: number): Coordinate { - this.assertIsNotNullOrUndefined(r); - return this.doSetR(r); } @@ -98,43 +87,34 @@ export abstract class AbstractCoordinate implements Coordinate { protected abstract doGetPhi(): number; public setPhi(phi: number): Coordinate { - this.assertIsNotNullOrUndefined(phi); - this.assertIsValidPhi(phi); - + IllegalArgumentException.assert(this.isValidPhi(phi)); return this.doSetPhi(phi); } protected abstract doSetPhi(phi: number): Coordinate; public calcGreatCircleDistance(other: Coordinate): number { - this.assertIsNotNullOrUndefined(other); - let lowerR = Math.min(this.getR(), other.getR()); let deltaPhi = Math.abs(other.getPhi() - this.getPhi()); return lowerR * deltaPhi; } public multiplyWith(other: Coordinate): Coordinate { - this.assertIsNotNullOrUndefined(other); - let newR = this.getR() * other.getR(); let newPhi = this.getPhi() + other.getPhi(); - return new PolarCoordinate(newR, newPhi); + return this.doCreate(newR, newPhi); } - protected assertIsNotNullOrUndefined(other: Object): void { - let condition: boolean = !IllegalArgumentException.isNullOrUndefined(other); - IllegalArgumentException.assertCondition(condition, "null or undefined argument"); + protected isValidR(r: number): boolean { + return r >= 0; } - protected assertIsValidPhi(phi: number): void { - let condition: boolean = (phi < 0) || (phi >= 2*Math.PI); - IllegalArgumentException.assertCondition(condition, "invalid phi value"); + protected isValidPhi(phi: number): boolean { + return (phi >= 0) && (phi < 2*Math.PI); } - protected assertIsValidDelChar(d: string) { - let condition: boolean = (d.length == 1); - IllegalArgumentException.assertCondition(condition, "invalid delimiter character"); + protected isValidDelChar(d: string): boolean { + return d.length == 1; } } \ No newline at end of file diff --git a/src/adap-b06/coordinates/CartesianCoordinate.ts b/src/adap-b06/coordinates/CartesianCoordinate.ts index fc961ebd..6acd2c54 100644 --- a/src/adap-b06/coordinates/CartesianCoordinate.ts +++ b/src/adap-b06/coordinates/CartesianCoordinate.ts @@ -14,14 +14,22 @@ export class CartesianCoordinate extends AbstractCoordinate { protected initialize(x?: number, y?: number): void { if (x != undefined) { - this.doSetX(x); + this.x = x; } if (y != undefined) { - this.doSetY(y); + this.y = y; } } + protected doCreate(x: number, y: number): Coordinate { + return new CartesianCoordinate(x, y); + } + + public asDataString(): string { + return this.doGetX() + '#' + this.doGetY(); + } + public getOrigin(): Coordinate { return new CartesianCoordinate(0, 0); } diff --git a/src/adap-b06/coordinates/Coordinate.ts b/src/adap-b06/coordinates/Coordinate.ts index df980094..86ed5bce 100644 --- a/src/adap-b06/coordinates/Coordinate.ts +++ b/src/adap-b06/coordinates/Coordinate.ts @@ -1,12 +1,11 @@ import { Equality } from "../common/Equality"; import { Cloneable } from "../common/Cloneable"; -import { Printable } from "../common/Printable"; /** * A coordinate (here) is a point in a two-dimensional coordinate system. * The coordinate system may be cartesian or polar; coordinates should be interchangeable. */ -export interface Coordinate extends Equality, Cloneable, Printable { +export interface Coordinate extends Cloneable, Equality { /** * Returns the origin of the coordinate system diff --git a/src/adap-b06/coordinates/PolarCoordinate.ts b/src/adap-b06/coordinates/PolarCoordinate.ts index abddbb1c..ad5f5add 100644 --- a/src/adap-b06/coordinates/PolarCoordinate.ts +++ b/src/adap-b06/coordinates/PolarCoordinate.ts @@ -14,14 +14,22 @@ export class PolarCoordinate extends AbstractCoordinate { protected initialize(r?: number, phi?: number): void { if (r != undefined) { - this.setR(r); + this.r = r; } if (phi != undefined) { - this.setPhi(phi); + this.phi = phi; } } + protected doCreate(r: number, phi: number): Coordinate { + return new PolarCoordinate(r, phi); + } + + public asDataString(): string { + return this.doGetR() + '#' + this.doGetPhi(); + } + public getOrigin(): Coordinate { return new PolarCoordinate(0, 0); } diff --git a/src/adap-b06/examples/Functions.ts b/src/adap-b06/examples/Functions.ts new file mode 100644 index 00000000..7be8c65e --- /dev/null +++ b/src/adap-b06/examples/Functions.ts @@ -0,0 +1,3 @@ +export function calculateDuration1(distance: number, speed: number): number { + return distance / speed; +} \ No newline at end of file diff --git a/src/adap-b06/examples/PostalAddress.ts b/src/adap-b06/examples/PostalAddress.ts new file mode 100644 index 00000000..0a4200a4 --- /dev/null +++ b/src/adap-b06/examples/PostalAddress.ts @@ -0,0 +1,17 @@ +export class PostalAddress { + + protected street: string; + protected city: string; + protected state: string; + protected postalCode: string; + protected country: string; + + constructor(st: string, city: string, state: string, pc: string, country: string) { + this.street = st; + this.city = city; + this.state = state; + this.postalCode = pc; + this.country = country; + } + +} \ No newline at end of file diff --git a/src/adap-b06/examples/QuantityUnit.ts b/src/adap-b06/examples/QuantityUnit.ts new file mode 100644 index 00000000..27c02279 --- /dev/null +++ b/src/adap-b06/examples/QuantityUnit.ts @@ -0,0 +1,55 @@ +import { IllegalArgumentException } from "../common/IllegalArgumentException"; + +export class QuantityUnit { + + protected value: number; + protected unit: SIUnit; + + constructor(value: number, unit: SIUnit) { + this.value = value; + this.unit = unit; + } + +} + +export enum BaseUnit { + m = 0, + kg = 1, + s = 2, + A = 3, + K = 4, + cd = 5, + mol = 6 +} + +export class SIUnit { + + protected exponents: number[] = [0, 0, 0, 0, 0, 0, 0]; + + constructor(exponents: number[]) { + this.exponents = [...exponents]; + } + + public isEqual(other: SIUnit): boolean { + return this.exponents.every((v, i) => v === other.exponents[i]); + } + + public add(other: SIUnit): SIUnit { + IllegalArgumentException.assert(!other.isEqual(this)); + return new SIUnit(this.exponents); + } + + public multiply(other: SIUnit): SIUnit { + let result: number[] = [0, 0, 0, 0, 0, 0, 0]; + for (let i = 0; i < this.exponents.length; i++) { + result[1] = this.exponents[i] + other.exponents[i]; + }; + return new SIUnit(result); + } + +} + +export function calculateDuration2(distance: number, speed: number): QuantityUnit { + IllegalArgumentException.assert(speed != 0); + return new QuantityUnit(distance / speed, new SIUnit([0, 0, 1, 0, 0, 0, 0])); +} diff --git a/src/adap-b06/examples/Ranges.ts b/src/adap-b06/examples/Ranges.ts new file mode 100644 index 00000000..7ef259fa --- /dev/null +++ b/src/adap-b06/examples/Ranges.ts @@ -0,0 +1,46 @@ +export class RangeBound { + + protected value: T; + protected inclusive: boolean; + + constructor(value: T, inclusive: boolean) { + this.value = value; + this.inclusive = inclusive; + } + + public getValue(): T { + return this.value; + } + + public isInclusive(): boolean { + return this.inclusive; + } + +} + +export class Range { + + protected lowerBound: RangeBound; + protected upperBound: RangeBound; + + constructor(lowerBound: RangeBound, upperBound: RangeBound) { + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + public includes(value: T): boolean { + let lowerValue = this.lowerBound.getValue(); + let upperValue = this.upperBound.getValue(); + return true; // @todo + } + + + public getLowerBound(): RangeBound { + return this.lowerBound; + } + + public getUpperBound(): RangeBound { + return this.upperBound; + } + +} diff --git a/src/adap-b06/names/README.txt b/src/adap-b06/names/README.txt new file mode 100644 index 00000000..316d0d62 --- /dev/null +++ b/src/adap-b06/names/README.txt @@ -0,0 +1 @@ +Your homework goes into this folder \ No newline at end of file diff --git a/test/adap-b06/coordinates/Coordinates.test.ts b/test/adap-b06/coordinates/Coordinates.test.ts new file mode 100644 index 00000000..c57a1b96 --- /dev/null +++ b/test/adap-b06/coordinates/Coordinates.test.ts @@ -0,0 +1,26 @@ +import { describe, it, expect } from "vitest"; + +import { Coordinate } from "../../../src/adap-b06/coordinates/Coordinate"; +import { CartesianCoordinate } from "../../../src/adap-b06/coordinates/CartesianCoordinate"; +import { PolarCoordinate } from "../../../src/adap-b06/coordinates/PolarCoordinate"; + +describe("Equality test", () => { + it("test isEqual", () => { + let c1: Coordinate = new CartesianCoordinate(0, 0); + let p1: Coordinate = new PolarCoordinate(0, 0); + expect(c1.isEqual(p1)).toBe(true); + expect(c1.getHashCode() == p1.getHashCode()).toBe(true); + + c1 = c1.setX(2); + p1 = p1.setR(2); + expect(c1.isEqual(p1)).toBe(true); + expect(c1.getHashCode() == p1.getHashCode()).toBe(true); + + c1 = c1.setY(1); + p1 = p1.setPhi(Math.PI / 2); + expect(c1.isEqual(p1)).toBe(false); + expect(c1.getHashCode() == p1.getHashCode()).toBe(false); + }); +}); + +