archimate 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.projectile +18 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +4 -0
- data/.yardocs +15 -0
- data/Gemfile +5 -0
- data/Guardfile +27 -0
- data/LICENSE +201 -0
- data/README.md +94 -0
- data/Rakefile +29 -0
- data/TODOs.org +286 -0
- data/archimate.gemspec +51 -0
- data/bin/archimate +17 -0
- data/bin/console +13 -0
- data/bin/setup +8 -0
- data/exe/archidiff +7 -0
- data/exe/archidiff-summary +7 -0
- data/exe/archimate +7 -0
- data/exe/archimerge +7 -0
- data/exe/fmtxml +7 -0
- data/lib/archimate/cli/archi.rb +189 -0
- data/lib/archimate/cli/cleanup.rb +54 -0
- data/lib/archimate/cli/conflict_resolver.rb +39 -0
- data/lib/archimate/cli/convert.rb +39 -0
- data/lib/archimate/cli/diff.rb +31 -0
- data/lib/archimate/cli/diff_summary.rb +101 -0
- data/lib/archimate/cli/duper.rb +85 -0
- data/lib/archimate/cli/lint.rb +16 -0
- data/lib/archimate/cli/mapper.rb +82 -0
- data/lib/archimate/cli/merge.rb +49 -0
- data/lib/archimate/cli/merger.rb +109 -0
- data/lib/archimate/cli/stats.rb +42 -0
- data/lib/archimate/cli/svger.rb +40 -0
- data/lib/archimate/color.rb +53 -0
- data/lib/archimate/config.rb +35 -0
- data/lib/archimate/data_model/any_attribute.rb +13 -0
- data/lib/archimate/data_model/any_element.rb +15 -0
- data/lib/archimate/data_model/archimate_node.rb +181 -0
- data/lib/archimate/data_model/bounds.rb +72 -0
- data/lib/archimate/data_model/color.rb +47 -0
- data/lib/archimate/data_model/concept.rb +14 -0
- data/lib/archimate/data_model/concern.rb +17 -0
- data/lib/archimate/data_model/connection.rb +107 -0
- data/lib/archimate/data_model/constants.rb +82 -0
- data/lib/archimate/data_model/container.rb +17 -0
- data/lib/archimate/data_model/diagram.rb +40 -0
- data/lib/archimate/data_model/diffable_array.rb +213 -0
- data/lib/archimate/data_model/diffable_primitive.rb +83 -0
- data/lib/archimate/data_model/documentation.rb +14 -0
- data/lib/archimate/data_model/element.rb +87 -0
- data/lib/archimate/data_model/font.rb +54 -0
- data/lib/archimate/data_model/label.rb +19 -0
- data/lib/archimate/data_model/lang_string.rb +41 -0
- data/lib/archimate/data_model/location.rb +31 -0
- data/lib/archimate/data_model/metadata.rb +14 -0
- data/lib/archimate/data_model/model.rb +217 -0
- data/lib/archimate/data_model/modeling_note.rb +13 -0
- data/lib/archimate/data_model/named_referenceable.rb +14 -0
- data/lib/archimate/data_model/organization.rb +40 -0
- data/lib/archimate/data_model/property.rb +25 -0
- data/lib/archimate/data_model/property_definition.rb +16 -0
- data/lib/archimate/data_model/referenceable.rb +29 -0
- data/lib/archimate/data_model/relationship.rb +85 -0
- data/lib/archimate/data_model/schema_info.rb +18 -0
- data/lib/archimate/data_model/style.rb +32 -0
- data/lib/archimate/data_model/view.rb +12 -0
- data/lib/archimate/data_model/view_concept.rb +18 -0
- data/lib/archimate/data_model/view_node.rb +114 -0
- data/lib/archimate/data_model/viewpoint.rb +87 -0
- data/lib/archimate/data_model.rb +54 -0
- data/lib/archimate/diff/archimate_array_reference.rb +113 -0
- data/lib/archimate/diff/archimate_identified_node_reference.rb +41 -0
- data/lib/archimate/diff/archimate_node_attribute_reference.rb +70 -0
- data/lib/archimate/diff/archimate_node_reference.rb +80 -0
- data/lib/archimate/diff/change.rb +49 -0
- data/lib/archimate/diff/conflict.rb +31 -0
- data/lib/archimate/diff/conflicts/base_conflict.rb +53 -0
- data/lib/archimate/diff/conflicts/deleted_items_child_updated_conflict.rb +30 -0
- data/lib/archimate/diff/conflicts/deleted_items_referenced_conflict.rb +63 -0
- data/lib/archimate/diff/conflicts/path_conflict.rb +51 -0
- data/lib/archimate/diff/conflicts.rb +89 -0
- data/lib/archimate/diff/delete.rb +41 -0
- data/lib/archimate/diff/difference.rb +113 -0
- data/lib/archimate/diff/insert.rb +43 -0
- data/lib/archimate/diff/merge.rb +70 -0
- data/lib/archimate/diff/move.rb +51 -0
- data/lib/archimate/diff.rb +17 -0
- data/lib/archimate/export/csv_export.rb +32 -0
- data/lib/archimate/export/cypher.rb +171 -0
- data/lib/archimate/export/graph_ml.rb +131 -0
- data/lib/archimate/export/n_quads.rb +142 -0
- data/lib/archimate/file_format.rb +30 -0
- data/lib/archimate/file_formats/archi_file_format.rb +151 -0
- data/lib/archimate/file_formats/archi_file_reader.rb +252 -0
- data/lib/archimate/file_formats/archi_file_writer.rb +230 -0
- data/lib/archimate/file_formats/archimate_v2.rb +461 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_lang_string.rb +35 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_metadata.rb +115 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_property_definitions.rb +28 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_property_defs.rb +27 -0
- data/lib/archimate/file_formats/model_exchange_file_reader.rb +237 -0
- data/lib/archimate/file_formats/model_exchange_file_reader_21.rb +73 -0
- data/lib/archimate/file_formats/model_exchange_file_reader_30.rb +134 -0
- data/lib/archimate/file_formats/model_exchange_file_writer.rb +157 -0
- data/lib/archimate/file_formats/model_exchange_file_writer_21.rb +143 -0
- data/lib/archimate/file_formats/model_exchange_file_writer_30.rb +153 -0
- data/lib/archimate/file_formats/writer.rb +56 -0
- data/lib/archimate/lint/duplicate_entities.rb +121 -0
- data/lib/archimate/lint/linter.rb +146 -0
- data/lib/archimate/logging.rb +55 -0
- data/lib/archimate/maybe_io.rb +34 -0
- data/lib/archimate/progress_indicator.rb +29 -0
- data/lib/archimate/svg/archimate.css +232 -0
- data/lib/archimate/svg/child.rb +29 -0
- data/lib/archimate/svg/connection.rb +184 -0
- data/lib/archimate/svg/css_style.rb +31 -0
- data/lib/archimate/svg/diagram.rb +87 -0
- data/lib/archimate/svg/entity/and_junction.rb +14 -0
- data/lib/archimate/svg/entity/application_collaboration.rb +14 -0
- data/lib/archimate/svg/entity/application_component.rb +41 -0
- data/lib/archimate/svg/entity/application_event.rb +13 -0
- data/lib/archimate/svg/entity/application_function.rb +13 -0
- data/lib/archimate/svg/entity/application_interaction.rb +13 -0
- data/lib/archimate/svg/entity/application_interface.rb +13 -0
- data/lib/archimate/svg/entity/application_process.rb +13 -0
- data/lib/archimate/svg/entity/application_service.rb +13 -0
- data/lib/archimate/svg/entity/artifact.rb +39 -0
- data/lib/archimate/svg/entity/assessment.rb +14 -0
- data/lib/archimate/svg/entity/base_entity.rb +128 -0
- data/lib/archimate/svg/entity/business_actor.rb +14 -0
- data/lib/archimate/svg/entity/business_collaboration.rb +14 -0
- data/lib/archimate/svg/entity/business_event.rb +10 -0
- data/lib/archimate/svg/entity/business_function.rb +13 -0
- data/lib/archimate/svg/entity/business_interaction.rb +13 -0
- data/lib/archimate/svg/entity/business_interface.rb +13 -0
- data/lib/archimate/svg/entity/business_object.rb +13 -0
- data/lib/archimate/svg/entity/business_process.rb +13 -0
- data/lib/archimate/svg/entity/business_role.rb +14 -0
- data/lib/archimate/svg/entity/business_service.rb +13 -0
- data/lib/archimate/svg/entity/capability.rb +14 -0
- data/lib/archimate/svg/entity/communication_network.rb +14 -0
- data/lib/archimate/svg/entity/communication_path.rb +14 -0
- data/lib/archimate/svg/entity/constraint.rb +15 -0
- data/lib/archimate/svg/entity/contract.rb +13 -0
- data/lib/archimate/svg/entity/course_of_action.rb +14 -0
- data/lib/archimate/svg/entity/data_entity.rb +29 -0
- data/lib/archimate/svg/entity/data_object.rb +13 -0
- data/lib/archimate/svg/entity/deliverable.rb +13 -0
- data/lib/archimate/svg/entity/device.rb +48 -0
- data/lib/archimate/svg/entity/diagram_model_reference.rb +22 -0
- data/lib/archimate/svg/entity/diagram_object.rb +30 -0
- data/lib/archimate/svg/entity/distribution_network.rb +14 -0
- data/lib/archimate/svg/entity/driver.rb +14 -0
- data/lib/archimate/svg/entity/equipment.rb +16 -0
- data/lib/archimate/svg/entity/event_entity.rb +36 -0
- data/lib/archimate/svg/entity/facility.rb +16 -0
- data/lib/archimate/svg/entity/function_entity.rb +14 -0
- data/lib/archimate/svg/entity/gap.rb +21 -0
- data/lib/archimate/svg/entity/goal.rb +14 -0
- data/lib/archimate/svg/entity/group.rb +24 -0
- data/lib/archimate/svg/entity/implementation_event.rb +13 -0
- data/lib/archimate/svg/entity/infrastructure_function.rb +13 -0
- data/lib/archimate/svg/entity/infrastructure_interface.rb +13 -0
- data/lib/archimate/svg/entity/infrastructure_service.rb +13 -0
- data/lib/archimate/svg/entity/interaction_entity.rb +14 -0
- data/lib/archimate/svg/entity/interface_entity.rb +35 -0
- data/lib/archimate/svg/entity/junction.rb +19 -0
- data/lib/archimate/svg/entity/location.rb +14 -0
- data/lib/archimate/svg/entity/material.rb +14 -0
- data/lib/archimate/svg/entity/meaning.rb +40 -0
- data/lib/archimate/svg/entity/motivation_entity.rb +40 -0
- data/lib/archimate/svg/entity/network.rb +14 -0
- data/lib/archimate/svg/entity/node.rb +33 -0
- data/lib/archimate/svg/entity/node_shape.rb +64 -0
- data/lib/archimate/svg/entity/note.rb +30 -0
- data/lib/archimate/svg/entity/or_junction.rb +14 -0
- data/lib/archimate/svg/entity/outcome.rb +15 -0
- data/lib/archimate/svg/entity/path.rb +14 -0
- data/lib/archimate/svg/entity/plateau.rb +15 -0
- data/lib/archimate/svg/entity/principle.rb +15 -0
- data/lib/archimate/svg/entity/process_entity.rb +63 -0
- data/lib/archimate/svg/entity/product.rb +20 -0
- data/lib/archimate/svg/entity/rect.rb +13 -0
- data/lib/archimate/svg/entity/rect_entity.rb +25 -0
- data/lib/archimate/svg/entity/representation.rb +31 -0
- data/lib/archimate/svg/entity/requirement.rb +15 -0
- data/lib/archimate/svg/entity/resource.rb +14 -0
- data/lib/archimate/svg/entity/rounded_rect_entity.rb +23 -0
- data/lib/archimate/svg/entity/service_entity.rb +51 -0
- data/lib/archimate/svg/entity/sketch_model_sticky.rb +14 -0
- data/lib/archimate/svg/entity/stakeholder.rb +15 -0
- data/lib/archimate/svg/entity/system_software.rb +14 -0
- data/lib/archimate/svg/entity/technology_collaboration.rb +14 -0
- data/lib/archimate/svg/entity/technology_event.rb +13 -0
- data/lib/archimate/svg/entity/technology_function.rb +13 -0
- data/lib/archimate/svg/entity/technology_interaction.rb +13 -0
- data/lib/archimate/svg/entity/technology_interface.rb +13 -0
- data/lib/archimate/svg/entity/technology_process.rb +13 -0
- data/lib/archimate/svg/entity/technology_service.rb +13 -0
- data/lib/archimate/svg/entity/value.rb +27 -0
- data/lib/archimate/svg/entity/work_package.rb +14 -0
- data/lib/archimate/svg/entity.rb +93 -0
- data/lib/archimate/svg/entity_factory.rb +17 -0
- data/lib/archimate/svg/extents.rb +27 -0
- data/lib/archimate/svg/point.rb +7 -0
- data/lib/archimate/svg/svg_template.rb +27 -0
- data/lib/archimate/svg/svg_template.svg.erb +169 -0
- data/lib/archimate/version.rb +4 -0
- data/lib/archimate.rb +114 -0
- metadata +623 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "highline"
|
4
|
+
|
5
|
+
module Archimate
|
6
|
+
module Cli
|
7
|
+
class ConflictResolver
|
8
|
+
def initialize
|
9
|
+
@config = Config.instance
|
10
|
+
# TODO: pull the stdin/stdout from the app config
|
11
|
+
@hl = HighLine.new(STDIN, STDOUT)
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO: this implementation has much to be written
|
15
|
+
def resolve(conflict)
|
16
|
+
return [] unless @config.interactive
|
17
|
+
base_local_diffs = conflict.base_local_diffs
|
18
|
+
base_remote_diffs = conflict.base_remote_diffs
|
19
|
+
choice = @hl.choose do |menu|
|
20
|
+
menu.prompt = conflict
|
21
|
+
menu.choice(:local, text: base_local_diffs.map(&:to_s).join("\n\t\t"))
|
22
|
+
menu.choice(:remote, text: base_remote_diffs.map(&:to_s).join("\n\t\t"))
|
23
|
+
# menu.choice(:neither, help: "Don't choose either set of diffs")
|
24
|
+
# menu.choice(:edit, help: "Edit the diffs (coming soon)")
|
25
|
+
# menu.choice(:quit, help: "I'm in over my head. Just stop!")
|
26
|
+
menu.select_by = :index_or_name
|
27
|
+
end
|
28
|
+
case choice
|
29
|
+
when :local
|
30
|
+
base_local_diffs
|
31
|
+
when :remote
|
32
|
+
base_remote_diffs
|
33
|
+
else
|
34
|
+
error "Unexpected choice #{choice.inspect}."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Archimate
|
4
|
+
module Cli
|
5
|
+
class Convert
|
6
|
+
include Archimate::Logging
|
7
|
+
|
8
|
+
SUPPORTED_FORMATS = %w[meff2.1 meff3.0 archi nquads graphml csv cypher].freeze
|
9
|
+
|
10
|
+
attr_reader :model
|
11
|
+
|
12
|
+
def initialize(model)
|
13
|
+
@model = model
|
14
|
+
end
|
15
|
+
|
16
|
+
def convert(export_format, output_io, output_dir)
|
17
|
+
return unless output_io && model
|
18
|
+
case export_format
|
19
|
+
when "archi"
|
20
|
+
Archimate::FileFormats::ArchiFileWriter.new(model).write(output_io)
|
21
|
+
when "meff2.1"
|
22
|
+
Archimate::FileFormats::ModelExchangeFileWriter21.new(model).write(output_io)
|
23
|
+
when "meff3.0"
|
24
|
+
Archimate::FileFormats::ModelExchangeFileWriter30.new(model).write(output_io)
|
25
|
+
when "nquads"
|
26
|
+
output_io.write(Archimate::Export::NQuads.new(model).to_nq)
|
27
|
+
when "graphml"
|
28
|
+
output_io.write(Archimate::Export::GraphML.new(model).to_graphml)
|
29
|
+
when "csv"
|
30
|
+
Archimate::Export::CSVExport.new(model).to_csv(output_dir: output_dir)
|
31
|
+
when "cypher"
|
32
|
+
Archimate::Export::Cypher.new(output_io).to_cypher(model)
|
33
|
+
else
|
34
|
+
error { "Export to '#{export_format}' is not supported yet." }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Archimate
|
3
|
+
module Cli
|
4
|
+
class Diff
|
5
|
+
attr_reader :local, :remote
|
6
|
+
|
7
|
+
def self.diff(local_file, remote_file)
|
8
|
+
local = Archimate.read(local_file)
|
9
|
+
remote = Archimate.read(remote_file)
|
10
|
+
|
11
|
+
my_diff = Diff.new(local, remote)
|
12
|
+
my_diff.diff
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(local, remote)
|
16
|
+
@local = local
|
17
|
+
@remote = remote
|
18
|
+
end
|
19
|
+
|
20
|
+
def diff
|
21
|
+
diffs = Archimate.diff(local, remote)
|
22
|
+
|
23
|
+
diffs.each { |d| puts d }
|
24
|
+
|
25
|
+
puts "\n\n#{diffs.size} Differences"
|
26
|
+
|
27
|
+
diffs
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Archimate
|
5
|
+
module Cli
|
6
|
+
class DiffSummary
|
7
|
+
using DataModel::DiffableArray
|
8
|
+
using DataModel::DiffablePrimitive
|
9
|
+
|
10
|
+
DIFF_KINDS = %w(Delete Change Insert).freeze
|
11
|
+
|
12
|
+
attr_reader :local, :remote
|
13
|
+
|
14
|
+
def self.diff(local_file, remote_file, options = { verbose: true })
|
15
|
+
logger.info "Reading #{local_file}"
|
16
|
+
local = Archimate.read(local_file)
|
17
|
+
logger.info "Reading #{remote_file}"
|
18
|
+
remote = Archimate.read(remote_file)
|
19
|
+
|
20
|
+
my_diff = DiffSummary.new(local, remote)
|
21
|
+
my_diff.diff
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(local, remote)
|
25
|
+
@local = local
|
26
|
+
@remote = remote
|
27
|
+
@summary = Hash.new { |hash, key| hash[key] = Hash.new { |k_hash, k_key| k_hash[k_key] = 0 } }
|
28
|
+
end
|
29
|
+
|
30
|
+
def diff
|
31
|
+
logger.info "Calculating differences"
|
32
|
+
diffs = Archimate.diff(local, remote)
|
33
|
+
|
34
|
+
puts Color.color("Summary of differences", :headline)
|
35
|
+
puts "\n"
|
36
|
+
|
37
|
+
summary_element_diffs = diffs.group_by { |diff| diff.summary_element.class.to_s.split("::").last }
|
38
|
+
summarize_elements summary_element_diffs["Element"]
|
39
|
+
summarize "Organization", summary_element_diffs["Organization"]
|
40
|
+
summarize "Relationship", summary_element_diffs["Relationship"]
|
41
|
+
summarize_diagrams summary_element_diffs["Diagram"]
|
42
|
+
|
43
|
+
puts "Total Diffs: #{diffs.size}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def summarize(title, diffs)
|
47
|
+
return if diffs.nil? || diffs.empty?
|
48
|
+
by_kind = diffs_by_kind(diffs)
|
49
|
+
|
50
|
+
puts color(title)
|
51
|
+
DIFF_KINDS.each do |kind|
|
52
|
+
puts format(" #{color(kind)}: #{by_kind[kind]&.size}") if by_kind.key?(kind)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def diffs_by_kind(diffs)
|
57
|
+
diffs
|
58
|
+
.group_by(&:summary_element)
|
59
|
+
.each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |(summary_element, element_diffs), a|
|
60
|
+
top_level_diff = element_diffs.find { |diff| summary_element == diff.target.value }
|
61
|
+
if top_level_diff
|
62
|
+
a[top_level_diff.kind] << summary_element
|
63
|
+
else
|
64
|
+
a["Change"] << summary_element
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def summarize_elements(diffs)
|
70
|
+
return if diffs.nil? || diffs.empty?
|
71
|
+
puts Color.color("Elements", :headline)
|
72
|
+
by_layer = diffs.group_by { |diff| diff.summary_element.layer }
|
73
|
+
summarize "Business", by_layer["Business"]
|
74
|
+
summarize "Application", by_layer["Application"]
|
75
|
+
summarize "Technology", by_layer["Technology"]
|
76
|
+
summarize "Motivation", by_layer["Motivation"]
|
77
|
+
summarize "Implementation and Migration", by_layer["Implementation and Migration"]
|
78
|
+
summarize "Connectors", by_layer["Connectors"]
|
79
|
+
end
|
80
|
+
|
81
|
+
def summarize_diagrams(diffs)
|
82
|
+
return if diffs.nil? || diffs.empty?
|
83
|
+
puts Color.color("Diagrams", :headline)
|
84
|
+
|
85
|
+
by_kind = diffs_by_kind(diffs)
|
86
|
+
%w(Delete Change Insert).each do |kind|
|
87
|
+
next unless by_kind.key?(kind)
|
88
|
+
diagram_names = by_kind[kind].uniq.map(&:name)
|
89
|
+
puts " #{color(kind)}"
|
90
|
+
# TODO: make this magic number an option
|
91
|
+
diagram_names[0..14].each { |diagram_name| puts " #{diagram_name}" }
|
92
|
+
puts " ... and #{diagram_names.size - 15} more" if diagram_names.size > 15
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def color(kind)
|
97
|
+
Color.color(kind, kind)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "highline"
|
4
|
+
|
5
|
+
module Archimate
|
6
|
+
module Cli
|
7
|
+
class Duper
|
8
|
+
def initialize(model, output, mergeall = false)
|
9
|
+
@model = model
|
10
|
+
@output = output
|
11
|
+
@mergeall = mergeall
|
12
|
+
@cli = HighLine.new
|
13
|
+
@skipall = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def list
|
17
|
+
dupes = Archimate::Lint::DuplicateEntities.new(@model)
|
18
|
+
|
19
|
+
dupes.each do |element_type, _name, entities|
|
20
|
+
@output.puts "#{element_type} has potential duplicates: \n\t#{entities.join(",\n\t")}\n"
|
21
|
+
end
|
22
|
+
@output.puts "Total Possible Duplicates: #{dupes.count}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge
|
26
|
+
dupes = Archimate::Lint::DuplicateEntities.new(@model)
|
27
|
+
if dupes.empty?
|
28
|
+
@output.puts "No potential duplicates detected"
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
dupes.each do |element_type, name, entities|
|
33
|
+
handle_duplicate(element_type, name, entities)
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: make the output writer be an argument
|
37
|
+
Archimate::FileFormats::ArchiFileWriter.new(@model).write(@output)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Belongs to merge
|
41
|
+
# 1. Determine which one is the *original*
|
42
|
+
protected def handle_duplicate(element_type, name, entities)
|
43
|
+
return if @skipall
|
44
|
+
first_entity = entities.first
|
45
|
+
choice = @mergeall ? first_entity : choices(element_type, name, entities)
|
46
|
+
|
47
|
+
case choice
|
48
|
+
when :mergeall
|
49
|
+
@mergeall = true
|
50
|
+
choice = first_entity
|
51
|
+
when :skip
|
52
|
+
choice = nil
|
53
|
+
@cli.say("Skipping")
|
54
|
+
when :skipall
|
55
|
+
@skipall = true
|
56
|
+
choice = nil
|
57
|
+
@cli.say("Skipping")
|
58
|
+
end
|
59
|
+
|
60
|
+
return unless choice
|
61
|
+
@model.merge_entities(choice, entities)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Belongs to handle_duplicate
|
65
|
+
protected def choices(element_type, name, entities)
|
66
|
+
@cli.choose do |menu|
|
67
|
+
# TODO: set up a layout that permits showing a repr of the copies
|
68
|
+
# to permit making the choice of the original
|
69
|
+
menu.header = "There are #{entities.size} #{element_type}s that are potentially duplicate"
|
70
|
+
menu.prompt = "What to do with potential duplicates?"
|
71
|
+
entities.each_with_index do |entity, idx|
|
72
|
+
menu.choice(entity, help: "Merge entities into this entity")
|
73
|
+
end
|
74
|
+
# menu.choice(:merge, help: "Merge elements into a single element", text: "Merge elements")
|
75
|
+
menu.choice(:mergeall, help: "Merge all elements from here on", text: "Merge all elements")
|
76
|
+
# menu.choices(:rename, "Rename to eliminate duplicates", "Rename element(s)")
|
77
|
+
menu.choice(:skip, help: "Don't change the elements", text: "Skip")
|
78
|
+
menu.choice(:skipall, help: "Skip the rest of the duplicates", text: "Skip the rest")
|
79
|
+
menu.select_by = :index_or_name
|
80
|
+
menu.help("Help", "don't panic")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Archimate
|
4
|
+
module Cli
|
5
|
+
class Lint
|
6
|
+
def initialize(model, output_io)
|
7
|
+
@model = model
|
8
|
+
@output_io = output_io
|
9
|
+
end
|
10
|
+
|
11
|
+
def lint
|
12
|
+
Archimate::Lint::Linter.new(@model).report(@output_io)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Archimate
|
4
|
+
module Cli
|
5
|
+
class Mapper
|
6
|
+
HEADERS = %w[id name viewpoint].freeze
|
7
|
+
COL_DIVIDER = " | "
|
8
|
+
|
9
|
+
attr_reader :model
|
10
|
+
|
11
|
+
def initialize(model, output_io)
|
12
|
+
@model = model
|
13
|
+
@output = output_io
|
14
|
+
end
|
15
|
+
|
16
|
+
def header_row(widths, headers)
|
17
|
+
titles = []
|
18
|
+
widths.each_with_index { |w, i| titles << format("%-#{w}s", headers[i]) }
|
19
|
+
@output.puts titles.map { |t| Color.color(t.capitalize, %i[bold blue]) }.join(Color.color(COL_DIVIDER, :light_black))
|
20
|
+
@output.puts Color.color(widths.map { |w| "-" * w }.join("-+-"), :light_black)
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_diagrams(diagrams)
|
24
|
+
diagrams.map { |e| [e.id, e.name, e.viewpoint, e.type] }.map do |row|
|
25
|
+
row[2] = case row[3]
|
26
|
+
when "canvas:CanvasModel"
|
27
|
+
["Canvas", row[4]].compact.join(": ")
|
28
|
+
when "archimate:SketchModel"
|
29
|
+
"Sketch"
|
30
|
+
when "archimate:ArchimateDiagramModel"
|
31
|
+
DataModel::Constants::VIEWPOINTS[(row[2] || 0).to_i]
|
32
|
+
else
|
33
|
+
row[3]
|
34
|
+
end
|
35
|
+
row[0] = Color.color("#{row[0]}.png", :underline)
|
36
|
+
row
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def compute_column_widths(diagrams, headers)
|
41
|
+
initial_widths = headers.map(&:size)
|
42
|
+
diagrams.each_with_object(initial_widths) do |diagram, memo|
|
43
|
+
diagram.slice(0, headers.size).each_with_index do |o, i|
|
44
|
+
memo[i] = !o.nil? && Color.uncolor(o).size > memo[i] ? Color.uncolor(o).size : memo[i]
|
45
|
+
end
|
46
|
+
memo
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def output_diagrams(diagrams, widths)
|
51
|
+
diagrams.sort_by { |a| a[1] }.each do |m|
|
52
|
+
row = []
|
53
|
+
m.slice(0, widths.size).each_with_index { |c, i| row << format("%-#{widths[i]}s", c) }
|
54
|
+
@output.puts row.join(Color.color(COL_DIVIDER, :light_black))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_organization_hash(organizations, parent = "", hash = {})
|
59
|
+
organizations.each_with_object(hash) do |i, a|
|
60
|
+
organization_path = [parent, i.name].join("/")
|
61
|
+
a[organization_path] = i
|
62
|
+
build_organization_hash(i.organizations, organization_path, a)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def map
|
67
|
+
widths = compute_column_widths(process_diagrams(model.diagrams), HEADERS)
|
68
|
+
adjusted_widths = widths.inject(COL_DIVIDER.size * (HEADERS.size - 1), &:+)
|
69
|
+
header_row(widths, HEADERS)
|
70
|
+
organization_paths = build_organization_hash(model.organizations)
|
71
|
+
organization_paths.keys.sort.each do |organization_name|
|
72
|
+
diagrams = organization_paths[organization_name].items.map { |i| model.lookup(i) }.select { |i| i.is_a?(DataModel::Diagram) }
|
73
|
+
next if diagrams.empty?
|
74
|
+
@output.puts(Color.color(format("%-#{adjusted_widths}s", organization_name), %i[bold green on_light_black]))
|
75
|
+
output_diagrams(process_diagrams(diagrams), widths)
|
76
|
+
end
|
77
|
+
|
78
|
+
@output.puts "\n#{model.diagrams.size} Diagrams"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
@@ -0,0 +1,109 @@
|
|
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
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Archimate
|
4
|
+
module Cli
|
5
|
+
class Stats
|
6
|
+
attr_reader :output_io
|
7
|
+
attr_reader :model
|
8
|
+
|
9
|
+
def initialize(model, output_io)
|
10
|
+
@model = model
|
11
|
+
@output_io = output_io
|
12
|
+
end
|
13
|
+
|
14
|
+
def statistics
|
15
|
+
output_io.puts Color.color("#{model.name} ArchiMate Model Statistics\n", :headline)
|
16
|
+
|
17
|
+
output_io.puts "Elements:"
|
18
|
+
elements_by_layer.each do |layer, elements|
|
19
|
+
output_io.puts row(layer, elements.size, layer)
|
20
|
+
end
|
21
|
+
output_io.puts row("Total Elements", model.elements.size, :horizontal_line)
|
22
|
+
output_io.puts row("Relationships", model.relationships.size, :Relationship)
|
23
|
+
output_io.puts row("Diagrams", model.diagrams.size, :Diagram)
|
24
|
+
end
|
25
|
+
|
26
|
+
def elements_by_layer
|
27
|
+
@elements_by_layer ||= model.elements.group_by(&:layer)
|
28
|
+
end
|
29
|
+
|
30
|
+
def title_width
|
31
|
+
@title_width ||= (elements_by_layer.keys + ["Total Elements"]).map(&:size).max
|
32
|
+
end
|
33
|
+
|
34
|
+
def row(title, size, color)
|
35
|
+
@title_format_str ||= "%#{title_width}s"
|
36
|
+
@count_format_str ||= "%7d"
|
37
|
+
|
38
|
+
Color.color(format(@title_format_str, title), color) + format(@count_format_str, size)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Archimate
|
4
|
+
module Cli
|
5
|
+
# This class is used to export SVG diagrams as defined in the given model
|
6
|
+
class Svger
|
7
|
+
def self.export_svgs(archi_file, output_dir)
|
8
|
+
new(Archimate.read(archi_file).diagrams, output_dir).export_svgs
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(diagrams, output_dir)
|
12
|
+
@diagrams = diagrams
|
13
|
+
@output_dir = output_dir
|
14
|
+
end
|
15
|
+
|
16
|
+
def export_svgs
|
17
|
+
progress = ProgressIndicator.new(total: @diagrams.size, title: "Writing SVGs")
|
18
|
+
@diagrams.each do |diagram|
|
19
|
+
export(diagram)
|
20
|
+
progress.increment
|
21
|
+
end
|
22
|
+
ensure
|
23
|
+
progress.finish
|
24
|
+
end
|
25
|
+
|
26
|
+
def export(diagram, file_name = nil)
|
27
|
+
file_name = Cli.process_svg_filename(file_name || diagram.id)
|
28
|
+
File.open(File.join(@output_dir, file_name), "wb") do |svg_file|
|
29
|
+
svg_file.write(Svg::Diagram.new(diagram).to_svg)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.process_svg_filename(name)
|
35
|
+
file_name = name.strip.tr("/", "-")
|
36
|
+
file_name += ".svg" unless file_name =~ /\.svg$/
|
37
|
+
file_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "highline"
|
4
|
+
|
5
|
+
HighLine.color_scheme = HighLine::ColorScheme.new do |cs|
|
6
|
+
cs[:headline] = %i[underline bold yellow on_black]
|
7
|
+
cs[:horizontal_line] = %i[bold white]
|
8
|
+
cs[:even_row] = [:green]
|
9
|
+
cs[:odd_row] = [:magenta]
|
10
|
+
cs[:error] = %i[bold red]
|
11
|
+
cs[:warning] = %i[bold yellow]
|
12
|
+
cs[:debug] = [:gray]
|
13
|
+
cs[:insert] = %i[bold green]
|
14
|
+
cs[:change] = %i[bold yellow]
|
15
|
+
cs[:move] = %i[bold yellow]
|
16
|
+
cs[:delete] = %i[bold red]
|
17
|
+
cs[:Business] = %i[black on_light_yellow]
|
18
|
+
cs[:Application] = %i[black on_light_blue]
|
19
|
+
cs[:Technology] = %i[black on_light_green]
|
20
|
+
cs[:Motivation] = %i[black on_light_magenta]
|
21
|
+
cs[:"Implementation and Migration"] = %i[black on_light_red]
|
22
|
+
cs[:Physical] = %i[black on_light_green]
|
23
|
+
cs[:Connectors] = %i[black on_light_gray]
|
24
|
+
cs[:unknown_layer] = %i[black on_gray]
|
25
|
+
cs[:Model] = [:cyan]
|
26
|
+
cs[:Connection] = [:blue]
|
27
|
+
cs[:Organization] = [:cyan]
|
28
|
+
cs[:Relationship] = %i[black on_light_gray]
|
29
|
+
cs[:Diagram] = %i[black on_cyan]
|
30
|
+
cs[:path] = [:light_blue]
|
31
|
+
end
|
32
|
+
|
33
|
+
module Archimate
|
34
|
+
class Color
|
35
|
+
def self.layer_color(layer, str)
|
36
|
+
layer_sym = layer.to_sym
|
37
|
+
sym = HighLine.color_scheme.include?(layer_sym) ? layer_sym : :unknown_layer
|
38
|
+
color(str, sym)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.data_model(str)
|
42
|
+
layer_color(str, str)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.color(str, args)
|
46
|
+
HighLine.color(str, args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.uncolor(str)
|
50
|
+
HighLine.uncolor(str)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|