conformance_scd.alc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. include "primitives.alh"
  2. include "library.alh"
  3. include "object_operations.alh"
  4. include "constructors.alh"
  5. include "modelling.alh"
  6. Boolean function wrap_conformance(model : Element):
  7. String result
  8. result = conformance_scd(model)
  9. log("Conformance result: " + result)
  10. output("Success: " + result)
  11. if (result == "OK"):
  12. return True!
  13. else:
  14. return False!
  15. Boolean function is_direct_instance(model : Element, instance : String, type : String):
  16. // Just check whether or not the type mapping specifies the type as the type of the instance
  17. return value_eq(model["type_mapping"][instance], type)!
  18. Boolean function is_nominal_instance(model : Element, instance : String, type : String):
  19. if (bool_not(dict_in(model["metamodel"]["model"], type))):
  20. // type is not even in the specified metamodel, so this will never work
  21. return False!
  22. if (bool_and(is_edge(model["metamodel"]["model"][type]), bool_not(is_edge(model["model"][instance])))):
  23. // type is an edge, but we aren't
  24. return False!
  25. if (bool_not(dict_in(model["type_mapping"], instance))):
  26. // doesn't even have a type
  27. return False!
  28. if (value_eq(model["type_mapping"][instance], type)):
  29. return True!
  30. return is_nominal_subtype(model["metamodel"], model["type_mapping"][instance], type)!
  31. Boolean function is_nominal_subtype(metamodel : Element, subclass : String, superclass : String):
  32. if (element_eq(metamodel["model"][subclass], metamodel["model"][superclass])):
  33. return True!
  34. if (bool_not(dict_in(metamodel["model"], subclass))):
  35. return False!
  36. if (bool_not(dict_in(metamodel["model"], superclass))):
  37. return False!
  38. return set_in(get_superclasses(metamodel, subclass), superclass)!
  39. Element function precompute_cardinalities(model : Element):
  40. Integer slc
  41. Integer suc
  42. Integer tlc
  43. Integer tuc
  44. String key
  45. Element tmp_dict
  46. Element cardinalities
  47. Element keys
  48. Element metamodel
  49. Boolean optional
  50. metamodel = model["metamodel"]
  51. keys = allInstances(metamodel, "Association")
  52. cardinalities = create_node()
  53. while (0 < list_len(keys)):
  54. key = set_pop(keys)
  55. tmp_dict = create_node()
  56. slc = read_attribute(metamodel, key, "source_lower_cardinality")
  57. suc = read_attribute(metamodel, key, "source_upper_cardinality")
  58. tlc = read_attribute(metamodel, key, "target_lower_cardinality")
  59. tuc = read_attribute(metamodel, key, "target_upper_cardinality")
  60. if (element_neq(slc, read_root())):
  61. dict_add_fast(tmp_dict, "slc", slc)
  62. if (element_neq(suc, read_root())):
  63. dict_add_fast(tmp_dict, "suc", suc)
  64. if (element_neq(tlc, read_root())):
  65. dict_add_fast(tmp_dict, "tlc", tlc)
  66. if (element_neq(tuc, read_root())):
  67. dict_add_fast(tmp_dict, "tuc", tuc)
  68. if (list_len(tmp_dict) > 0):
  69. dict_add_fast(cardinalities, key, tmp_dict)
  70. keys = allInstances(metamodel, "AttributeLink")
  71. while (0 < list_len(keys)):
  72. key = set_pop(keys)
  73. tmp_dict = create_node()
  74. // Attributes always have 1 max
  75. dict_add_fast(tmp_dict, "tuc", 1)
  76. // Depending on whether it is optional or not, the cardinality is changed
  77. optional = read_attribute(metamodel, key, "optional")
  78. if (optional):
  79. dict_add_fast(tmp_dict, "tlc", 0)
  80. else:
  81. dict_add_fast(tmp_dict, "tlc", 1)
  82. if (list_len(tmp_dict) > 0):
  83. dict_add_fast(cardinalities, key, tmp_dict)
  84. return cardinalities!
  85. String function conformance_scd(model : Element):
  86. // Initialization
  87. Element keys
  88. Element metamodel
  89. Element typing
  90. String model_name
  91. String src_model
  92. String dst_model
  93. String src_metamodel
  94. String dst_metamodel
  95. Element element
  96. Element check_list
  97. String check_type
  98. Element cardinalities
  99. Element scd
  100. Integer instances
  101. String type_name
  102. Element spo_cache
  103. Element spi_cache
  104. Element constraint_function
  105. Element reverse_m
  106. Element reverse_mm
  107. log("Make reverse dictionary for model")
  108. reverse_m = make_reverse_dictionary(model["model"])
  109. log("Make reverse dictionary for metamodel")
  110. reverse_mm = make_reverse_dictionary(model["metamodel"]["model"])
  111. spo_cache = create_node()
  112. spi_cache = create_node()
  113. // Load in variables
  114. scd = import_node("models/SimpleClassDiagrams")
  115. metamodel = model["metamodel"]
  116. typing = model["type_mapping"]
  117. // Create dictionary with all associations and allowed cardinalities
  118. if (list_len(model["model"]) > 0):
  119. cardinalities = precompute_cardinalities(model)
  120. // Iterate over each element of the model, finding out whether everything is fine
  121. keys = dict_keys(model["model"])
  122. while (0 < list_len(keys)):
  123. model_name = set_pop(keys)
  124. type_name = read_type(model, model_name)
  125. element = model["model"][model_name]
  126. log("Check " + model_name)
  127. log(" : " + type_name)
  128. if (bool_not(dict_in(typing, model_name))):
  129. return "Model has no type specified: " + model_info(model, model_name)!
  130. if (bool_not(dict_in(metamodel["model"], typing[model_name]))):
  131. return "Type of element not in specified metamodel: " + model_info(model, model_name)!
  132. if (is_edge(element)):
  133. src_model = reverse_m[cast_id2s(read_edge_src(element))]
  134. dst_model = reverse_m[cast_id2s(read_edge_dst(element))]
  135. src_metamodel = reverse_mm[cast_id2s(read_edge_src(metamodel["model"][typing[model_name]]))]
  136. dst_metamodel = reverse_mm[cast_id2s(read_edge_dst(metamodel["model"][typing[model_name]]))]
  137. if (bool_not(is_nominal_instance(model, src_model, src_metamodel))):
  138. log("got: " + src_model)
  139. log("expected: " + src_metamodel)
  140. return "Source of model edge not typed by source of type: " + model_info(model, model_name)!
  141. if (bool_not(is_nominal_instance(model, dst_model, dst_metamodel))):
  142. log("got: " + dst_model)
  143. log("expected: " + dst_metamodel)
  144. return "Destination of model edge not typed by destination of type: " + model_info(model, model_name)!
  145. // Check cardinality for all of our edges
  146. //
  147. // SLC..SUC TLC..TUC
  148. // A ---------------------> B
  149. //
  150. // First the incoming, so we are at B in the above figure
  151. if (bool_not(dict_in(spo_cache, type_name))):
  152. dict_add_fast(spo_cache, type_name, selectPossibleOutgoing(metamodel, type_name, dict_keys(cardinalities)))
  153. check_list = set_copy(spo_cache[type_name])
  154. while (0 < list_len(check_list)):
  155. check_type = set_pop(check_list)
  156. if (dict_in(cardinalities, check_type)):
  157. // Cardinalities defined for this association, so check them
  158. if (bool_or(dict_in(cardinalities[check_type], "tlc"), dict_in(cardinalities[check_type], "tuc"))):
  159. instances = list_len(allOutgoingAssociationInstances(model, model_name, check_type))
  160. if (dict_in(cardinalities[check_type], "tlc")):
  161. // A lower cardinality was defined at the target
  162. if (integer_gt(cardinalities[check_type]["tlc"], instances)):
  163. String error
  164. error = (("Lower cardinality violation for outgoing edge of type " + check_type) + " at ") + model_info(model, model_name)
  165. return error!
  166. if (dict_in(cardinalities[check_type], "tuc")):
  167. // An upper cardinality was defined at the target
  168. if (integer_lt(cardinalities[check_type]["tuc"], instances)):
  169. String error
  170. error = (("Upper cardinality violation for outgoing edge of type " + check_type) + " at ") + model_info(model, model_name)
  171. return error!
  172. // Identical, but for outgoing, and thus for A in the figure
  173. if (bool_not(dict_in(spi_cache, type_name))):
  174. dict_add_fast(spi_cache, type_name, selectPossibleIncoming(metamodel, type_name, dict_keys(cardinalities)))
  175. check_list = set_copy(spi_cache[type_name])
  176. while (0 < list_len(check_list)):
  177. check_type = set_pop(check_list)
  178. if (dict_in(cardinalities, check_type)):
  179. // Cardinalities defined for this association, so check them
  180. if (bool_or(dict_in(cardinalities[check_type], "slc"), dict_in(cardinalities[check_type], "suc"))):
  181. instances = list_len(allIncomingAssociationInstances(model, model_name, check_type))
  182. if (dict_in(cardinalities[check_type], "slc")):
  183. // A lower cardinality was defined at the source
  184. if (integer_gt(cardinalities[check_type]["slc"], instances)):
  185. String error
  186. error = (("Lower cardinality violation for incoming edge of type " + check_type) + " at ") + model_info(model, model_name)
  187. return error!
  188. if (dict_in(cardinalities[check_type], "suc")):
  189. // An upper cardinality was defined at the source
  190. if (integer_lt(cardinalities[check_type]["suc"], instances)):
  191. String error
  192. error = (("Upper cardinality violation for incoming edge of type " + check_type) + " at ") + model_info(model, model_name)
  193. return error!
  194. constraint_function = read_attribute(metamodel, typing[model_name], "constraint")
  195. if (element_neq(constraint_function, read_root())):
  196. String result
  197. Element func
  198. func = get_func_AL_model(import_node(constraint_function))
  199. result = func(model, model_name)
  200. if (result != "OK"):
  201. return result!
  202. // Check multiplicities, if they are defined (optional)
  203. Element metamodel_keys
  204. String metamodel_element
  205. Element mm_model
  206. Integer attr_value
  207. mm_model = metamodel["model"]
  208. metamodel_keys = dict_keys(mm_model)
  209. while (read_nr_out(metamodel_keys) > 0):
  210. metamodel_element = set_pop(metamodel_keys)
  211. // Lower multiplicities
  212. attr_value = read_attribute(metamodel, metamodel_element, "lower_cardinality")
  213. if (element_neq(attr_value, read_root())):
  214. // We have defined a lower cardinality, so check number of instances!
  215. if (attr_value > list_len(allInstances(model, metamodel_element))):
  216. return "Lower cardinality violated for class: " + metamodel_element!
  217. // Upper multiplicities
  218. attr_value = read_attribute(metamodel, metamodel_element, "upper_cardinality")
  219. if (element_neq(attr_value, read_root())):
  220. // We have defined a lower cardinality, so check number of instances!
  221. if (attr_value < list_len(allInstances(model, metamodel_element))):
  222. return "Upper cardinality violated for class: " + metamodel_element!
  223. // Check all ComplexAttributes recursively
  224. Element all_complex_types
  225. Element complex_instances
  226. String complex_instance
  227. String complex_type
  228. String result
  229. all_complex_types = allInstances(model["metamodel"]["metamodel"], "ComplexAttribute")
  230. while (read_nr_out(all_complex_types) > 0):
  231. complex_type = set_pop(all_complex_types)
  232. complex_instances = allInstances(model, complex_type)
  233. while (read_nr_out(complex_instances) > 0):
  234. complex_instance = set_pop(complex_instances)
  235. complex_type = read_attribute(model["metamodel"], read_type(model, complex_instance), "type")
  236. result = check_location_conformance(model["model"][complex_instance], complex_type)
  237. if (result != "OK"):
  238. return ((("Complex attribute doesn't match for: " + complex_instance) + "\n Message: ") + result)!
  239. // Structure seems fine, now do global constraints
  240. Element global_constraints
  241. String constraint
  242. Element func
  243. global_constraints = allInstances(model, "GlobalConstraint")
  244. while (read_nr_out(global_constraints) > 0):
  245. constraint = set_pop(global_constraints)
  246. func = get_func_AL_model(import_node(read_attribute(model, constraint, "global_constraint")))
  247. result = func(model)
  248. if (result != "OK"):
  249. return result!
  250. return "OK"!
  251. String function check_location_conformance(instance_location : String, type_location : String):
  252. // Check whether the instance is typed by the type
  253. Element instance
  254. Element type
  255. instance = import_node(instance_location)
  256. type = import_node(type_location)
  257. if (element_neq(instance["metamodel"], type)):
  258. return "Instance not statically typed by specified metamodel"!
  259. return conformance_scd(instance)!
  260. Element function set_model_constraints(model : Element, func : Element):
  261. if (dict_in(model, "constraints")):
  262. dict_delete(model, "constraints")
  263. dict_add_fast(model, "constraints", func)
  264. return model!
  265. Element function generate_bottom_type_mapping(model : Element):
  266. Element mm
  267. mm = model["metamodel"]["model"]
  268. dict_delete(model, "type_mapping")
  269. Element tm
  270. tm = create_node()
  271. dict_add_fast(model, "type_mapping", tm)
  272. // Iterate over every element
  273. Element elem_keys
  274. String elem
  275. elem_keys = dict_keys(model["model"])
  276. while (read_nr_out(elem_keys) > 0):
  277. elem = set_pop(elem_keys)
  278. if (is_edge(model["model"][elem])):
  279. dict_add_fast(tm, elem, "Edge")
  280. else:
  281. dict_add_fast(tm, elem, "Node")
  282. return model!
  283. String function model_info(model : Element, name : String):
  284. return name!
  285. // For more detailed information
  286. String result
  287. result = ""
  288. result = (result + "\nModel name: ") + name
  289. result = (result + "\nType: ") + cast_v2s(model["type_mapping"][name])
  290. result = (result + "\nValue: ") + cast_v2s(model["model"][name])
  291. result = (result + "\nSource: ") + cast_v2s(reverseKeyLookup(model["model"], read_edge_src(model["model"][name])))
  292. result = (result + "\nDestination: ") + cast_v2s(reverseKeyLookup(model["model"], read_edge_dst(model["model"][name])))
  293. return result!