activefacts 0.8.10 → 0.8.12
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.
- data/Rakefile +3 -2
- data/bin/afgen +25 -23
- data/bin/cql +9 -8
- data/css/orm2.css +23 -3
- data/examples/CQL/CompanyDirectorEmployee.cql +1 -1
- data/examples/CQL/Diplomacy.cql +3 -3
- data/examples/CQL/Insurance.cql +27 -21
- data/examples/CQL/Metamodel.cql +12 -8
- data/examples/CQL/MetamodelNext.cql +172 -149
- data/examples/CQL/ServiceDirector.cql +17 -17
- data/examples/CQL/Supervision.cql +3 -5
- data/examples/CQL/WaiterTips.cql +1 -1
- data/examples/CQL/unit.cql +1 -1
- data/index.html +0 -0
- data/lib/activefacts/cql/FactTypes.treetop +41 -8
- data/lib/activefacts/cql/Language/English.treetop +10 -0
- data/lib/activefacts/cql/ObjectTypes.treetop +3 -1
- data/lib/activefacts/cql/Terms.treetop +34 -53
- data/lib/activefacts/cql/compiler.rb +1 -1
- data/lib/activefacts/cql/compiler/clause.rb +21 -8
- data/lib/activefacts/cql/compiler/constraint.rb +3 -1
- data/lib/activefacts/cql/compiler/entity_type.rb +1 -1
- data/lib/activefacts/cql/compiler/fact_type.rb +9 -3
- data/lib/activefacts/cql/compiler/join.rb +3 -0
- data/lib/activefacts/cql/compiler/value_type.rb +9 -4
- data/lib/activefacts/cql/parser.rb +11 -3
- data/lib/activefacts/generate/oo.rb +3 -3
- data/lib/activefacts/generate/ordered.rb +0 -4
- data/lib/activefacts/input/orm.rb +305 -250
- data/lib/activefacts/persistence/tables.rb +6 -0
- data/lib/activefacts/support.rb +18 -0
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +59 -20
- data/lib/activefacts/vocabulary/metamodel.rb +23 -13
- data/lib/activefacts/vocabulary/verbaliser.rb +5 -3
- data/spec/absorption_spec.rb +3 -2
- data/spec/cql/comparison_spec.rb +1 -3
- data/spec/cql/context_spec.rb +1 -1
- data/spec/cql/entity_type_spec.rb +2 -2
- data/spec/cql/expressions_spec.rb +2 -4
- data/spec/cql/fact_type_matching_spec.rb +55 -3
- data/spec/cql/parser/fact_types_spec.rb +3 -0
- data/spec/cql/role_matching_spec.rb +8 -7
- data/spec/cql/samples_spec.rb +10 -2
- data/spec/cql_dm_spec.rb +2 -1
- data/spec/helpers/array_matcher.rb +18 -35
- data/spec/helpers/diff_matcher.rb +34 -13
- data/spec/helpers/file_matcher.rb +27 -43
- data/spec/helpers/string_matcher.rb +23 -33
- data/spec/norma_cql_spec.rb +1 -0
- data/spec/norma_tables_spec.rb +1 -2
- metadata +95 -102
@@ -496,7 +496,9 @@ module ActiveFacts
|
|
496
496
|
|
497
497
|
def compile
|
498
498
|
@constraint = @constellation.ValueConstraint(:new)
|
499
|
-
|
499
|
+
raise "Units on value constraints are not yet processed (at line #{'REVISIT'})" if @units
|
500
|
+
# @string.line_of(node.interval.first)
|
501
|
+
|
500
502
|
@value_ranges.each do |range|
|
501
503
|
min, max = Array === range ? range : [range, range]
|
502
504
|
v_range = @constellation.ValueRange(
|
@@ -412,7 +412,7 @@ module ActiveFacts
|
|
412
412
|
debug :mode, "Creating new ValueType PresenceConstraint"
|
413
413
|
else
|
414
414
|
debug :mode, "Marking existing ValueType PresenceConstraint as preferred"
|
415
|
-
rs1.all_presence_constraint
|
415
|
+
rs1.all_presence_constraint.single.is_preferred_identifier = true
|
416
416
|
end
|
417
417
|
end
|
418
418
|
|
@@ -138,6 +138,8 @@ module ActiveFacts
|
|
138
138
|
if @fact_type.entity_type and @name != @fact_type.entity_type.name
|
139
139
|
raise "Cannot objectify fact type as #{@name} and as #{@fact_type.entity_type.name}"
|
140
140
|
end
|
141
|
+
e = @constellation.EntityType[[@vocabulary, @name]]
|
142
|
+
raise "You can't objectify #{@name}, it already exists" if e
|
141
143
|
e = @constellation.EntityType(@vocabulary, @name, :fact_type => @fact_type)
|
142
144
|
e.create_implicit_fact_types
|
143
145
|
if @pragmas
|
@@ -193,9 +195,13 @@ module ActiveFacts
|
|
193
195
|
sort_by{ |clause| clause.side_effects.cost }
|
194
196
|
fact_types = @existing_clauses.map{ |clause| clause.fact_type }.uniq.compact
|
195
197
|
|
196
|
-
return nil if fact_types.empty?
|
197
|
-
|
198
|
-
|
198
|
+
return nil if fact_types.empty? # There are no matched fact types
|
199
|
+
|
200
|
+
if @clauses.size == 1 && @existing_clauses[0].side_effects.cost != 0
|
201
|
+
debug :matching, "There's only a single clause, but it's not an exact match"
|
202
|
+
return nil
|
203
|
+
end
|
204
|
+
|
199
205
|
if (fact_types.size > 1)
|
200
206
|
# There must be only one fact type with exact matches:
|
201
207
|
if @existing_clauses[0].side_effects.cost != 0 or
|
@@ -11,6 +11,9 @@ module ActiveFacts
|
|
11
11
|
debug :join, "Creating join node #{join.all_join_node.size} for #{variable.inspect}"
|
12
12
|
variable.join_node = @constellation.JoinNode(join, join.all_join_node.size, :object_type => variable.player)
|
13
13
|
if literal = variable.refs.detect{|r| r.literal}
|
14
|
+
if literal.kind_of?(ActiveFacts::CQL::Compiler::VarRef)
|
15
|
+
literal = literal.literal
|
16
|
+
end
|
14
17
|
unit = @constellation.Unit.detect{|k, v| [v.name, v.plural_name].include? literal.unit} if literal.unit
|
15
18
|
variable.join_node.value = [literal.literal.to_s, literal.is_a?(String), unit]
|
16
19
|
end
|
@@ -16,9 +16,13 @@ module ActiveFacts
|
|
16
16
|
def compile
|
17
17
|
if (@numerator.to_f / @denominator.to_i != 1.0)
|
18
18
|
coefficient = @constellation.Coefficient(
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
@numerator,
|
20
|
+
@denominator.to_i,
|
21
|
+
!@approximately
|
22
|
+
# REVISIT: Activefacts-api is complaining at present. The following is better and should work:
|
23
|
+
# :numerator => @numerator,
|
24
|
+
# :denominator => @denominator.to_i,
|
25
|
+
# :is_precise => !@approximately
|
22
26
|
)
|
23
27
|
else
|
24
28
|
coefficient = nil
|
@@ -113,7 +117,8 @@ module ActiveFacts
|
|
113
117
|
|
114
118
|
unless @unit.empty?
|
115
119
|
unit_name, exponent = *@unit[0]
|
116
|
-
unit = @constellation.
|
120
|
+
unit = @constellation.Name[unit_name].unit ||
|
121
|
+
@constellation.Name[unit_name].unit_as_plural_name
|
117
122
|
raise "Unit #{unit_name} for value type #{@name} is not defined" unless unit
|
118
123
|
if exponent != 1
|
119
124
|
base_unit = unit
|
@@ -18,6 +18,14 @@ require 'activefacts/cql/FactTypes'
|
|
18
18
|
require 'activefacts/cql/Context'
|
19
19
|
require 'activefacts/cql/CQLParser'
|
20
20
|
|
21
|
+
module ActiveFacts
|
22
|
+
module CQL
|
23
|
+
class Parser < CQLParser
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
require 'activefacts/cql/nodes'
|
28
|
+
|
21
29
|
class Treetop::Runtime::SyntaxNode
|
22
30
|
def node_type
|
23
31
|
terminal? ? :keyword : :composite
|
@@ -33,7 +41,7 @@ module ActiveFacts
|
|
33
41
|
end
|
34
42
|
|
35
43
|
# Extend the generated parser:
|
36
|
-
class Parser
|
44
|
+
class Parser
|
37
45
|
include ActiveFacts
|
38
46
|
|
39
47
|
# The Context manages some key information revealed or needed during parsing
|
@@ -63,12 +71,12 @@ module ActiveFacts
|
|
63
71
|
end
|
64
72
|
|
65
73
|
def new_leading_adjective_term(adj, term)
|
66
|
-
index_name(@role_names, "#{adj} #{term}", term) && debug(:context, "new
|
74
|
+
index_name(@role_names, "#{adj} #{term}", term) && debug(:context, "new compound term '#{adj}- #{term}'")
|
67
75
|
true
|
68
76
|
end
|
69
77
|
|
70
78
|
def new_trailing_adjective_term(adj, term)
|
71
|
-
index_name(@role_names, "#{term} #{adj}", term) && debug(:context, "new
|
79
|
+
index_name(@role_names, "#{term} #{adj}", term) && debug(:context, "new compound term '#{term} -#{adj}'")
|
72
80
|
true
|
73
81
|
end
|
74
82
|
|
@@ -105,12 +105,12 @@ module ActiveFacts
|
|
105
105
|
|
106
106
|
# REVISIT: Consider whether NOT to use the adjective if it's a prefix of the role_name
|
107
107
|
la = preferred_role_ref.leading_adjective
|
108
|
-
role_words << la.gsub(/ /,'_') if la && la != "" and !role.role_name
|
108
|
+
role_words << la.gsub(/[- ]/,'_') if la && la != "" and !role.role_name
|
109
109
|
|
110
|
-
role_words << (role_name || role.object_type.name.gsub(/ /,'_'))
|
110
|
+
role_words << (role_name || role.object_type.name.gsub(/[- ]/,'_'))
|
111
111
|
# REVISIT: Same when trailing_adjective is a suffix of the role_name
|
112
112
|
ta = preferred_role_ref.trailing_adjective
|
113
|
-
role_words << ta.gsub(/ /,'_') if ta && ta != "" and !role_name
|
113
|
+
role_words << ta.gsub(/[- ]/,'_') if ta && ta != "" and !role_name
|
114
114
|
n = role_words.map{|w| w.gsub(/([a-z])([A-Z]+)/,'\1_\2').downcase}*"_"
|
115
115
|
# debug "\tresult=#{n}"
|
116
116
|
n.gsub(' ','_')
|
@@ -82,10 +82,6 @@ module ActiveFacts
|
|
82
82
|
# Skip value-type derived units
|
83
83
|
next if unit.name =~ /\^/
|
84
84
|
|
85
|
-
# Don't dump fundamental units which have normal derived units, they are implied:
|
86
|
-
next if unit.is_fundamental &&
|
87
|
-
unit.all_derivation_as_base_unit.detect{|d| d.derived_unit.name !~ /\^/}
|
88
|
-
|
89
85
|
if !done_banner
|
90
86
|
done_banner = true
|
91
87
|
units_banner
|
@@ -131,6 +131,7 @@ module ActiveFacts
|
|
131
131
|
# Everything we build will be indexed here:
|
132
132
|
@by_id = {}
|
133
133
|
|
134
|
+
list_subtypes
|
134
135
|
read_entity_types
|
135
136
|
read_value_types
|
136
137
|
read_fact_types
|
@@ -171,56 +172,77 @@ module ActiveFacts
|
|
171
172
|
# Now the value types:
|
172
173
|
value_types = []
|
173
174
|
x_value_types = @x_model.xpath("orm:Objects/orm:ValueType")
|
174
|
-
|
175
|
+
@value_type_id_read = {}
|
175
176
|
x_value_types.each{|x|
|
176
|
-
id = x['id']
|
177
|
-
name = (x['Name'] || "").gsub(/\s+/,' ').gsub(/-/,'_').strip
|
178
|
-
name = nil if name.size == 0
|
179
|
-
|
180
177
|
next if x['IsImplicitBooleanValue']
|
178
|
+
value_types << read_value_type(x)
|
179
|
+
}
|
180
|
+
end
|
181
181
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
182
|
+
def read_value_type x
|
183
|
+
id = x['id']
|
184
|
+
return if @value_type_id_read[id] # Don't read the same value type twice
|
185
|
+
@value_type_id_read[id] = true
|
186
|
+
|
187
|
+
name = (x['Name'] || "").gsub(/\s+/,' ').gsub(/-/,'_').strip
|
188
|
+
name = nil if name.size == 0
|
189
|
+
|
190
|
+
cdt = x.xpath('orm:ConceptualDataType')[0]
|
191
|
+
scale = cdt['Scale']
|
192
|
+
scale = scale != "" && scale.to_i
|
193
|
+
length = cdt['Length']
|
194
|
+
length = length != "" && length.to_i
|
195
|
+
length = nil if length <= 0
|
196
|
+
base_type = @x_by_id[cdt['ref']]
|
197
|
+
type_name = "#{base_type.name}"
|
198
|
+
type_name.sub!(/^orm:/,'')
|
199
|
+
|
200
|
+
type_name.sub!(/DataType\Z/,'')
|
201
|
+
type_name = DataTypeMapping[type_name] || type_name
|
202
|
+
if !length and type_name =~ /\(([0-9]+)\)/
|
203
|
+
length = $1.to_i
|
204
|
+
end
|
205
|
+
type_name = type_name.sub(/\(([0-9]*)\)/,'')
|
206
|
+
|
207
|
+
subtype_roles = x.xpath("orm:PlayedRoles/orm:SubtypeMetaRole")
|
208
|
+
if !subtype_roles.empty?
|
209
|
+
subtype_role_id = subtype_roles[0]['ref']
|
210
|
+
subtype_role = @x_by_id[subtype_role_id]
|
211
|
+
subtyping_fact_roles = subtype_role.parent
|
212
|
+
supertype_id = subtyping_fact_roles.xpath("orm:SupertypeMetaRole/orm:RolePlayer")[0]['ref']
|
213
|
+
x_supertype = @x_by_id[supertype_id]
|
214
|
+
read_value_type x_supertype unless @value_type_id_read[supertype_id]
|
215
|
+
supertype = @by_id[supertype_id]
|
216
|
+
supertype_name = x_supertype['Name']
|
217
|
+
raise "Supertype of #{name} is post-defined but recursiving processing failed" unless supertype
|
218
|
+
raise "Supertype #{supertype_name} of #{name} is not a value type" unless supertype.kind_of? ActiveFacts::Metamodel::ValueType
|
219
|
+
value_super_type = @constellation.ValueType(@vocabulary, supertype_name)
|
220
|
+
else
|
199
221
|
# REVISIT: Need to handle standard types better here:
|
200
222
|
value_super_type = type_name != name ? @constellation.ValueType(@vocabulary, type_name) : nil
|
223
|
+
end
|
201
224
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
ar = @constellation.AllowedRange(vt.value_constraint, v_range)
|
221
|
-
}
|
225
|
+
@by_id[id] =
|
226
|
+
vt = @constellation.ValueType(@vocabulary, name)
|
227
|
+
vt.supertype = value_super_type
|
228
|
+
vt.length = length if length
|
229
|
+
vt.scale = scale if scale && scale != 0
|
230
|
+
independent = x['IsIndependent']
|
231
|
+
vt.is_independent = true if independent && independent == 'true'
|
232
|
+
personal = x['IsPersonal']
|
233
|
+
vt.pronoun = 'personal' if personal && personal == 'true'
|
234
|
+
|
235
|
+
x_vr = x.xpath("orm:ValueRestriction/orm:ValueConstraint")
|
236
|
+
x_vr.each{|vr|
|
237
|
+
x_ranges = vr.xpath("orm:ValueRanges/orm:ValueRange")
|
238
|
+
next if x_ranges.size == 0
|
239
|
+
vt.value_constraint = @by_id[vr['id']] = @constellation.ValueConstraint(:new)
|
240
|
+
x_ranges.each{|x_range|
|
241
|
+
v_range = value_range(x_range)
|
242
|
+
ar = @constellation.AllowedRange(vt.value_constraint, v_range)
|
222
243
|
}
|
223
244
|
}
|
245
|
+
vt
|
224
246
|
end
|
225
247
|
|
226
248
|
def value_range(x_range)
|
@@ -254,9 +276,7 @@ module ActiveFacts
|
|
254
276
|
end
|
255
277
|
end
|
256
278
|
|
257
|
-
def
|
258
|
-
# Handle the subtype fact types:
|
259
|
-
facts = []
|
279
|
+
def list_subtypes
|
260
280
|
@x_subtypes = @x_model.xpath("orm:Facts/orm:SubtypeFact")
|
261
281
|
if @document.namespaces['xmlns:oialtocdb']
|
262
282
|
oialtocdb = @document.xpath("ormRoot:ORM2/oialtocdb:MappingCustomization")
|
@@ -264,6 +284,11 @@ module ActiveFacts
|
|
264
284
|
else
|
265
285
|
@x_mappings = []
|
266
286
|
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def read_subtypes
|
290
|
+
# Handle the subtype fact types:
|
291
|
+
facts = []
|
267
292
|
|
268
293
|
debug :orm, "Reading sub-types" do
|
269
294
|
@x_subtypes.each{|x|
|
@@ -287,6 +312,10 @@ module ActiveFacts
|
|
287
312
|
throw "For Subtype fact #{name}, the subtype #{subtype_id} was not found" if !subtype
|
288
313
|
debug :orm, "#{subtype.name} is a subtype of #{supertype.name}"
|
289
314
|
|
315
|
+
# We already handled ValueType subtyping:
|
316
|
+
next if subtype.kind_of? ActiveFacts::Metamodel::ValueType or
|
317
|
+
supertype.kind_of? ActiveFacts::Metamodel::ValueType
|
318
|
+
|
290
319
|
inheritance_fact = @constellation.TypeInheritance(subtype, supertype, :fact_type_id => :new)
|
291
320
|
if x["IsPrimary"] == "true" or # Old way
|
292
321
|
x["PreferredIdentificationPath"] == "true" # Newer
|
@@ -339,19 +368,14 @@ module ActiveFacts
|
|
339
368
|
next if x.xpath("orm:DerivationRule").size > 0
|
340
369
|
throw "Nested fact #{fact_id} not found" if !fact_type
|
341
370
|
|
342
|
-
#
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
nested_type = @constellation.EntityType(@vocabulary, name)
|
351
|
-
independent = x['IsIndependent']
|
352
|
-
nested_type.is_independent = true if independent && independent == 'true' && !is_implied
|
353
|
-
nested_type.fact_type = fact_type
|
354
|
-
end
|
371
|
+
debug :orm, "NestedType #{name} is #{id}, nests #{fact_type.fact_type_id}"
|
372
|
+
@nested_types <<
|
373
|
+
@by_id[id] =
|
374
|
+
nested_type = @constellation.EntityType(@vocabulary, name)
|
375
|
+
independent = x['IsIndependent']
|
376
|
+
nested_type.is_independent = true if independent && independent == 'true' && !is_implied
|
377
|
+
nested_type.is_implied_by_objectification = is_implied
|
378
|
+
nested_type.fact_type = fact_type
|
355
379
|
}
|
356
380
|
end
|
357
381
|
end
|
@@ -477,16 +501,24 @@ module ActiveFacts
|
|
477
501
|
role = role_ref.role
|
478
502
|
|
479
503
|
word = '\b[A-Za-z_][A-Za-z0-9_]+\b'
|
480
|
-
leading_adjectives_re = "#{word}
|
481
|
-
trailing_adjectives_re = "(?:#{word} +)
|
504
|
+
leading_adjectives_re = "#{word}-+(?: +#{word})*"
|
505
|
+
trailing_adjectives_re = "(?:#{word} +)*-+#{word}"
|
482
506
|
role_with_adjectives_re =
|
483
507
|
%r| ?(#{leading_adjectives_re})? *\{#{i}\} *(#{trailing_adjectives_re})? ?|
|
484
508
|
|
509
|
+
# A hyphenated pre-bound reading looks like this:
|
510
|
+
# <orm:Data>{0} has pre-- bound {1}</orm:Data>
|
511
|
+
|
485
512
|
text.gsub!(role_with_adjectives_re) {
|
486
513
|
# REVISIT: Don't want to strip all spaces here any more:
|
487
514
|
#puts "text=#{text.inspect}, la=#{$1.inspect}, ta=#{$2.inspect}" if $1 || $2
|
488
|
-
la = ($1||'').gsub(/\s+/,' ')
|
489
|
-
ta = ($2||'').gsub(/\s+/,' ')
|
515
|
+
la = ($1||'').gsub(/\s+/,' ') # Strip duplicate spaces
|
516
|
+
ta = ($2||'').gsub(/\s+/,' ')
|
517
|
+
# When we have "aaa-bbb" we want "aaa bbb"
|
518
|
+
# When we have "aaa- bbb" we want "aaa bbb"
|
519
|
+
# When we have "aaa-- bbb" we want "aaa-bbb"
|
520
|
+
la = la.sub(/(-)?- ?/,'\1').strip
|
521
|
+
ta = ta.sub(/ ?(-)?-/,'\1').strip
|
490
522
|
#puts "Setting leading adj #{la.inspect} from #{text.inspect} for #{role_ref.role.object_type.name}" if la != ""
|
491
523
|
# REVISIT: Dunno what's up here, but removing the "if" test makes this chuck exceptions:
|
492
524
|
role_ref.leading_adjective = la if la != ""
|
@@ -575,7 +607,9 @@ module ActiveFacts
|
|
575
607
|
# Talk about why this wasn't found - this shouldn't happen.
|
576
608
|
if (!x_nests || !implied)
|
577
609
|
#puts "="*60
|
578
|
-
|
610
|
+
# We skip creating TypeInheritance implied fact types for ValueType inheritance
|
611
|
+
return nil if x_role.name = 'orm:SubtypeMetaRole' or x_role.name = 'orm:SupertypeMetaRole'
|
612
|
+
raise "Skipping #{why}, #{x_role.name} #{id} not found"
|
579
613
|
|
580
614
|
if (x_nests)
|
581
615
|
puts "Role is on #{implied ? "implied " : ""}objectification #{x_object}"
|
@@ -795,196 +829,204 @@ module ActiveFacts
|
|
795
829
|
# object type, which might involve subtyping or objectification joins.
|
796
830
|
#
|
797
831
|
def make_joins(constraint_type, name, role_sequences)
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
remaining
|
816
|
-
|
817
|
-
|
832
|
+
begin
|
833
|
+
# Get the object types constrained for each position in the role sequences.
|
834
|
+
# Supertyping joins may be needed to reach them.
|
835
|
+
end_points = [] # An array of the common supertype for matching role_refs across the sequences
|
836
|
+
end_joins = [] # An array of booleans indicating whether any role_sequence requires a subtyping join
|
837
|
+
role_sequences[0].all_role_ref.size.times do |i|
|
838
|
+
role_refs = role_sequences.map{|rs| rs.all_role_ref.detect{|rr| rr.ordinal == i}}
|
839
|
+
if (fact_types = role_refs.map{|rr| rr.role.fact_type}).uniq.size == 1
|
840
|
+
# $stderr.puts(role_sequences.map{|rs| rs.all_role_ref.map{|rr| rr.role.fact_type.describe(rr.role)}}.inspect)
|
841
|
+
raise "In #{constraint_type} #{name} role sequence #{i}, there is a faulty join involving just 1 fact type: '#{fact_types[0].default_reading}'"
|
842
|
+
end
|
843
|
+
if (players = role_refs.map{|rr| rr.role.object_type}).uniq.size == 1
|
844
|
+
end_point = players[0]
|
845
|
+
end_joins[i] = false
|
846
|
+
else
|
847
|
+
# Can the players be joined using a subtyping join?
|
848
|
+
common_supertypes = players[1..-1].
|
849
|
+
inject(players[0].supertypes_transitive) do |remaining, player|
|
850
|
+
remaining & player.supertypes_transitive
|
851
|
+
end
|
852
|
+
end_point = common_supertypes[0]
|
818
853
|
|
819
|
-
|
820
|
-
|
854
|
+
raise "constrained roles of #{constraint_type} #{name} are incompatible (#{players.map(&:name)*', '})" if common_supertypes.size == 0
|
855
|
+
end_joins[i] = true
|
856
|
+
end
|
857
|
+
end_points[i] = end_point
|
821
858
|
end
|
822
|
-
end_points[i] = end_point
|
823
|
-
end
|
824
859
|
|
825
|
-
|
826
|
-
sequence_join_over = []
|
827
|
-
if role_sequences[0].all_role_ref.size > 1 # There are joins within each sequence.
|
860
|
+
# For each role_sequence, find the object type over which the join is implied (nil if no join)
|
828
861
|
sequence_join_over = []
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
862
|
+
if role_sequences[0].all_role_ref.size > 1 # There are joins within each sequence.
|
863
|
+
sequence_join_over = []
|
864
|
+
sequence_joined_roles = []
|
865
|
+
role_sequences.map do |rs|
|
866
|
+
join_over, joined_roles = *ActiveFacts::Metamodel.join_roles_over(rs.all_role_ref.map{|rr| rr.role})
|
867
|
+
sequence_join_over << join_over
|
868
|
+
sequence_joined_roles << joined_roles
|
869
|
+
end
|
834
870
|
end
|
835
|
-
end
|
836
|
-
|
837
|
-
# If there are no joins, we can drop out here.
|
838
|
-
if sequence_join_over.compact.empty? && !end_joins.detect{|e| true}
|
839
|
-
return
|
840
|
-
end
|
841
871
|
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
debug :join, "#{constraint_type} join constraint #{name} constrains #{
|
846
|
-
end_points.zip(end_joins).map{|(p,j)| p.name+(j ? ' & subtypes':'')}*', '
|
847
|
-
}#{
|
848
|
-
if role_sequences[0].all_role_ref.size > 1
|
849
|
-
", joined over #{
|
850
|
-
sequence_join_over.zip(sequence_joined_roles).map{|o, roles|
|
851
|
-
(o ? o.name : '(none)') +
|
852
|
-
(roles ? " to (#{roles.map{|role| role ? role.fact_type.default_reading : 'null'}*','})" : '')
|
853
|
-
}*', '}"
|
854
|
-
else
|
855
|
-
''
|
872
|
+
# If there are no joins, we can drop out here.
|
873
|
+
if sequence_join_over.compact.empty? && !end_joins.detect{|e| true}
|
874
|
+
return true
|
856
875
|
end
|
857
|
-
}" do
|
858
|
-
|
859
|
-
# There may be one join per role sequence:
|
860
|
-
role_sequences.zip(sequence_join_over||[], sequence_joined_roles||[]).map do |role_sequence, join_over, joined_roles|
|
861
|
-
# Skip if there's no join here (sequence join nor end-point subset join)
|
862
|
-
role_refs = role_sequence.all_role_ref_in_order
|
863
|
-
if !join_over and !role_refs.detect{|rr| rr.role.object_type != end_points[rr.ordinal]}
|
864
|
-
# No sequence join nor end_point join here
|
865
|
-
next
|
866
|
-
end
|
867
876
|
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
debug :join, "Making subtyping join steps from #{subtype.name} to #{end_point.name}" do
|
893
|
-
# There may be more than one supertyping level. Make the steps:
|
894
|
-
subtyping_steps = subtype_join_steps(join, subtype, end_point)
|
895
|
-
js = subtyping_steps[0]
|
896
|
-
constrained_join_role = subtyping_steps[-1].input_join_role
|
897
|
-
|
898
|
-
# Replace the constrained role and node with the supertype ones:
|
899
|
-
end_node = join.all_join_node.detect{|jn| jn.object_type == end_point }
|
900
|
-
projecting_jr = js.output_join_role
|
901
|
-
end_role = js.fact_type.all_role.detect{|r| r.object_type == end_point }
|
902
|
-
role_node = join.all_join_node.detect{|jn| jn.object_type == role_ref.role.object_type }
|
903
|
-
end
|
877
|
+
debug :join, "#{constraint_type} join constraint #{name} over #{role_sequences.map{|rs|rs.describe}*', '}"
|
878
|
+
|
879
|
+
join = nil
|
880
|
+
debug :join, "#{constraint_type} join constraint #{name} constrains #{
|
881
|
+
end_points.zip(end_joins).map{|(p,j)| (p ? p.name : 'NULL')+(j ? ' & subtypes':'')}*', '
|
882
|
+
}#{
|
883
|
+
if role_sequences[0].all_role_ref.size > 1
|
884
|
+
", joined over #{
|
885
|
+
sequence_join_over.zip(sequence_joined_roles).map{|o, roles|
|
886
|
+
(o ? o.name : '(none)') +
|
887
|
+
(roles ? " to (#{roles.map{|role| role ? role.fact_type.default_reading : 'null'}*','})" : '')
|
888
|
+
}*', '}"
|
889
|
+
else
|
890
|
+
''
|
891
|
+
end
|
892
|
+
}" do
|
893
|
+
|
894
|
+
# There may be one join per role sequence:
|
895
|
+
role_sequences.zip(sequence_join_over||[], sequence_joined_roles||[]).map do |role_sequence, join_over, joined_roles|
|
896
|
+
# Skip if there's no join here (sequence join nor end-point subset join)
|
897
|
+
role_refs = role_sequence.all_role_ref_in_order
|
898
|
+
if !join_over and !role_refs.detect{|rr| rr.role.object_type != end_points[rr.ordinal]}
|
899
|
+
# No sequence join nor end_point join here
|
900
|
+
next
|
904
901
|
end
|
905
902
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
903
|
+
# A RoleSequence for the actual join end-points
|
904
|
+
replacement_rs = @constellation.RoleSequence(:new)
|
905
|
+
|
906
|
+
join = @constellation.Join(:new)
|
907
|
+
join_node = nil
|
908
|
+
join_role = nil
|
909
|
+
role_refs.zip(joined_roles||[]).each_with_index do |(role_ref, joined_role), i|
|
910
910
|
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
911
|
+
# Each role_ref is to an object joined via joined_role to join_node (or which will be the join_node)
|
912
|
+
|
913
|
+
# Create a join node for the actual end-point (supertype of the constrained roles)
|
914
|
+
end_point = end_points[i]
|
915
|
+
unless end_point
|
916
|
+
raise "In #{constraint_type} #{name}, there is a faulty or non-translated join"
|
915
917
|
end
|
916
|
-
debug :join, "
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
debug :join, "
|
918
|
+
debug :join, "Join Node #{join.all_join_node.size} is for #{end_point.name}"
|
919
|
+
end_node = @constellation.JoinNode(join, join.all_join_node.size, :object_type => end_point)
|
920
|
+
|
921
|
+
# We're going to rewrite the constraint to constrain the supertype roles, but assume they're the same:
|
922
|
+
role_node = end_node
|
923
|
+
end_role = role_ref.role
|
924
|
+
|
925
|
+
# Create subtyping join steps at the end-point, if needed:
|
926
|
+
projecting_jr = nil
|
927
|
+
constrained_join_role = nil
|
928
|
+
if (subtype = role_ref.role.object_type) != end_point
|
929
|
+
debug :join, "Making subtyping join steps from #{subtype.name} to #{end_point.name}" do
|
930
|
+
# There may be more than one supertyping level. Make the steps:
|
931
|
+
subtyping_steps = subtype_join_steps(join, subtype, end_point)
|
932
|
+
js = subtyping_steps[0]
|
933
|
+
constrained_join_role = subtyping_steps[-1].input_join_role
|
934
|
+
|
935
|
+
# Replace the constrained role and node with the supertype ones:
|
936
|
+
end_node = join.all_join_node.detect{|jn| jn.object_type == end_point }
|
937
|
+
projecting_jr = js.output_join_role
|
938
|
+
end_role = js.fact_type.all_role.detect{|r| r.object_type == end_point }
|
939
|
+
role_node = join.all_join_node.detect{|jn| jn.object_type == role_ref.role.object_type }
|
940
|
+
end
|
928
941
|
end
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
942
|
+
|
943
|
+
raise "Internal error in #{constraint_type} #{name}: making illegal reference to join node, end role mismatch" if end_role.object_type != end_node.object_type
|
944
|
+
rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => end_role)
|
945
|
+
projecting_jr ||= (constrained_join_role = @constellation.JoinRole(end_node, end_role))
|
946
|
+
projecting_jr.role_ref = rr # Project this RoleRef
|
947
|
+
|
948
|
+
if join_over
|
949
|
+
if !join_node # Create the JoinNode when processing the first role
|
950
|
+
debug :join, "Join Node #{join.all_join_node.size} is over #{join_over.name}"
|
951
|
+
join_node = @constellation.JoinNode(join, join.all_join_node.size, :object_type => join_over)
|
952
|
+
end
|
953
|
+
debug :join, "Making join step from #{end_point.name} to #{join_over.name}" do
|
954
|
+
rs = @constellation.RoleSequence(:new)
|
955
|
+
# Detect the fact type over which we're joining (may involve objectification)
|
956
|
+
raise "Internal error in #{constraint_type} #{name}: making illegal reference to join node, object type mismatch" if role_ref.role.object_type != role_node.object_type
|
957
|
+
@constellation.RoleRef(rs, 0, :role => role_ref.role)
|
936
958
|
role_jr = @constellation.JoinRole(role_node, role_ref.role)
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
959
|
+
raise "Internal error in #{constraint_type} #{name}: making illegal reference to join node, joined_role mismatch" if joined_role.object_type != join_node.object_type
|
960
|
+
@constellation.RoleRef(rs, 1, :role => joined_role)
|
961
|
+
join_jr = @constellation.JoinRole(join_node, joined_role)
|
962
|
+
|
963
|
+
js = @constellation.JoinStep(role_jr, join_jr, :fact_type => joined_role.fact_type)
|
964
|
+
debug :join, "New join step #{js.describe}"
|
965
|
+
end
|
966
|
+
else
|
967
|
+
debug :join, "Need join step for non-join_over role #{end_point.name} #{role_ref.describe} in #{role_ref.role.fact_type.default_reading}"
|
968
|
+
if (roles = role_ref.role.fact_type.all_role.to_a).size > 1
|
969
|
+
# Here we have an end join (step already created) but no sequence join
|
970
|
+
if join_node
|
971
|
+
raise "Internal error in #{constraint_type} #{name}: making illegal join step" if role_ref.role.object_type != role_node.object_type
|
972
|
+
join_jr = @constellation.JoinRole(join_node, join_role)
|
951
973
|
role_jr = @constellation.JoinRole(role_node, role_ref.role)
|
952
|
-
js =
|
953
|
-
role_ref.role
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
974
|
+
js = @constellation.JoinStep(join_jr, role_jr, :fact_type => role_ref.role.fact_type)
|
975
|
+
roles -= [join_role, role_ref.role]
|
976
|
+
roles.each do |incidental_role|
|
977
|
+
jn = @constellation.JoinNode(join, join.all_join_node.size, :object_type => incidental_role.object_type)
|
978
|
+
jr = @constellation.JoinRole(jn, incidental_role, :join_step => js)
|
979
|
+
end
|
980
|
+
else
|
981
|
+
if role_sequence.all_role_ref.size > 1
|
982
|
+
join_node = role_node
|
983
|
+
join_role = role_ref.role
|
984
|
+
else
|
985
|
+
# There's no join in this role sequence, so we'd drop of fthe bottom without doing the right things. Why?
|
986
|
+
# Without this case, Supervision.orm omits "that runs Company" from the exclusion constraint, and I'm not sure why.
|
987
|
+
# I think the "then" code causes it to drop out the bottom without making the step (which is otherwise made in every case, see CompanyDirectorEmployee for example)
|
988
|
+
role_jr = @constellation.JoinRole(role_node, role_ref.role)
|
989
|
+
js = nil
|
990
|
+
role_ref.role.fact_type.all_role.each do |role|
|
991
|
+
next if role == role_jr.role
|
992
|
+
next if role_sequence.all_role_ref.detect{|rr| rr.role == role}
|
993
|
+
jn = @constellation.JoinNode(join, join.all_join_node.size, :object_type => role.object_type)
|
994
|
+
jr = @constellation.JoinRole(jn, role)
|
995
|
+
if js
|
996
|
+
jr.join_step = js # Incidental role
|
997
|
+
else
|
998
|
+
js = @constellation.JoinStep(role_jr, jr, :fact_type => role_ref.role.fact_type)
|
999
|
+
end
|
962
1000
|
end
|
963
1001
|
end
|
964
1002
|
end
|
1003
|
+
else
|
1004
|
+
# Unary fact type, make a Join Step from and to the constrained_join_role
|
1005
|
+
jr = @constellation.JoinRole(constrained_join_role.join_node, role_ref.role)
|
1006
|
+
js = @constellation.JoinStep(jr, jr, :fact_type => role_ref.role.fact_type)
|
965
1007
|
end
|
966
|
-
else
|
967
|
-
# Unary fact type, make a Join Step from and to the constrained_join_role
|
968
|
-
jr = @constellation.JoinRole(constrained_join_role.join_node, role_ref.role)
|
969
|
-
js = @constellation.JoinStep(jr, jr, :fact_type => role_ref.role.fact_type)
|
970
1008
|
end
|
971
1009
|
end
|
972
|
-
end
|
973
1010
|
|
974
|
-
|
975
|
-
|
976
|
-
|
1011
|
+
# Thoroughly check that this is a valid join
|
1012
|
+
join.validate
|
1013
|
+
debug :join, "Join has projected nodes #{replacement_rs.describe}"
|
977
1014
|
|
978
|
-
|
979
|
-
|
1015
|
+
# Constrain the replacement role sequence, which has the attached join:
|
1016
|
+
role_sequences[role_sequences.index(role_sequence)] = replacement_rs
|
1017
|
+
end
|
1018
|
+
return true
|
980
1019
|
end
|
1020
|
+
rescue => e
|
1021
|
+
$stderr.puts '// '+e.to_s
|
1022
|
+
return false
|
981
1023
|
end
|
982
1024
|
|
983
1025
|
end
|
984
1026
|
|
985
1027
|
def read_exclusion_constraints
|
986
1028
|
x_exclusion_constraints = @x_model.xpath("orm:Constraints/orm:ExclusionConstraint")
|
987
|
-
debug :orm, "Reading exclusion constraints" do
|
1029
|
+
debug :orm, "Reading #{x_exclusion_constraints.size} exclusion constraints" do
|
988
1030
|
x_exclusion_constraints.each{|x|
|
989
1031
|
id = x['id']
|
990
1032
|
name = x["Name"] || ''
|
@@ -1008,7 +1050,7 @@ module ActiveFacts
|
|
1008
1050
|
|
1009
1051
|
next if role_sequences.compact.size != role_sequences.size # Role sequence missing; includes a derived fact type role
|
1010
1052
|
|
1011
|
-
make_joins('exclusion', name+(x_mandatory ? '/'+x_mandatory['Name'] : ''), role_sequences)
|
1053
|
+
next unless make_joins('exclusion', name+(x_mandatory ? '/'+x_mandatory['Name'] : ''), role_sequences)
|
1012
1054
|
|
1013
1055
|
ec = @constellation.SetExclusionConstraint(:new)
|
1014
1056
|
ec.vocabulary = @vocabulary
|
@@ -1040,8 +1082,10 @@ module ActiveFacts
|
|
1040
1082
|
)
|
1041
1083
|
}
|
1042
1084
|
|
1043
|
-
|
1044
|
-
|
1085
|
+
# Role sequence missing; includes a derived fact type role
|
1086
|
+
next if role_sequences.compact.size != role_sequences.size
|
1087
|
+
|
1088
|
+
next unless make_joins('equality', name, role_sequences)
|
1045
1089
|
|
1046
1090
|
ec = @constellation.SetEqualityConstraint(:new)
|
1047
1091
|
ec.vocabulary = @vocabulary
|
@@ -1071,7 +1115,7 @@ module ActiveFacts
|
|
1071
1115
|
)
|
1072
1116
|
}
|
1073
1117
|
next if role_sequences.compact.size != role_sequences.size # Role sequence missing; includes a derived fact type role
|
1074
|
-
make_joins('subset', name, role_sequences)
|
1118
|
+
next unless make_joins('subset', name, role_sequences)
|
1075
1119
|
|
1076
1120
|
ec = @constellation.SubsetConstraint(:new)
|
1077
1121
|
ec.vocabulary = @vocabulary
|
@@ -1296,17 +1340,20 @@ module ActiveFacts
|
|
1296
1340
|
bounds = x_shape['AbsoluteBounds']
|
1297
1341
|
case shape_type = x_shape.name
|
1298
1342
|
when 'FactTypeShape'
|
1299
|
-
|
1343
|
+
if subject
|
1344
|
+
read_fact_type_shape diagram, x_shape, is_expanded, bounds, subject
|
1345
|
+
# else REVISIT: probably a derived fact type
|
1346
|
+
end
|
1300
1347
|
when 'ExternalConstraintShape', 'FrequencyConstraintShape'
|
1301
1348
|
# REVISIT: The offset might depend on the constraint type. This is right for subset and other round ones.
|
1302
|
-
position = convert_position(bounds, Gravity::
|
1349
|
+
position = convert_position(bounds, Gravity::C)
|
1303
1350
|
shape = @constellation.ConstraintShape(
|
1304
1351
|
:new, :diagram => diagram, :position => position, :is_expanded => is_expanded,
|
1305
1352
|
:constraint => subject
|
1306
1353
|
)
|
1307
1354
|
when 'RingConstraintShape'
|
1308
1355
|
# REVISIT: The offset might depend on the ring constraint type. This is right for basic round ones.
|
1309
|
-
position = convert_position(bounds, Gravity::
|
1356
|
+
position = convert_position(bounds, Gravity::C)
|
1310
1357
|
shape = @constellation.RingConstraintShape(
|
1311
1358
|
:new, :diagram => diagram, :position => position, :is_expanded => is_expanded,
|
1312
1359
|
:constraint => subject
|
@@ -1315,10 +1362,12 @@ module ActiveFacts
|
|
1315
1362
|
when 'ModelNoteShape'
|
1316
1363
|
# REVISIT: Add model notes
|
1317
1364
|
when 'ObjectTypeShape'
|
1365
|
+
position = convert_position(bounds, Gravity::C)
|
1366
|
+
# $stderr.puts "#{subject.name}: bounds=#{bounds} -> position = (#{position.x}, #{position.y})"
|
1318
1367
|
shape = @constellation.ObjectTypeShape(
|
1319
1368
|
:new, :diagram => diagram, :position => position, :is_expanded => is_expanded,
|
1320
1369
|
:object_type => subject,
|
1321
|
-
:
|
1370
|
+
:position => position
|
1322
1371
|
)
|
1323
1372
|
else
|
1324
1373
|
raise "Unknown shape #{x_shape.name}"
|
@@ -1342,21 +1391,22 @@ module ActiveFacts
|
|
1342
1391
|
when 'VerticalRotatedRight'; 'right'
|
1343
1392
|
else nil
|
1344
1393
|
end
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1394
|
+
|
1395
|
+
# Position of a fact type is the centre of the row of role boxes
|
1396
|
+
offs_x = 11
|
1397
|
+
offs_y = -12
|
1398
|
+
if fact_type.entity_type
|
1399
|
+
offs_x -= 12
|
1400
|
+
offs_y -= 9 if !fact_type.entity_type.is_implied_by_objectification
|
1351
1401
|
end
|
1352
1402
|
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1403
|
+
position = convert_position(bounds, Gravity::S, offs_x, offs_y)
|
1404
|
+
|
1405
|
+
# $stderr.puts "#{fact_type.describe}: bounds=#{bounds} -> position = (#{position.x}, #{position.y})"
|
1406
|
+
|
1357
1407
|
debug :orm, "REVISIT: Can't place rotated fact type correctly on diagram yet" if rotation_setting
|
1358
1408
|
|
1359
|
-
debug :orm, "fact type at #{position.x},#{position.y} has display_role_names_setting=#{display_role_names_setting.inspect}, rotation_setting=#{rotation_setting.inspect}
|
1409
|
+
debug :orm, "fact type at #{position.x},#{position.y} has display_role_names_setting=#{display_role_names_setting.inspect}, rotation_setting=#{rotation_setting.inspect}"
|
1360
1410
|
shape = @constellation.FactTypeShape(
|
1361
1411
|
:new,
|
1362
1412
|
:diagram => diagram,
|
@@ -1386,9 +1436,9 @@ module ActiveFacts
|
|
1386
1436
|
position = convert_position(xr_shape['AbsoluteBounds'])
|
1387
1437
|
case xr_shape.name
|
1388
1438
|
when 'ObjectifiedFactTypeNameShape'
|
1389
|
-
@constellation.ObjectifiedFactTypeNameShape(shape, :diagram => diagram, :position => position, :is_expanded => false)
|
1439
|
+
@constellation.ObjectifiedFactTypeNameShape(shape, :shape_id => :new, :diagram => diagram, :position => position, :is_expanded => false)
|
1390
1440
|
when 'ReadingShape'
|
1391
|
-
@constellation.ReadingShape(:new, :diagram => diagram, :position => position, :is_expanded => false, :reading => fact_type.preferred_reading)
|
1441
|
+
@constellation.ReadingShape(shape, :shape_id => :new, :fact_type_shape=>shape, :diagram => diagram, :position => position, :is_expanded => false, :reading => fact_type.preferred_reading)
|
1392
1442
|
when 'RoleNameShape'
|
1393
1443
|
role = @by_id[xr_shape.xpath("ormDiagram:Subject")[0]['ref']]
|
1394
1444
|
role_display = role_display_for_role(shape, x_role_display, role)
|
@@ -1403,7 +1453,7 @@ module ActiveFacts
|
|
1403
1453
|
debug :orm, "Fact type '#{fact_type.preferred_reading.expand}' has #{xr_shape.name} for #{constraint.inspect}"
|
1404
1454
|
|
1405
1455
|
role_display = role_display_for_role(shape, x_role_display, constraint.role)
|
1406
|
-
debug :orm, "ValueConstraintShape is on #{
|
1456
|
+
debug :orm, "ValueConstraintShape is on #{role_display.ordinal}'th role (by #{x_role_display.size > 0 ? 'role_display' : 'fact roles'})"
|
1407
1457
|
@constellation.ValueConstraintShape(
|
1408
1458
|
:new, :diagram => diagram, :position => position, :is_expanded => false,
|
1409
1459
|
:constraint => constraint,
|
@@ -1418,16 +1468,21 @@ module ActiveFacts
|
|
1418
1468
|
# Find or create the RoleDisplay for this role in this fact_type_shape, given (possibly empty) x_role_display nodes:
|
1419
1469
|
def role_display_for_role(fact_type_shape, x_role_display, role)
|
1420
1470
|
if x_role_display.size == 0
|
1421
|
-
|
1471
|
+
# There's no x_role_display, which means the roles are in displayed
|
1472
|
+
# the same order as in the fact type. However, we need a RoleDisplay
|
1473
|
+
# to attach a ReadingShape or ValueConstraintShape, so make them all.
|
1474
|
+
fact_type_shape.fact_type.all_role.each{|r| @constellation.RoleDisplay(fact_type_shape, r.ordinal, :role => r) }
|
1475
|
+
role_ordinal = fact_type_shape.fact_type.all_role_in_order.index(role)
|
1422
1476
|
else
|
1423
1477
|
role_ordinal = x_role_display.map{|rd| @by_id[rd['ref']]}.index(role)
|
1424
1478
|
end
|
1425
1479
|
role_display = @constellation.RoleDisplay(fact_type_shape, role_ordinal, :role => role)
|
1426
1480
|
end
|
1427
1481
|
|
1428
|
-
DIAGRAM_SCALE =
|
1429
|
-
def convert_position(bounds, gravity = Gravity::
|
1482
|
+
DIAGRAM_SCALE = 96*1.5
|
1483
|
+
def convert_position(bounds, gravity = Gravity::C, xoffs = 0, yoffs = 0)
|
1430
1484
|
return nil unless bounds
|
1485
|
+
# Bounds is top, left, width, height in inches
|
1431
1486
|
bf = bounds.split(/, /).map{|b|b.to_f}
|
1432
1487
|
sizefrax = [
|
1433
1488
|
[0, 0], [1, 0], [2, 0],
|
@@ -1435,8 +1490,8 @@ module ActiveFacts
|
|
1435
1490
|
[0, 2], [1, 2], [2, 2],
|
1436
1491
|
]
|
1437
1492
|
|
1438
|
-
x = (DIAGRAM_SCALE * bf[0]+bf[2]*sizefrax[gravity][0]/2).round + xoffs
|
1439
|
-
y = (DIAGRAM_SCALE * bf[1]+bf[3]*sizefrax[gravity][1]/2).round + yoffs
|
1493
|
+
x = (DIAGRAM_SCALE * (bf[0]+bf[2]*sizefrax[gravity][0]/2)).round + xoffs
|
1494
|
+
y = (DIAGRAM_SCALE * (bf[1]+bf[3]*sizefrax[gravity][1]/2)).round + yoffs
|
1440
1495
|
@constellation.Position(x, y)
|
1441
1496
|
end
|
1442
1497
|
|