@itwin/ecschema-metadata
Version:
ECObjects core concepts in typescript
709 lines • 51.2 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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