@accordproject/concerto-cto
Version:
Parser for Concerto CTO files
435 lines • 17 kB
JavaScript
;
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const concerto_metamodel_1 = require("@accordproject/concerto-metamodel");
/**
* Returns true if the metamodel is a MapDeclaration
* @param {object} mm - the metamodel
* @return {boolean} the string for that model
*/
function isMap(mm) {
return mm.$class === `${concerto_metamodel_1.MetaModelNamespace}.MapDeclaration`;
}
/**
* Returns true if the metamodel is a ScalarDeclaration
* @param {object} mm - the metamodel
* @return {boolean} the string for that model
*/
function isScalar(mm) {
return [
`${concerto_metamodel_1.MetaModelNamespace}.BooleanScalar`,
`${concerto_metamodel_1.MetaModelNamespace}.IntegerScalar`,
`${concerto_metamodel_1.MetaModelNamespace}.LongScalar`,
`${concerto_metamodel_1.MetaModelNamespace}.DoubleScalar`,
`${concerto_metamodel_1.MetaModelNamespace}.StringScalar`,
`${concerto_metamodel_1.MetaModelNamespace}.DateTimeScalar`,
].includes(mm.$class);
}
/**
* Create decorator argument string from a metamodel
* @param {object} mm - the metamodel
* @return {string} the string for the decorator argument
*/
function decoratorArgFromMetaModel(mm) {
let result = '';
switch (mm.$class) {
case `${concerto_metamodel_1.MetaModelNamespace}.DecoratorTypeReference`:
const typeRef = mm;
result += `${typeRef.type.name}${!!typeRef.isArray ? '[]' : ''}`;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.DecoratorString`:
const strRef = mm;
result += `"${strRef.value}"`;
break;
default:
result += `${mm.value}`;
break;
}
return result;
}
/**
* Create decorator string from a metamodel
* @param {object} mm - the metamodel
* @return {string} the string for the decorator
*/
function decoratorFromMetaModel(mm) {
let result = '';
result += `@${mm.name}`;
if (mm.arguments) {
result += '(';
result += mm.arguments.map(decoratorArgFromMetaModel).join(',');
result += ')';
}
return result;
}
/**
* Create decorators string from a metamodel
* @param {object} mm - the metamodel
* @param {string} prefix - indentation
* @return {string} the string for the decorators
*/
function decoratorsFromMetaModel(mm, prefix) {
let result = '';
result += mm.map(decoratorFromMetaModel).join(`\n${prefix}`);
result += `\n${prefix}`;
return result;
}
/**
* Create type string from a metamodel
*
* @param {object} mm - the metamodel
* @return {string} the string for the type
*/
function typeFromMetaModel(mm) {
let result = '';
switch (mm.$class) {
case `${concerto_metamodel_1.MetaModelNamespace}.EnumProperty`:
break;
case `${concerto_metamodel_1.MetaModelNamespace}.BooleanScalar`:
case `${concerto_metamodel_1.MetaModelNamespace}.BooleanProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.BooleanMapValueType`:
result += ' Boolean';
break;
case `${concerto_metamodel_1.MetaModelNamespace}.DateTimeProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.DateTimeScalar`:
case `${concerto_metamodel_1.MetaModelNamespace}.DateTimeMapKeyType`:
case `${concerto_metamodel_1.MetaModelNamespace}.DateTimeMapValueType`:
result += ' DateTime';
break;
case `${concerto_metamodel_1.MetaModelNamespace}.DoubleProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.DoubleScalar`:
case `${concerto_metamodel_1.MetaModelNamespace}.DoubleMapValueType`:
result += ' Double';
break;
case `${concerto_metamodel_1.MetaModelNamespace}.IntegerProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.IntegerScalar`:
case `${concerto_metamodel_1.MetaModelNamespace}.IntegerMapValueType`:
result += ' Integer';
break;
case `${concerto_metamodel_1.MetaModelNamespace}.LongProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.LongScalar`:
case `${concerto_metamodel_1.MetaModelNamespace}.LongMapValueType`:
result += ' Long';
break;
case `${concerto_metamodel_1.MetaModelNamespace}.StringProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.StringScalar`:
case `${concerto_metamodel_1.MetaModelNamespace}.StringMapKeyType`:
case `${concerto_metamodel_1.MetaModelNamespace}.StringMapValueType`:
result += ' String';
break;
case `${concerto_metamodel_1.MetaModelNamespace}.ObjectProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.ObjectMapKeyType`:
case `${concerto_metamodel_1.MetaModelNamespace}.ObjectMapValueType`:
result += ` ${mm.type.name}`;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.RelationshipProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.RelationshipMapValueType`:
result += ` ${mm.type.name}`;
break;
}
return result;
}
/**
* Create modifiers string from a metamodel
*
* @param {object} mm - the metamodel
* @return {string} the string for the modifiers
*/
function modifiersFromMetaModel(mm) {
let result = '';
let defaultString = '';
let validatorString = '';
switch (mm.$class) {
case `${concerto_metamodel_1.MetaModelNamespace}.EnumProperty`:
break;
case `${concerto_metamodel_1.MetaModelNamespace}.BooleanProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.BooleanScalar`:
if (mm.defaultValue === true || mm.defaultValue === false) {
if (mm.defaultValue) {
defaultString += ' default=true';
}
else {
defaultString += ' default=false';
}
}
break;
case `${concerto_metamodel_1.MetaModelNamespace}.DateTimeProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.DateTimeScalar`:
if (mm.defaultValue) {
defaultString += ` default="${mm.defaultValue}"`;
}
break;
case `${concerto_metamodel_1.MetaModelNamespace}.DoubleProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.DoubleScalar`:
if (mm.defaultValue !== undefined) {
const doubleString = toDoubleString(mm.defaultValue);
defaultString += ` default=${doubleString}`;
}
if (mm.validator) {
const lowerString = mm.validator.lower !== undefined ? toDoubleString(mm.validator.lower) : '';
const upperString = mm.validator.upper !== undefined ? toDoubleString(mm.validator.upper) : '';
validatorString += ` range=[${lowerString},${upperString}]`;
}
break;
case `${concerto_metamodel_1.MetaModelNamespace}.IntegerProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.IntegerScalar`:
if (mm.defaultValue !== undefined) {
defaultString += ` default=${mm.defaultValue.toString()}`;
}
if (mm.validator) {
const lowerString = mm.validator.lower !== undefined ? mm.validator.lower : '';
const upperString = mm.validator.upper !== undefined ? mm.validator.upper : '';
validatorString += ` range=[${lowerString},${upperString}]`;
}
break;
case `${concerto_metamodel_1.MetaModelNamespace}.LongProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.LongScalar`:
if (mm.defaultValue !== undefined) {
defaultString += ` default=${mm.defaultValue.toString()}`;
}
if (mm.validator) {
const lowerString = mm.validator.lower !== undefined ? mm.validator.lower : '';
const upperString = mm.validator.upper !== undefined ? mm.validator.upper : '';
validatorString += ` range=[${lowerString},${upperString}]`;
}
break;
case `${concerto_metamodel_1.MetaModelNamespace}.StringProperty`:
case `${concerto_metamodel_1.MetaModelNamespace}.StringScalar`:
if (mm.defaultValue) {
defaultString += ` default="${mm.defaultValue}"`;
}
if (mm.validator) {
validatorString += ` regex=/${mm.validator.pattern}/${mm.validator.flags || ''}`;
}
if (mm.lengthValidator) {
const minLength = mm.lengthValidator.minLength !== undefined ? mm.lengthValidator.minLength : '';
const maxLength = mm.lengthValidator.maxLength !== undefined ? mm.lengthValidator.maxLength : '';
validatorString += ` length=[${minLength},${maxLength}]`;
}
break;
case `${concerto_metamodel_1.MetaModelNamespace}.ObjectProperty`:
if (mm.defaultValue) {
defaultString += ` default="${mm.defaultValue}"`;
}
break;
}
return result + defaultString + validatorString;
}
/**
* Format a numeric literal as a Double token for CTO output.
* @param {number} value - numeric value to format
* @returns {string} CTO-compatible Double literal
*/
function toDoubleString(value) {
const normalizedValue = Number(value).toLocaleString('en-US', {
useGrouping: false,
// Intl.NumberFormat permits 1..21; use the maximum to avoid exponent output.
maximumSignificantDigits: 21,
});
if (!normalizedValue.includes('.')) {
return `${normalizedValue}.0`;
}
return normalizedValue;
}
/**
* Create property string from a metamodel
*
* @param {object} prop - the property metamodel object
* @return {string} the string for the property
*/
function propertyFromMetaModel(prop) {
let result = '';
if (prop.decorators) {
result += decoratorsFromMetaModel(prop.decorators, ' ');
}
if (prop.$class === `${concerto_metamodel_1.MetaModelNamespace}.RelationshipProperty`) {
result += '-->';
}
else {
result += 'o';
}
result += typeFromMetaModel(prop);
if (prop.isArray) {
result += '[]';
}
result += ` ${prop.name}`;
result += modifiersFromMetaModel(prop);
if (prop.isOptional) {
result += ' optional';
}
return result;
}
/**
* Create map type string from a metamodel map
*
* @param {object} entry - the map metamodel object
* @return {string} the string for the map
*/
function mapFromMetaModel(entry) {
let result = '';
if (entry.decorators) {
result += decoratorsFromMetaModel(entry.decorators, ' ');
}
if (entry.$class === `${concerto_metamodel_1.MetaModelNamespace}.RelationshipMapValueType`) {
result += '-->';
}
else {
result += 'o';
}
result += typeFromMetaModel(entry);
return result;
}
/**
* Create declaration string from a metamodel
*
* @param {object} mm - the declaration metamodel object
* @return {string} the string for the declaration
*/
function declFromMetaModel(mm) {
let result = '';
if (mm.decorators) {
result += decoratorsFromMetaModel(mm.decorators, '');
}
if (isScalar(mm)) {
result += `scalar ${mm.name} extends`;
result += typeFromMetaModel(mm);
result += modifiersFromMetaModel(mm);
}
else if (isMap(mm)) {
result += `map ${mm.name} {`;
const mapDecl = mm;
result += `\n ${mapFromMetaModel(mapDecl.key)}`;
result += `\n ${mapFromMetaModel(mapDecl.value)}`;
result += '\n}';
}
else {
const conceptDecl = mm;
if (conceptDecl.isAbstract) {
result += 'abstract ';
}
switch (mm.$class) {
case `${concerto_metamodel_1.MetaModelNamespace}.AssetDeclaration`:
result += `asset ${mm.name} `;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.ConceptDeclaration`:
result += `concept ${mm.name} `;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.EventDeclaration`:
result += `event ${mm.name} `;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.ParticipantDeclaration`:
result += `participant ${mm.name} `;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.TransactionDeclaration`:
result += `transaction ${mm.name} `;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.EnumDeclaration`:
result += `enum ${mm.name} `;
break;
}
// Handle identified
if (conceptDecl.identified) {
if (conceptDecl.identified.$class === `${concerto_metamodel_1.MetaModelNamespace}.IdentifiedBy`) {
const identifiedBy = conceptDecl.identified;
result += `identified by ${identifiedBy.name} `;
}
else {
result += 'identified ';
}
}
// Handle supertype
if (conceptDecl.superType) {
if (conceptDecl.superType.name === mm.name) {
throw new Error(`The declaration "${mm.name}" cannot extend itself.`);
}
result += `extends ${conceptDecl.superType.name} `;
}
result += '{';
// Handle properties
conceptDecl.properties.forEach((property) => {
result += `\n ${propertyFromMetaModel(property)}`;
});
result += '\n}';
}
return result;
}
/**
* Converts a metamodel instance to a CTO string
*
* @param {*} metaModel - the metamodel instance
* @return {string} the CTO model as a string
*/
function toCTO(metaModel) {
let result = '';
// version
if (metaModel.concertoVersion) {
result += `concerto version "${metaModel.concertoVersion}"\n\n`;
}
// decorators
if (metaModel.decorators && metaModel.decorators.length > 0) {
result += decoratorsFromMetaModel(metaModel.decorators, '');
}
// namespace
result += `namespace ${metaModel.namespace}`;
// imports
if (metaModel.imports && metaModel.imports.length > 0) {
result += '\n';
metaModel.imports.forEach((imp) => {
switch (imp.$class) {
case `${concerto_metamodel_1.MetaModelNamespace}.ImportType`:
case `${concerto_metamodel_1.MetaModelNamespace}.ImportTypeFrom`: {
const typedImport = imp;
result += `\nimport ${imp.namespace}.${typedImport.name}`;
break;
}
case `${concerto_metamodel_1.MetaModelNamespace}.ImportAll`:
case `${concerto_metamodel_1.MetaModelNamespace}.ImportAllFrom`:
result += `\nimport ${imp.namespace}.*`;
break;
case `${concerto_metamodel_1.MetaModelNamespace}.ImportTypes`: {
const typesImport = imp;
const aliasedTypes = typesImport.aliasedTypes
? new Map(typesImport.aliasedTypes.map((aliasedType) => [
aliasedType.name,
aliasedType.aliasedName,
]))
: new Map();
const commaSeparatedTypesString = typesImport.types
.map((type) => aliasedTypes.has(type)
? `${type} as ${aliasedTypes.get(type)}`
: type)
.join(',');
result += `\nimport ${imp.namespace}.{${commaSeparatedTypesString}}`;
break;
}
default:
throw new Error('Unrecognized import');
}
if (imp.uri) {
result += ` from ${imp.uri}`;
}
});
}
// declarations
if (metaModel.declarations && metaModel.declarations.length > 0) {
metaModel.declarations.forEach((decl) => {
result += `\n\n${declFromMetaModel(decl)}`;
});
}
return result;
}
module.exports = {
toCTO,
};
//# sourceMappingURL=printer.js.map