archimate 1.1.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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