UNPKG

11.8 kBJavaScriptView Raw
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15'use strict';
16
17var dayjs = require('dayjs');
18var utc = require('dayjs/plugin/utc');
19dayjs.extend(utc);
20var {
21 ModelManager,
22 Factory,
23 Serializer,
24 Introspector
25} = require('@accordproject/concerto-core');
26var {
27 CommonMarkModel
28} = require('@accordproject/markdown-common').CommonMarkModel;
29var {
30 CiceroMarkModel
31} = require('@accordproject/markdown-cicero').CiceroMarkModel;
32var {
33 ConcertoMetaModel
34} = require('@accordproject/markdown-cicero').ConcertoMetaModel;
35var TemplateMarkModel = require('./externalModels/TemplateMarkModel').TemplateMarkModel;
36var normalizeNLs = require('./normalize').normalizeNLs;
37var TypeVisitor = require('./TypeVisitor');
38var FormulaVisitor = require('./FormulaVisitor');
39var MarkdownIt = require('markdown-it');
40var MarkdownItTemplate = require('@accordproject/markdown-it-template');
41var FromMarkdownIt = require('@accordproject/markdown-common').FromMarkdownIt;
42var templaterules = require('./templaterules');
43
44/**
45 * Model manager for TemplateMark
46 * @param {object} options - optional parameters
47 * @param {number} [options.utcOffset] - UTC Offset for this execution
48 * @returns {object} model manager and utilities for TemplateMark
49 */
50function mkTemplateMarkManager(options) {
51 var result = {};
52 result.modelManager = new ModelManager(options);
53 result.modelManager.addCTOModel(CommonMarkModel, 'commonmark.cto');
54 result.modelManager.addCTOModel(ConcertoMetaModel, 'metamodel.cto');
55 result.modelManager.addCTOModel(CiceroMarkModel, 'ciceromark.cto');
56 result.modelManager.addCTOModel(TemplateMarkModel, 'templatemark.cto');
57 result.factory = new Factory(result.modelManager);
58 result.serializer = new Serializer(result.factory, result.modelManager, {
59 utcOffset: 0
60 });
61 return result;
62}
63var templateMarkManager = mkTemplateMarkManager();
64
65/**
66 * Check to see if a ClassDeclaration is an instance of the specified fully qualified
67 * type name.
68 * @internal
69 * @param {ClassDeclaration} classDeclaration The class to test
70 * @param {String} fqt The fully qualified type name.
71 * @returns {boolean} True if classDeclaration an instance of the specified fully
72 * qualified type name, false otherwise.
73 */
74function instanceOf(classDeclaration, fqt) {
75 // Check to see if this is an exact instance of the specified type.
76 if (classDeclaration.getFullyQualifiedName() === fqt) {
77 return true;
78 }
79 // Now walk the class hierachy looking to see if it's an instance of the specified type.
80 var superTypeDeclaration = classDeclaration.getSuperTypeDeclaration();
81 while (superTypeDeclaration) {
82 if (superTypeDeclaration.getFullyQualifiedName() === fqt) {
83 return true;
84 }
85 superTypeDeclaration = superTypeDeclaration.getSuperTypeDeclaration();
86 }
87 return false;
88}
89
90/**
91 * Returns the template model for the template
92 * @param {object} introspector - the model introspector for this template
93 * @param {string} templateKind - either 'clause' or 'contract'
94 * @throws {Error} if no template model is found, or multiple template models are found
95 * @returns {ClassDeclaration} the template model for the template
96 */
97function findTemplateModel(introspector, templateKind) {
98 var modelType = 'org.accordproject.contract.Contract';
99 if (templateKind !== 'contract') {
100 modelType = 'org.accordproject.contract.Clause';
101 }
102 var templateModels = introspector.getClassDeclarations().filter(item => {
103 return !item.isAbstract() && instanceOf(item, modelType);
104 });
105 if (templateModels.length > 1) {
106 throw new Error("Found multiple instances of ".concat(modelType, ". The model for the template must contain a single asset that extends ").concat(modelType, "."));
107 } else if (templateModels.length === 0) {
108 throw new Error("Failed to find an asset that extends ".concat(modelType, ". The model for the template must contain a single asset that extends ").concat(modelType, "."));
109 } else {
110 return templateModels[0];
111 }
112}
113
114/**
115 * Returns the template model for a type
116 * @param {object} introspector - the model introspector for this template
117 * @param {string} elementType - the element type
118 * @throws {Error} if no template model is found, or multiple template models are found
119 * @returns {ClassDeclaration} the template model for the template
120 */
121function findElementModel(introspector, elementType) {
122 return introspector.getClassDeclaration(elementType);
123}
124
125/**
126 * Decorate TemplateMark DOM with its types
127 * @param {object} template the TemplateMark DOM
128 * @param {object} introspector - the introspector for this template
129 * @param {string} model - the model
130 * @param {string} templateKind - either 'clause' or 'contract'
131 * @param {object} options - optional parameters
132 * @param {number} [options.utcOffset] - UTC Offset for this execution
133 * @returns {object} the typed TemplateMark DOM
134 */
135function templateMarkTypingGen(template, introspector, model, templateKind, options) {
136 var input = templateMarkManager.serializer.fromJSON(template, options);
137 var parameters = {
138 templateMarkModelManager: templateMarkManager.modelManager,
139 introspector: introspector,
140 model: model,
141 kind: templateKind
142 };
143 var visitor = new TypeVisitor();
144 input.accept(visitor, parameters);
145 var result = Object.assign({}, templateMarkManager.serializer.toJSON(input, options));
146
147 // Calculates formula dependencies
148 var fvisitor = new FormulaVisitor();
149 result = fvisitor.calculateDependencies(templateMarkManager.modelManager.serializer, result, options);
150 return result;
151}
152
153/**
154 * Decorate TemplateMark DOM with its types
155 * @param {object} template the TemplateMark DOM
156 * @param {object} modelManager - the modelManager for this template
157 * @param {string} templateKind - either 'clause' or 'contract'
158 * @returns {object} the typed TemplateMark DOM
159 */
160function templateMarkTyping(template, modelManager, templateKind) {
161 var introspector = new Introspector(modelManager);
162 var model = findTemplateModel(introspector, templateKind);
163 return templateMarkTypingGen(template, introspector, model, templateKind);
164}
165
166/**
167 * Decorate TemplateMark DOM with its types
168 * @param {object} template the TemplateMark DOM
169 * @param {object} modelManager - the modelManager for this template
170 * @param {string} elementType - the element type
171 * @returns {object} the typed TemplateMark DOM
172 */
173function templateMarkTypingFromType(template, modelManager, elementType) {
174 var introspector = new Introspector(modelManager);
175 var model = findElementModel(introspector, elementType);
176 var rootNode = {
177 '$class': 'org.accordproject.commonmark.Document',
178 'xmlns': 'http://commonmark.org/xml/1.0',
179 'nodes': [{
180 '$class': 'org.accordproject.templatemark.ContractDefinition',
181 'name': 'top',
182 'nodes': template
183 }]
184 };
185 var rootNodeTyped = templateMarkTypingGen(rootNode, introspector, model, 'clause');
186 return rootNodeTyped.nodes[0].nodes;
187}
188
189/**
190 * Converts a templatemark string to a token stream
191 * @param {object} input the templatemark string
192 * @returns {object} the token stream
193 */
194function templateToTokens(input) {
195 var norm = normalizeNLs(input);
196 var parser = new MarkdownIt({
197 html: true
198 }).use(MarkdownItTemplate);
199 return parser.parse(norm, {});
200}
201
202/**
203 * Converts a template token strean string to an untyped TemplateMark DOM
204 * @param {object} tokenStream the template token stream
205 * @returns {object} the TemplateMark DOM
206 */
207function tokensToUntypedTemplateMarkGen(tokenStream) {
208 var fromMarkdownIt = new FromMarkdownIt(templaterules);
209 var partialTemplate = fromMarkdownIt.toCommonMark(tokenStream);
210 var result = templateMarkManager.serializer.toJSON(templateMarkManager.serializer.fromJSON(partialTemplate));
211 return result.nodes;
212}
213
214/**
215 * Converts a template token strean string to an untyped TemplateMark DOM
216 * @param {object} tokenStream the template token stream
217 * @param {string} templateKind - either 'clause' or 'contract'
218 * @returns {object} the TemplateMark DOM
219 */
220function tokensToUntypedTemplateMark(tokenStream, templateKind) {
221 var partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream);
222 if (templateKind === 'contract') {
223 return {
224 '$class': 'org.accordproject.commonmark.Document',
225 'xmlns': 'http://commonmark.org/xml/1.0',
226 'nodes': [{
227 '$class': 'org.accordproject.templatemark.ContractDefinition',
228 'name': 'top',
229 'nodes': partialTemplate
230 }]
231 };
232 } else {
233 return {
234 '$class': 'org.accordproject.commonmark.Document',
235 'xmlns': 'http://commonmark.org/xml/1.0',
236 'nodes': [{
237 '$class': 'org.accordproject.templatemark.ClauseDefinition',
238 'name': 'top',
239 'nodes': partialTemplate
240 }]
241 };
242 }
243}
244
245/**
246 * Converts a template token strean string to an untyped TemplateMark DOM
247 * @param {object} tokenStream the template token stream
248 * @param {string} templateKind - either 'clause' or 'contract'
249 * @returns {object} the TemplateMark DOM
250 */
251function tokensToUntypedTemplateMarkFragment(tokenStream) {
252 var partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream);
253 return {
254 '$class': 'org.accordproject.commonmark.Document',
255 'xmlns': 'http://commonmark.org/xml/1.0',
256 'nodes': [{
257 '$class': 'org.accordproject.templatemark.ClauseDefinition',
258 'name': 'top',
259 'nodes': partialTemplate
260 }]
261 };
262}
263
264/**
265 * @param {object} modelManager - the model manager
266 * @param {object} type - The type from the model source to generate a JSON for
267 * @return {object} the generated JSON instance
268 */
269function generateJSON(modelManager, type) {
270 var factory = new Factory(modelManager);
271 var serializer = new Serializer(factory, modelManager);
272 switch (type) {
273 case 'DateTime':
274 return dayjs.utc();
275 case 'Integer':
276 return 0;
277 case 'Long':
278 return 0;
279 case 'Double':
280 return 0.0;
281 case 'Boolean':
282 return false;
283 case 'String':
284 return '';
285 default:
286 {
287 var classDeclaration = modelManager.getType(type);
288 if (classDeclaration.isEnum()) {
289 throw new Error('Cannot generate JSON for an enumerated type directly, the type should be contained in Concept, Asset, Transaction or Event declaration');
290 }
291 var ns = classDeclaration.getNamespace();
292 var name = classDeclaration.getName();
293 var factoryOptions = {
294 includeOptionalFields: true,
295 generate: true
296 };
297 if (classDeclaration.isConcept()) {
298 var concept = factory.newConcept(ns, name, null, factoryOptions);
299 return serializer.toJSON(concept);
300 }
301 var resource = factory.newResource(ns, name, 'resource1', null, factoryOptions);
302 return serializer.toJSON(resource);
303 }
304 }
305}
306module.exports.findTemplateModel = findTemplateModel;
307module.exports.templateMarkManager = templateMarkManager;
308module.exports.templateToTokens = templateToTokens;
309module.exports.tokensToUntypedTemplateMarkFragment = tokensToUntypedTemplateMarkFragment;
310module.exports.tokensToUntypedTemplateMark = tokensToUntypedTemplateMark;
311module.exports.templateMarkTyping = templateMarkTyping;
312module.exports.templateMarkTypingFromType = templateMarkTypingFromType;
313module.exports.generateJSON = generateJSON;
\No newline at end of file