archimate 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.projectile +18 -0
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +4 -0
  6. data/.yardocs +15 -0
  7. data/Gemfile +5 -0
  8. data/Guardfile +27 -0
  9. data/LICENSE +201 -0
  10. data/README.md +94 -0
  11. data/Rakefile +29 -0
  12. data/TODOs.org +286 -0
  13. data/archimate.gemspec +51 -0
  14. data/bin/archimate +17 -0
  15. data/bin/console +13 -0
  16. data/bin/setup +8 -0
  17. data/exe/archidiff +7 -0
  18. data/exe/archidiff-summary +7 -0
  19. data/exe/archimate +7 -0
  20. data/exe/archimerge +7 -0
  21. data/exe/fmtxml +7 -0
  22. data/lib/archimate/cli/archi.rb +189 -0
  23. data/lib/archimate/cli/cleanup.rb +54 -0
  24. data/lib/archimate/cli/conflict_resolver.rb +39 -0
  25. data/lib/archimate/cli/convert.rb +39 -0
  26. data/lib/archimate/cli/diff.rb +31 -0
  27. data/lib/archimate/cli/diff_summary.rb +101 -0
  28. data/lib/archimate/cli/duper.rb +85 -0
  29. data/lib/archimate/cli/lint.rb +16 -0
  30. data/lib/archimate/cli/mapper.rb +82 -0
  31. data/lib/archimate/cli/merge.rb +49 -0
  32. data/lib/archimate/cli/merger.rb +109 -0
  33. data/lib/archimate/cli/stats.rb +42 -0
  34. data/lib/archimate/cli/svger.rb +40 -0
  35. data/lib/archimate/color.rb +53 -0
  36. data/lib/archimate/config.rb +35 -0
  37. data/lib/archimate/data_model/any_attribute.rb +13 -0
  38. data/lib/archimate/data_model/any_element.rb +15 -0
  39. data/lib/archimate/data_model/archimate_node.rb +181 -0
  40. data/lib/archimate/data_model/bounds.rb +72 -0
  41. data/lib/archimate/data_model/color.rb +47 -0
  42. data/lib/archimate/data_model/concept.rb +14 -0
  43. data/lib/archimate/data_model/concern.rb +17 -0
  44. data/lib/archimate/data_model/connection.rb +107 -0
  45. data/lib/archimate/data_model/constants.rb +82 -0
  46. data/lib/archimate/data_model/container.rb +17 -0
  47. data/lib/archimate/data_model/diagram.rb +40 -0
  48. data/lib/archimate/data_model/diffable_array.rb +213 -0
  49. data/lib/archimate/data_model/diffable_primitive.rb +83 -0
  50. data/lib/archimate/data_model/documentation.rb +14 -0
  51. data/lib/archimate/data_model/element.rb +87 -0
  52. data/lib/archimate/data_model/font.rb +54 -0
  53. data/lib/archimate/data_model/label.rb +19 -0
  54. data/lib/archimate/data_model/lang_string.rb +41 -0
  55. data/lib/archimate/data_model/location.rb +31 -0
  56. data/lib/archimate/data_model/metadata.rb +14 -0
  57. data/lib/archimate/data_model/model.rb +217 -0
  58. data/lib/archimate/data_model/modeling_note.rb +13 -0
  59. data/lib/archimate/data_model/named_referenceable.rb +14 -0
  60. data/lib/archimate/data_model/organization.rb +40 -0
  61. data/lib/archimate/data_model/property.rb +25 -0
  62. data/lib/archimate/data_model/property_definition.rb +16 -0
  63. data/lib/archimate/data_model/referenceable.rb +29 -0
  64. data/lib/archimate/data_model/relationship.rb +85 -0
  65. data/lib/archimate/data_model/schema_info.rb +18 -0
  66. data/lib/archimate/data_model/style.rb +32 -0
  67. data/lib/archimate/data_model/view.rb +12 -0
  68. data/lib/archimate/data_model/view_concept.rb +18 -0
  69. data/lib/archimate/data_model/view_node.rb +114 -0
  70. data/lib/archimate/data_model/viewpoint.rb +87 -0
  71. data/lib/archimate/data_model.rb +54 -0
  72. data/lib/archimate/diff/archimate_array_reference.rb +113 -0
  73. data/lib/archimate/diff/archimate_identified_node_reference.rb +41 -0
  74. data/lib/archimate/diff/archimate_node_attribute_reference.rb +70 -0
  75. data/lib/archimate/diff/archimate_node_reference.rb +80 -0
  76. data/lib/archimate/diff/change.rb +49 -0
  77. data/lib/archimate/diff/conflict.rb +31 -0
  78. data/lib/archimate/diff/conflicts/base_conflict.rb +53 -0
  79. data/lib/archimate/diff/conflicts/deleted_items_child_updated_conflict.rb +30 -0
  80. data/lib/archimate/diff/conflicts/deleted_items_referenced_conflict.rb +63 -0
  81. data/lib/archimate/diff/conflicts/path_conflict.rb +51 -0
  82. data/lib/archimate/diff/conflicts.rb +89 -0
  83. data/lib/archimate/diff/delete.rb +41 -0
  84. data/lib/archimate/diff/difference.rb +113 -0
  85. data/lib/archimate/diff/insert.rb +43 -0
  86. data/lib/archimate/diff/merge.rb +70 -0
  87. data/lib/archimate/diff/move.rb +51 -0
  88. data/lib/archimate/diff.rb +17 -0
  89. data/lib/archimate/export/csv_export.rb +32 -0
  90. data/lib/archimate/export/cypher.rb +171 -0
  91. data/lib/archimate/export/graph_ml.rb +131 -0
  92. data/lib/archimate/export/n_quads.rb +142 -0
  93. data/lib/archimate/file_format.rb +30 -0
  94. data/lib/archimate/file_formats/archi_file_format.rb +151 -0
  95. data/lib/archimate/file_formats/archi_file_reader.rb +252 -0
  96. data/lib/archimate/file_formats/archi_file_writer.rb +230 -0
  97. data/lib/archimate/file_formats/archimate_v2.rb +461 -0
  98. data/lib/archimate/file_formats/model_exchange_file/xml_lang_string.rb +35 -0
  99. data/lib/archimate/file_formats/model_exchange_file/xml_metadata.rb +115 -0
  100. data/lib/archimate/file_formats/model_exchange_file/xml_property_definitions.rb +28 -0
  101. data/lib/archimate/file_formats/model_exchange_file/xml_property_defs.rb +27 -0
  102. data/lib/archimate/file_formats/model_exchange_file_reader.rb +237 -0
  103. data/lib/archimate/file_formats/model_exchange_file_reader_21.rb +73 -0
  104. data/lib/archimate/file_formats/model_exchange_file_reader_30.rb +134 -0
  105. data/lib/archimate/file_formats/model_exchange_file_writer.rb +157 -0
  106. data/lib/archimate/file_formats/model_exchange_file_writer_21.rb +143 -0
  107. data/lib/archimate/file_formats/model_exchange_file_writer_30.rb +153 -0
  108. data/lib/archimate/file_formats/writer.rb +56 -0
  109. data/lib/archimate/lint/duplicate_entities.rb +121 -0
  110. data/lib/archimate/lint/linter.rb +146 -0
  111. data/lib/archimate/logging.rb +55 -0
  112. data/lib/archimate/maybe_io.rb +34 -0
  113. data/lib/archimate/progress_indicator.rb +29 -0
  114. data/lib/archimate/svg/archimate.css +232 -0
  115. data/lib/archimate/svg/child.rb +29 -0
  116. data/lib/archimate/svg/connection.rb +184 -0
  117. data/lib/archimate/svg/css_style.rb +31 -0
  118. data/lib/archimate/svg/diagram.rb +87 -0
  119. data/lib/archimate/svg/entity/and_junction.rb +14 -0
  120. data/lib/archimate/svg/entity/application_collaboration.rb +14 -0
  121. data/lib/archimate/svg/entity/application_component.rb +41 -0
  122. data/lib/archimate/svg/entity/application_event.rb +13 -0
  123. data/lib/archimate/svg/entity/application_function.rb +13 -0
  124. data/lib/archimate/svg/entity/application_interaction.rb +13 -0
  125. data/lib/archimate/svg/entity/application_interface.rb +13 -0
  126. data/lib/archimate/svg/entity/application_process.rb +13 -0
  127. data/lib/archimate/svg/entity/application_service.rb +13 -0
  128. data/lib/archimate/svg/entity/artifact.rb +39 -0
  129. data/lib/archimate/svg/entity/assessment.rb +14 -0
  130. data/lib/archimate/svg/entity/base_entity.rb +128 -0
  131. data/lib/archimate/svg/entity/business_actor.rb +14 -0
  132. data/lib/archimate/svg/entity/business_collaboration.rb +14 -0
  133. data/lib/archimate/svg/entity/business_event.rb +10 -0
  134. data/lib/archimate/svg/entity/business_function.rb +13 -0
  135. data/lib/archimate/svg/entity/business_interaction.rb +13 -0
  136. data/lib/archimate/svg/entity/business_interface.rb +13 -0
  137. data/lib/archimate/svg/entity/business_object.rb +13 -0
  138. data/lib/archimate/svg/entity/business_process.rb +13 -0
  139. data/lib/archimate/svg/entity/business_role.rb +14 -0
  140. data/lib/archimate/svg/entity/business_service.rb +13 -0
  141. data/lib/archimate/svg/entity/capability.rb +14 -0
  142. data/lib/archimate/svg/entity/communication_network.rb +14 -0
  143. data/lib/archimate/svg/entity/communication_path.rb +14 -0
  144. data/lib/archimate/svg/entity/constraint.rb +15 -0
  145. data/lib/archimate/svg/entity/contract.rb +13 -0
  146. data/lib/archimate/svg/entity/course_of_action.rb +14 -0
  147. data/lib/archimate/svg/entity/data_entity.rb +29 -0
  148. data/lib/archimate/svg/entity/data_object.rb +13 -0
  149. data/lib/archimate/svg/entity/deliverable.rb +13 -0
  150. data/lib/archimate/svg/entity/device.rb +48 -0
  151. data/lib/archimate/svg/entity/diagram_model_reference.rb +22 -0
  152. data/lib/archimate/svg/entity/diagram_object.rb +30 -0
  153. data/lib/archimate/svg/entity/distribution_network.rb +14 -0
  154. data/lib/archimate/svg/entity/driver.rb +14 -0
  155. data/lib/archimate/svg/entity/equipment.rb +16 -0
  156. data/lib/archimate/svg/entity/event_entity.rb +36 -0
  157. data/lib/archimate/svg/entity/facility.rb +16 -0
  158. data/lib/archimate/svg/entity/function_entity.rb +14 -0
  159. data/lib/archimate/svg/entity/gap.rb +21 -0
  160. data/lib/archimate/svg/entity/goal.rb +14 -0
  161. data/lib/archimate/svg/entity/group.rb +24 -0
  162. data/lib/archimate/svg/entity/implementation_event.rb +13 -0
  163. data/lib/archimate/svg/entity/infrastructure_function.rb +13 -0
  164. data/lib/archimate/svg/entity/infrastructure_interface.rb +13 -0
  165. data/lib/archimate/svg/entity/infrastructure_service.rb +13 -0
  166. data/lib/archimate/svg/entity/interaction_entity.rb +14 -0
  167. data/lib/archimate/svg/entity/interface_entity.rb +35 -0
  168. data/lib/archimate/svg/entity/junction.rb +19 -0
  169. data/lib/archimate/svg/entity/location.rb +14 -0
  170. data/lib/archimate/svg/entity/material.rb +14 -0
  171. data/lib/archimate/svg/entity/meaning.rb +40 -0
  172. data/lib/archimate/svg/entity/motivation_entity.rb +40 -0
  173. data/lib/archimate/svg/entity/network.rb +14 -0
  174. data/lib/archimate/svg/entity/node.rb +33 -0
  175. data/lib/archimate/svg/entity/node_shape.rb +64 -0
  176. data/lib/archimate/svg/entity/note.rb +30 -0
  177. data/lib/archimate/svg/entity/or_junction.rb +14 -0
  178. data/lib/archimate/svg/entity/outcome.rb +15 -0
  179. data/lib/archimate/svg/entity/path.rb +14 -0
  180. data/lib/archimate/svg/entity/plateau.rb +15 -0
  181. data/lib/archimate/svg/entity/principle.rb +15 -0
  182. data/lib/archimate/svg/entity/process_entity.rb +63 -0
  183. data/lib/archimate/svg/entity/product.rb +20 -0
  184. data/lib/archimate/svg/entity/rect.rb +13 -0
  185. data/lib/archimate/svg/entity/rect_entity.rb +25 -0
  186. data/lib/archimate/svg/entity/representation.rb +31 -0
  187. data/lib/archimate/svg/entity/requirement.rb +15 -0
  188. data/lib/archimate/svg/entity/resource.rb +14 -0
  189. data/lib/archimate/svg/entity/rounded_rect_entity.rb +23 -0
  190. data/lib/archimate/svg/entity/service_entity.rb +51 -0
  191. data/lib/archimate/svg/entity/sketch_model_sticky.rb +14 -0
  192. data/lib/archimate/svg/entity/stakeholder.rb +15 -0
  193. data/lib/archimate/svg/entity/system_software.rb +14 -0
  194. data/lib/archimate/svg/entity/technology_collaboration.rb +14 -0
  195. data/lib/archimate/svg/entity/technology_event.rb +13 -0
  196. data/lib/archimate/svg/entity/technology_function.rb +13 -0
  197. data/lib/archimate/svg/entity/technology_interaction.rb +13 -0
  198. data/lib/archimate/svg/entity/technology_interface.rb +13 -0
  199. data/lib/archimate/svg/entity/technology_process.rb +13 -0
  200. data/lib/archimate/svg/entity/technology_service.rb +13 -0
  201. data/lib/archimate/svg/entity/value.rb +27 -0
  202. data/lib/archimate/svg/entity/work_package.rb +14 -0
  203. data/lib/archimate/svg/entity.rb +93 -0
  204. data/lib/archimate/svg/entity_factory.rb +17 -0
  205. data/lib/archimate/svg/extents.rb +27 -0
  206. data/lib/archimate/svg/point.rb +7 -0
  207. data/lib/archimate/svg/svg_template.rb +27 -0
  208. data/lib/archimate/svg/svg_template.svg.erb +169 -0
  209. data/lib/archimate/version.rb +4 -0
  210. data/lib/archimate.rb +114 -0
  211. metadata +623 -0
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+ require "nokogiri"
3
+
4
+ module Archimate
5
+ module FileFormats
6
+ # Archimate version 2.1 Model Exchange Format Writer
7
+ class ModelExchangeFileWriter21 < ModelExchangeFileWriter
8
+ def initialize(model)
9
+ super
10
+ end
11
+
12
+ def model_attrs
13
+ model_namespaces.merge(
14
+ "xsi:schemaLocation" => model.schema_locations.join(" "),
15
+ "identifier" => identifier(model.id)
16
+ )
17
+ end
18
+
19
+ def serialize_model(xml)
20
+ xml.model(model_attrs) do
21
+ ModelExchangeFile::XmlMetadata.new(model.metadata).serialize(xml)
22
+ ModelExchangeFile::XmlLangString.new(model.name, :name).serialize(xml)
23
+ ModelExchangeFile::XmlLangString.new(model.documentation, :documentation).serialize(xml)
24
+ serialize_properties(xml, model)
25
+ serialize_elements(xml)
26
+ serialize_relationships(xml)
27
+ serialize_organization_root(xml, model.organizations)
28
+ serialize_property_defs(xml)
29
+ serialize_views(xml)
30
+ end
31
+ end
32
+
33
+ def serialize_property_defs(xml)
34
+ return if model.property_definitions.empty?
35
+ ModelExchangeFile::XmlPropertyDefs.new(model.property_definitions).serialize(xml)
36
+ end
37
+
38
+ def serialize_label(xml, str)
39
+ return if str.nil? || str.strip.empty?
40
+ name_attrs = str.lang && !str.lang.empty? ? {"xml:lang" => str.lang} : {}
41
+ xml.label(name_attrs) { xml.text text_proc(str) }
42
+ end
43
+
44
+ def serialize_organization_root(xml, organizations)
45
+ return unless organizations && organizations.size > 0
46
+ xml.organization do
47
+ serialize(xml, organizations)
48
+ end
49
+ end
50
+
51
+ def serialize_item(xml, item)
52
+ xml.item(identifierref: identifier(item))
53
+ end
54
+
55
+ def serialize_property(xml, property)
56
+ xml.property(identifierref: property.property_definition.id) do
57
+ ModelExchangeFile::XmlLangString.new(property.value, :value).serialize(xml)
58
+ end
59
+ end
60
+
61
+ def serialize_views(xml)
62
+ return if model.diagrams.empty?
63
+ xml.views do
64
+ serialize(xml, model.diagrams)
65
+ end
66
+ end
67
+
68
+ def serialize_diagram(xml, diagram)
69
+ xml.view(
70
+ remove_nil_values(
71
+ identifier: identifier(diagram.id),
72
+ viewpoint: diagram.viewpoint_type,
73
+ "xsi:type": diagram.type
74
+ )
75
+ ) do
76
+ elementbase(xml, diagram)
77
+ serialize(xml, diagram.nodes)
78
+ serialize(xml, diagram.connections)
79
+ end
80
+ end
81
+
82
+ def view_node_attrs(view_node, x_offset = 0, y_offset = 0)
83
+ attrs = {
84
+ identifier: identifier(view_node.id),
85
+ elementref: nil,
86
+ x: view_node.bounds ? (view_node.bounds&.x + x_offset).round : nil,
87
+ y: view_node.bounds ? (view_node.bounds&.y + y_offset).round : nil,
88
+ w: view_node.bounds&.width&.round,
89
+ h: view_node.bounds&.height&.round,
90
+ type: nil
91
+ }
92
+ if view_node.archimate_element
93
+ attrs[:elementref] = identifier(view_node.archimate_element)
94
+ elsif view_node.model
95
+ # Since it doesn't seem to be forbidden, we just assume we can use
96
+ # the elementref for views in views
97
+ attrs[:elementref] = view_node.model
98
+ attrs[:type] = "model"
99
+ else
100
+ attrs[:type] = "group"
101
+ end
102
+ remove_nil_values(attrs)
103
+ end
104
+
105
+ def serialize_view_node(xml, view_node, x_offset = 0, y_offset = 0)
106
+ attrs = view_node_attrs(view_node, x_offset, y_offset)
107
+ xml.node(attrs) do
108
+ serialize_label(xml, view_node.name) if attrs[:type] == "group"
109
+ serialize(xml, view_node.style) if view_node.style
110
+ view_node.nodes.each do |c|
111
+ serialize_view_node(xml, c) # , attrs[:x].to_f, attrs[:y].to_f)
112
+ end
113
+ end
114
+ end
115
+
116
+ def font_style_string(font)
117
+ font&.style_string
118
+ end
119
+
120
+ def serialize_connection(xml, sc)
121
+ xml.connection(
122
+ identifier: identifier(sc.id),
123
+ relationshipref: identifier(sc.relationship),
124
+ source: identifier(sc.source),
125
+ target: identifier(sc.target)
126
+ ) do
127
+ serialize(xml, sc.bendpoints)
128
+ serialize(xml, sc.style)
129
+ end
130
+ end
131
+
132
+ def meff_type(el_type)
133
+ el_type = el_type.sub(/^/, "")
134
+ case el_type
135
+ when 'AndJunction', 'OrJunction'
136
+ 'Junction'
137
+ else
138
+ el_type
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+ require "nokogiri"
3
+
4
+ module Archimate
5
+ module FileFormats
6
+ class ModelExchangeFileWriter30 < ModelExchangeFileWriter
7
+ def initialize(model)
8
+ super
9
+ end
10
+
11
+ def model_attrs
12
+ remove_nil_values(
13
+ model_namespaces.merge(
14
+ "xsi:schemaLocation" => model.schema_locations.join(" "),
15
+ "identifier" => identifier(model.id),
16
+ "version" => model.version
17
+ )
18
+ )
19
+ end
20
+
21
+ def serialize_model(xml)
22
+ xml.model(model_attrs) do
23
+ ModelExchangeFile::XmlLangString.new(model.name, :name).serialize(xml)
24
+ ModelExchangeFile::XmlLangString.new(model.documentation, :documentation).serialize(xml)
25
+ ModelExchangeFile::XmlMetadata.new(model.metadata).serialize(xml)
26
+ serialize_properties(xml, model)
27
+ serialize_elements(xml)
28
+ serialize_relationships(xml)
29
+ serialize_organization_root(xml, model.organizations)
30
+ serialize_property_defs(xml)
31
+ serialize_views(xml)
32
+ end
33
+ end
34
+
35
+ def serialize_property_defs(xml)
36
+ return if model.property_definitions.empty?
37
+ ModelExchangeFile::XmlPropertyDefinitions.new(model.property_definitions).serialize(xml)
38
+ end
39
+
40
+ def serialize_label(xml, str, tag = :name)
41
+ return if str.nil? || str.strip.empty?
42
+ name_attrs = str.lang && !str.lang.empty? ? {"xml:lang" => str.lang} : {}
43
+ xml.send(tag, name_attrs) { xml.text text_proc(str) }
44
+ end
45
+
46
+ def relationship_attributes(relationship)
47
+ attrs = super
48
+ attrs["accessType"] = relationship.access_type if relationship.access_type
49
+ attrs
50
+ end
51
+
52
+ def serialize_organization_root(xml, organizations)
53
+ return unless organizations && organizations.size > 0
54
+ xml.organizations do
55
+ serialize_organization_body(xml, organizations[0])
56
+ end
57
+ end
58
+
59
+ def serialize_item(xml, item)
60
+ xml.item(identifierRef: identifier(item))
61
+ end
62
+
63
+ def serialize_property(xml, property)
64
+ xml.property("propertyDefinitionRef" => property.property_definition.id) do
65
+ ModelExchangeFile::XmlLangString.new(property.value, :value).serialize(xml)
66
+ end
67
+ end
68
+
69
+ def serialize_views(xml)
70
+ return if model.diagrams.empty?
71
+ xml.views do
72
+ xml.diagrams {
73
+ serialize(xml, model.diagrams)
74
+ }
75
+ end
76
+ end
77
+
78
+ def serialize_diagram(xml, diagram)
79
+ xml.view(
80
+ remove_nil_values(
81
+ identifier: identifier(diagram.id),
82
+ "xsi:type": diagram.type,
83
+ viewpoint: diagram.viewpoint_type
84
+ )
85
+ ) do
86
+ elementbase(xml, diagram)
87
+ serialize(xml, diagram.nodes)
88
+ serialize(xml, diagram.connections)
89
+ end
90
+ end
91
+
92
+ def view_node_attrs(view_node, x_offset = 0, y_offset = 0)
93
+ attrs = {
94
+ identifier: identifier(view_node.id),
95
+ elementRef: nil,
96
+ "xsi:type" => view_node.type,
97
+ "x" => view_node.bounds ? (view_node.bounds&.x + x_offset).round : nil,
98
+ "y" => view_node.bounds ? (view_node.bounds&.y + y_offset).round : nil,
99
+ "w" => view_node.bounds&.width&.round,
100
+ "h" => view_node.bounds&.height&.round
101
+ }
102
+ if view_node.archimate_element
103
+ attrs[:elementRef] = identifier(view_node.archimate_element)
104
+ elsif view_node.model
105
+ # Since it doesn't seem to be forbidden, we just assume we can use
106
+ # the elementref for views in views
107
+ attrs[:elementRef] = view_node.model
108
+ attrs[:type] = "model"
109
+ end
110
+ remove_nil_values(attrs)
111
+ end
112
+
113
+ def serialize_view_node(xml, view_node, x_offset = 0, y_offset = 0)
114
+ attrs = view_node_attrs(view_node, x_offset, y_offset)
115
+ xml.node(attrs) do
116
+ serialize_label(xml, view_node.name, :label)
117
+ serialize(xml, view_node.style) if view_node.style
118
+ view_node.nodes.each do |c|
119
+ serialize_view_node(xml, c) # , view_node_attrs[:x].to_f, view_node_attrs[:y].to_f)
120
+ end
121
+ end
122
+ end
123
+
124
+ def font_style_string(font)
125
+ case font&.style
126
+ when 1
127
+ "italic"
128
+ when 2
129
+ "bold"
130
+ when 3
131
+ "bold italic"
132
+ end
133
+ end
134
+
135
+ def serialize_connection(xml, sc)
136
+ xml.connection(
137
+ identifier: identifier(sc.id),
138
+ relationshipRef: identifier(sc.relationship),
139
+ "xsi:type": sc.type,
140
+ source: identifier(sc.source),
141
+ target: identifier(sc.target)
142
+ ) do
143
+ serialize(xml, sc.style)
144
+ serialize(xml, sc.bendpoints)
145
+ end
146
+ end
147
+
148
+ def meff_type(el_type)
149
+ el_type.sub(/^/, "")
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'archimate/file_formats/model_exchange_file/xml_lang_string'
4
+
5
+ module Archimate
6
+ module FileFormats
7
+ class Writer
8
+ attr_reader :model
9
+
10
+ def self.write(model, output_io)
11
+ writer = new(model)
12
+ writer.write(output_io)
13
+ end
14
+
15
+ def initialize(model)
16
+ @model = model
17
+ end
18
+
19
+ def serialize(xml, collection)
20
+ Array(collection).each do |item|
21
+ case item
22
+ when DataModel::Location
23
+ serialize_location(xml, item)
24
+ when DataModel::Bounds
25
+ serialize_bounds(xml, item)
26
+ when DataModel::ViewNode
27
+ serialize_view_node(xml, item)
28
+ when DataModel::Diagram
29
+ serialize_diagram(xml, item)
30
+ when DataModel::Documentation
31
+ ModelExchangeFile::XmlLangString.new(item, :documentation).serialize(xml)
32
+ when DataModel::Element
33
+ serialize_element(xml, item)
34
+ when DataModel::Organization
35
+ serialize_organization(xml, item)
36
+ when DataModel::Property
37
+ serialize_property(xml, item)
38
+ when DataModel::Relationship
39
+ serialize_relationship(xml, item)
40
+ when DataModel::Connection
41
+ serialize_connection(xml, item)
42
+ when DataModel::Style
43
+ serialize_style(xml, item)
44
+ else
45
+ raise TypeError, "Unexpected item type #{item.class}"
46
+ end
47
+ end
48
+ end
49
+
50
+ def remove_nil_values(h)
51
+ h.delete_if { |_k, v| v.nil? }
52
+ h
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,121 @@
1
+ module Archimate
2
+ module Lint
3
+ STOP_WORDS = %w(
4
+ a able about above according accordingly across actually after afterwards again against all allow allows
5
+ almost alone along already also although always am among amongst amoungst amount an and another any anybody
6
+ anyhow anyone anything anyway anyways anywhere apart appear appreciate appropriate are around as aside ask
7
+ asking associated at available away awfully back be became because become becomes becoming been before
8
+ beforehand behind being believe below beside besides best better between beyond bill both bottom brief but
9
+ by call came can can't cannot cant cause causes certain certainly changes clearly co com come comes computer
10
+ con concerning consequently consider considering contain containing contains corresponding could couldn't
11
+ couldnt course cry currently de dear definitely describe described despite detail did didn't different do
12
+ does doesn't doing don't done down downwards due during each edu eg eight either eleven else elsewhere empty
13
+ enough entirely especially et etc even ever every everybody everyone everything everywhere ex exactly example
14
+ except far few fifteen fifth fify fill find fire first five followed following follows for former formerly
15
+ forth forty found four from front full further furthermore get gets getting give given gives go goes going
16
+ gone got gotten greetings had hadn't happens hardly has hasn't hasnt have haven't having he he's hello help
17
+ hence her here here's hereafter hereby herein hereupon hers herself hi him himself his hither hopefully how
18
+ howbeit however hundred i i'd i'll i'm i've ie if ignored immediate in inasmuch inc indeed indicate indicated
19
+ indicates inner insofar instead interest into inward is isn't it it'd it'll it's its itself just keep keeps
20
+ kept know known knows last lately later latter latterly least less lest let let's like liked likely little
21
+ look looking looks ltd made mainly many may maybe me mean meanwhile merely might mill mine more moreover most
22
+ mostly move much must my myself name namely nd near nearly necessary need needs neither never nevertheless
23
+ new next nine no nobody non none noone nor normally not nothing novel now nowhere obviously of off often oh
24
+ ok okay old on once one ones only onto or other others otherwise ought our ours ourselves out outside over
25
+ overall own part particular particularly per perhaps placed please plus possible presumably probably provides
26
+ put que quite qv rather rd re really reasonably regarding regardless regards relatively respectively right
27
+ said same saw say saying says second secondly see seeing seem seemed seeming seems seen self selves sensible
28
+ sent serious seriously seven several shall she should shouldn't show side since sincere six sixty so some
29
+ somebody somehow someone something sometime sometimes somewhat somewhere soon sorry specified specify
30
+ specifying still sub such sup sure system take taken tell ten tends th than thank thanks thanx that that's
31
+ thats the their theirs them themselves then thence there there's thereafter thereby therefore therein theres
32
+ thereupon these they they'd they'll they're they've thick thin think third this thorough thoroughly those
33
+ though three through throughout thru thus tis to together too took top toward towards tried tries truly try
34
+ trying twas twelve twenty twice two un under unfortunately unless unlikely until unto up upon us use used
35
+ useful uses using usually value various very via viz vs want wants was wasn't way we we'd we'll we're we've
36
+ welcome well went were weren't what what's whatever when whence whenever where where's whereafter whereas
37
+ whereby wherein whereupon wherever whether which while whither who who's whoever whole whom whose why will
38
+ willing wish with within without won't wonder would wouldn't yes yet you you'd you'll you're you've your
39
+ yours yourself yourselves zero).freeze
40
+
41
+ class DuplicateEntities
42
+ attr_reader :word_count
43
+ # TODO: Add option to permit additional project specific stop words
44
+ # TODO: Do relationships after elements are merged
45
+ def initialize(model)
46
+ @model = model
47
+ @dupes = nil
48
+ @count = nil
49
+ @word_count = {}
50
+ @ignored_entity_types = %w(Junction AndJunction OrJunction)
51
+ dupe_list
52
+ end
53
+
54
+ def count
55
+ @dupes.reduce(0) { |total, (_tag, ary)| total + ary.size }
56
+ end
57
+
58
+ def empty?
59
+ count.zero?
60
+ end
61
+
62
+ def each(&block)
63
+ @dupes.keys.sort.each do |name|
64
+ ary = @dupes[name]
65
+ element_type = ary.first.type
66
+ block.call(element_type, name, ary)
67
+ end
68
+ end
69
+
70
+ protected
71
+ def candidate_entities
72
+ @model.entities.select do |entity|
73
+ (entity.is_a?(DataModel::Element) || entity.is_a?(DataModel::Relationship)) &&
74
+ !@ignored_entity_types.include?(entity.type)
75
+ end
76
+ end
77
+
78
+ def dupe_list
79
+ @dupes = candidate_entities.each_with_object({}) do |entity, hash|
80
+ tag = entity_hash_name(entity)
81
+ hash[tag] = hash.fetch(tag, []) << entity
82
+ end
83
+ @dupes.delete_if { |_tag, entities| entities.size <= 1 }
84
+ # @word_count.sort_by(&:last).reverse.each { |ak, av| puts "#{ak}: #{av}" }
85
+ @dupes
86
+ end
87
+
88
+ def entity_hash_name(entity)
89
+ layer = DataModel::Constants::ELEMENT_LAYER.fetch(entity.type, nil)
90
+ layer_sort_order = layer ? DataModel::Constants::LAYER_ELEMENTS.keys.index(layer) : 9
91
+ [
92
+ entity.class.name, # Taking advantage of Element being before Relationship
93
+ layer_sort_order.to_s,
94
+ entity.type,
95
+ simplify(entity)
96
+ ].join("/")
97
+ end
98
+
99
+ # This method takes an entity name and simplifies it for duplicate determination
100
+ # This might be configurable in the future
101
+ # 1. names are explicitly identical
102
+ # 2. names differ only in case
103
+ # 3. names differ only in whitespace
104
+ # 4. names differ only in punctuation
105
+ # 5. names differ only by stop-words (list of words such as "the", "api", etc.)
106
+ def simplify(entity)
107
+ name = entity.name&.dup || ""
108
+ name.sub!("(copy)", "") # (copy) is a special case inserted by the Archi tool
109
+ name.downcase!
110
+ name.gsub!(/[[:punct:]]/, "") unless entity.is_a?(DataModel::Relationship)
111
+ name.strip!
112
+ words = name.split(/\s/)
113
+ words.each { |word| @word_count[word] = @word_count.fetch(word, 0) + 1 }
114
+ name = words.reject { |word| STOP_WORDS.include?(word) }.join("")
115
+ name.delete!(" \t\n\r")
116
+ name = "#{name}:#{entity.source}:#{entity.target}" if entity.is_a?(DataModel::Relationship)
117
+ name
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Lint
4
+ # lint notes
5
+ #
6
+ # [x] Unused element (not used in a view)
7
+ # [x] Unused relation (not used in a view)
8
+ # [x] Object in a Viewpoint (invalid object for viewpoint)
9
+ # [x] Duplicate same name for type
10
+ # [x] empty view
11
+ # [?] visual nesting without relationship
12
+ # [ ] Warn about sketch model
13
+ # [x] warn about names with (copy)
14
+ # [x] blank element names
15
+ class Linter
16
+ include FileFormats::ArchimateV2
17
+
18
+ attr_reader :model
19
+
20
+ def initialize(model)
21
+ @model = model
22
+ @indent = "\n" + " " * 8
23
+ end
24
+
25
+ # TODO: consider a means to sort lint issues by entity instead of lint category
26
+ # this would point out the particularly problematic items that could be
27
+ # dealt with appropriately.
28
+ def report(output_io)
29
+ [
30
+ [unused_elements, "Unused Element"],
31
+ [duplicate_elements, "Duplicate Items"],
32
+ [entity_naming_rules, "Naming Rules"],
33
+ [unused_relationships, "Unused Relationship"],
34
+ [duplicate_relationships, "Duplicate Relationships"],
35
+ [empty_views, "Empty View"],
36
+ [invalid_for_viewpoint, "Invalid Type for Viewpoint"],
37
+ [nesting_without_relation, "Visual Nesting"]
38
+ ]
39
+ .map { |issues, title| issues.map { |issue| "#{title}: #{format_issue(issue)}" } }
40
+ .tap { |issues| report_subsection(issues, output_io) }
41
+ .tap { |issues| output_io.puts("Total Issues: #{issues.flatten.size}") }
42
+ end
43
+
44
+ def format_issue(issue)
45
+ ary = Array(issue)
46
+ return issue if ary.size == 1
47
+ "#{@indent}#{ary.join(@indent)}"
48
+ end
49
+
50
+ def report_subsection(issues, output_io)
51
+ issues.each do |sub_issues|
52
+ output_io.puts(sub_issues)
53
+ output_io.puts("Sub Total: #{sub_issues.size}")
54
+ end
55
+ end
56
+
57
+ def unused_elements
58
+ referenced_elements = diagram_referenced_ids
59
+ model.elements.reject { |el| referenced_elements.include?(el.id) }
60
+ end
61
+
62
+ def unused_relationships
63
+ referenced_ids = diagram_referenced_ids
64
+ model.relationships.reject { |el| referenced_ids.include?(el.id) }
65
+ end
66
+
67
+ def empty_views
68
+ model.diagrams.select { |diagram| diagram.nodes.empty? }
69
+ end
70
+
71
+ def duplicate_elements
72
+ duplicates(model.elements, ->(el) { [el.type, name_for_comparison(el.name)] })
73
+ end
74
+
75
+ def duplicate_relationships
76
+ duplicates(model.relationships, ->(el) { [el.type, el.source, el.target, name_for_comparison(el.name)] })
77
+ end
78
+
79
+ def name_for_comparison(name)
80
+ name&.strip&.downcase
81
+ end
82
+
83
+ def duplicates(array, group_by_proc)
84
+ array
85
+ .group_by { |el| group_by_proc.call(el) }
86
+ .each_with_object([]) do |(_key, ary), dupes|
87
+ dupes << ary.map(&:to_s) if ary.size > 1
88
+ end
89
+ end
90
+
91
+ def diagram_referenced_ids
92
+ @ref_ids ||= model.diagrams.inject([]) { |memo, dia| memo.concat(dia.referenced_identified_nodes) }.uniq
93
+ end
94
+
95
+ def invalid_for_viewpoint
96
+ model.diagrams.each_with_object([]) do |diagram, errors|
97
+ next if diagram.total_viewpoint?
98
+ valid_entity_types = VIEWPOINTS[diagram.viewpoint][:entities]
99
+ valid_relation_types = VIEWPOINTS[diagram.viewpoint][:relations]
100
+ invalid_elements = diagram.all_nodes.reject do |child|
101
+ child.element&.type.nil? || valid_entity_types.include?(child.element&.type)
102
+ end
103
+ invalid_relations = diagram.connections.reject do |sc|
104
+ sc.element&.type.nil? || valid_relation_types.include?(sc.element&.type)
105
+ end
106
+ next unless !invalid_elements.empty? || !invalid_relations.empty?
107
+ errors << format(
108
+ "%s viewpoint %s#{@indent}%s",
109
+ diagram,
110
+ diagram.viewpoint,
111
+ (invalid_elements + invalid_relations).map(&:element).map(&:to_s).join(@indent)
112
+ )
113
+ end
114
+ end
115
+
116
+ # Nesting is valid for relations:
117
+ # * CompositionRelationship
118
+ # * AggregationRelationship
119
+ # * AssignmentRelationship
120
+ def nesting_without_relation
121
+ model.find_by_class(DataModel::ViewNode).each_with_object([]) do |parent, errors|
122
+ missing_relations = parent.nodes.reject do |child|
123
+ model.relationships.any? do |rel|
124
+ parent.archimate_element.nil? ||
125
+ child.archimate_element.nil? ||
126
+ (rel.source == parent.archimate_element && rel.target == child.archimate_element)
127
+ end
128
+ end
129
+ errors.concat(missing_relations.map { |child| "#{child.element} should not nest in #{parent.element} without valid relationship" })
130
+ end
131
+ end
132
+
133
+ def entity_naming_rules
134
+ model.elements.each_with_object([]) do |entity, errors|
135
+ if entity.name.nil? || entity.name.empty?
136
+ errors << "#{entity} name is empty"
137
+ elsif entity.name.size < 2
138
+ errors << "#{entity} name #{entity.name.inspect} is too short"
139
+ elsif entity.name.include?("(copy)")
140
+ errors << "#{entity} name #{entity.name.inspect} contains '(copy)'"
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ # This is a helper module to make logging calls a little less clunky
5
+ module Logging
6
+ def update(sym, val)
7
+ @logger = val if sym == :logger
8
+ end
9
+
10
+ def logger
11
+ unless defined?(@logger)
12
+ config = Archimate::Config.instance
13
+ config.add_observer(self)
14
+ @logger = config.logger
15
+ end
16
+ @logger
17
+ end
18
+
19
+ def debug(progname = nil, &block)
20
+ logger.debug(progname, &block)
21
+ end
22
+
23
+ def error(progname = nil, &block)
24
+ logger.error(progname, &block)
25
+ end
26
+
27
+ def fatal(progname = nil, &block)
28
+ logger.fatal(progname, &block)
29
+ end
30
+
31
+ def info(progname = nil, &block)
32
+ logger.info(progname, &block)
33
+ end
34
+
35
+ def self.logger
36
+ Archimate::Config.instance.logger
37
+ end
38
+
39
+ def self.debug(progname = nil, &block)
40
+ logger.debug(progname, &block)
41
+ end
42
+
43
+ def self.error(progname = nil, &block)
44
+ logger.error(progname, &block)
45
+ end
46
+
47
+ def self.fatal(progname = nil, &block)
48
+ logger.fatal(progname, &block)
49
+ end
50
+
51
+ def self.info(progname = nil, &block)
52
+ logger.info(progname, &block)
53
+ end
54
+ end
55
+ end