UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

194 lines • 9.32 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Metadata */ import { XmlSerializationUtils } from "../Deserialization/XmlSerializationUtils"; import { SchemaItemType } from "../ECObjects"; import { formatStringRgx } from "@itwin/core-quantity"; import { ECSchemaError, ECSchemaStatus } from "../Exception"; /** * Overrides of a Format, from a Schema, and is SchemaItem that is used specifically on KindOfQuantity. * @public @preview */ export class OverrideFormat { _precision; _units; /** The Format that this OverrideFormat is extending */ parent; /** The name of this OverrideFormat. * * This should be set to the [FormatString]($docs/bis/ec/kindofquantity/#format-string) which represents the format override. */ name; /** @internal */ constructor(parent, precision, unitAndLabels) { this.parent = parent; this.name = OverrideFormat.createOverrideFormatFullName(parent, precision, unitAndLabels); this._precision = precision; this._units = unitAndLabels; } // Properties that can be overriden get precision() { return (undefined === this._precision) ? this.parent.precision : this._precision; } get units() { return (undefined === this._units) ? this.parent.units : this._units; } // Properties that cannot be overriden get fullName() { return this.name; } get roundFactor() { return this.parent.roundFactor; } get type() { return this.parent.type; } get minWidth() { return this.parent.minWidth; } get scientificType() { return this.parent.scientificType; } get showSignOption() { return this.parent.showSignOption; } get decimalSeparator() { return this.parent.decimalSeparator; } get thousandSeparator() { return this.parent.thousandSeparator; } get uomSeparator() { return this.parent.uomSeparator; } get stationSeparator() { return this.parent.stationSeparator; } get stationOffsetSize() { return this.parent.stationOffsetSize; } get formatTraits() { return this.parent.formatTraits; } get spacer() { return this.parent.spacer; } get includeZero() { return this.parent.includeZero; } hasFormatTrait(formatTrait) { return (this.parent.formatTraits & formatTrait) === formatTrait; } /** Returns the format string of this override in the Xml full name format. * @internal */ fullNameXml(koqSchema) { let fullName = XmlSerializationUtils.createXmlTypedName(koqSchema, this.parent.schema, this.parent.name); if (undefined !== this.precision) fullName += `(${this.precision.toString()})`; if (undefined === this._units) return fullName; for (const [unit, unitLabel] of this._units) { const unitSchema = koqSchema.context.getSchemaSync(unit.schemaKey); if (unitSchema === undefined) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The unit schema ${unit.schemaKey} is not found in the context.`); fullName += "["; fullName += XmlSerializationUtils.createXmlTypedName(koqSchema, unitSchema, unit.name); if (unitLabel !== undefined) fullName += `|${unitLabel}`; fullName += `]`; } return fullName; } /** * Creates a valid OverrideFormat fullName from the parent Format and overridden units. * @param parent The parent Format. * @param unitAndLabels The overridden unit and labels collection. */ static createOverrideFormatFullName(parent, precision, unitAndLabels) { let fullName = parent.fullName; if (precision) fullName += `(${precision.toString()})`; if (undefined === unitAndLabels) return fullName; for (const [unit, unitLabel] of unitAndLabels) if (undefined === unitLabel) fullName += `[${unit.fullName}]`; else fullName += `[${unit.fullName}|${unitLabel}]`; return fullName; } /** Parses the format string into the parts that make up an Override Format * @param formatString */ static parseFormatString(formatString) { const match = formatString.split(formatStringRgx); // split string based on regex groups if (undefined === match[1]) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The format string, ${formatString}, on KindOfQuantity is missing a format.`); let precision; if (undefined !== match[2] && undefined !== match[3]) { const overrideString = match[2]; const tokens = []; let prevPos = 1; // Initial position is the character directly after the opening '(' in the override string. let currPos; // TODO need to include `,` as a valid search argument. while (-1 !== (currPos = overrideString.indexOf(")", prevPos))) { tokens.push(overrideString.substring(prevPos, currPos)); prevPos = currPos + 1; } if (overrideString.length > 0 && undefined === tokens.find((token) => { return "" !== token; // there is at least one token that is not empty. })) { throw new ECSchemaError(ECSchemaStatus.InvalidECJson, ``); } // The first override parameter overrides the default precision of the format const precisionIndx = 0; if (tokens.length >= precisionIndx + 1) { if (tokens[precisionIndx].length > 0) { precision = Number.parseInt(tokens[precisionIndx], 10); if (Number.isNaN(precision)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The format string '${formatString}' on KindOfQuantity has a precision override '${tokens[precisionIndx]}' that is not number.`); } } } let i = 4; let unitAndLabels; while (i < match.length - 1) { // The regex match ends with an empty last value, which causes problems when exactly 4 unit overrides as specified, so ignore this last empty value if (undefined === match[i]) break; // Unit override required if (undefined === match[i + 1]) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, ``); if (undefined === unitAndLabels) unitAndLabels = []; if (undefined !== match[i + 2]) // matches '|' unitAndLabels.push([match[i + 1], match[i + 3] ?? ""]); // add unit name and label override (if '|' matches and next value is undefined, save it as an empty string) else unitAndLabels.push([match[i + 1], undefined]); // add unit name i += 4; } return { name: match[1], precision, unitAndLabels }; } /** * @internal */ static isOverrideFormat(format) { const overrideFormat = format; return overrideFormat !== undefined && overrideFormat.name !== undefined && overrideFormat.parent !== undefined && overrideFormat.parent.schemaItemType === SchemaItemType.Format; } /** * Returns a JSON object that contains the specification for the OverrideFormat where the precision and units properties have been overriden. * If the precision and/or units properties have been overriden, the returned object will contain a "name" and a "parent" property. * The "name" property identifies the OverrideFormat object itself and the "parent" property identifies the Format that has been overriden. * This method is not intended for complete serialization as it does not serialize any of the schema item properties. */ getFormatProps() { const formatJson = this.parent.toJSON(); if (this.parent.fullName !== this.fullName) { // Update name and parent properties to distinguish it from parent Format formatJson.name = this.fullName; formatJson.parent = this.parent.fullName; } // Update Precision overriden property formatJson.precision = this.precision; if (this.units !== undefined) { // Update Units overriden property const units = []; for (const unit of this.units) { units.push({ name: unit[0].fullName, label: unit[1], }); } formatJson.composite = { spacer: (this.spacer !== " ") ? this.spacer : undefined, includeZero: (this.includeZero === false) ? this.includeZero : undefined, units, }; } return formatJson; } } /** * @internal */ export function getFormatProps(format) { return OverrideFormat.isOverrideFormat(format) ? format.getFormatProps() : format.toJSON(); } //# sourceMappingURL=OverrideFormat.js.map