ea 0.6.50 → 0.6.52

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b916fbabbb74feb86f428a75fc8619acad646f676767544c1128e0ba635d3f52
4
- data.tar.gz: dc470c0abf5864ccf0fa0c5d42d76a429b2491c30df15e6c19ff1231416b88f7
3
+ metadata.gz: 592e737c7721bcd03e989dc13572a79c4e8aa53aa654daad717398edb66377ef
4
+ data.tar.gz: 351a11a714b03d40859c3ad86000eb22efc9edde45d3bb376961fd0f1cbad44d
5
5
  SHA512:
6
- metadata.gz: 49e1694bc33b133631b8baae3abe2cc0e0b154b06dbc1dc39b1ad6801b781939a80ab17edb63eb07bb58726aeff81d6210e3ae9198172cee2e68943cb7ae9de6
7
- data.tar.gz: fc902135136a3103cc8456651d56c2945320f208aebfc8fd72be47c20bc389f9206f0704e559e7c824b2a1818dd238e5ab02c4559f48992e2047babd7d572ea1
6
+ metadata.gz: e6cdf30f3261579e60f8d72a48b50adf011d4a2afc6398de8edff40ee2eeb701c4a2f605262714fadd19d6e28d2144df3fb7fa51bb04ce4b9306b04beda16df1
7
+ data.tar.gz: 0d0cb7de7ad76775de6751904ea3de403d8d645ad3c7acfee0d4394237d0d330846927ba8d720cf7dd5293c44c9c46c3c8c9bd604119962c45dc98ffc34423bb
@@ -10,6 +10,7 @@ module Ea
10
10
  attribute :name, :string
11
11
  attribute :classifier_id, :string
12
12
  attribute :classifier_name, :string
13
+ attribute :stereotype, :string
13
14
  attribute :package_id, :string
14
15
  attribute :package_name, :string
15
16
  attribute :qualified_name, :string
@@ -22,6 +23,7 @@ module Ea
22
23
  map "name", to: :name
23
24
  map "classifierId", to: :classifier_id
24
25
  map "classifierName", to: :classifier_name
26
+ map "stereotype", to: :stereotype
25
27
  map "packageId", to: :package_id
26
28
  map "packageName", to: :package_name
27
29
  map "qualifiedName", to: :qualified_name
@@ -112,12 +112,14 @@ module Ea
112
112
  end
113
113
 
114
114
  # EA stores the element font size in tenths of a point in the
115
- # objectstyle string. 0 means "use default", and 80/90 (8pt /
116
- # 9pt) are EA's classic defaults the dialogs write back when
117
- # the user never customized — verified against EA-published
118
- # reference images, where such elements render at the theme
119
- # size. Only genuinely custom sizes are surfaced as explicit
120
- # element fonts.
115
+ # objectstyle string, SHIFTED one point up from what it
116
+ # renders: explicit fontsz 100 renders 9pt (0D31AED1: every
117
+ # element stores 100, all 321 texts publish at 9pt), 70
118
+ # renders 6pt, 140 renders 13pt. 0 means "use default", and
119
+ # 80/90 (8pt / 9pt) are EA's classic defaults the dialogs
120
+ # write back when the user never customized — such elements
121
+ # render at the theme size. Only genuinely custom sizes are
122
+ # surfaced as explicit element fonts.
121
123
  USE_DEFAULT_FONTSZ = [0, 80, 90].freeze
122
124
 
123
125
  def font_size_from(style_hash)
@@ -126,23 +128,9 @@ module Ea
126
128
 
127
129
  return nil if raw.to_i.zero?
128
130
 
129
- stored = (raw.to_f / 10).round
130
- return stored - 1 if note_element? && stored > 1
131
-
132
131
  return nil if USE_DEFAULT_FONTSZ.include?(raw.to_i)
133
132
 
134
- stored
135
- end
136
-
137
- # Text/Note elements carry their real font size in fontsz
138
- # shifted by one point (stored 100 renders 9pt, 80 renders
139
- # 7pt - corpus-verified on EA-published note SVGs);
140
- # classifiers keep the use-default convention.
141
- def note_element?
142
- return false unless database && @current_obj_row
143
-
144
- obj = database.find_object(@current_obj_row.ea_object_id.to_i)
145
- obj&.object_type.to_s == "Text"
133
+ (raw.to_f / 10).round - 1
146
134
  end
147
135
 
148
136
  def build_connectors(diagram_row)
@@ -36,6 +36,7 @@ module Ea
36
36
  name: row.name.to_s,
37
37
  classifier_id: classifier_id_for(row),
38
38
  classifier_name: classifier_name_for(row),
39
+ stereotype: row.stereotype.to_s,
39
40
  package_id: package_id_for(row),
40
41
  package_name: package_name_for(row),
41
42
  qualified_name: qualified_name_for(row),
@@ -7,9 +7,10 @@ module Ea
7
7
  # Instance slot compartment. Renders each slot on an
8
8
  # InstanceSpecification as "name op value", then appends
9
9
  # "(from PackageName)" in italics below the slots when the
10
- # instance has a classifier. Skipped entirely when the model
11
- # element is not an InstanceSpecification or has neither
12
- # slots nor a classifier.
10
+ # instance is foreign to the diagram's package. Skipped
11
+ # entirely when the model element is not an
12
+ # InstanceSpecification or has neither slots nor a
13
+ # classifier.
13
14
  module InstanceSlots
14
15
  ROW_OFFSET = 13
15
16
  SUBTITLE_EXTRA_OFFSET = 10
@@ -21,7 +22,7 @@ module Ea
21
22
 
22
23
  instance = context.model_element
23
24
  slots = instance.slots || []
24
- subtitle = from_package_subtitle(instance)
25
+ subtitle = from_package_subtitle(instance, context.diagram)
25
26
  return nil if slots.empty? && subtitle.nil?
26
27
 
27
28
  first_y = context.geometry.attr_first_y
@@ -44,7 +45,17 @@ module Ea
44
45
  end
45
46
  module_function :instance?
46
47
 
47
- def from_package_subtitle(instance)
48
+ # EA draws "(from PackageName)" only for instances FOREIGN
49
+ # to the diagram's own package: every instance diagram in
50
+ # the corpus has local instances and EA renders the
51
+ # subtitle nowhere (FC590D99: 23 local FIG2010 instances,
52
+ # zero "(from" lines in the published SVG).
53
+ def from_package_subtitle(instance, diagram)
54
+ if diagram && instance.package_id && diagram.package_id &&
55
+ instance.package_id == diagram.package_id
56
+ return nil
57
+ end
58
+
48
59
  pkg = instance.package_name
49
60
  return nil if pkg.nil? || pkg.empty?
50
61
 
@@ -22,6 +22,7 @@ module Ea
22
22
  :bounds_width, :font_size, :family,
23
23
  :off_canvas_parent_name,
24
24
  :foreign_package_name,
25
+ :suppress_stereotypes,
25
26
  keyword_init: true)
26
27
 
27
28
  PROVIDERS = [
@@ -39,9 +40,18 @@ module Ea
39
40
  end
40
41
 
41
42
  # Greedy word wrap measured with the header's own font.
42
- # EA wraps overflowing headers at word boundaries
43
- # (81F92FC7: instance header "new ownership: LA_Right"
43
+ # EA wraps overflowing headers at word boundaries and after
44
+ # "::" qualifiers (F851A657: class "Content
45
+ # information::MD_FeatureCatalogueDescription" renders as
46
+ # "Content information::" / "MD_FeatureCatalogueDescription";
47
+ # 81F92FC7: instance header "new ownership: LA_Right"
44
48
  # renders as "new ownership:" / "LA_Right").
49
+ # Headers wrap against a USABLE width of box width - 6:
50
+ # EA wrapped "SU_PB2: LA_SpatialUnit" (parts 36 + 59 +
51
+ # space 4 = 99) inside a 100px box, while
52
+ # "FuelStation: LA_BAUnit" (94) stayed single in a 100px
53
+ # box - the corpus boundary is margin 5 wrapped vs 6
54
+ # unwrapped (FC590D99 vs CBC03448).
45
55
  def self.wrap_words(text, context, weight)
46
56
  font_style = weight == :bold_italic ? "italic" : "normal"
47
57
  width = lambda do |s|
@@ -51,15 +61,17 @@ module Ea
51
61
  ).round
52
62
  end
53
63
  return [text] if context.bounds_width.nil?
54
- return [text] if width.call(text) <= context.bounds_width.to_i
64
+ usable = context.bounds_width.to_i - 6
65
+ return [text] if width.call(text) <= usable
55
66
 
67
+ segments = wrap_segments(text)
56
68
  lines = []
57
69
  current = +""
58
- text.split(" ").each do |word|
59
- candidate = current.empty? ? word : "#{current} #{word}"
60
- if !current.empty? && width.call(candidate) > context.bounds_width.to_i
70
+ segments.each do |segment|
71
+ candidate = current + segment
72
+ if !current.empty? && width.call(candidate) > usable
61
73
  lines << current
62
- current = word
74
+ current = +segment.lstrip
63
75
  else
64
76
  current = candidate
65
77
  end
@@ -67,6 +79,32 @@ module Ea
67
79
  lines << current unless current.empty?
68
80
  lines
69
81
  end
82
+
83
+ # Break opportunities: after each space and after each
84
+ # "::". Each segment carries the separator that PRECEDES
85
+ # it ("" after "::", " " after a word), so joining all
86
+ # segments reproduces the original text exactly.
87
+ def self.wrap_segments(text)
88
+ segments = []
89
+ pending = ""
90
+ text.split(" ").each do |word|
91
+ chunk = +""
92
+ word.split(/(::)/).each_slice(2) do |part, sep|
93
+ chunk << pending << part
94
+ pending = ""
95
+ if sep
96
+ chunk << sep
97
+ segments << chunk.dup
98
+ chunk.clear
99
+ end
100
+ end
101
+ unless chunk.empty?
102
+ segments << chunk.dup
103
+ pending = " "
104
+ end
105
+ end
106
+ segments
107
+ end
70
108
  end
71
109
  end
72
110
  end
@@ -6,22 +6,37 @@ module Ea
6
6
  module Element
7
7
  module HeaderLineProvider
8
8
  # InstanceSpecification: short-circuits the pipeline. The
9
- # header for an instance is "name: Classifier" on one bold
10
- # line, word-wrapped when it overflows the box (81F92FC7:
11
- # "new ownership:" / "LA_Right").
9
+ # header for an instance is its own «stereotype» line (the
10
+ # t_object.Stereotype value, verbatim - «featureType»,
11
+ # «FeatureType», «type» …) followed by "name: Classifier"
12
+ # on one bold line, word-wrapped when it overflows the box
13
+ # (81F92FC7: "new ownership:" / "LA_Right"). The stereotype
14
+ # line is suppressed by the diagram pdata flag
15
+ # HideEStereo=1 (FC590D99 shows it; the Annex C "Case"
16
+ # diagrams with HideEStereo=1 show none).
12
17
  class InstanceSpec
13
18
  def self.call(context)
14
19
  inst = context.classifier
15
20
  return [] unless inst.is_a?(Ea::Model::InstanceSpecification)
16
21
 
22
+ lines = stereotype_lines(inst, context)
17
23
  label = inst.name.to_s
18
24
  if inst.classifier_name && !inst.classifier_name.empty?
19
25
  label = "#{label}: #{inst.classifier_name}"
20
26
  end
21
- HeaderLinePipeline.wrap_words(label, context, :bold).map do |line|
27
+ lines + HeaderLinePipeline.wrap_words(label, context, :bold).map do |line|
22
28
  [line, :bold]
23
29
  end
24
30
  end
31
+
32
+ def self.stereotype_lines(inst, context)
33
+ return [] if context.suppress_stereotypes
34
+
35
+ st = inst.stereotype.to_s
36
+ return [] if st.empty?
37
+
38
+ [["«#{st}»", :normal]]
39
+ end
25
40
  end
26
41
  end
27
42
  end
@@ -58,7 +58,11 @@ module Ea
58
58
  name, context.font_size || 9, nil,
59
59
  family: context.family, weight: "700", style: font_style
60
60
  ).round
61
- len > context.bounds_width
61
+ # Same usable width as wrap_words: box width - 6.
62
+ # EA wraps "Content information::MD_FeatureCatalogue-
63
+ # Description" (parts 86 + 137 + space 4 = 227) inside
64
+ # a 230px box (F851A657).
65
+ len > context.bounds_width.to_i - 6
62
66
  end
63
67
 
64
68
  # EA renders the plain classifier name in element headers;
@@ -17,6 +17,10 @@ module Ea
17
17
  def self.call(context)
18
18
  classifier = context.classifier
19
19
  return [] if classifier.is_a?(Ea::Model::InstanceSpecification)
20
+ # Diagram pdata HideEStereo=1 suppresses classifier
21
+ # stereotype lines too (the Annex C "Case" diagrams
22
+ # render zero «...» lines, classes included).
23
+ return [] if context.suppress_stereotypes
20
24
 
21
25
  labels = label_for(classifier, context.umldi_keyword)
22
26
  return [] if labels.empty?
@@ -126,7 +126,8 @@ module Ea
126
126
  font_size: size,
127
127
  family: family,
128
128
  off_canvas_parent_name: parent_name,
129
- foreign_package_name: foreign_package_name_for(classifier)) : []
129
+ foreign_package_name: foreign_package_name_for(classifier),
130
+ suppress_stereotypes: suppress_stereotypes?) : []
130
131
  is_classifier = classifier.is_a?(Ea::Model::Classifier)
131
132
  attr_lines = if is_classifier && show_attributes? &&
132
133
  !classifier.is_a?(Ea::Model::Interface) &&
@@ -262,7 +263,7 @@ module Ea
262
263
  first = header_lines.first
263
264
  return theme.compartments.header_top_padding unless first
264
265
 
265
- if first.last == :italic
266
+ if first.last == :italic || first.last == :italic_tight
266
267
  6 # ghost-led: EA seats the right-aligned ghost at +13
267
268
  elsif first.first.to_s.start_with?("«")
268
269
  enumeration_or_interface?(classifier) ? 6 : 9
@@ -276,6 +277,14 @@ module Ea
276
277
  classifier.is_a?(Ea::Model::Interface)
277
278
  end
278
279
 
280
+ # t_diagram pdata HideEStereo=1 suppresses every stereotype
281
+ # line on the diagram - instance boxes ("Case C25" renders
282
+ # no «featureType» despite every instance carrying one) and
283
+ # classifier boxes alike.
284
+ def suppress_stereotypes?
285
+ diagram.style.to_s.match?(/HideEStereo=1/)
286
+ end
287
+
279
288
  # A stereotype group header («Property») sits 4px below where
280
289
  # plain rows start (divider+18 vs divider+14 at 7pt).
281
290
  def attr_first_offset_for(attr_lines)
@@ -42,8 +42,11 @@ module Ea
42
42
  explicit = element&.font_size
43
43
  explicit = nil if explicit&.zero?
44
44
  return explicit if explicit
45
- return diagram_default_size if diagram_default_size
46
45
 
46
+ # An element's own explicit fontsz never re-sizes its
47
+ # neighbours: default elements always render at the theme
48
+ # size (9BA1CE54: one 9pt label + six use-default
49
+ # instances; EA publishes the six at 7pt).
47
50
  DEFAULT_ELEMENT_FONT_SIZE
48
51
  end
49
52
 
@@ -77,13 +80,6 @@ module Ea
77
80
  most_common(families)
78
81
  end
79
82
 
80
- def diagram_default_size
81
- sizes = element_sizes
82
- return nil if sizes.empty?
83
-
84
- most_common(sizes)
85
- end
86
-
87
83
  FALLBACK_DEFAULT_FAMILY = "Calibri"
88
84
 
89
85
  def most_common(values)
@@ -97,10 +93,6 @@ module Ea
97
93
  (diagram.elements || []).map(&:font_family).compact
98
94
  end
99
95
 
100
- def element_sizes
101
- (diagram.elements || []).map(&:font_size).compact.reject(&:zero?)
102
- end
103
-
104
96
  def tally_values(values)
105
97
  values.each_with_object(Hash.new(0)) { |v, acc| acc[v] += 1 }
106
98
  end
data/lib/ea/version.rb CHANGED
@@ -4,5 +4,5 @@ module Ea
4
4
  # Gem release version. Extracted to its own file so the
5
5
  # `gem-release` tool's `gem bump` can find and update it via the
6
6
  # standard `lib/<gem>/version.rb` convention.
7
- VERSION = "0.6.50"
7
+ VERSION = "0.6.52"
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.50
4
+ version: 0.6.52
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.