UNPKG

4.42 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
17const Decorator = require('./decorator');
18const IllegalModelException = require('./illegalmodelexception');
19
20/**
21 * Decorated defines a model element that may have decorators attached.
22 *
23 * @private
24 * @abstract
25 * @class
26 * @memberof module:concerto-core
27 */
28class Decorated {
29
30 /**
31 * Create a Decorated from an Abstract Syntax Tree. The AST is the
32 * result of parsing.
33 *
34 * @param {ModelFile} modelFile - the model file
35 * @param {string} ast - the AST created by the parser
36 * @throws {IllegalModelException}
37 */
38 constructor(modelFile, ast) {
39 if(!modelFile) {
40 throw new Error('modelFile not specified');
41 } else if(!ast) {
42 throw new Error('ast not specified');
43 }
44 this.modelFile = modelFile;
45 this.ast = ast;
46 }
47
48 /**
49 * Returns the ModelFile that defines this class.
50 *
51 * @return {ModelFile} the owning ModelFile
52 */
53 getModelFile() {
54 return this.modelFile;
55 }
56
57 /**
58 * Visitor design pattern
59 * @param {Object} visitor - the visitor
60 * @param {Object} parameters - the parameter
61 * @return {Object} the result of visiting or null
62 * @private
63 */
64 accept(visitor,parameters) {
65 return visitor.visit(this, parameters);
66 }
67
68 /**
69 * Process the AST and build the model
70 *
71 * @throws {IllegalModelException}
72 * @private
73 */
74 process() {
75 this.decorators = [];
76
77 if(this.ast.decorators) {
78 for(let n=0; n < this.ast.decorators.length; n++ ) {
79 let thing = this.ast.decorators[n];
80 let modelFile = this.getModelFile();
81 let modelManager = modelFile.getModelManager();
82 let factories = modelManager.getDecoratorFactories();
83 let decorator;
84 for (let factory of factories) {
85 decorator = factory.newDecorator(this, thing);
86 if (decorator) {
87 break;
88 }
89 }
90 if (!decorator) {
91 decorator = new Decorator(this, thing);
92 }
93 this.decorators.push(decorator);
94 }
95 }
96 }
97
98 /**
99 * Semantic validation of the structure of this decorated. Subclasses should
100 * override this method to impose additional semantic constraints on the
101 * contents/relations of fields.
102 *
103 * @throws {IllegalModelException}
104 * @private
105 */
106 validate() {
107
108 for(let n=0; n < this.decorators.length; n++) {
109 let decorator = this.decorators[n];
110 decorator.validate();
111
112 // check we don't have this decorator twice
113 for(let i=n+1; i < this.decorators.length; i++) {
114 let otherDecorator = this.decorators[i];
115 if(decorator.getName() === otherDecorator.getName()) {
116 throw new IllegalModelException(`Duplicate decorator ${decorator.getName()}`,this.modelFile, this.ast.location);
117 }
118 }
119 }
120 }
121
122 /**
123 * Returns the decorators for this class.
124 *
125 * @return {Decorator[]} the decorators for the class
126 */
127 getDecorators() {
128 return this.decorators;
129 }
130
131 /**
132 * Returns the decorator for this class with a given name.
133 * @param {string} name - the name of the decorator
134 * @return {Decorator} the decorator attached to this class with the given name, or null if it does not exist.
135 */
136 getDecorator(name) {
137 for(let n=0; n < this.decorators.length; n++) {
138 let decorator = this.decorators[n];
139 if(decorator.getName() === name) {
140 return decorator;
141 }
142 }
143
144 return null;
145 }
146}
147
148module.exports = Decorated;