import { ValidatorFn, AbstractControl } from '@angular/forms'; import { logger } from "../util/logger"; export class VersionNumber { private _version = []; public get version(): string { return this.toString() } public set version(v: string) { this._version = v.split("."); } /** * Construct a new version number object * @param version version number as string, e.g "6.0.1". Must be a set of numbers separated by .s */ constructor(version: string) { // TODO check string against regex this.version = version; } /** * Get the version number as a string */ public toString(): string { return this._version.join("."); } /** * Return a new VersionNumber which has the next major version * @returns {VersionNumber} the next major version */ public nextMajorVersion(): VersionNumber { let clone = JSON.parse(JSON.stringify(this._version)); clone[0] = parseInt(clone[0]) + 1; clone[0] = clone[0].toString(); for (let i = 1; i < clone.length; i++) clone[i] = 0; //zero out subsequent indexes return new VersionNumber(clone.join(".")) } /** * Return a new VersionNumber which has the next major version * @returns {VersionNumber} the next major version */ public nextMinorVersion(): VersionNumber { if (this.granularity < 2) { logger.error(`cannot increment minor version of ${this.toString()}`) return this; //cannot increment } let clone = JSON.parse(JSON.stringify(this._version)); clone[1] = parseInt(clone[1]) + 1; clone[1] = clone[1].toString(); for (let i = 2; i < clone.length; i++) clone[i] = 0; //zero out subsequent indexes return new VersionNumber(clone.join(".")) } /** * Get the level of granularity of the version. * E.g 10.12.4 would have a granularity of 3, * and 6.2 would have a granularity of 2 * @returns {number} the level of granularity */ public get granularity(): number { return this._version.length; } /** * Get the sub-version of the version number * @param index the index to get. E.g index of 2 with version 1.2.3.4 would return "3" */ public getSubVersion(index: number) { if (index > this.granularity || index < 0) throw "cannot fetch sub-version, index out of bounds"; return this._version[index]; } /** * Compare this version number to another version number. * Note: higher granularity versions are considered by this implementation to be greater versions, * so 1.0.0.0 is greater than 1.0. * @param {VersionNumber} that the version to compare to * @returns {number} 0 if the same, + if this is greater, - if that is greater */ public compareTo(that: VersionNumber): number { //TODO test this for (let i = 0; i < Math.max(this.granularity, that.granularity); i++) { if (this.granularity == that.granularity && this.granularity < i) return 0; //same version if (this.granularity < i) return -1; if (that.granularity < i) return 1; if (this.getSubVersion(i) == that.getSubVersion(i)) continue; return this.getSubVersion(i) - that.getSubVersion(i); } return 0; //same version } /** * Check if this version number is more than one increment above the other version number * e.g v1.0 -> v3.0 is true, v1.1 -> v1.6 is true, v1.0 -> v1.2 is true, v1.0 -> 2.1 is true, v1.0 -> v2.0 is false * @param {VersionNumber} that version number to check against * @returns {boolean} true if the version number has incremented at least twice */ public isDoubleIncrement(that: VersionNumber): boolean { // sum up increments of each index let timesIncremented = 0; //track the number of times it has been incremented for (let i = 0; i < Math.max(this.granularity, that.granularity); i++) { let thisVersionIndex = this.granularity >= i? this.getSubVersion(i) : 0; let thatVersionIndex = timesIncremented > 0? 0 : that.granularity >= i? that.getSubVersion(i) : 0; //if it has been incremented at a higher index, expect later indexes to be 0 timesIncremented += Math.max(thisVersionIndex - thatVersionIndex, 0); if (timesIncremented > 1) return true; } return timesIncremented > 1; } /** * Is this version number formatted correctly? * @returns {boolean} true if valid */ public valid(): boolean { return /^(\d+\.)*\d+$/.test(this.toString()); } } /** * form field validator checking if the version number is formatted correctly */ export function versionNumberFormatValidator(): ValidatorFn { const pattern = /^(\d+\.)*\d+$/; return (control: AbstractControl): {[key:string]: any} | null => { const valid = pattern.test(control.value); return !valid ? { "invalidVersionFormat": { value: control.value }} : null; } } /** * form field validator checking if the version has been incremented * @param previousVersion the version the object was previously, for comparison */ export function versionNumberIncrementValidator(previousVersion: VersionNumber): ValidatorFn { return (control: AbstractControl): {[key:string]: any} | null => { const valid = new VersionNumber(control.value).compareTo(previousVersion) > 0; return !valid ? { "versionNotIncremented": { value: control.value }} : null; } }