archimate 1.1.1 → 1.2.1

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.
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "parallel"
4
-
5
- module Archimate
6
- module Cli
7
- class Merge
8
- include Logging
9
-
10
- attr_reader :base, :local, :remote, :merged_file
11
-
12
- def self.merge(base_file, remote_file, local_file, merged_file)
13
- Logging.debug { "Reading base file: #{base_file}, local file: #{local_file}, remote file: #{remote_file}" }
14
- base, local, remote = Parallel.map([base_file, local_file, remote_file], in_processes: 3) do |file|
15
- Archimate.read(file)
16
- end
17
- Logging.debug { "Merged file is #{merged_file}" }
18
-
19
- Merge.new(base, local, remote, merged_file).run_merge
20
- end
21
-
22
- def initialize(base, local, remote, merged_file)
23
- @base = base
24
- @local = local
25
- @remote = remote
26
- @merged_file = merged_file
27
- @merge = Archimate::Diff::Merge.new
28
- end
29
-
30
- def run_merge
31
- debug { "Starting merging" }
32
- merged, conflicts = @merge.three_way(base, local, remote)
33
- # TODO: there should be no conflicts here
34
- debug do
35
- <<~MSG
36
- Done merging
37
- #{conflicts}
38
- MSG
39
- end
40
-
41
- File.open(merged_file, "w") do |file|
42
- # TODO: this should be controlled by the options and the defaulted to the read format
43
- debug { "Serializing" }
44
- Archimate::FileFormats::ArchiFileWriter.write(merged, file)
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Archimate
4
- module Cli
5
- # Merger is a class that is a decorator on Archimate::DataModel::Model
6
- # to provide the capability to merge another model into itself
7
- #
8
- # TODO: provide for a conflict resolver instance
9
- # TODO: provide an option to determine if potential matches are merged
10
- # or if the conflict resolver should be asked.
11
- class Merger # < SimpleDelegator
12
- # def initialize(primary_model, conflict_resolver)
13
- # super(primary_model)
14
- # @resolver = conflict_resolver
15
- # end
16
-
17
- # What merge does:
18
- # For all entities: (other than Model...):
19
- # - PropertyDefinition
20
- # - View
21
- # - Viewpoint
22
- # Entity:
23
- # look for a matching entity: with result
24
- # 1. Found a matching entity: goto entity merge
25
- # 2. Found no matching entity, but id conflicts: gen new id, goto add entity
26
- # 3. Found no matching entity: goto add entity
27
- # entity merge:
28
- # 1. merge (with func from deduper)
29
- # add entity:
30
- # 1. add entity to model
31
- # add remapping entry to map from entities other model id to id in this model
32
- # Relationship:
33
- # def merge(other_model)
34
- # other_model.entities.each do |entity|
35
- # # TODO: matching entity should use the same criteria that DuplicateEntities uses.
36
- # my_entity = find_matching_entity(entity)
37
- # if my_entity
38
- # end
39
- # end
40
-
41
- # TODO: handle inner text of elements
42
- # TODO: handle merging by element type
43
-
44
- def hash_to_attr(h)
45
- h.map { |k, v| "#{k}=\"#{v}\"" }.join(" ")
46
- end
47
-
48
- def e_to_s(e)
49
- "#{e.name} #{hash_to_attr(e.attributes)}"
50
- end
51
-
52
- # Merge node1, node2
53
- # For node
54
- # For each child
55
- # If has a matching child
56
- def merge(doc1, doc2)
57
- doc2.children.each do |e|
58
- next if e.name == "text" && e.text.strip.empty?
59
- # p = e.path
60
- # if p =~ /\[\d+\]$/
61
- # p = p.gsub(/\[\d+\]$/, "[@name=\"#{e.attr("name")}\"]")
62
- # end
63
- # puts "Looking for #{p}"``
64
- # matches = doc1.xpath(p)
65
- css = ">#{e.name}"
66
- # puts css
67
- css += "[name=\"#{e.attr('name')}\"]" if e.attributes.include?("name")
68
- css += "[xsi|type=\"#{e.attr('xsi:type')}\"]" if e.attributes.include?("xsi:type")
69
- matches = doc1.css(css)
70
- if !matches.empty? # We have a potential match
71
- # puts "Match?"
72
- # puts " Doc2: #{e_to_s(e)}"
73
- # matches.each do |e1|
74
- # puts " Doc1: #{e_to_s(e1)}"
75
- # end
76
- merge(matches[0], e) unless matches.size > 1
77
- else # No match insert the node into the tree TODO: handle id conflicts
78
- doc1.add_child(e)
79
- end
80
- end
81
- doc1
82
- end
83
-
84
- def id_hash_for(doc)
85
- doc.css("[id]").each_with_object({}) do |obj, memo|
86
- memo[obj["id"]] = obj
87
- memo
88
- end
89
- end
90
-
91
- def conflicting_ids(doc1, doc2)
92
- doc_id_hash1 = id_hash_for(doc1)
93
- doc_id_hash2 = id_hash_for(doc2)
94
- cids = Set.new(doc_id_hash1.keys) & doc_id_hash2.keys
95
- # puts "ID Conflicts:"
96
- # puts cids.to_a.join(",")
97
- cids
98
- end
99
-
100
- def merge_files(file1, file2)
101
- doc1 = Nokogiri::XML(File.open(file1))
102
- doc2 = Nokogiri::XML(File.open(file2))
103
-
104
- # cids = conflicting_ids(doc1, doc2)
105
- merge(doc1.root, doc2.root).document
106
- end
107
- end
108
- end
109
- end
@@ -1,17 +0,0 @@
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
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Archimate
4
- module Diff
5
- class ArchimateArrayReference < ArchimateNodeReference
6
- using DataModel::DiffableArray
7
- using DataModel::DiffablePrimitive
8
-
9
- attr_reader :array_index
10
-
11
- def initialize(array, array_index)
12
- unless array.is_a?(Array)
13
- raise(
14
- TypeError,
15
- "array argument must be an Array, was #{array.class}"
16
- )
17
- end
18
- unless array_index.is_a?(Integer)
19
- raise(
20
- TypeError,
21
- "array_index argument must be a Integer, was #{array_index.class} #{array_index.inspect}"
22
- )
23
- end
24
- unless array_index >= 0 && array_index < array.size
25
- raise(
26
- ArgumentError,
27
- "array_index argument a valid index for array #{array_index.inspect}"
28
- )
29
- end
30
- super(array)
31
- @array_index = array_index
32
- end
33
-
34
- def value
35
- archimate_node[array_index]
36
- end
37
-
38
- def to_s
39
- value.to_s
40
- end
41
-
42
- def lookup_in_model(model)
43
- result = lookup_parent_in_model(model)
44
- raise TypeError, "result was #{result.class} expected Array" unless result.is_a?(Array)
45
- result[array_index]
46
- end
47
-
48
- def path(options = {})
49
- @array_ref_path ||= [
50
- super,
51
- case value
52
- when DataModel::Referenceable
53
- value.id
54
- else
55
- array_index
56
- end
57
- ].map(&:to_s).reject(&:empty?).join("/")
58
- end
59
-
60
- # For inserts - we can't be sure of what is available (without an expensive sort)
61
- # So lookup the first previous value that exists in to_model and insert it after that
62
- # value instead of a fixed index.
63
- def find_insert_index_in_ary(ary)
64
- return -1 if array_index.zero?
65
- my_idx = (array_index - 1).downto(0).find(-1) do |idx|
66
- ary.smart_include?(archimate_node[idx])
67
- end
68
- ary.smart_find(archimate_node[my_idx])
69
- end
70
-
71
- def insert(to_model)
72
- ary_in_model = lookup_parent_in_model(to_model)
73
- insert_idx = find_insert_index_in_ary(ary_in_model) + 1
74
- ary_in_model.insert(insert_idx, value)
75
- end
76
-
77
- def delete(to_model)
78
- ary_in_model = lookup_parent_in_model(to_model)
79
- if ary_in_model.nil?
80
- $stderr.puts "lookup parent in model failed for path #{path}"
81
- return nil
82
- end
83
- idx = ary_in_model.smart_find(value)
84
- if idx
85
- ary_in_model.delete_at(idx)
86
- else
87
- $stderr.puts "Couldn't find item #{value.inspect} in path #{path} to delete in to_model"
88
- end
89
- end
90
-
91
- def change(to_model, from_value)
92
- ary_in_model = lookup_parent_in_model(to_model)
93
- idx = ary_in_model.smart_find(from_value)
94
- if idx.nil?
95
- $stderr.puts "Couldn't find value #{from_value.inspect} in path #{path} to change in to_model, adding to end of list"
96
- idx = ary_in_model.size
97
- end
98
- ary_in_model[idx] = value
99
- end
100
-
101
- def move(to_model, _from_ref)
102
- ary_in_model = lookup_parent_in_model(to_model)
103
- insert_idx = parent.previous_item_index(ary_in_model, value) + 1
104
- current_idx = ary_in_model.smart_find(value)
105
- deleted_value = ary_in_model.delete_at(current_idx)
106
- ary_in_model.insert(
107
- insert_idx,
108
- deleted_value
109
- )
110
- end
111
- end
112
- end
113
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Archimate
4
- module Diff
5
- class ArchimateReferenceableReference < ArchimateNodeReference
6
- using DataModel::DiffableArray
7
-
8
- def initialize(archimate_node)
9
- unless archimate_node.is_a?(DataModel::Referenceable)
10
- raise(
11
- TypeError,
12
- "archimate_node is a #{archimate_node.class}, Referenceable was expected"
13
- )
14
- end
15
- super
16
- end
17
-
18
- def lookup_in_model(model)
19
- raise TypeError unless model.is_a?(DataModel::Model)
20
- # There can be only one Model so return the model argument if
21
- # this node reference is a Model. This escape is required in case
22
- # the Model this is being applied to has a different id than the
23
- # model of the attribute this reference refers to.
24
- return model if archimate_node.is_a?(DataModel::Model)
25
- model.lookup(archimate_node.id)
26
- end
27
-
28
- def to_s
29
- archimate_node.to_s
30
- end
31
-
32
- def value
33
- archimate_node
34
- end
35
-
36
- def parent
37
- archimate_node.parent
38
- end
39
- end
40
- end
41
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Archimate
4
- module Diff
5
- class ArchimateNodeAttributeReference < ArchimateNodeReference
6
- attr_reader :attribute
7
-
8
- def initialize(archimate_node, attribute)
9
- unless archimate_node.is_a?(DataModel::ArchimateNode)
10
- raise(
11
- TypeError,
12
- "archimate_node must be an ArchimateNode, was #{archimate_node.class}"
13
- )
14
- end
15
- unless attribute.is_a?(Symbol)
16
- raise(
17
- TypeError,
18
- "Node #{archimate_node.class} attribute must be a sym, was a #{attribute.class} value #{attribute.inspect}"
19
- )
20
- end
21
- unless archimate_node.class.schema.keys.include?(attribute)
22
- raise(
23
- ArgumentError,
24
- "Attribute #{attribute} invalid for class #{archimate_node.class}"
25
- )
26
- end
27
- super(archimate_node)
28
- @attribute = attribute
29
- end
30
-
31
- def ==(other)
32
- super && attribute == other.attribute
33
- end
34
-
35
- def lookup_in_model(model)
36
- recurse_lookup_in_model(archimate_node, model)[attribute]
37
- end
38
-
39
- def to_s
40
- attribute.to_s
41
- end
42
-
43
- def value
44
- archimate_node[attribute]
45
- end
46
-
47
- def path(options = {})
48
- @node_attribute_ref_path ||= [
49
- super, @attribute
50
- ].map(&:to_s).reject(&:empty?).join("/")
51
- end
52
-
53
- def insert(to_model)
54
- lookup_parent_in_model(to_model).set(attribute, value)
55
- end
56
-
57
- def delete(to_model)
58
- lookup_parent_in_model(to_model).delete(attribute)
59
- end
60
-
61
- def change(to_model, _from_value)
62
- lookup_parent_in_model(to_model).set(attribute, value)
63
- end
64
-
65
- def move(_to_model)
66
- raise "Move is not valid for ArchimateNodes"
67
- end
68
- end
69
- end
70
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Archimate
4
- module Diff
5
- class ArchimateNodeReference
6
- using DataModel::DiffablePrimitive
7
- using DataModel::DiffableArray
8
-
9
- attr_reader :archimate_node
10
-
11
- # There should be only a few things that are valid here:
12
- # 1. An archimate node and a attribute name sym
13
- # 2. An array and index
14
- # Produces a NodeReference instance for the given parameters
15
- def self.for_node(node, child_node)
16
- case node
17
- when DataModel::ArchimateNode
18
- ArchimateNodeAttributeReference.new(node, child_node)
19
- when Array
20
- ArchimateArrayReference.new(node, child_node)
21
- else
22
- raise TypeError, "Node references need to be either an ArchimateNode or an Array"
23
- end
24
- end
25
-
26
- def initialize(archimate_node)
27
- unless archimate_node.is_a?(DataModel::ArchimateNode) || archimate_node.is_a?(Array)
28
- raise(
29
- TypeError,
30
- "archimate_node must be an ArchimateNode or Array, was #{archimate_node.class}"
31
- )
32
- end
33
- raise "new WTF? parent at path #{archimate_node.path} is a #{archimate_node.class} but isn't assigned a model" if archimate_node.in_model.nil? && !archimate_node.is_a?(DataModel::Model)
34
- @archimate_node = archimate_node
35
- end
36
-
37
- def ==(other)
38
- other.is_a?(self.class) &&
39
- value == other.value
40
- end
41
-
42
- def to_s
43
- value.to_s
44
- end
45
-
46
- def lookup_in_model(model)
47
- recurse_lookup_in_model(archimate_node, model)
48
- end
49
-
50
- def recurse_lookup_in_model(node, model)
51
- return nil if node.nil?
52
- raise TypeError, "node argument must be ArchimateNode or Array, was a #{node.class}" unless node.is_a?(Array) || node.is_a?(DataModel::ArchimateNode)
53
- raise TypeError, "model argument must be a Model, was a #{model.class}" unless model.is_a?(DataModel::Model)
54
- if node.is_a?(DataModel::Model)
55
- return model
56
- elsif node.is_a?(DataModel::Referenceable)
57
- return model.lookup(node.id)
58
- else
59
- node_parent_in_model = recurse_lookup_in_model(node.parent, model)
60
- node_parent_in_model[node.parent_attribute_name] unless node_parent_in_model.nil?
61
- end
62
- end
63
-
64
- def lookup_parent_in_model(model)
65
- raise "WTF? parent at path #{path} is a #{parent.class} but isn't assigned a model" if parent.in_model.nil? && !parent.is_a?(DataModel::Model)
66
- result = recurse_lookup_in_model(parent, model)
67
- $stderr.puts "Unable to lookup parent with path #{path}" if result.nil?
68
- result
69
- end
70
-
71
- def parent
72
- archimate_node
73
- end
74
-
75
- def path(options = {})
76
- @node_ref_path ||= archimate_node.path(options)
77
- end
78
- end
79
- end
80
- end