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,51 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Conflicts
5
+ class PathConflict < BaseConflict
6
+ def initialize(base_local_diffs, base_remote_diffs)
7
+ super
8
+ @associative = true
9
+ end
10
+
11
+ def describe
12
+ "Checking for Differences in one change set that conflict with changes in other change set at the same path"
13
+ end
14
+
15
+ def diff_conflicts(diff1, diff2)
16
+ same_path_but_diff(diff1, diff2) && in_conflict?(diff1, diff2)
17
+ end
18
+
19
+ private
20
+
21
+ def same_path_but_diff(a, b)
22
+ a.path == b.path && a != b
23
+ end
24
+
25
+ # I'm in conflict if:
26
+ # 1. ld and rd are both changes to the same path (but not the same change)
27
+ # 2. one is a change, the other a delete and changed_from is the same
28
+ # 3. both are inserts of the different identifyable nodes with the same id
29
+ #
30
+ # If I'm not an identifyable node and my parent is an array, then two inserts are ok
31
+ def in_conflict?(local_diff, remote_diff)
32
+ return true if
33
+ local_diff.target.parent.is_a?(Array) &&
34
+ local_diff.target.value.is_a?(DataModel::Referenceable) &&
35
+ local_diff.target.value.id == remote_diff.target.value.id &&
36
+ local_diff != remote_diff
37
+
38
+ case [local_diff, remote_diff].map { |d| d.class.name.split('::').last }.sort
39
+ when %w(Change Change)
40
+ local_diff.changed_from.value == remote_diff.changed_from.value &&
41
+ local_diff.target.value != remote_diff.target.value
42
+ when %w(Change Delete)
43
+ true
44
+ else
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'parallel'
5
+ require 'archimate/diff/conflicts/base_conflict'
6
+ require 'archimate/diff/conflicts/deleted_items_child_updated_conflict'
7
+ require 'archimate/diff/conflicts/deleted_items_referenced_conflict'
8
+ require 'archimate/diff/conflicts/path_conflict'
9
+
10
+ module Archimate
11
+ module Diff
12
+ class Conflicts
13
+ extend Forwardable
14
+
15
+ attr_reader :base_local_diffs
16
+ attr_reader :base_remote_diffs
17
+
18
+ def_delegator :@conflicts, :empty?
19
+ def_delegator :@conflicts, :size
20
+ def_delegator :@conflicts, :first
21
+ def_delegator :@conflicts, :map
22
+ def_delegator :@conflicts, :each
23
+
24
+ include Archimate::Logging
25
+
26
+ def initialize(base_local_diffs, base_remote_diffs)
27
+ @base_local_diffs = base_local_diffs
28
+ @base_remote_diffs = base_remote_diffs
29
+ @conflict_finders = [PathConflict, DeletedItemsChildUpdatedConflict, DeletedItemsReferencedConflict]
30
+ @conflicts = nil
31
+ @conflicting_diffs = nil
32
+ @unconflicted_diffs = nil
33
+ # TODO: consider making this an argument
34
+ @conflict_resolver = Cli::ConflictResolver.new
35
+ end
36
+
37
+ # TODO: refactor this method elsewhere
38
+ # resolve iterates through the set of conflicting diffs asking the user
39
+ # (if running interactively) and return the set of diffs that can be applied.
40
+ #
41
+ # To keep diffs reasonably human readable in logs, the local diffs should
42
+ # be applied first followed by the remote diffs.
43
+ def resolve
44
+ debug do
45
+ <<~MSG
46
+ Filtering out #{conflicts.size} conflicts from #{base_local_diffs.size + base_remote_diffs.size} diffs
47
+ Remaining diffs #{unconflicted_diffs.size}
48
+ MSG
49
+ end
50
+
51
+ conflicts.each_with_object(unconflicted_diffs) do |conflict, diffs|
52
+ # TODO: this will result in diffs being out of order from their
53
+ # original order. diffs should be flagged as conflicted and
54
+ # this method should instead remove the conflicted flag.
55
+ diffs.concat(@conflict_resolver.resolve(conflict))
56
+ # TODO: if the conflict is resolved, it should be removed from the
57
+ # @conflicts array.
58
+ end
59
+ end
60
+
61
+ def conflicts
62
+ @conflicts ||= find_conflicts
63
+ end
64
+
65
+ def conflicting_diffs
66
+ @conflicting_diffs ||= conflicts.map(&:diffs).flatten
67
+ end
68
+
69
+ def unconflicted_diffs
70
+ @unconflicted_diffs ||=
71
+ (base_local_diffs + base_remote_diffs) - conflicting_diffs
72
+ end
73
+
74
+ def to_s
75
+ "Conflicts:\n\n#{conflicts.map(&:to_s).join("\n\n")}\n"
76
+ end
77
+
78
+ private
79
+
80
+ def find_conflicts
81
+ # TODO: Running this in parallel breaks currently.
82
+ # @conflicts = Parallel.map(@conflict_finders, in_processes: 3) { |cf_class|
83
+ @conflicts = @conflict_finders.map { |cf_class|
84
+ cf_class.new(base_local_diffs, base_remote_diffs).conflicts
85
+ }.flatten
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Delete < Difference
5
+ using DataModel::DiffablePrimitive
6
+ using DataModel::DiffableArray
7
+
8
+ # Create a new Delete difference
9
+ #
10
+ # @param target [ArchimateNodeReference] Element that was deleted
11
+ def initialize(target)
12
+ super
13
+ end
14
+
15
+ def to_s
16
+ # Note - the explicit to_s is required to access the DiffableArray
17
+ # implementation if the parent is an Array.
18
+ "#{diff_type} #{target} from #{target.parent.to_s}"
19
+ end
20
+
21
+ def apply(el)
22
+ target.delete(el)
23
+ el
24
+ end
25
+
26
+ def delete?
27
+ true
28
+ end
29
+
30
+ def kind
31
+ "Delete"
32
+ end
33
+
34
+ private
35
+
36
+ def diff_type
37
+ Color.color('DELETE:', :delete)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Archimate
6
+ module Diff
7
+ class Difference
8
+ using DataModel::DiffableArray
9
+ using DataModel::DiffablePrimitive
10
+ extend Forwardable
11
+
12
+ ARRAY_RE = Regexp.compile(/\[(\d+)\]/)
13
+ PATH_ROOT_SORT_ORDER = %w[elements relationships diagrams organizations].freeze
14
+
15
+ # delete: something responds to parent, child attribute (which is thing deleted - and could be sym for archimate nodes or index for array), value
16
+ # insert: something responds to parent, child attribute (or index), value, after value (to help with inserts)
17
+ # change: something responds to parent, child attribute (or index), value, changed from value
18
+ # move: something responds to parent, child index, value, after value) move after a particular value
19
+ attr_reader :target
20
+ attr_reader :changed_from
21
+
22
+ def_delegator :@target, :path
23
+
24
+ # Re-thinking.
25
+ #
26
+ # Requirements:
27
+ #
28
+ # 1. User friendly display of what is different in context
29
+ # 2. Able to apply the diff to another model (which was based on the "base" of the diff)
30
+ #
31
+ # Delete: example
32
+ # ArchimateNode child.bounds
33
+ # ArchimateNode, attribute model, "name"
34
+ # DiffableArray, ArchimateNode model.elements, element
35
+ # bendpoint attributes under connection
36
+ # documentation
37
+ # properties
38
+ # child/style/fill_color
39
+ # child/style/font/name
40
+ #
41
+ # @param target [Dry::Struct with id attribute] the element operated on (why is array treated as a special case?)
42
+ # @param changed_from [same class as target] (optional) for change this is the previous value
43
+ # def initialize(changed_from, target)
44
+ def initialize(target, changed_from = nil)
45
+ # raise TypeError, "Expected target to be an ArchimateNodeReference" unless target.is_a?(ArchimateNodeReference)
46
+ @target = target
47
+ @changed_from = changed_from
48
+ end
49
+
50
+ def ==(other)
51
+ other.is_a?(self.class) &&
52
+ @target == other.target &&
53
+ @changed_from == other.changed_from
54
+ end
55
+
56
+ # Difference sorting is based on the path.
57
+ # Top level components are sorted in this order: (elements, relationships, diagrams, organizations)
58
+ # Array entries are sorted by numeric order
59
+ # Others are sorted alphabetically
60
+ # TODO: this isn't complete
61
+ def <=>(other)
62
+ a = path_to_array
63
+ b = other.path_to_array
64
+
65
+ part_a = a.shift
66
+ part_b = b.shift
67
+ res = PATH_ROOT_SORT_ORDER.index(part_a) <=> PATH_ROOT_SORT_ORDER.index(part_b)
68
+ return res unless res.zero?
69
+
70
+ until a.empty? || b.empty?
71
+ part_a = a.shift
72
+ part_b = b.shift
73
+
74
+ return part_a <=> part_b unless (part_a <=> part_b).zero?
75
+ end
76
+
77
+ return -1 if a.empty?
78
+ return 1 if b.empty?
79
+ part_a <=> part_b
80
+ end
81
+
82
+ def delete?
83
+ false
84
+ end
85
+
86
+ def change?
87
+ false
88
+ end
89
+
90
+ def insert?
91
+ false
92
+ end
93
+
94
+ def move?
95
+ false
96
+ end
97
+
98
+ def path_to_array
99
+ path(force_array_index: :index).split("/").map do |p|
100
+ md = ARRAY_RE.match(p)
101
+ md ? md[1].to_i : p
102
+ end
103
+ end
104
+
105
+ def summary_element
106
+ summary_elements = [DataModel::Element, DataModel::Organization, DataModel::Relationship, DataModel::Diagram, DataModel::Model]
107
+ se = target.value.primitive? ? target.parent : target.value
108
+ se = se.parent while summary_elements.none? { |c| se.is_a?(c) }
109
+ se
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Insert < Difference
5
+ using DataModel::DiffableArray
6
+
7
+ # Create a new Insert difference
8
+ #
9
+ # @param inserted_element [Archimate::DataModel::ArchimateNode] Element
10
+ # that was inserted
11
+ # @param sub_path [str] Path under inserted_element for primitive values
12
+ def initialize(target)
13
+ super
14
+ end
15
+
16
+ def to_s
17
+ # Note - the explicit to_s is required to access the DiffableArray
18
+ # implementation if the parent is an Array.
19
+ "#{diff_type} #{target} into #{target.parent.to_s}"
20
+ end
21
+
22
+ def apply(to_model)
23
+ throw TypeError, "Expected a Archimate::DataModel::Model, was a #{to_model.class}" unless to_model.is_a?(DataModel::Model)
24
+ target.insert(to_model)
25
+ to_model
26
+ end
27
+
28
+ def insert?
29
+ true
30
+ end
31
+
32
+ def kind
33
+ "Insert"
34
+ end
35
+
36
+ private
37
+
38
+ def diff_type
39
+ Color.color('INSERT:', :insert)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parallel"
4
+
5
+ module Archimate
6
+ module Diff
7
+ class Merge
8
+ include Archimate::Logging
9
+ using DataModel::DiffableArray
10
+
11
+ def three_way(base, local, remote)
12
+ debug { "Computing base:local & base:remote diffs" }
13
+ base_local_diffs, base_remote_diffs = Parallel.map([[base, local], [base, remote]],
14
+ in_processes: 2) do |base_model, other_model|
15
+ base_model.diff(other_model)
16
+ end
17
+
18
+ debug "Finding Conflicts in #{base_local_diffs.size + base_remote_diffs.size} diffs"
19
+ conflicts = Conflicts.new(base_local_diffs, base_remote_diffs)
20
+ resolved_diffs = conflicts.resolve
21
+
22
+ [apply_diffs(resolved_diffs, base.clone), conflicts]
23
+ end
24
+
25
+ # Applies the set of diffs to the model returning a
26
+ # new model with the diffs applied.
27
+ def apply_diffs(diffs, model)
28
+ debug { "Applying #{diffs.size} diffs" }
29
+ progressbar = ProgressIndicator.new(total: diffs.size, title: "Applying diffs")
30
+ diffs
31
+ .inject(model) do |model_a, diff|
32
+ progressbar.increment
33
+ diff.apply(model_a)
34
+ end
35
+ .rebuild_index
36
+ .organize
37
+ ensure
38
+ progressbar&.finish
39
+ end
40
+
41
+ # # TODO: not currently used
42
+ # def find_merged_duplicates
43
+ # [@base_local_diffs, @base_remote_diffs].map do |diffs|
44
+ # deleted_element_diffs = diffs.select(&:delete?).select(&:element?)
45
+ # deleted_element_diffs.each_with_object({}) do |diff, a|
46
+ # element = diff.from_model.elements[diff.element_idx]
47
+ # found = diff.from_model.elements.select do |el|
48
+ # el != element && el.type == element.type && el.name == element.name
49
+ # end
50
+ # next if found.empty?
51
+ # a[diff] = found
52
+ # debug { "\nFound potential de-duplication:" }
53
+ # debug { "\t#{diff}" }
54
+ # debug { "Might be replaced with:\n\t#{found.map(&:to_s).join(" }\n\t")}\n\n"
55
+ # end
56
+ # end
57
+ # end
58
+
59
+ # # TODO: not currently used
60
+ # def filter_path_conflicts(diffs)
61
+ # diffs.sort { |a, b| a.path_to_array.size <=> b.path_to_array.size }.each_with_object([]) do |i, e|
62
+ # diffs.delete(i)
63
+ # path_conflicts = diffs.select { |d| d.path.start_with?(i.path) }
64
+ # path_conflicts.each { |d| diffs.delete(d) }
65
+ # e << i
66
+ # end
67
+ # end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Move < Difference
5
+ using DataModel::DiffablePrimitive
6
+ using DataModel::DiffableArray
7
+
8
+ # Create a new Move difference
9
+ #
10
+ # @param target [Archimate::Diff::ArchimateNodeReference] reference to
11
+ # ArchimateNode that was changed
12
+ # @param changed_from [Archimate::Diff::ArchimateNodeReference] Element
13
+ # that was changed
14
+ def initialize(target, changed_from)
15
+ super(target, changed_from)
16
+ end
17
+
18
+ def to_s
19
+ # Note - the explicit to_s is required to access the DiffableArray
20
+ # implementation if the parent is an Array.
21
+ "#{diff_type} #{changed_from.parent&.to_s} #{Color.color(target.to_s, :change)} moved to #{target.array_index}"
22
+ end
23
+
24
+ # TODO: patch is a better name than apply
25
+ def apply(to_model)
26
+ unless to_model.is_a?(DataModel::Model)
27
+ throw(
28
+ TypeError,
29
+ "Expected a Archimate::DataModel::Model, was a #{to_model.class}"
30
+ )
31
+ end
32
+ target.move(to_model, changed_from)
33
+ to_model
34
+ end
35
+
36
+ def move?
37
+ true
38
+ end
39
+
40
+ def kind
41
+ "Move"
42
+ end
43
+
44
+ private
45
+
46
+ def diff_type
47
+ Color.color('MOVE:', :move)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ require 'archimate/diff/difference'
5
+ require 'archimate/diff/archimate_node_reference'
6
+ require 'archimate/diff/archimate_identified_node_reference'
7
+ require 'archimate/diff/archimate_array_reference'
8
+ require 'archimate/diff/archimate_node_attribute_reference'
9
+ require 'archimate/diff/change'
10
+ require 'archimate/diff/conflict'
11
+ require 'archimate/diff/conflicts'
12
+ require 'archimate/diff/delete'
13
+ require 'archimate/diff/insert'
14
+ require 'archimate/diff/merge'
15
+ require 'archimate/diff/move'
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require "csv"
3
+
4
+ # This module takes an ArchiMate model and builds a set of CSV files representing it.
5
+ module Archimate
6
+ module Export
7
+ class CSVExport
8
+ attr_reader :model
9
+
10
+ def initialize(model)
11
+ @model = model
12
+ end
13
+
14
+ def to_csv(output_dir: ".")
15
+ (model.relationships + model.elements)
16
+ .group_by(&:type).each do |type, elements|
17
+ CSV.open(
18
+ File.join(output_dir, "#{type}.csv"),
19
+ "wb",
20
+ force_quotes: true
21
+ ) do |csv|
22
+ headers = elements.first.struct_instance_variables
23
+ csv << headers.map(&:to_s)
24
+ elements.each do |element|
25
+ csv << headers.map { |attr| element[attr] }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module takes an ArchiMate model and builds GraphML representation of it.
4
+ #
5
+ # Node Schema:
6
+ # Properties:
7
+ # name: Element.name
8
+ # nodelId: Element.id
9
+ # layer: Element.layer
10
+ # documentation: Element.documentation
11
+ # Element.properties
12
+ # ("prop:#{Property.key}"): Property.value
13
+ # Labels:
14
+ # Element.type
15
+ #
16
+ # Edge Schema:
17
+ # Properties:
18
+ # weight (of Relationship Type as in table below)
19
+ # relationshipId: Relationship.id
20
+ # name: Relationship.name
21
+ # documentation: Relationship.documentation
22
+ # accessType: Relationship.access_type
23
+ # Labels:
24
+ # Relationship.type
25
+ #
26
+ # | Weight | Name |
27
+ # |--------+----------------|
28
+ # | 1 | Association |
29
+ # | 2 | Access |
30
+ # | 3 | Used by |
31
+ # | 4 | Realization |
32
+ # | 5 | Assignment |
33
+ # | 6 | Aggregation |
34
+ # | 7 | Composition |
35
+ # | 8 | Flow |
36
+ # | 9 | Triggering |
37
+ # | 10 | Grouping |
38
+ # | 11 | Junction |
39
+ # | 12 | Specialization |
40
+ # Structural stronger than Dependency Relationships
41
+ # ServingRelationship == UsedByRelationship
42
+
43
+
44
+ module Archimate
45
+ module Export
46
+ class Cypher
47
+ attr_reader :output_io
48
+
49
+ WEIGHTS = {
50
+ 'GroupingRelationship' => 0,
51
+ 'JunctionRelationship' => 0,
52
+ 'AssociationRelationship' => 0,
53
+ 'SpecialisationRelationship' => 1,
54
+ 'FlowRelationship' => 2,
55
+ 'TriggeringRelationship' => 3,
56
+ 'InfluenceRelationship' => 4,
57
+ 'AccessRelationship' => 5,
58
+ 'UsedByRelationship' => 6,
59
+ 'RealisationRelationship' => 7,
60
+ 'AssignmentRelationship' => 8,
61
+ 'AggregationRelationship' => 9,
62
+ 'CompositionRelationship' => 10
63
+ }
64
+
65
+ def initialize(output_io)
66
+ @output_io = output_io
67
+ end
68
+
69
+ def to_cypher(model)
70
+ write_cypher_header(model)
71
+ write_nodes(model.elements)
72
+ create_indexes(model.elements)
73
+ write_relationships(model.relationships)
74
+ # TODO: write properties
75
+ end
76
+
77
+ def write_cypher_header(model)
78
+ write "// Cypher import script of ArchiMate model #{model.name}. Produced #{DateTime.now}\n"
79
+ end
80
+
81
+ def write_nodes(elements)
82
+ write "\n// Nodes\n"
83
+ elements.each do |element|
84
+ props = add_docs(
85
+ {
86
+ layer: element.layer.delete(" "),
87
+ name: element.name,
88
+ nodeId: element.id
89
+ }.merge(
90
+ element.properties.each_with_object({}) do |prop, memo|
91
+ memo["prop:#{prop.key}"] = prop.value unless prop.value.nil?
92
+ end
93
+ ), element.documentation
94
+ )
95
+
96
+ write(
97
+ node(
98
+ element.type,
99
+ props
100
+ )
101
+ )
102
+ end
103
+ end
104
+
105
+ def create_indexes(elements)
106
+ write "\n// Indexes\n"
107
+ elements.map(&:type).uniq.each do |label|
108
+ write "CREATE INDEX ON :#{label}(name);"
109
+ write "CREATE INDEX ON :#{label}(nodeId);"
110
+ end
111
+ end
112
+
113
+ def write_relationships(relationships)
114
+ write "\n// Relationships\n"
115
+ relationships.each do |rel|
116
+ write relationship(rel)
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def node(label, properties = {})
123
+ "CREATE (n:#{label} #{props(properties)});"
124
+ end
125
+
126
+ def relationship(rel)
127
+ "MATCH #{source(rel)},#{target(rel)} " \
128
+ "CREATE (s)-#{relationship_def(rel)}->(t);"
129
+ end
130
+
131
+ def props(properties)
132
+ "{ #{properties.reject { |_k, v| v.nil? }.map { |k, v| "`#{k}`: #{v.inspect}" }.join(', ')} }"
133
+ end
134
+
135
+ def source(rel)
136
+ "(s #{props(nodeId: rel.source)})"
137
+ end
138
+
139
+ def weight(t)
140
+ return 0 unless WEIGHTS.include?(t)
141
+ WEIGHTS[t]
142
+ end
143
+
144
+ def add_docs(h, l)
145
+ t = l.map(&:text).join("\n").strip
146
+ return h if t.empty?
147
+ h.merge(documentation: t)
148
+ end
149
+
150
+ def relationship_def(rel)
151
+ h = add_docs(
152
+ {
153
+ name: rel.name,
154
+ relationshipId: rel.id,
155
+ accessType: rel.access_type,
156
+ weight: weight(rel.type)
157
+ }, rel.documentation
158
+ )
159
+ "[r:#{rel.type} #{props(h)}]"
160
+ end
161
+
162
+ def target(rel)
163
+ "(t #{props(nodeId: rel.target)})"
164
+ end
165
+
166
+ def write(str)
167
+ @output_io.puts(str)
168
+ end
169
+ end
170
+ end
171
+ end