@itwin/presentation-common
Version:
Common pieces for iModel.js presentation packages
166 lines • 7.48 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Core
*/
import { Format, FormatterSpec, ParserSpec } from "@itwin/core-quantity";
import { getFormatProps, InvertedUnit, KindOfQuantity, SchemaKey, SchemaMatchType, SchemaUnitProvider, } from "@itwin/ecschema-metadata";
/**
* An utility for formatting property values based on `KindOfQuantity` and unit system.
* @public
*/
export class KoqPropertyValueFormatter {
_schemaContext;
_unitsProvider;
_defaultFormats;
constructor(_schemaContext, defaultFormats) {
this._schemaContext = _schemaContext;
this._unitsProvider = new SchemaUnitProvider(_schemaContext);
this._defaultFormats = defaultFormats
? Object.entries(defaultFormats).reduce((acc, [phenomenon, unitSystemFormats]) => ({ ...acc, [phenomenon.toUpperCase()]: unitSystemFormats }), {})
: /* c8 ignore next */ undefined;
}
async format(value, options) {
const formatterSpec = await this.getFormatterSpec(options);
if (!formatterSpec) {
return undefined;
}
return formatterSpec.applyFormatting(value);
}
async getFormatterSpec(options) {
const formattingProps = await getFormattingProps(this._schemaContext, this._defaultFormats, options);
if (!formattingProps) {
return undefined;
}
const { formatProps, persistenceUnitName } = formattingProps;
const persistenceUnit = await this._unitsProvider.findUnitByName(persistenceUnitName);
const format = await Format.createFromJSON("", this._unitsProvider, formatProps);
return FormatterSpec.create("", format, this._unitsProvider, persistenceUnit);
}
async getParserSpec(options) {
const formattingProps = await getFormattingProps(this._schemaContext, this._defaultFormats, options);
if (!formattingProps) {
return undefined;
}
const { formatProps, persistenceUnitName } = formattingProps;
const persistenceUnit = await this._unitsProvider.findUnitByName(persistenceUnitName);
const format = await Format.createFromJSON("", this._unitsProvider, formatProps);
return ParserSpec.create(format, this._unitsProvider, persistenceUnit);
}
}
async function getFormattingProps(schemaLocater, defaultFormats, options) {
const { koqName, unitSystem } = options;
const koq = await getKoq(schemaLocater, koqName);
if (!koq) {
return undefined;
}
const persistenceUnit = await koq.persistenceUnit;
/* c8 ignore next 3 */
if (!persistenceUnit) {
return undefined;
}
const formatProps = await getKoqFormatProps(koq, persistenceUnit, defaultFormats, unitSystem);
if (!formatProps) {
return undefined;
}
return { formatProps, persistenceUnitName: persistenceUnit.fullName };
}
async function getKoq(schemaLocater, fullName) {
const [schemaName, propKoqName] = fullName.split(":");
const schema = await schemaLocater.getSchema(new SchemaKey(schemaName), SchemaMatchType.Latest);
if (!schema) {
return undefined;
}
return schema.getItem(propKoqName, KindOfQuantity);
}
async function getKoqFormatProps(koq, persistenceUnit, defaultFormats, unitSystem) {
const unitSystemMatchers = getUnitSystemGroupMatchers(unitSystem);
// use one of KOQ presentation format that matches requested unit system
const presentationFormat = await getKoqPresentationFormat(koq, unitSystemMatchers);
if (presentationFormat) {
return getFormatProps(presentationFormat);
}
// use one of the formats in default formats map if there is one for matching phenomena and requested unit
// system combination
if (defaultFormats && unitSystem) {
const actualPersistenceUnit = persistenceUnit instanceof InvertedUnit ? /* c8 ignore next */ await persistenceUnit.invertsUnit : persistenceUnit;
const phenomenon = await actualPersistenceUnit?.phenomenon;
if (phenomenon && defaultFormats[phenomenon.name.toUpperCase()]) {
const defaultPhenomenonFormats = defaultFormats[phenomenon.name.toUpperCase()];
for (const defaultUnitSystemFormat of Array.isArray(defaultPhenomenonFormats)
? /* c8 ignore next */ defaultPhenomenonFormats
: [defaultPhenomenonFormats]) {
if (defaultUnitSystemFormat.unitSystems.includes(unitSystem)) {
return defaultUnitSystemFormat.format;
}
}
}
}
// use persistence unit format if it matches requested unit system and matching presentation format was not found
const persistenceUnitSystem = await persistenceUnit.unitSystem;
if (persistenceUnitSystem && unitSystemMatchers.some((matcher) => matcher(persistenceUnitSystem))) {
return getPersistenceUnitFormatProps(persistenceUnit);
}
// use default presentation format if persistence unit does not match requested unit system
if (koq.defaultPresentationFormat) {
return getFormatProps(await koq.defaultPresentationFormat);
}
return undefined;
}
async function getKoqPresentationFormat(koq, unitSystemMatchers) {
const presentationFormats = koq.presentationFormats;
for (const matcher of unitSystemMatchers) {
for (const lazyFormat of presentationFormats) {
const format = await lazyFormat;
const lazyUnit = format.units && format.units[0][0];
/* c8 ignore next 3 */
if (!lazyUnit) {
continue;
}
const unit = await lazyUnit;
const currentUnitSystem = await unit.unitSystem;
if (currentUnitSystem && matcher(currentUnitSystem)) {
return format;
}
}
}
return undefined;
}
function getPersistenceUnitFormatProps(persistenceUnit) {
// Same as Format "DefaultRealU" in Formats ecschema
return {
formatTraits: ["keepSingleZero", "keepDecimalPoint", "showUnitLabel"],
precision: 6,
type: "Decimal",
uomSeparator: " ",
decimalSeparator: ".",
composite: {
units: [
{
name: persistenceUnit.fullName,
label: persistenceUnit.label,
},
],
},
};
}
function getUnitSystemGroupMatchers(groupKey) {
function createMatcher(name) {
const names = Array.isArray(name) ? name : [name];
return (unitSystem) => names.some((n) => n === unitSystem.name.toUpperCase());
}
switch (groupKey) {
case "imperial":
return ["IMPERIAL", "USCUSTOM", "INTERNATIONAL", "FINANCE"].map(createMatcher);
case "metric":
return [["SI", "METRIC"], "INTERNATIONAL", "FINANCE"].map(createMatcher);
case "usCustomary":
return ["USCUSTOM", "INTERNATIONAL", "FINANCE"].map(createMatcher);
case "usSurvey":
return ["USSURVEY", "USCUSTOM", "INTERNATIONAL", "FINANCE"].map(createMatcher);
}
return [];
}
//# sourceMappingURL=KoqPropertyValueFormatter.js.map