archimate 1.1.0

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.
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