UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

709 lines • 51.2 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { ECSchemaError, ECSchemaStatus } from "../Exception"; import { ECName } from "../ECName"; import { AbstractParser } from "./AbstractParser"; import { SchemaReadHelper } from "./Helper"; function isObject(x) { return typeof (x) === "object"; } const SCHEMAURL_JSON = "https://dev\\.bentley\\.com/json_schemas/ec"; /** @internal */ export class JsonParser extends AbstractParser { _rawSchema; _schemaName; _currentItemFullName; constructor(rawSchema) { super(); if (!isObject(rawSchema)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `Invalid JSON object.`); this._rawSchema = rawSchema; this._schemaName = rawSchema.name; this._ecSpecVersion = JsonParser.parseJSUri(rawSchema.$schema); } get getECSpecVersion() { return this._ecSpecVersion; } static parseJSUri(uri) { if (uri === undefined) return undefined; const match = uri.match(`^${SCHEMAURL_JSON}/([0-9]+)/ecschema$`); if (!match) return; const readVersion = parseInt(match[1][0], 10); const writeVersion = parseInt(match[1][1], 10); return { readVersion, writeVersion }; } /** * Type checks Schema and returns SchemaProps interface * @param this._rawSchema * @returns SchemaProps */ parseSchema() { if (undefined === this._rawSchema.name) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `An ECSchema is missing the required 'name' attribute.`); if (typeof (this._rawSchema.name) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `An ECSchema has an invalid 'name' attribute. It should be of type 'string'.`); if (undefined === this._rawSchema.$schema) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECSchema ${this._schemaName} is missing the required '$schema' attribute.`); if (typeof (this._rawSchema.$schema) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECSchema ${this._schemaName} has an invalid '$schema' attribute. It should be of type 'string'.`); if (undefined === this._rawSchema.version) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECSchema ${this._schemaName} is missing the required 'version' attribute.`); if (typeof (this._rawSchema.version) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECSchema ${this._schemaName} has an invalid 'version' attribute. It should be of type 'string'.`); if (undefined !== this._rawSchema.alias) { if (typeof (this._rawSchema.alias) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECSchema ${this._schemaName} has an invalid 'alias' attribute. It should be of type 'string'.`); } if (undefined !== this._rawSchema.label) { if (typeof (this._rawSchema.label) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECSchema ${this._schemaName} has an invalid 'label' attribute. It should be of type 'string'.`); } if (undefined !== this._rawSchema.description) { if (typeof (this._rawSchema.description) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECSchema ${this._schemaName} has an invalid 'description' attribute. It should be of type 'string'.`); } const ecVersions = JsonParser.parseJSUri(this._rawSchema.$schema); return { ...this._rawSchema, ecSpecMajorVersion: ecVersions?.readVersion, ecSpecMinorVersion: ecVersions?.writeVersion, }; } *getReferences() { if (undefined !== this._rawSchema.references) { if (!Array.isArray(this._rawSchema.references)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._rawSchema.name} has an invalid 'references' attribute. It should be of type 'object[]'.`); for (const ref of this._rawSchema.references) { yield this.checkSchemaReference(ref); } } } checkSchemaReference(jsonObj) { if (!isObject(jsonObj)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._schemaName} has an invalid 'references' attribute. It should be of type 'object[]'.`); if (undefined === jsonObj.name) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._schemaName} has an invalid 'references' attribute. One of the references is missing the required 'name' attribute.`); if (typeof (jsonObj.name) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._schemaName} has an invalid 'references' attribute. One of the references has an invalid 'name' attribute. It should be of type 'string'.`); if (undefined === jsonObj.version) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._schemaName} has an invalid 'references' attribute. One of the references is missing the required 'version' attribute.`); if (typeof (jsonObj.version) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._schemaName} has an invalid 'references' attribute. One of the references has an invalid 'version' attribute. It should be of type 'string'.`); return jsonObj; } *getItems() { const items = this._rawSchema.items; if (undefined !== items) { if (!isObject(items) || Array.isArray(items)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._schemaName} has an invalid 'items' attribute. It should be of type 'object'.`); // eslint-disable-next-line guard-for-in for (const itemName in items) { const item = items[itemName]; if (!isObject(item)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `A SchemaItem in ${this._schemaName} is an invalid JSON object.`); if (!ECName.validate(itemName)) throw new ECSchemaError(ECSchemaStatus.InvalidECName, `A SchemaItem in ${this._schemaName} has an invalid 'name' attribute. '${itemName}' is not a valid ECName.`); if (undefined === item.schemaItemType) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The SchemaItem ${this._schemaName}.${itemName} is missing the required 'schemaItemType' attribute.`); if (typeof (item.schemaItemType) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The SchemaItem ${this._schemaName}.${itemName} has an invalid 'schemaItemType' attribute. It should be of type 'string'.`); this._currentItemFullName = `${this._schemaName}.${itemName}`; yield [itemName, item.schemaItemType, item]; } } } findItem(itemName) { const items = this._rawSchema.items; if (undefined !== items) { if (!isObject(items) || Array.isArray(items)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The schema ${this._schemaName} has an invalid 'items' attribute. It should be of type 'object'.`); const item = items[itemName]; if (undefined !== item) { if (!isObject(item)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `A SchemaItem in ${this._schemaName} is an invalid JSON object.`); if (!ECName.validate(itemName)) throw new ECSchemaError(ECSchemaStatus.InvalidECName, `A SchemaItem in ${this._schemaName} has an invalid 'name' attribute. '${itemName}' is not a valid ECName.`); if (undefined === item.schemaItemType) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The SchemaItem ${this._schemaName}.${itemName} is missing the required 'schemaItemType' attribute.`); if (typeof (item.schemaItemType) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The SchemaItem ${this._schemaName}.${itemName} has an invalid 'schemaItemType' attribute. It should be of type 'string'.`); this._currentItemFullName = `${this._schemaName}.${itemName}`; return [itemName, item.schemaItemType, item]; } } return undefined; } /** * Type checks all Schema Item attributes. * @param jsonObj The JSON object to check if it represents a Schema Item. */ checkSchemaItemProps(jsonObj) { if (undefined !== jsonObj.description) { if (typeof (jsonObj.description) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The SchemaItem ${this._currentItemFullName} has an invalid 'description' attribute. It should be of type 'string'.`); } if (undefined !== jsonObj.label) { if (typeof (jsonObj.label) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The SchemaItem ${this._currentItemFullName} has an invalid 'label' attribute. It should be of type 'string'.`); } } *getProperties(jsonObj, itemName) { const properties = jsonObj.properties; if (undefined !== properties) { if (!Array.isArray(properties)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECClass ${itemName} has an invalid 'properties' attribute. It should be of type 'object[]'.`); for (const property of properties) { if (!isObject(property)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `An ECProperty in ${itemName} is an invalid JSON object.`); if (undefined === property.name) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `An ECProperty in ${itemName} is missing the required 'name' attribute.`); if (typeof (property.name) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `An ECProperty in ${itemName} has an invalid 'name' attribute. It should be of type 'string'.`); if (undefined === property.type) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${itemName}.${property.name} does not have the required 'type' attribute.`); if (typeof (property.type) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${itemName}.${property.name} has an invalid 'type' attribute. It should be of type 'string'.`); if (!this.isValidPropertyType(property.type)) { if (SchemaReadHelper.isECSpecVersionNewer(this._ecSpecVersion)) return; throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${itemName}.${property.name} has an invalid 'type' attribute. '${property.type}' is not a valid type.`); } yield [property.name, property.type, property]; } } } /** * Type checks Class and returns ClassProps interface * @param jsonObj The JSON object to check if it represents a Class. */ checkClassProps(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined !== jsonObj.modifier) { if (typeof (jsonObj.modifier) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECClass ${this._currentItemFullName} has an invalid 'modifier' attribute. It should be of type 'string'.`); } if (undefined !== jsonObj.baseClass) { if (typeof (jsonObj.baseClass) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECClass ${this._currentItemFullName} has an invalid 'baseClass' attribute. It should be of type 'string'.`); } if (undefined !== jsonObj.customAttributes) { if (!Array.isArray(jsonObj.customAttributes)) { throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECClass ${this._currentItemFullName} has an invalid 'customAttributes' attribute. It should be of type 'array'.`); } } } /** * Type checks entity class and returns EntityClassProps interface * @param jsonObj * @returns EntityClassProps */ parseEntityClass(jsonObj) { this.checkClassProps(jsonObj); if (undefined !== jsonObj.mixins) { if (!Array.isArray(jsonObj.mixins)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECEntityClass ${this._currentItemFullName} has an invalid 'mixins' attribute. It should be of type 'string[]'.`); for (const mixinName of jsonObj.mixins) { if (typeof (mixinName) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECEntityClass ${this._currentItemFullName} has an invalid 'mixins' attribute. It should be of type 'string[]'.`); } } return { ...jsonObj, originalECSpecMajorVersion: this._ecSpecVersion?.readVersion, originalECSpecMinorVersion: this._ecSpecVersion?.writeVersion, }; } /** * Type checks mixin and returns MixinProps interface * @param jsonObj * @returns MixinProps */ parseMixin(jsonObj) { this.checkClassProps(jsonObj); if (undefined === jsonObj.appliesTo) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Mixin ${this._currentItemFullName} is missing the required 'appliesTo' attribute.`); if (typeof (jsonObj.appliesTo) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Mixin ${this._currentItemFullName} has an invalid 'appliesTo' attribute. It should be of type 'string'.`); return { ...jsonObj, originalECSpecMajorVersion: this._ecSpecVersion?.readVersion, originalECSpecMinorVersion: this._ecSpecVersion?.writeVersion, }; } /** * Type checks custom attribute class and returns CustomAttributeClassProps interface * @param jsonObj * @returns CustomAttributeClassProps */ parseCustomAttributeClass(jsonObj) { this.checkClassProps(jsonObj); if (undefined === jsonObj.appliesTo) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The CustomAttributeClass ${this._currentItemFullName} is missing the required 'appliesTo' attribute.`); if (typeof (jsonObj.appliesTo) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The CustomAttributeClass ${this._currentItemFullName} has an invalid 'appliesTo' attribute. It should be of type 'string'.`); return { ...jsonObj, originalECSpecMajorVersion: this._ecSpecVersion?.readVersion, originalECSpecMinorVersion: this._ecSpecVersion?.writeVersion, }; } parseStructClass(jsonObj) { this.checkClassProps(jsonObj); return { ...jsonObj, originalECSpecMajorVersion: this._ecSpecVersion?.readVersion, originalECSpecMinorVersion: this._ecSpecVersion?.writeVersion, }; } parseUnitSystem(jsonObj) { this.checkSchemaItemProps(jsonObj); return jsonObj; } /** * Type checks Relationship Class and returns RelationshipClassProps interface * @param jsonObj * @returns RelationshipClassProps */ parseRelationshipClass(jsonObj) { this.checkClassProps(jsonObj); if (undefined === jsonObj.strength) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} is missing the required 'strength' attribute.`); if (typeof (jsonObj.strength) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} has an invalid 'strength' attribute. It should be of type 'string'.`); if (undefined === jsonObj.strengthDirection) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} is missing the required 'strengthDirection' attribute.`); if (typeof (jsonObj.strengthDirection) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} has an invalid 'strengthDirection' attribute. It should be of type 'string'.`); if (undefined === jsonObj.source) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} is missing the required source constraint.`); if (!isObject(jsonObj.source)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} has an invalid source constraint. It should be of type 'object'.`); this.checkRelationshipConstraintProps(jsonObj.source, true); if (undefined === jsonObj.target) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} is missing the required target constraint.`); if (!isObject(jsonObj.target)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The RelationshipClass ${this._currentItemFullName} has an invalid target constraint. It should be of type 'object'.`); this.checkRelationshipConstraintProps(jsonObj.target, false); return { ...jsonObj, originalECSpecMajorVersion: this._ecSpecVersion?.readVersion, originalECSpecMinorVersion: this._ecSpecVersion?.writeVersion, }; } /** * Type checks Relationship Constraint and returns RelationshipConstraintProps interface. * @param jsonObj * @param isSource For sake of error message, is this relationship constraint a source or target * @returns RelationshipConstraintProps */ checkRelationshipConstraintProps(jsonObj, isSource) { const constraintName = `${(isSource) ? "Source" : "Target"} Constraint of ${this._currentItemFullName}`; // most specific name to call RelationshipConstraint if (undefined === jsonObj.multiplicity) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} is missing the required 'multiplicity' attribute.`); if (typeof (jsonObj.multiplicity) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} has an invalid 'multiplicity' attribute. It should be of type 'string'.`); if (undefined === jsonObj.roleLabel) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} is missing the required 'roleLabel' attribute.`); if (typeof (jsonObj.roleLabel) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} has an invalid 'roleLabel' attribute. It should be of type 'string'.`); if (undefined === jsonObj.polymorphic) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} is missing the required 'polymorphic' attribute.`); if (typeof (jsonObj.polymorphic) !== "boolean") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} has an invalid 'polymorphic' attribute. It should be of type 'boolean'.`); if (undefined !== jsonObj.abstractConstraint && typeof (jsonObj.abstractConstraint) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} has an invalid 'abstractConstraint' attribute. It should be of type 'string'.`); if (undefined === jsonObj.constraintClasses) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} is missing the required 'constraintClasses' attribute.`); if (!Array.isArray(jsonObj.constraintClasses)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} has an invalid 'constraintClasses' attribute. It should be of type 'string[]'.`); for (const constraintClassName of jsonObj.constraintClasses) { if (typeof (constraintClassName) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} has an invalid 'constraintClasses' attribute. It should be of type 'string[]'.`); } if (undefined !== jsonObj.customAttributes && !Array.isArray(jsonObj.customAttributes)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ${constraintName} has an invalid 'customAttributes' attribute. It should be of type 'array'.`); } /** * Type checks Enumeration and returns EnumerationProps interface * @param jsonObj * @returns EnumerationProps */ parseEnumeration(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined === jsonObj.type) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} is missing the required 'type' attribute.`); if (typeof (jsonObj.type) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an invalid 'type' attribute. It should be of type 'string'.`); const isValidEnumerationType = (type) => { type = type.toLowerCase(); return (type === "int") || (type === "integer") || (type === "string"); }; if (!isValidEnumerationType(jsonObj.type)) { if (SchemaReadHelper.isECSpecVersionNewer(this._ecSpecVersion)) jsonObj.type = "string"; else throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an invalid 'type' attribute. It should be either "int" or "string".`); } if (undefined !== jsonObj.isStrict) { // TODO: make required if (typeof (jsonObj.isStrict) !== "boolean") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an invalid 'isStrict' attribute. It should be of type 'boolean'.`); } if (undefined === jsonObj.enumerators) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} is missing the required 'enumerators' attribute.`); if (!Array.isArray(jsonObj.enumerators)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an invalid 'enumerators' attribute. It should be of type 'object[]'.`); for (const enumerator of jsonObj.enumerators) { if (!isObject(enumerator)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an invalid 'enumerators' attribute. It should be of type 'object[]'.`); if (undefined === enumerator.value) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an enumerator that is missing the required attribute 'value'.`); // TODO: Should this really be handled here? const expectedType = jsonObj.type; const receivedType = (typeof (enumerator.value) === "number") ? "int" : typeof (enumerator.value); if (!expectedType.includes(receivedType)) // is receivedType a substring of expectedType? - easiest way to check "int" === "int" | "integer" && "string" === "string" throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an incompatible type. It must be "${expectedType}", not "${receivedType}".`); if (undefined === enumerator.name) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an enumerator that is missing the required attribute 'name'.`); if (typeof (enumerator.name) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an enumerator with an invalid 'name' attribute. It should be of type 'string'.`); if (undefined !== enumerator.label) { if (typeof (enumerator.label) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an enumerator with an invalid 'label' attribute. It should be of type 'string'.`); } if (undefined !== enumerator.description) { if (typeof (enumerator.description) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Enumeration ${this._currentItemFullName} has an enumerator with an invalid 'description' attribute. It should be of type 'string'.`); } } return jsonObj; } /** * Type checks KindOfQuantity and returns KindOfQuantityProps interface * @param jsonObj * @returns KindOfQuantityProps */ parseKindOfQuantity(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined === jsonObj.relativeError) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The KindOfQuantity ${this._currentItemFullName} is missing the required 'relativeError' attribute.`); if (typeof (jsonObj.relativeError) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The KindOfQuantity ${this._currentItemFullName} has an invalid 'relativeError' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.presentationUnits) { if (!Array.isArray(jsonObj.presentationUnits) && typeof (jsonObj.presentationUnits) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The KindOfQuantity ${this._currentItemFullName} has an invalid 'presentationUnits' attribute. It should be of type 'string' or 'string[]'.`); } if (undefined === jsonObj.persistenceUnit) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The KindOfQuantity ${this._currentItemFullName} is missing the required 'persistenceUnit' attribute.`); if (typeof (jsonObj.persistenceUnit) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The KindOfQuantity ${this._currentItemFullName} has an invalid 'persistenceUnit' attribute. It should be of type 'string'.`); return jsonObj; } /** * Type checks Property Category and returns PropertyCategoryProps interface * @param jsonObj * @returns PropertyCategoryProps */ parsePropertyCategory(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined !== jsonObj.priority) { // TODO: make required if (typeof (jsonObj.priority) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The PropertyCategory ${this._currentItemFullName} has an invalid 'priority' attribute. It should be of type 'number'.`); } return jsonObj; } /** * Type checks unit and returns UnitProps interface * @param jsonObj * @returns UnitProps */ parseUnit(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined === jsonObj.phenomenon) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} does not have the required 'phenomenon' attribute.`); if (typeof (jsonObj.phenomenon) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} has an invalid 'phenomenon' attribute. It should be of type 'string'.`); if (undefined === jsonObj.unitSystem) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} does not have the required 'unitSystem' attribute.`); if (typeof (jsonObj.unitSystem) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} has an invalid 'unitSystem' attribute. It should be of type 'string'.`); if (undefined === jsonObj.definition) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} does not have the required 'definition' attribute.`); if (typeof (jsonObj.definition) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} has an invalid 'definition' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.numerator) { if (typeof (jsonObj.numerator) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} has an invalid 'numerator' attribute. It should be of type 'number'.`); } if (undefined !== jsonObj.denominator) { if (typeof (jsonObj.denominator) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} has an invalid 'denominator' attribute. It should be of type 'number'.`); } if (undefined !== jsonObj.offset) { if (typeof (jsonObj.offset) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Unit ${this._currentItemFullName} has an invalid 'offset' attribute. It should be of type 'number'.`); } return jsonObj; } /** * Type checks inverted unit and returns InvertedUnitProps interface * @param jsonObj * @returns InvertedUnitProps */ parseInvertedUnit(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined === jsonObj.invertsUnit) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The InvertedUnit ${this._currentItemFullName} does not have the required 'invertsUnit' attribute.`); if (typeof (jsonObj.invertsUnit) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The InvertedUnit ${this._currentItemFullName} has an invalid 'invertsUnit' attribute. It should be of type 'string'.`); if (undefined === jsonObj.unitSystem) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The InvertedUnit ${this._currentItemFullName} does not have the required 'unitSystem' attribute.`); if (typeof (jsonObj.unitSystem) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The InvertedUnit ${this._currentItemFullName} has an invalid 'unitSystem' attribute. It should be of type 'string'.`); return jsonObj; } /** * Type checks constant and returns ConstantProps interface * @param jsonObj * @returns ConstantProps */ parseConstant(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined === jsonObj.phenomenon) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Constant ${this._currentItemFullName} does not have the required 'phenomenon' attribute.`); if (typeof (jsonObj.phenomenon) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Constant ${this._currentItemFullName} has an invalid 'phenomenon' attribute. It should be of type 'string'.`); if (undefined === jsonObj.definition) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Constant ${this._currentItemFullName} does not have the required 'definition' attribute.`); if (typeof (jsonObj.definition) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Constant ${this._currentItemFullName} has an invalid 'definition' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.numerator) { if (typeof (jsonObj.numerator) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Constant ${this._currentItemFullName} has an invalid 'numerator' attribute. It should be of type 'number'.`); } if (undefined !== jsonObj.denominator) { if (typeof (jsonObj.denominator) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Constant ${this._currentItemFullName} has an invalid 'denominator' attribute. It should be of type 'number'.`); } return jsonObj; } /** * Type checks phenomenon and returns PhenomenonProps interface * @param jsonObj * @returns PhenomenonProps */ parsePhenomenon(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined === jsonObj.definition) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Phenomenon ${this._currentItemFullName} does not have the required 'definition' attribute.`); if (typeof (jsonObj.definition) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Phenomenon ${this._currentItemFullName} has an invalid 'definition' attribute. It should be of type 'string'.`); return jsonObj; } /** * Type checks format and returns SchemaItemFormatProps interface * @param jsonObj * @returns SchemaItemFormatProps */ parseFormat(jsonObj) { this.checkSchemaItemProps(jsonObj); if (undefined === jsonObj.type) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} does not have the required 'type' attribute.`); if (typeof (jsonObj.type) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'type' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.precision && typeof (jsonObj.precision) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'precision' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.roundFactor && typeof (jsonObj.roundFactor) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'roundFactor' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.minWidth && typeof (jsonObj.minWidth) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'minWidth' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.showSignOption && typeof (jsonObj.showSignOption) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'showSignOption' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.formatTraits) { if (!Array.isArray(jsonObj.formatTraits) && typeof (jsonObj.formatTraits) !== "string") // must be either an array of strings or a string throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'formatTraits' attribute. It should be of type 'string' or 'string[]'.`); } if (undefined !== jsonObj.decimalSeparator && typeof (jsonObj.decimalSeparator) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'decimalSeparator' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.thousandSeparator && typeof (jsonObj.thousandSeparator) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'thousandSeparator' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.uomSeparator && typeof (jsonObj.uomSeparator) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'uomSeparator' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.scientificType && typeof (jsonObj.scientificType) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'scientificType' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.stationOffsetSize && typeof (jsonObj.stationOffsetSize) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'stationOffsetSize' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.stationSeparator && typeof (jsonObj.stationSeparator) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'stationSeparator' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.composite) { // optional if (!isObject(jsonObj.composite)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'composite' object.`); if (undefined !== jsonObj.composite.includeZero && typeof (jsonObj.composite.includeZero) !== "boolean") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has a Composite with an invalid 'includeZero' attribute. It should be of type 'boolean'.`); if (undefined !== jsonObj.composite.spacer && typeof (jsonObj.composite.spacer) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has a Composite with an invalid 'spacer' attribute. It should be of type 'string'.`); // if composite is defined if (undefined === jsonObj.composite.units) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'Composite' attribute. It should have 1-4 units.`); if (!Array.isArray(jsonObj.composite.units)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has a Composite with an invalid 'units' attribute. It should be of type 'object[]'.`); for (let i = 0; i < jsonObj.composite.units.length; i++) { if (!isObject(jsonObj.composite.units[i])) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has a Composite with an invalid 'units' attribute. It should be of type 'object[]'.`); if (undefined === jsonObj.composite.units[i].name) // required throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has a Composite with an invalid 'units' attribute. The object at position ${i} is missing the required 'name' attribute.`); if (typeof (jsonObj.composite.units[i].name) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has a Composite with an invalid 'units' attribute. The object at position ${i} has an invalid 'name' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.composite.units[i].label && typeof (jsonObj.composite.units[i].label) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has a Composite with an invalid 'units' attribute. The object at position ${i} has an invalid 'label' attribute. It should be of type 'string'.`); } } return jsonObj; } isValidPropertyType(type) { type = type.toLowerCase(); return (type === "primitiveproperty") || (type === "structproperty") || (type === "primitivearrayproperty") || (type === "structarrayproperty") || (type === "navigationproperty"); } /** * Type checks property and returns PropertyProps interface * @param jsonObj * @returns PropertyProps */ checkPropertyProps(jsonObj) { const propName = jsonObj.name; if (undefined !== jsonObj.label && typeof (jsonObj.label) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'label' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.description && typeof (jsonObj.description) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'description' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.priority && typeof (jsonObj.priority) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'priority' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.isReadOnly && typeof (jsonObj.isReadOnly) !== "boolean") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'isReadOnly' attribute. It should be of type 'boolean'.`); if (undefined !== jsonObj.category && typeof (jsonObj.category) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'category' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.kindOfQuantity && typeof (jsonObj.kindOfQuantity) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'kindOfQuantity' attribute. It should be of type 'string'.`); if (undefined !== jsonObj.inherited && typeof (jsonObj.inherited) !== "boolean") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'inherited' attribute. It should be of type 'boolean'.`); if (undefined !== jsonObj.customAttributes && !Array.isArray(jsonObj.customAttributes)) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'customAttributes' attribute. It should be of type 'array'.`); return jsonObj; } checkPropertyTypename(jsonObj) { const propName = jsonObj.name; if (undefined === jsonObj.typeName) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} is missing the required 'typeName' attribute.`); if (typeof (jsonObj.typeName) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'typeName' attribute. It should be of type 'string'.`); } checkPropertyMinAndMaxOccurs(jsonObj) { const propName = jsonObj.name; if (undefined !== jsonObj.minOccurs && typeof (jsonObj.minOccurs) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'minOccurs' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.maxOccurs && typeof (jsonObj.maxOccurs) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'maxOccurs' attribute. It should be of type 'number'.`); } /** * Type checks PrimitiveOrEnumProperty and returns PrimitiveOrEnumPropertyBaseProps interface * @param jsonObj * @returns PrimitiveOrEnumPropertyBaseProps */ checkPrimitiveOrEnumPropertyBaseProps(jsonObj) { this.checkPropertyProps(jsonObj); this.checkPropertyTypename(jsonObj); const propName = jsonObj.name; if (undefined !== jsonObj.minLength && typeof (jsonObj.minLength) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'minLength' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.maxLength && typeof (jsonObj.maxLength) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'maxLength' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.minValue && typeof (jsonObj.minValue) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'minValue' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.maxValue && typeof (jsonObj.maxValue) !== "number") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'maxValue' attribute. It should be of type 'number'.`); if (undefined !== jsonObj.extendedTypeName && typeof (jsonObj.extendedTypeName) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The ECProperty ${this._currentItemFullName}.${propName} has an invalid 'extendedTypeName' attribute. It should be of type 'string'.`); return jsonObj; } /** * Type checks PrimitiveProperty and returns PrimitivePropertyProps interface * @param jsonObj * @returns PrimitivePropertyProps */ parsePrimitiveProperty(jsonObj) { this.checkPrimitiveOrEnumPropertyBaseProps(jsonObj); return jsonObj; } /** * Type checks StructProperty and returns StructPropertyProps interface * @param jsonObj * @returns StructPropertyProps */ parseStructProperty(jsonObj) { this.checkPropertyProps(jsonObj); this.checkPropertyTypename(jsonObj); return jsonObj; } /** * Type checks PrimitiveArrayProperty and returns PrimitiveArrayPropertyProps interface * @param jsonObj * @returns PrimitiveArrayPropertyProps */ parsePrimitiveArrayProperty(jsonObj) { this.checkPrimitiveOrEnumPropertyBaseProps(jsonObj); this.checkPropertyMinAndMaxOccurs(jsonObj); return jsonObj; } /** * Type checks StructArrayProperty and returns StructArrayPropertyProps interface * @param jsonObj * @returns StructArrayPropertyProps */ parseStructArrayProperty(jsonObj) { this.checkPropertyProps(jsonObj); this.checkPropertyTypename(jsonObj); this.checkPropertyMinAndMaxOccurs(jsonObj); return jsonObj; } /** * Type checks NavigationProperty and returns NavigationPropertyProps interface * @param jsonObj * @returns NavigationPropertyProps */ parseNavigationProperty(jsonObj) { this.checkPropertyProps(jsonObj); const fullname = `${this._currentItemFullName}.${jsonObj.name}`; if (undefined === jsonObj.relationshipName) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Navigation Property ${fullname} is missing the required 'relationshipName' property.`); if (typeof (jsonObj.relationshipName) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Navigation Property ${fullname} has an invalid 'relationshipName' property. It should be of type 'string'.`); if (undefined === jsonObj.direction) throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Navigation Property ${fullname} is missing the required 'direction' property.`); if (typeof (jsonObj.direction) !== "string") throw new ECSchemaError(ECSchemaStatus.InvalidECJson, `The Navigation Property ${fullname} has an invalid 'direction' property. It should be of type 'string'.`); return jsonObj; } getSchemaCustomAttributeProviders() { return this.getCustomAttributeProviders(this._rawSchema, "Schema", this._schemaName); } getClassCustomAttributeProviders(jsonObj) { return this.getCustomAttributeProviders(jsonObj, "ECClass", this._currentItemFullName); } getPropertyCustomAttributeProviders(jsonObj) { return this.getCustomAttributeProviders(jsonObj, "ECProperty", `${this._currentItemFullName}.${jsonObj.name}`); } getRelationshipConstraintCustomAttributeProviders(jsonObj) { const sourceCustomAttributes = this.getCustomAttributeProviders(jsonObj.source, "Source Constraint of", this._currentItemFullName); const targetCustomAttributes = this.getCustomAttributeProviders(jsonObj.target, "Target Constraint of", this._currentItemFullName); return [sourceCustomAttributes, targetCustomAttributes]; } *getCustomAttributeProviders(jsonObj, type, name) { if (undefined !== jsonObj.customAttributes) { if (!Array.isArray(jsonObj.customAttributes)) thr