bocl 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bocl/Evaluator.py +456 -0
- bocl/OCLWrapper.py +44 -0
- bocl/__init__.py +0 -0
- bocl-0.1.0.dist-info/METADATA +90 -0
- bocl-0.1.0.dist-info/RECORD +10 -0
- bocl-0.1.0.dist-info/WHEEL +5 -0
- bocl-0.1.0.dist-info/top_level.txt +2 -0
- models/__init__.py +0 -0
- models/library_object.py +161 -0
- models/researcher_object.py +136 -0
bocl/Evaluator.py
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
from besser.BUML.notations.ocl.FactoryInstance import Factory
|
|
2
|
+
from besser.BUML.metamodel.ocl.rules import *
|
|
3
|
+
from besser.BUML.metamodel.structural.structural import Property
|
|
4
|
+
class Evaluator:
|
|
5
|
+
"""The Evaluator class evaluates the OCL constraints based on the object model.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
debug = this attribute is used for debugging purposes and with default value false
|
|
11
|
+
allObjSat = list of all objects that satisfy the contraint
|
|
12
|
+
"""
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.debug = False
|
|
15
|
+
self.allObjSat = []
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_value(self,name, obj):
|
|
19
|
+
"""The get_value function to retrieve value of attribute from object.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
name: name of the attribute
|
|
23
|
+
object: Object from object model
|
|
24
|
+
"""
|
|
25
|
+
for slot in obj.slots:
|
|
26
|
+
if name == slot.attribute.name:
|
|
27
|
+
if slot.attribute.type.name == 'str':
|
|
28
|
+
return '"'+ slot.value.value +'"'
|
|
29
|
+
else:
|
|
30
|
+
return slot.value.value
|
|
31
|
+
def checkInObj(self,obj,source):
|
|
32
|
+
"""The checkInObj function identifies the object that contains the source in slots.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
obj: Object from object model
|
|
36
|
+
source: source of the current object in the CSTree
|
|
37
|
+
"""
|
|
38
|
+
for s in obj.slots:
|
|
39
|
+
if s.attribute.name == source.name:
|
|
40
|
+
return obj
|
|
41
|
+
def checkInLinkEnds(self,obj, source):
|
|
42
|
+
"""The checkInLinkEnds function identifies the source in the linkend of the object.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
obj: Object from object model
|
|
46
|
+
source: source of the current object in the CSTree
|
|
47
|
+
"""
|
|
48
|
+
toRet = []
|
|
49
|
+
for link in obj.links:
|
|
50
|
+
for connection in link.connections:
|
|
51
|
+
if connection.association_end.name == source.name:
|
|
52
|
+
toRet.append(connection.object)
|
|
53
|
+
|
|
54
|
+
return toRet
|
|
55
|
+
def handleForAll(self, tree, allObjs, logicalExp):
|
|
56
|
+
"""The handleForAll function handles forAll expression from OCL MM.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
tree: Tree that is constructed using OCL Parser
|
|
60
|
+
allObjs: all objects from object model
|
|
61
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
62
|
+
"""
|
|
63
|
+
logicalExp[0] = logicalExp[0] +" ("
|
|
64
|
+
for index in range(len(allObjs)):
|
|
65
|
+
if len(tree.get_body)>0:
|
|
66
|
+
self.update_logical_exp(tree.get_body[0],logicalExp,allObjs[index])
|
|
67
|
+
if index < len(allObjs)-1:
|
|
68
|
+
logicalExp[0] = logicalExp[0] + " and "
|
|
69
|
+
logicalExp[0] = logicalExp[0] +" )"
|
|
70
|
+
pass
|
|
71
|
+
def handle(self, source, obj, logicalExp,rightSide = False):
|
|
72
|
+
"""The handle function handles Property Call expression from OCL MM.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
source: one child in OCL CST
|
|
76
|
+
obj: one object from object model
|
|
77
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
78
|
+
rightSide: to identify the side of logical expression
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
if isinstance(source, PropertyCallExpression) or isinstance(source,Property):
|
|
82
|
+
# if len(source.name.split('.'))==2:
|
|
83
|
+
allObjs = self.checkInObj(obj,source)
|
|
84
|
+
if allObjs is None:
|
|
85
|
+
allObjs = self.checkInLinkEnds(obj, source)
|
|
86
|
+
for index in range(len(allObjs)):
|
|
87
|
+
objs = allObjs[index]
|
|
88
|
+
if len(allObjs) > 1 or rightSide:
|
|
89
|
+
logicalExp[0] = logicalExp[0] + " [ "
|
|
90
|
+
for i in range (len(objs.slots)):
|
|
91
|
+
logicalExp[0] = logicalExp[0] + "\""+str(objs.slots[i].attribute.name)+ str(objs.slots[i].value.value) + "\""
|
|
92
|
+
if i < len(objs.slots)-1:
|
|
93
|
+
logicalExp[0] = logicalExp[0]+","
|
|
94
|
+
if not rightSide:
|
|
95
|
+
if len(allObjs) > 1 and index< len(allObjs)-1:
|
|
96
|
+
logicalExp[0] = logicalExp[0] + " ], "
|
|
97
|
+
else:
|
|
98
|
+
logicalExp[0] = logicalExp[0] + " ] "
|
|
99
|
+
if len(allObjs) > 1 and index< len(allObjs)-1:
|
|
100
|
+
logicalExp[0] = logicalExp[0] + " , "
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
pass
|
|
104
|
+
pass
|
|
105
|
+
def handleExcludes(self, tree, obj, logicalExp):
|
|
106
|
+
"""The handleExcludes function handles excludes construct.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
tree: Tree that is constructed using OCL Parser
|
|
110
|
+
obj: one object from object model
|
|
111
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
112
|
+
"""
|
|
113
|
+
# tempLogicalExp =[""]
|
|
114
|
+
args = tree.arguments
|
|
115
|
+
self.checkAndAdd(logicalExp, " and ")
|
|
116
|
+
logicalExp[0] = logicalExp[0] + " [ "
|
|
117
|
+
self.handle(tree.source, obj, logicalExp)
|
|
118
|
+
logicalExp[0] = logicalExp[0] + " ] not in "
|
|
119
|
+
if len(args)==1: #Property
|
|
120
|
+
logicalExp[0] = logicalExp[0] + " [ "
|
|
121
|
+
self.handle(args[0],obj,logicalExp,True)
|
|
122
|
+
logicalExp[0] = logicalExp[0] + " ]"
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
def handleIncludes(self, tree, obj, logicalExp):
|
|
126
|
+
"""The handleIncludes function handles includes construct.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
tree: Tree that is constructed using OCL Parser
|
|
130
|
+
obj: one object from object model
|
|
131
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
# tempLogicalExp =[""]
|
|
135
|
+
args = tree.arguments
|
|
136
|
+
self.checkAndAdd(logicalExp, " and ")
|
|
137
|
+
logicalExp[0] = logicalExp[0] + " [ "
|
|
138
|
+
self.handle(tree.source, obj, logicalExp)
|
|
139
|
+
logicalExp[0] = logicalExp[0] + " ] in "
|
|
140
|
+
if len(args)==1: #Property
|
|
141
|
+
logicalExp[0] = logicalExp[0] + " [ "
|
|
142
|
+
self.handle(args[0],obj,logicalExp,True)
|
|
143
|
+
logicalExp[0] = logicalExp[0] + " ]"
|
|
144
|
+
pass
|
|
145
|
+
def handleExists(self,tree,allObjs,logicalExp):
|
|
146
|
+
"""The handleExists function handles exists construct.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
tree: Tree that is constructed using OCL Parser
|
|
150
|
+
allObjs: all objects from object model
|
|
151
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
152
|
+
"""
|
|
153
|
+
logicalExp[0] = logicalExp[0] + " ( "
|
|
154
|
+
temp = logicalExp[0]
|
|
155
|
+
for index in range(len(allObjs)):
|
|
156
|
+
if len(tree.get_body) > 0:
|
|
157
|
+
self.update_logical_exp(tree.get_body[0], logicalExp, allObjs[index])
|
|
158
|
+
if index < len(allObjs) - 1:
|
|
159
|
+
logicalExp[0] = logicalExp[0] + " or "
|
|
160
|
+
if temp == logicalExp[0]:
|
|
161
|
+
logicalExp[0] = logicalExp[0] + " False "
|
|
162
|
+
|
|
163
|
+
logicalExp[0] = logicalExp[0] + " )"
|
|
164
|
+
def handleCollect(self,tree,allObjs,logicalExp):
|
|
165
|
+
"""The handleCollect function handles collect construct.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
tree: Tree that is constructed using OCL Parser
|
|
169
|
+
allObjs: all objects from object model
|
|
170
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
171
|
+
"""
|
|
172
|
+
self.allObjSat.append([])
|
|
173
|
+
for obj in allObjs:
|
|
174
|
+
expression = [""]
|
|
175
|
+
if len(tree.get_body) > 0:
|
|
176
|
+
self.update_logical_exp(tree.get_body[0], expression, obj)
|
|
177
|
+
self.preprocessLogicalExp(expression)
|
|
178
|
+
if eval (expression[0]) == True:
|
|
179
|
+
self.allObjSat[-1].append(obj)
|
|
180
|
+
|
|
181
|
+
def verifyBody (self,tree, obj,logicalExp,source,allObjs= None):
|
|
182
|
+
"""The verifyBody function verifies the body of different constructs (e.g., forAll, exists).
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
tree: Tree that is constructed using OCL Parser
|
|
186
|
+
obj: one object from object model
|
|
187
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
188
|
+
source: source of the obj in CSTree
|
|
189
|
+
"""
|
|
190
|
+
expressionType = tree.name
|
|
191
|
+
if allObjs is None:
|
|
192
|
+
allObjs = self.checkInObj(obj,source)
|
|
193
|
+
if allObjs is None:
|
|
194
|
+
allObjs = self.checkInLinkEnds(obj, source)
|
|
195
|
+
if source.name == "ALLInstances":
|
|
196
|
+
className = source.source.name
|
|
197
|
+
allObjs = self.getValidObjects(className, self.om)
|
|
198
|
+
|
|
199
|
+
if expressionType == "forAll":
|
|
200
|
+
self.handleForAll(tree, allObjs, logicalExp)
|
|
201
|
+
elif expressionType == "exists":
|
|
202
|
+
self.handleExists(tree,allObjs,logicalExp)
|
|
203
|
+
elif expressionType == "collect":
|
|
204
|
+
self.handleCollect(tree,allObjs,logicalExp)
|
|
205
|
+
elif expressionType == "select":
|
|
206
|
+
self.handleCollect(tree,allObjs,logicalExp)
|
|
207
|
+
pass
|
|
208
|
+
def getID(self,slots):
|
|
209
|
+
"""The getID function gets the ID attribute from the Object.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
slots: slots of the object
|
|
213
|
+
"""
|
|
214
|
+
for s in slots:
|
|
215
|
+
if s.get_attribute.is_id:
|
|
216
|
+
return s
|
|
217
|
+
def addToExp(self, item, logicalExp):
|
|
218
|
+
"""The addToExp function adds the item to logicalExpression.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
item: item to add in Logical Expression
|
|
222
|
+
logicalExp: logical Expression
|
|
223
|
+
"""
|
|
224
|
+
if logicalExp[0] == "":
|
|
225
|
+
logicalExp[0] = logicalExp[0] + item
|
|
226
|
+
else:
|
|
227
|
+
logicalExp[0] = logicalExp[0] + " and "+item
|
|
228
|
+
def handleSize(self, tree,obj,logicalExp):
|
|
229
|
+
"""The handleSize function handles the size construct.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
tree: Tree that is constructed using OCL Parser
|
|
233
|
+
obj: one object from object model
|
|
234
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
235
|
+
"""
|
|
236
|
+
self.checkAndAdd(logicalExp , " and ")
|
|
237
|
+
logicalExp[0] = logicalExp[0] + "("
|
|
238
|
+
if isinstance(tree.source, Property):
|
|
239
|
+
allObjs = self.checkInObj(obj, tree.source)
|
|
240
|
+
if allObjs is None:
|
|
241
|
+
allObjs = self.checkInLinkEnds(obj, tree.source)
|
|
242
|
+
logicalExp[0] = logicalExp[0] + str(len(allObjs))
|
|
243
|
+
for arg in tree.arguments:
|
|
244
|
+
|
|
245
|
+
if isinstance(arg, IntegerLiteralExpression) or isinstance(arg,
|
|
246
|
+
RealLiteralExpression) or isinstance(arg,
|
|
247
|
+
BooleanLiteralExpression):
|
|
248
|
+
logicalExp[0] = logicalExp[0] + str(arg.value)
|
|
249
|
+
else:
|
|
250
|
+
logicalExp[0] = logicalExp[0]+str(arg)
|
|
251
|
+
elif isinstance(tree.source,LoopExp):
|
|
252
|
+
if tree.source.source is not None:
|
|
253
|
+
source = tree.source.source
|
|
254
|
+
if source.name == "ALLInstances":
|
|
255
|
+
className = source.source.name
|
|
256
|
+
allObjs = self.getValidObjects(className, self.om)
|
|
257
|
+
else:
|
|
258
|
+
allObjs = self.checkInObj(obj, source)
|
|
259
|
+
if allObjs is None:
|
|
260
|
+
allObjs = self.checkInLinkEnds(obj, source)
|
|
261
|
+
self.handleCollect(tree.source,allObjs,logicalExp)
|
|
262
|
+
if len(self.allObjSat)>0:
|
|
263
|
+
logicalExp[0] = logicalExp[0] +str(len(self.allObjSat[-1]))
|
|
264
|
+
self.allObjSat.pop()
|
|
265
|
+
for arg in tree.arguments:
|
|
266
|
+
|
|
267
|
+
if isinstance(arg, IntegerLiteralExpression) or isinstance(arg,
|
|
268
|
+
RealLiteralExpression) or isinstance(arg,
|
|
269
|
+
BooleanLiteralExpression):
|
|
270
|
+
logicalExp[0] = logicalExp[0] + str(arg.value)
|
|
271
|
+
else:
|
|
272
|
+
logicalExp[0] = logicalExp[0]+str(arg)
|
|
273
|
+
logicalExp[0] = logicalExp[0] + ")"
|
|
274
|
+
|
|
275
|
+
def handleIFExp(self,tree, obj, logicalExp):
|
|
276
|
+
"""The handleIFExp function handles the If construct.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
tree: Tree that is constructed using OCL Parser
|
|
280
|
+
obj: one object from object model
|
|
281
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
tempExp = [""]
|
|
285
|
+
self.update_logical_exp(tree.ifCondition, tempExp, obj)
|
|
286
|
+
self.preprocessLogicalExp(tempExp)
|
|
287
|
+
self.checkAndAdd(logicalExp," and ")
|
|
288
|
+
if eval(tempExp[0])==True:
|
|
289
|
+
self.update_logical_exp(tree.thenExpression, logicalExp, obj)
|
|
290
|
+
else:
|
|
291
|
+
self.update_logical_exp(tree.elseCondition, logicalExp, obj)
|
|
292
|
+
|
|
293
|
+
pass
|
|
294
|
+
def handleOCLIsTypeOf(self, tree,obj,logicalExp):
|
|
295
|
+
"""The handleOCLIsTypeOf function handles the OCLIsTypeOf construct.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
tree: Tree that is constructed using OCL Parser
|
|
299
|
+
obj: one object from object model
|
|
300
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
301
|
+
"""
|
|
302
|
+
if isinstance(tree.source, Property):
|
|
303
|
+
sourceType = tree.source.type.name
|
|
304
|
+
self.checkAndAdd(logicalExp, " and ")
|
|
305
|
+
for index in range(len(tree.arguments)):
|
|
306
|
+
arg = tree.arguments[index].name
|
|
307
|
+
if arg == "String":
|
|
308
|
+
logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"str\""
|
|
309
|
+
elif arg == "Integer":
|
|
310
|
+
logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"int\""
|
|
311
|
+
elif arg == "Boolean":
|
|
312
|
+
logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"bool\""
|
|
313
|
+
elif arg == "Real":
|
|
314
|
+
logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"float\""
|
|
315
|
+
if index >0 and index < len(tree.arguments)-1:
|
|
316
|
+
self.checkAndAdd(logicalExp, "and")
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def update_logical_exp(self, tree, logicalExp, obj):
|
|
320
|
+
"""The update_logical_exp function updates the logical expression.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
tree: Tree that is constructed using OCL Parser
|
|
324
|
+
obj: one object from object model
|
|
325
|
+
logicalExp: Expression to evaluate at the end to get the result
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
if isinstance(tree,PropertyCallExpression):
|
|
329
|
+
if tree.source == None:
|
|
330
|
+
pass
|
|
331
|
+
|
|
332
|
+
if isinstance(tree, LoopExp):#forAll, select,..etc
|
|
333
|
+
source = tree.source
|
|
334
|
+
if obj.classifier.name == self.roothandler.get_context_name():
|
|
335
|
+
self.verifyBody(tree,obj,logicalExp,source)
|
|
336
|
+
if isinstance(tree, IfExp):
|
|
337
|
+
self.handleIFExp(tree, obj, logicalExp)
|
|
338
|
+
if isinstance(tree, OperationCallExpression):
|
|
339
|
+
|
|
340
|
+
if tree.name == "EXCLUDES":
|
|
341
|
+
self.handleExcludes(tree, obj, logicalExp)
|
|
342
|
+
elif tree.name == "INCLUDES":
|
|
343
|
+
self.handleIncludes(tree, obj, logicalExp)
|
|
344
|
+
elif tree.name == "Size":
|
|
345
|
+
self.handleSize(tree,obj,logicalExp)
|
|
346
|
+
self.checkAndAdd(logicalExp, " and ")
|
|
347
|
+
elif tree.name == "OCLISTYPEOF":
|
|
348
|
+
self.handleOCLIsTypeOf(tree,obj,logicalExp)
|
|
349
|
+
elif tree.name == "ALLInstances":
|
|
350
|
+
pass #handled elsewhere
|
|
351
|
+
else:
|
|
352
|
+
args = tree.arguments
|
|
353
|
+
for arg in args:
|
|
354
|
+
if hasattr(arg,'name'):
|
|
355
|
+
if isinstance(arg,IntegerLiteralExpression) or isinstance(arg,RealLiteralExpression) or isinstance(arg,BooleanLiteralExpression):
|
|
356
|
+
logicalExp[0] = logicalExp[0] + str(arg.value)
|
|
357
|
+
elif (isinstance(arg,StringLiteralExpression)):
|
|
358
|
+
logicalExp[0] = logicalExp[0] + '"'+str(arg.value)+'"'
|
|
359
|
+
else:
|
|
360
|
+
logicalExp[0]= logicalExp[0] + str(self.get_value(arg.name,obj))
|
|
361
|
+
else:
|
|
362
|
+
if str(arg).lower() == "and" or str(arg).lower() =='or':
|
|
363
|
+
self.checkAndAdd(logicalExp[0],str(arg))
|
|
364
|
+
else:
|
|
365
|
+
logicalExp[0] = logicalExp[0] +" " +str(arg)+ " "
|
|
366
|
+
if tree.referredOperation is not None and( tree.referredOperation.get_infix_operator() == "and" or tree.referredOperation.get_infix_operator() == "or"):
|
|
367
|
+
self.checkAndAdd(logicalExp[0],tree.referredOperation.get_infix_operator())
|
|
368
|
+
if hasattr(tree, "source"):
|
|
369
|
+
if tree.source is not None:
|
|
370
|
+
self.update_logical_exp(tree.source, logicalExp, obj)
|
|
371
|
+
def checkAndAdd(self, logicalExpTemplate, toAdd):
|
|
372
|
+
"""The checkAndAdd function checks and updates the logical expression.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
toAdd: item to add in the logical expression
|
|
376
|
+
logicalExpTemplate: Expression to evaluate at the end to get the result
|
|
377
|
+
"""
|
|
378
|
+
if len(logicalExpTemplate[0])>5:
|
|
379
|
+
if toAdd == " and ":
|
|
380
|
+
if logicalExpTemplate[0][-5:] != " and ":
|
|
381
|
+
logicalExpTemplate[0] = logicalExpTemplate[0]+toAdd
|
|
382
|
+
elif toAdd == " and ":
|
|
383
|
+
if len(logicalExpTemplate[0]) > 0 and len(logicalExpTemplate[0])<=5:
|
|
384
|
+
logicalExpTemplate[0] = logicalExpTemplate[0] + toAdd
|
|
385
|
+
|
|
386
|
+
if len(logicalExpTemplate[0]) > 4:
|
|
387
|
+
if toAdd == " or ":
|
|
388
|
+
if logicalExpTemplate[0][-4:] != " or ":
|
|
389
|
+
logicalExpTemplate[0] = logicalExpTemplate[0] + toAdd
|
|
390
|
+
|
|
391
|
+
def valid_object(self,contextName, object):
|
|
392
|
+
"""the valid_object verifies if the current object is the one constraint is applied on
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
contextName: name of the context of OCL constraint
|
|
396
|
+
Object: Object from object model to check
|
|
397
|
+
|
|
398
|
+
"""
|
|
399
|
+
idSlot = self.getID(object.slots)
|
|
400
|
+
if idSlot.get_attribute.name == contextName:
|
|
401
|
+
return True
|
|
402
|
+
return False
|
|
403
|
+
def getValidObjects(self,contextName, objs):
|
|
404
|
+
"""the getValidObjects returns all the objects the constraint should be applied on
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
contextName: name of the context of OCL constraint
|
|
408
|
+
objs: all objects from object model
|
|
409
|
+
|
|
410
|
+
"""
|
|
411
|
+
|
|
412
|
+
toRet = []
|
|
413
|
+
for obj in objs.instances:
|
|
414
|
+
# if self.valid_object(contextName,obj):
|
|
415
|
+
if obj.classifier.name == contextName:
|
|
416
|
+
toRet.append(obj)
|
|
417
|
+
return toRet
|
|
418
|
+
|
|
419
|
+
def preprocessLogicalExp(self,exp):
|
|
420
|
+
"""the preprocessLogicalExp function preprocesses before evaluating to correct the syntax.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
exp: logical expression to be evaluated
|
|
424
|
+
|
|
425
|
+
"""
|
|
426
|
+
exp[0]=exp[0].replace(' = ', ' == ').replace('<>', ' != ')
|
|
427
|
+
if len(exp[0])>5:
|
|
428
|
+
if exp[0][-5:] == " and ":
|
|
429
|
+
exp[0] = exp[0][:-5]
|
|
430
|
+
if len(exp[0]) > 4:
|
|
431
|
+
if exp[0][-4:] == " or ":
|
|
432
|
+
exp[0] = exp[0][:-4]
|
|
433
|
+
def evaluate(self, rootHandler,objectModel):
|
|
434
|
+
"""the evaluate function is the main function to be called for evaluating any contraint.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
rootHandler: Object of class RootHandler that handles the tree
|
|
438
|
+
objectModel: Object Model from BUML models
|
|
439
|
+
"""
|
|
440
|
+
self.om = objectModel
|
|
441
|
+
self.roothandler = rootHandler
|
|
442
|
+
|
|
443
|
+
objs = self.getValidObjects(self.roothandler.get_context_name(),self.om)
|
|
444
|
+
tree = rootHandler.get_root()
|
|
445
|
+
logicalExpTemplate= [""]
|
|
446
|
+
for i in range(len(objs)):
|
|
447
|
+
self.update_logical_exp(tree, logicalExpTemplate, objs[i])
|
|
448
|
+
if len(objs)>1 and i < len(objs) -1:
|
|
449
|
+
self.checkAndAdd(logicalExpTemplate," and ")
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
if self.debug:
|
|
453
|
+
print("Logical Expression")
|
|
454
|
+
print(logicalExpTemplate[0])
|
|
455
|
+
self.preprocessLogicalExp(logicalExpTemplate)
|
|
456
|
+
return eval(logicalExpTemplate[0])
|
bocl/OCLWrapper.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from bocl.Evaluator import Evaluator
|
|
2
|
+
from besser.BUML.notations.ocl.BOCLLexer import BOCLLexer
|
|
3
|
+
from besser.BUML.notations.ocl.BOCLParser import BOCLParser
|
|
4
|
+
from besser.BUML.notations.ocl.BOCLListener import BOCLListener
|
|
5
|
+
from besser.BUML.notations.ocl.RootHandler import Root_Handler
|
|
6
|
+
from antlr4 import *
|
|
7
|
+
|
|
8
|
+
class OCLWrapper:
|
|
9
|
+
"""The OCLWrapper class is the wrapper around the evaluator class to prepare the construct needed by evaluator.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
dm: Domain model in BUML
|
|
13
|
+
om: object model in BUML
|
|
14
|
+
Attributes:
|
|
15
|
+
dm: Domain model in BUML
|
|
16
|
+
om: object model in BUML
|
|
17
|
+
"""
|
|
18
|
+
def __init__(self,dm,om):
|
|
19
|
+
self.dm = dm
|
|
20
|
+
self.om = om
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def evaluate(self,ocl):
|
|
24
|
+
"""the evaluate function takes the OCL constraint and evaluate using evaluator
|
|
25
|
+
Args:
|
|
26
|
+
ocl: Object of OCL class that constaints the OCL expression and context class
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# self.preprocess(ocl.expression)
|
|
30
|
+
input_stream = InputStream(ocl.expression)
|
|
31
|
+
rootHandler = Root_Handler(ocl,self.dm,self.om)
|
|
32
|
+
lexer = BOCLLexer(input_stream)
|
|
33
|
+
stream = CommonTokenStream(lexer)
|
|
34
|
+
parser = BOCLParser(stream)
|
|
35
|
+
tree = parser.oclFile()
|
|
36
|
+
listener = BOCLListener(rootHandler)
|
|
37
|
+
walker = ParseTreeWalker()
|
|
38
|
+
walker.walk(listener,tree)
|
|
39
|
+
|
|
40
|
+
eval = Evaluator()
|
|
41
|
+
|
|
42
|
+
return eval.evaluate(rootHandler, self.om)
|
|
43
|
+
|
|
44
|
+
pass
|
bocl/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: bocl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: BOCL
|
|
5
|
+
Author: Luxembourg Institute of Science and Technology
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://bocl-interpreter.readthedocs.io/en/latest/
|
|
8
|
+
Project-URL: Source Code, https://github.com/BESSER-PEARL/BOCL-Interpreter
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/BESSER-PEARL/BOCL-Interpreter/issues
|
|
10
|
+
Keywords: uml,code generation,python
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: Arpeggio ==2.0.2
|
|
14
|
+
Requires-Dist: colorama ==0.4.6
|
|
15
|
+
Requires-Dist: iniconfig ==2.0.0
|
|
16
|
+
Requires-Dist: packaging
|
|
17
|
+
Requires-Dist: pluggy ==1.2.0
|
|
18
|
+
Requires-Dist: pytest ==7.4.0
|
|
19
|
+
Requires-Dist: setuptools ==68.2.2
|
|
20
|
+
Requires-Dist: textX ==3.1.1
|
|
21
|
+
Requires-Dist: jinja2 ==3.1.2
|
|
22
|
+
Requires-Dist: antlr4-python3-runtime ==4.13.1
|
|
23
|
+
Requires-Dist: requests ==2.31.0
|
|
24
|
+
Requires-Dist: fastapi ==0.110.0
|
|
25
|
+
Requires-Dist: pydantic ==2.6.3
|
|
26
|
+
Requires-Dist: uvicorn ==0.28.0
|
|
27
|
+
Requires-Dist: besser
|
|
28
|
+
|
|
29
|
+
# BESSER Object Constraint Language Interpreter
|
|
30
|
+
|
|
31
|
+
**BOCL** is a tool designed to parse and evaluate the OCL constraints designed on **BESSER** models.
|
|
32
|
+
It is a python based implementation, that utilises parser and listener generated with the ANTLR4 grammer.
|
|
33
|
+
|
|
34
|
+
**BESSER** is a low-modeling low-code open-source platform built on top
|
|
35
|
+
of our Python-based personal interpretation of a "Universal Modeling Language"
|
|
36
|
+
|
|
37
|
+
## Basic Installation
|
|
38
|
+
We have tested BOCL with python 3.9+. We recommend creating a virtual environment (e.g. `venv <https://docs.python.org/3/tutorial/venv.html>`_,
|
|
39
|
+
`conda <https://docs.conda.io/en/latest/>`_).
|
|
40
|
+
|
|
41
|
+
Dependencies
|
|
42
|
+
************
|
|
43
|
+
BOCL depends on BESSER for parsing the OCL constraints and providing a concrete syntax tree (CST) to evaluation. The latest stable version of BESSER is available in the Python Package Index (PyPi) and can be installed using
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
$ pip install besser
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# BOCL Installation
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Stable version of BOCL interpreter can be installed via
|
|
53
|
+
|
|
54
|
+
$ pip install bocl
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Building From Source
|
|
59
|
+
|
|
60
|
+
To obtain the full code, including examples and tests, you can clone the git repository.
|
|
61
|
+
|
|
62
|
+
$ git clone https://github.com/BESSER-PEARL/BOCL-Interpreter
|
|
63
|
+
$ cd BOCL-Interpreter
|
|
64
|
+
|
|
65
|
+
To install the reqs please run the following commands in virtual environment (recommended)
|
|
66
|
+
|
|
67
|
+
$ pip install -r requirements.txt
|
|
68
|
+
|
|
69
|
+
## Contributing
|
|
70
|
+
|
|
71
|
+
We encourage contributions from the community and any comment is welcome!
|
|
72
|
+
|
|
73
|
+
If you are interested in contributing to this project, please read the [CONTRIBUTING.md](CONTRIBUTING.md) file.
|
|
74
|
+
|
|
75
|
+
## Code of Conduct
|
|
76
|
+
|
|
77
|
+
At BESSER, our commitment is centered on establishing and maintaining development environments that are welcoming, inclusive, safe and free from all forms of harassment. All participants are expected to voluntarily respect and support our [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
78
|
+
|
|
79
|
+
## Governance
|
|
80
|
+
|
|
81
|
+
The development of this project follows the governance rules described in the [GOVERNANCE.md](GOVERNANCE.md) document.
|
|
82
|
+
|
|
83
|
+
## Contact
|
|
84
|
+
You can reach us at: [info@besser-pearl.org](mailto:info@besser-pearl-org)
|
|
85
|
+
|
|
86
|
+
Website: https://besser-pearl.github.io/teampage/
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
This project is licensed under the [MIT](https://mit-license.org/) license.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
bocl/Evaluator.py,sha256=RtkRAiQNBxf1yvpLEoPWPGrNJ8j3rxcPPjzQ2i3cnFE,20231
|
|
2
|
+
bocl/OCLWrapper.py,sha256=wu2YsEca6LlmClXVaeBeS4oIElBFALROSyTXrFtjz0o,1462
|
|
3
|
+
bocl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
models/library_object.py,sha256=kuKXMhhuWAiNqHxEXWMnJhMSJTwt2bT7rVDiG8FRCTU,10452
|
|
6
|
+
models/researcher_object.py,sha256=cDpJdGu0hzmrhdZUF5dfO1P7UdjVNaBOiwilMVH8Rvk,8842
|
|
7
|
+
bocl-0.1.0.dist-info/METADATA,sha256=3SAWHY0HP9uBgWPIMuf7CJK7xvBJFC7RTumBoVDZP68,3242
|
|
8
|
+
bocl-0.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
9
|
+
bocl-0.1.0.dist-info/top_level.txt,sha256=d5ZFSziu4ds27nZoZcgWLHKMJsID-7w5M7grHxSYjoE,12
|
|
10
|
+
bocl-0.1.0.dist-info/RECORD,,
|
models/__init__.py
ADDED
|
File without changes
|
models/library_object.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
|
|
2
|
+
from besser.BUML.metamodel.structural import DomainModel, Class, Property, \
|
|
3
|
+
PrimitiveDataType, Multiplicity, BinaryAssociation,Constraint
|
|
4
|
+
from besser.BUML.metamodel.object import *
|
|
5
|
+
import datetime
|
|
6
|
+
|
|
7
|
+
#############################################
|
|
8
|
+
# Library - structural model definition #
|
|
9
|
+
#############################################
|
|
10
|
+
|
|
11
|
+
# Primitive DataTypes
|
|
12
|
+
t_int: PrimitiveDataType = PrimitiveDataType("int")
|
|
13
|
+
t_str: PrimitiveDataType = PrimitiveDataType("str")
|
|
14
|
+
t_date: PrimitiveDataType = PrimitiveDataType("date")
|
|
15
|
+
|
|
16
|
+
# Library attributes definition
|
|
17
|
+
library_name: Property = Property(name="name", type=t_str)
|
|
18
|
+
address: Property = Property(name="address", type=t_str)
|
|
19
|
+
# Library class definition
|
|
20
|
+
library: Class = Class (name="Library", attributes={library_name, address})
|
|
21
|
+
|
|
22
|
+
# Book attributes definition
|
|
23
|
+
title: Property = Property(name="title", type=t_str)
|
|
24
|
+
pages: Property = Property(name="pages", type=t_int)
|
|
25
|
+
release: Property = Property(name="release", type=t_date)
|
|
26
|
+
# Book class definition
|
|
27
|
+
book: Class = Class (name="Book", attributes={title, pages, release})
|
|
28
|
+
|
|
29
|
+
# Author attributes definition
|
|
30
|
+
author_name: Property = Property(name="name", type=t_str)
|
|
31
|
+
email: Property = Property(name="email", type=t_str)
|
|
32
|
+
# Author class definition
|
|
33
|
+
author: Class = Class (name="Author", attributes={author_name, email})
|
|
34
|
+
|
|
35
|
+
# Library-Book association definition
|
|
36
|
+
located_in: Property = Property(name="locatedIn",type=library, multiplicity=Multiplicity(1, 1))
|
|
37
|
+
has: Property = Property(name="has", type=book, multiplicity=Multiplicity(0, "*"))
|
|
38
|
+
lib_book_association: BinaryAssociation = BinaryAssociation(name="lib_book_assoc", ends={located_in, has})
|
|
39
|
+
|
|
40
|
+
# Book-Author association definition
|
|
41
|
+
publishes: Property = Property(name="publishes", type=book, multiplicity=Multiplicity(0, "*"))
|
|
42
|
+
writed_by: Property = Property(name="writedBy", type=author, multiplicity=Multiplicity(1, "*"))
|
|
43
|
+
book_author_association: BinaryAssociation = BinaryAssociation(name="book_author_assoc", ends={writed_by, publishes})
|
|
44
|
+
|
|
45
|
+
constraintPageNumber: Constraint = Constraint(name = "libraryPageNumber",context=library,expression="context Library inv inv1: self.has ->forAll(b:Book|b.pages>0)",language="OCL")
|
|
46
|
+
|
|
47
|
+
constraintBookPageNumber: Constraint = Constraint(name = "BookPageNumber",context=book,expression="context Book inv inv2: self.pages>0",language="OCL")
|
|
48
|
+
|
|
49
|
+
constraintTitleIncorrect: Constraint = Constraint(name = "BookTitleInc",context=book,expression="context Book inv inv2: self.title= 'NI')",language="OCL")
|
|
50
|
+
|
|
51
|
+
constraintTitleCorrect: Constraint = Constraint(name = "BookTitle",context=book,expression="context Book inv inv2: self.title <> 'NI'",language="OCL")
|
|
52
|
+
|
|
53
|
+
constraintLibraryExists: Constraint = Constraint(name = "LibarayExistsConst",context=library,expression="context Library inv inv3: self.has->exists( i_book : Book | i_book.pages <= 110 )",language="OCL")
|
|
54
|
+
|
|
55
|
+
# constraintLibraryExclude: Constraint = Constraint(name = "LibarayExclude",context=library,expression="context Library inv inv3: self.has->excludes()",language="OCL")
|
|
56
|
+
constraintLibrarySize: Constraint = Constraint(name = "LibaraySize",context=library,expression="context Library inv inv3: self.has->size()>1",language="OCL")
|
|
57
|
+
|
|
58
|
+
constraintLibraryCollect: Constraint = Constraint(name = "LibaryCollect",context=library,
|
|
59
|
+
expression="context Library inv inv3: self.has->collect(i_book : Book | i_book.pages <= 110)"
|
|
60
|
+
"->size()>0",language="OCL")
|
|
61
|
+
|
|
62
|
+
constraintLibraryIf: Constraint = Constraint(name ="LibaryIf", context=library,
|
|
63
|
+
expression="context Library inv inv3: if self.name <> 'NI' then self.has->exists"
|
|
64
|
+
"( i_book : Book | i_book.pages <= 110 ) else self.has->forAll(b:Book|b.pages>0)"
|
|
65
|
+
" endif", language="OCL")
|
|
66
|
+
|
|
67
|
+
constraintLibraryElse: Constraint = Constraint(name ="LibaryElse", context=library,
|
|
68
|
+
expression="context Library inv inv3: if self.name = 'NI' then self.has->exists"
|
|
69
|
+
"( i_book : Book | i_book.pages <= 110 ) else self.has->forAll(b:Book|b.pages>0)"
|
|
70
|
+
" endif", language="OCL")
|
|
71
|
+
|
|
72
|
+
constraintLibraryElseFalse: Constraint = Constraint(name ="LibaryElseFalse", context=library, expression="context Library inv inv3: if self.name = 'NI' then self.has->exists( i_book : Book | i_book.pages <= 110 ) else self.has->forAll(b:Book|b.pages<0) endif", language="OCL")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
constraintNameOCLIsTypeOf: Constraint = Constraint(name ="BookIsTypeOfStr", context=book, expression="context Book inv inv3: self.title.oclIsTypeOf(String)", language="OCL")
|
|
76
|
+
|
|
77
|
+
constraintNameOCLIsTypeOfInt: Constraint = Constraint(name ="BookIsTypeOfInt", context=book, expression="context Book inv inv3: self.pages.oclIsTypeOf(Integer)", language="OCL")
|
|
78
|
+
|
|
79
|
+
constraintNameOCLIsTypeOfIntFalse: Constraint = Constraint(name ="BookIsTypeOfIntFalse", context=book, expression="context Book inv inv3: self.pages.oclIsTypeOf(String)", language="OCL")
|
|
80
|
+
constraintifElseSize: Constraint = Constraint(name ="constraintifElseSize", context=library, expression="context Library inv inv3: if self.name <> 'NI' then self.has->exists( i_book : Book | i_book.pages <= 110 )->size()<3 else self.has->forAll(b:Book|b.pages>0) endif", language="OCL")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Domain model definition
|
|
84
|
+
library_model : DomainModel = DomainModel(name="Library model", types={library, book, author},
|
|
85
|
+
associations={lib_book_association, book_author_association},
|
|
86
|
+
constraints={
|
|
87
|
+
constraintBookPageNumber,
|
|
88
|
+
# constraintTitleIncorrect,
|
|
89
|
+
constraintPageNumber,
|
|
90
|
+
constraintTitleCorrect,
|
|
91
|
+
constraintLibraryExists,
|
|
92
|
+
constraintLibrarySize,
|
|
93
|
+
constraintLibraryCollect,
|
|
94
|
+
constraintLibraryIf,
|
|
95
|
+
constraintLibraryElse,
|
|
96
|
+
# constraintLibraryElseFalse,
|
|
97
|
+
constraintNameOCLIsTypeOf,
|
|
98
|
+
constraintNameOCLIsTypeOfInt,
|
|
99
|
+
# constraintNameOCLIsTypeOfIntFalse
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# constraints={constraintLibrarySize}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
#########################################
|
|
107
|
+
# Library - object model definition #
|
|
108
|
+
#########################################
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# Library object attributes
|
|
112
|
+
library_obj_name: AttributeLink = AttributeLink(attribute=library_name, value=DataValue(classifier=t_str, value="Library test"))
|
|
113
|
+
library_obj_address: AttributeLink = AttributeLink(attribute=address, value=DataValue(classifier=t_str, value="street 123"))
|
|
114
|
+
# Library object
|
|
115
|
+
library_obj: Object = Object(name="Library Object", classifier=library, slots=[library_obj_name, library_obj_address])
|
|
116
|
+
|
|
117
|
+
# Book object attributes
|
|
118
|
+
book_obj_name: AttributeLink = AttributeLink(attribute=title, value=DataValue(classifier=t_str, value="Book tittle"))
|
|
119
|
+
book_obj_pages: AttributeLink = AttributeLink(attribute=pages, value=DataValue(classifier=t_int, value=100))
|
|
120
|
+
book_obj_release: AttributeLink = AttributeLink(attribute=release, value=DataValue(classifier=t_date, value=datetime.datetime(2020, 3, 15)))
|
|
121
|
+
# Book object
|
|
122
|
+
book_obj: Object = Object(name="Book Object", classifier=book, slots=[book_obj_name, book_obj_pages])
|
|
123
|
+
|
|
124
|
+
# Book_2 object attributes
|
|
125
|
+
|
|
126
|
+
book_obj_name_2: AttributeLink = AttributeLink(attribute=title, value=DataValue(classifier=t_str, value="Book tittle_2"))
|
|
127
|
+
book_obj_pages_2: AttributeLink = AttributeLink(attribute=pages, value=DataValue(classifier=t_int, value=400))
|
|
128
|
+
book_obj_release_2: AttributeLink = AttributeLink(attribute=release, value=DataValue(classifier=t_date, value=datetime.datetime(2024, 3, 15)))
|
|
129
|
+
# Book object
|
|
130
|
+
book_obj_2: Object = Object(name="Book 2 Object", classifier=book, slots=[book_obj_name_2, book_obj_pages_2])
|
|
131
|
+
|
|
132
|
+
# Author object attributes
|
|
133
|
+
author_obj_name: AttributeLink = AttributeLink(attribute=author_name, value=DataValue(classifier=t_str, value="John Doe"))
|
|
134
|
+
author_obj_email: AttributeLink = AttributeLink(attribute=email, value=DataValue(classifier=t_str, value="john@doe.com"))
|
|
135
|
+
# Author object
|
|
136
|
+
author_obj: Object = Object(name="Author Object", classifier=author, slots=[author_obj_name, author_obj_email])
|
|
137
|
+
|
|
138
|
+
# Book object and Author object link
|
|
139
|
+
book_link_end1: LinkEnd = LinkEnd(name="book_end1", association_end=publishes, object=book_obj)
|
|
140
|
+
author_link_end: LinkEnd = LinkEnd(name="author_end", association_end=writed_by, object=author_obj)
|
|
141
|
+
author_book_link: Link = Link(name="author_book_link", association=book_author_association, connections=[book_link_end1,author_link_end])
|
|
142
|
+
|
|
143
|
+
# Book Library and Book object link
|
|
144
|
+
book_link_end2: LinkEnd = LinkEnd(name="book_end2", association_end=has, object=book_obj)
|
|
145
|
+
library_link_end: LinkEnd = LinkEnd(name="library_end", association_end=located_in, object=library_obj)
|
|
146
|
+
library_book_link: Link = Link(name="library_book_link", association=book_author_association, connections=[book_link_end2,library_link_end])
|
|
147
|
+
|
|
148
|
+
# Book object and Author object link
|
|
149
|
+
book_link_end2: LinkEnd = LinkEnd(name="book_end3", association_end=publishes, object=book_obj_2)
|
|
150
|
+
author_link_end2: LinkEnd = LinkEnd(name="author_end2", association_end=writed_by, object=author_obj)
|
|
151
|
+
author_book_link2: Link = Link(name="author_book_link2", association=book_author_association, connections=[book_link_end2,author_link_end2])
|
|
152
|
+
|
|
153
|
+
# Book Library and Book object link
|
|
154
|
+
book_link_end3: LinkEnd = LinkEnd(name="book_end4", association_end=has, object=book_obj_2)
|
|
155
|
+
library_link_end3: LinkEnd = LinkEnd(name="library_end3", association_end=located_in, object=library_obj)
|
|
156
|
+
library_book_link3: Link = Link(name="library_book_link3", association=book_author_association, connections=[book_link_end3,library_link_end3])
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# Object model definition
|
|
161
|
+
object_model: ObjectModel = ObjectModel(name="Object model", instances={library_obj, author_obj, book_obj,book_obj_2}, links={author_book_link, library_book_link,author_book_link2,library_book_link3})
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from besser.BUML.metamodel.structural import *
|
|
2
|
+
from besser.BUML.metamodel.object import *
|
|
3
|
+
|
|
4
|
+
#########################################
|
|
5
|
+
# Structural Model #
|
|
6
|
+
#########################################
|
|
7
|
+
|
|
8
|
+
# Primitive DataTypes
|
|
9
|
+
t_int: PrimitiveDataType = PrimitiveDataType("int")
|
|
10
|
+
t_str: PrimitiveDataType = PrimitiveDataType("str")
|
|
11
|
+
t_bool: PrimitiveDataType = PrimitiveDataType("bool")
|
|
12
|
+
|
|
13
|
+
# Paper class
|
|
14
|
+
tittle: Property = Property(name="tittle", type=t_str)
|
|
15
|
+
wordCount: Property = Property(name="wordcount", type=t_int)
|
|
16
|
+
studentPaper: Property = Property(name="studentPaper", type=t_bool)
|
|
17
|
+
paper: Class = Class(name="Paper", attributes={tittle, wordCount, studentPaper})
|
|
18
|
+
|
|
19
|
+
# Researcher class
|
|
20
|
+
name: Property = Property(name="name", type=t_str)
|
|
21
|
+
isStudent: Property = Property(name="isStudent", type=t_bool)
|
|
22
|
+
researcher: Class = Class(name="Researcher", attributes={name, isStudent})
|
|
23
|
+
|
|
24
|
+
# paper-researcher association
|
|
25
|
+
manuscript: Property = Property(name="manuscript", type=paper, multiplicity=Multiplicity(1, 1))
|
|
26
|
+
author: Property = Property(name="author", type=researcher, multiplicity=Multiplicity(1, 2))
|
|
27
|
+
writes: BinaryAssociation = BinaryAssociation(name="writes", ends={manuscript, author})
|
|
28
|
+
|
|
29
|
+
# researcher-paper association
|
|
30
|
+
submission: Property = Property(name="submission", type=paper, multiplicity=Multiplicity(1, 1))
|
|
31
|
+
referee: Property = Property(name="referee", type=researcher, multiplicity=Multiplicity(3, 3))
|
|
32
|
+
reviews: BinaryAssociation = BinaryAssociation(name="reviews", ends={submission, referee})
|
|
33
|
+
|
|
34
|
+
constraintResearcher: Constraint = Constraint(name="NoSelfReviews", context=researcher,
|
|
35
|
+
expression="context Researcher inv NoSelfReviews: self.submission->excludes(self.manuscript)", language="OCL")
|
|
36
|
+
|
|
37
|
+
constraintPaper: Constraint = Constraint(name="PaperLength", context=paper,
|
|
38
|
+
expression="context Paper inv :self.wordcount < 10000", language="OCL")
|
|
39
|
+
|
|
40
|
+
constraintAuthorsOfStudentPaper: Constraint = Constraint(name="AuthorsOfStudentPaper", context=paper,
|
|
41
|
+
expression="context Paper inv AuthorsOfStudentPaper: self.author->exists(x:Researcher | x.isStudent =True)", language="OCL")
|
|
42
|
+
|
|
43
|
+
constraintNoStudentReviewers: Constraint = Constraint(name="NoStudentReviewers", context=paper,
|
|
44
|
+
expression="context Paper inv NoStudentReviewers:self.referee->forAll(r:Researcher | r.isStudent =False)", language="OCL")
|
|
45
|
+
|
|
46
|
+
constraintLimitsOnStudentPapers: Constraint = Constraint(name="LimitsOnStudentPapers", context=paper,
|
|
47
|
+
expression="context Paper inv LimitsOnStudentPapers: Paper::allInstances()->exists(p:Paper | p.studentPaper = True) and Paper::allInstances()->select(p:Paper | p.studentPaper=True) ->size() < 5", language="OCL")
|
|
48
|
+
|
|
49
|
+
constraintPaperTitle: Constraint = Constraint(name="constraintPaperTitle", context=paper,
|
|
50
|
+
expression="context Paper inv title: self.tittle<> 'test'", language="OCL")
|
|
51
|
+
constraintResearcherName:Constraint = Constraint(name="consResearcherName", context=researcher,
|
|
52
|
+
expression="context Researcher inv title: self.name<> 'test'", language="OCL")
|
|
53
|
+
|
|
54
|
+
constraintPaperWordCountgreater: Constraint = Constraint(name="PaperLength100", context=paper,
|
|
55
|
+
expression="context Paper inv :self.wordcount > 1000", language="OCL")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Structural model
|
|
59
|
+
domain_model: DomainModel = DomainModel(name="my_model", types={paper, researcher}, associations={writes, reviews}, constraints = {
|
|
60
|
+
constraintResearcher,
|
|
61
|
+
constraintPaper,
|
|
62
|
+
constraintAuthorsOfStudentPaper,
|
|
63
|
+
constraintNoStudentReviewers,
|
|
64
|
+
constraintLimitsOnStudentPapers,
|
|
65
|
+
constraintPaperTitle,
|
|
66
|
+
constraintResearcherName,
|
|
67
|
+
constraintPaperWordCountgreater
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
###################################
|
|
72
|
+
# Object model #
|
|
73
|
+
###################################
|
|
74
|
+
|
|
75
|
+
# paper object
|
|
76
|
+
paper_name: AttributeLink = AttributeLink(value=DataValue(value="besser lowcode platform", classifier=None), attribute=tittle)
|
|
77
|
+
paper_words: AttributeLink = AttributeLink(value=DataValue(value=5000, classifier=None), attribute=wordCount)
|
|
78
|
+
paper_student_paper: AttributeLink = AttributeLink(value=DataValue(value=True, classifier=None), attribute=studentPaper)
|
|
79
|
+
paper_obj: Object = Object(name="besser paper", classifier=paper, slots=[paper_name, paper_words, paper_student_paper])
|
|
80
|
+
|
|
81
|
+
# researcher 1 object
|
|
82
|
+
r1_name: AttributeLink = AttributeLink(value=DataValue(value="Marc", classifier=None), attribute=name)
|
|
83
|
+
r1_is_student: AttributeLink = AttributeLink(value=DataValue(value=True, classifier=None), attribute=isStudent)
|
|
84
|
+
researcher_1: Object = Object(name="Marc researcher", classifier=researcher, slots=[r1_name, r1_is_student])
|
|
85
|
+
|
|
86
|
+
# researcher 2 object
|
|
87
|
+
r2_name: AttributeLink = AttributeLink(value=DataValue(value="James", classifier=None), attribute=name)
|
|
88
|
+
r2_is_student: AttributeLink = AttributeLink(value=DataValue(value=True, classifier=None), attribute=isStudent)
|
|
89
|
+
researcher_2: Object = Object(name="James researcher", classifier=researcher, slots=[r2_name, r2_is_student])
|
|
90
|
+
|
|
91
|
+
# researcher 3 object
|
|
92
|
+
r3_name: AttributeLink = AttributeLink(value=DataValue(value="Adam", classifier=None), attribute=name)
|
|
93
|
+
r3_is_student: AttributeLink = AttributeLink(value=DataValue(value=False, classifier=None), attribute=isStudent)
|
|
94
|
+
researcher_3: Object = Object(name="Adam researcher", classifier=researcher, slots=[r3_name, r3_is_student])
|
|
95
|
+
|
|
96
|
+
# researcher 4 object
|
|
97
|
+
r4_name: AttributeLink = AttributeLink(value=DataValue(value="Lola", classifier=None), attribute=name)
|
|
98
|
+
r4_is_student: AttributeLink = AttributeLink(value=DataValue(value=False, classifier=None), attribute=isStudent)
|
|
99
|
+
researcher_4: Object = Object(name="Lola researcher", classifier=researcher, slots=[r4_name, r4_is_student])
|
|
100
|
+
|
|
101
|
+
# researcher 5 object
|
|
102
|
+
r5_name: AttributeLink = AttributeLink(value=DataValue(value="Sarah", classifier=None), attribute=name)
|
|
103
|
+
r5_is_student: AttributeLink = AttributeLink(value=DataValue(value=False, classifier=None), attribute=isStudent)
|
|
104
|
+
researcher_5: Object = Object(name="Sarah researcher", classifier=researcher, slots=[r5_name, r5_is_student])
|
|
105
|
+
|
|
106
|
+
# Links definition
|
|
107
|
+
writes_1: Link = Link(name="writes", association=writes, connections=[
|
|
108
|
+
LinkEnd(name="manuscript", association_end=manuscript, object=paper_obj),
|
|
109
|
+
LinkEnd(name="author", association_end=author, object=researcher_1)
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
writes_2: Link = Link(name="writes", association=writes, connections=[
|
|
113
|
+
LinkEnd(name="researcher", association_end=manuscript, object=paper_obj),
|
|
114
|
+
LinkEnd(name="author", association_end=author, object=researcher_2)
|
|
115
|
+
])
|
|
116
|
+
|
|
117
|
+
reviews_1: Link = Link(name="writes", association=writes, connections=[
|
|
118
|
+
LinkEnd(name="submission", association_end=submission, object=paper_obj),
|
|
119
|
+
LinkEnd(name="referee", association_end=referee, object=researcher_3)
|
|
120
|
+
])
|
|
121
|
+
|
|
122
|
+
reviews_2: Link = Link(name="writes", association=writes, connections=[
|
|
123
|
+
LinkEnd(name="submission", association_end=submission, object=paper_obj),
|
|
124
|
+
LinkEnd(name="referee", association_end=referee, object=researcher_4)
|
|
125
|
+
])
|
|
126
|
+
|
|
127
|
+
reviews_3: Link = Link(name="writes", association=writes, connections=[
|
|
128
|
+
LinkEnd(name="submission", association_end=submission, object=paper_obj),
|
|
129
|
+
LinkEnd(name="referee", association_end=referee, object=researcher_5)
|
|
130
|
+
])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# object model
|
|
135
|
+
object_model: ObjectModel = ObjectModel(name="Object_model", instances={paper_obj, researcher_1, researcher_2, researcher_3, researcher_4, researcher_5},
|
|
136
|
+
links={writes_1, writes_2, reviews_1, reviews_2, reviews_3})
|