activefacts-cql 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +19 -0
- data/Rakefile +6 -0
- data/activefacts-cql.gemspec +29 -0
- data/bin/setup +7 -0
- data/lib/activefacts/cql.rb +7 -0
- data/lib/activefacts/cql/.gitignore +0 -0
- data/lib/activefacts/cql/Rakefile +14 -0
- data/lib/activefacts/cql/compiler.rb +156 -0
- data/lib/activefacts/cql/compiler/clause.rb +1137 -0
- data/lib/activefacts/cql/compiler/constraint.rb +581 -0
- data/lib/activefacts/cql/compiler/entity_type.rb +457 -0
- data/lib/activefacts/cql/compiler/expression.rb +443 -0
- data/lib/activefacts/cql/compiler/fact.rb +390 -0
- data/lib/activefacts/cql/compiler/fact_type.rb +421 -0
- data/lib/activefacts/cql/compiler/query.rb +106 -0
- data/lib/activefacts/cql/compiler/shared.rb +161 -0
- data/lib/activefacts/cql/compiler/value_type.rb +174 -0
- data/lib/activefacts/cql/parser.rb +234 -0
- data/lib/activefacts/cql/parser/CQLParser.treetop +167 -0
- data/lib/activefacts/cql/parser/Context.treetop +48 -0
- data/lib/activefacts/cql/parser/Expressions.treetop +67 -0
- data/lib/activefacts/cql/parser/FactTypes.treetop +358 -0
- data/lib/activefacts/cql/parser/Language/English.treetop +315 -0
- data/lib/activefacts/cql/parser/Language/French.treetop +315 -0
- data/lib/activefacts/cql/parser/Language/Mandarin.treetop +304 -0
- data/lib/activefacts/cql/parser/LexicalRules.treetop +253 -0
- data/lib/activefacts/cql/parser/ObjectTypes.treetop +210 -0
- data/lib/activefacts/cql/parser/Terms.treetop +183 -0
- data/lib/activefacts/cql/parser/ValueTypes.treetop +202 -0
- data/lib/activefacts/cql/parser/nodes.rb +49 -0
- data/lib/activefacts/cql/require.rb +36 -0
- data/lib/activefacts/cql/verbaliser.rb +804 -0
- data/lib/activefacts/cql/version.rb +5 -0
- data/lib/activefacts/input/cql.rb +43 -0
- data/lib/rubygems_plugin.rb +12 -0
- metadata +167 -0
@@ -0,0 +1,457 @@
|
|
1
|
+
module ActiveFacts
|
2
|
+
module CQL
|
3
|
+
class Compiler < ActiveFacts::CQL::Parser
|
4
|
+
|
5
|
+
class ReferenceMode
|
6
|
+
attr_reader :name, :value_constraint, :parameters
|
7
|
+
|
8
|
+
def initialize name, value_constraint, parameters
|
9
|
+
@name = name
|
10
|
+
@value_constraint = value_constraint
|
11
|
+
@parameters = parameters
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"identified by its #{name}" +
|
16
|
+
((p = @parameters).size > 0 ? '('+p*', '+')' : '') +
|
17
|
+
((v = @value_constraint) ? v.to_s : '')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class EntityType < ObjectType
|
22
|
+
def initialize name, supertypes, identification, pragmas, clauses, context_note
|
23
|
+
super name
|
24
|
+
@supertypes = supertypes
|
25
|
+
@identification = identification
|
26
|
+
@pragmas = pragmas
|
27
|
+
@clauses = clauses || []
|
28
|
+
@context_note = context_note
|
29
|
+
end
|
30
|
+
|
31
|
+
def compile
|
32
|
+
@entity_type = @vocabulary.valid_entity_type_name(@name) ||
|
33
|
+
@constellation.EntityType(@vocabulary, @name, :concept => :new)
|
34
|
+
@entity_type.is_independent = true if @pragmas.delete('independent')
|
35
|
+
|
36
|
+
# REVISIT: CQL needs a way to indicate whether subtype migration can occur.
|
37
|
+
# For example by saying "Xyz is a role of Abc".
|
38
|
+
@supertypes.each_with_index do |supertype_name, i|
|
39
|
+
add_supertype(supertype_name, @identification || i > 0)
|
40
|
+
end
|
41
|
+
@pragmas.each do |p|
|
42
|
+
@constellation.ConceptAnnotation(:concept => @entity_type.concept, :mapping_annotation => p)
|
43
|
+
end if @pragmas
|
44
|
+
|
45
|
+
context = CompilationContext.new(@vocabulary)
|
46
|
+
|
47
|
+
# Identification may be via a mode (create it) or by forward-referenced entity types (allow those):
|
48
|
+
prepare_identifier context
|
49
|
+
|
50
|
+
context.bind @clauses, @identification.is_a?(Array) ? @identification : []
|
51
|
+
|
52
|
+
# Create the fact types that define the identifying roles:
|
53
|
+
fact_types = create_identifying_fact_types context
|
54
|
+
|
55
|
+
# At this point, @identification is an array of References and/or Clauses (for unary fact types)
|
56
|
+
# Have to do this after creating the necessary fact types
|
57
|
+
complete_reference_mode_fact_type fact_types
|
58
|
+
|
59
|
+
# Find the roles to use if we have to create an identifying uniqueness constraint:
|
60
|
+
identifying_roles = bind_identifying_roles context
|
61
|
+
|
62
|
+
make_preferred_identifier_over_roles identifying_roles
|
63
|
+
|
64
|
+
if @context_note
|
65
|
+
@context_note.compile(@constellation, @entity_type)
|
66
|
+
end
|
67
|
+
|
68
|
+
@clauses.each do |clause|
|
69
|
+
next unless clause.context_note
|
70
|
+
clause.context_note.compile(@constellation, @entity_type)
|
71
|
+
end
|
72
|
+
|
73
|
+
@entity_type
|
74
|
+
end
|
75
|
+
|
76
|
+
def prepare_identifier context
|
77
|
+
# Figure out the identification mode or roles, if any:
|
78
|
+
if @identification
|
79
|
+
if @identification.is_a? ReferenceMode
|
80
|
+
make_entity_type_refmode_valuetypes(name, @identification.name, @identification.parameters)
|
81
|
+
vt_name = @reference_mode_value_type.name
|
82
|
+
@identification = [Compiler::Reference.new(vt_name, nil, nil, nil, nil, nil, @identification.value_constraint, nil)]
|
83
|
+
else
|
84
|
+
context.allowed_forward_terms = legal_forward_references(@identification)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Names used in the identifying roles list may be forward referenced:
|
90
|
+
def legal_forward_references(identification_phrases)
|
91
|
+
identification_phrases.map do |phrase|
|
92
|
+
phrase.is_a?(Reference) ? phrase.term : nil
|
93
|
+
end.compact.uniq
|
94
|
+
end
|
95
|
+
|
96
|
+
def bind_identifying_roles context
|
97
|
+
return unless @identification
|
98
|
+
@identification.map do |id|
|
99
|
+
if id.is_a?(Reference)
|
100
|
+
binding = id.binding
|
101
|
+
roles = binding.refs.map{|r|r.role || (rr=r.role_ref and rr.role)}.compact.uniq
|
102
|
+
raise "Looking for an occurrence of identifying role #{id.inspect}, but found #{roles.size == 0 ? "none" : roles.size}" if roles.size != 1
|
103
|
+
roles[0]
|
104
|
+
else
|
105
|
+
# id is a clause of a unary fact type.
|
106
|
+
id.identify_other_players context
|
107
|
+
id.bind context
|
108
|
+
matching_clause =
|
109
|
+
@clauses.detect { |clause| clause.phrases_match id.phrases }
|
110
|
+
raise "Unary identifying role '#{id.inspect}' is not found in the defined fact types" unless matching_clause
|
111
|
+
matching_clause.fact_type.all_role.single
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def make_preferred_identifier_over_roles identifying_roles
|
117
|
+
return unless identifying_roles && identifying_roles.size > 0
|
118
|
+
role_sequence = @constellation.RoleSequence(:new)
|
119
|
+
identifying_roles.each_with_index do |identifying_role, index|
|
120
|
+
@constellation.RoleRef(role_sequence, index, :role => identifying_role)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Find a uniqueness constraint as PI, or make one
|
124
|
+
pc = find_pc_over_roles(identifying_roles)
|
125
|
+
if (pc)
|
126
|
+
pc.is_preferred_identifier = true
|
127
|
+
pc.name = "#{@entity_type.name}PK" unless pc.name
|
128
|
+
trace :constraint, "Existing PC #{pc.verbalise} is now PK for #{@entity_type.name}"
|
129
|
+
else
|
130
|
+
# Add a unique constraint over all identifying roles
|
131
|
+
pc = @constellation.PresenceConstraint(
|
132
|
+
:new,
|
133
|
+
:vocabulary => @vocabulary,
|
134
|
+
:name => "#{@entity_type.name}PK", # Is this a useful name?
|
135
|
+
:role_sequence => role_sequence,
|
136
|
+
:is_preferred_identifier => true,
|
137
|
+
:max_frequency => 1 # Unique
|
138
|
+
#:is_mandatory => true,
|
139
|
+
#:min_frequency => 1,
|
140
|
+
)
|
141
|
+
trace :constraint, "Made new preferred PC GUID=#{pc.concept.guid} min=nil max=1 over #{role_sequence.describe}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def find_pc_over_roles(roles)
|
146
|
+
return nil if roles.size == 0 # Safeguard; this would chuck an exception otherwise
|
147
|
+
roles[0].all_role_ref.each do |role_ref|
|
148
|
+
next if role_ref.role_sequence.all_role_ref.map(&:role) != roles
|
149
|
+
pc = role_ref.role_sequence.all_presence_constraint.single # Will return nil if there's more than one.
|
150
|
+
#puts "Existing PresenceConstraint matches those roles!" if pc
|
151
|
+
return pc if pc
|
152
|
+
end
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
|
156
|
+
def create_identifying_fact_types context
|
157
|
+
fact_types = []
|
158
|
+
# Categorise the clauses into fact types according to the roles they play.
|
159
|
+
@clauses.inject({}) do |hash, clause|
|
160
|
+
players_key = clause.refs.map{|vr| vr.key.compact}.sort
|
161
|
+
(hash[players_key] ||= []) << clause
|
162
|
+
hash
|
163
|
+
end.each do |players_key, clauses|
|
164
|
+
# REVISIT: Loose binding goes here; it might merge some Compiler#Roles
|
165
|
+
|
166
|
+
fact_type = create_identifying_fact_type(context, clauses)
|
167
|
+
fact_types << fact_type if fact_type
|
168
|
+
unless fact_type.all_role.detect{|r| r.object_type == @entity_type}
|
169
|
+
objectify_existing_fact_type(fact_type)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
fact_types
|
173
|
+
end
|
174
|
+
|
175
|
+
def create_identifying_fact_type context, clauses
|
176
|
+
# See if any fact type already exists (this ET cannot be a player, but might objectify it)
|
177
|
+
existing_clauses = clauses.select{ |clause| clause.match_existing_fact_type context }
|
178
|
+
if negation = existing_clauses.detect{|c| c.certainty == false }
|
179
|
+
raise "#{@name} cannot be identified by negated fact type #{negation.inspect}"
|
180
|
+
end
|
181
|
+
any_matched = existing_clauses.size > 0
|
182
|
+
|
183
|
+
operation = any_matched ? 'Objectifying' : 'Creating'
|
184
|
+
player_names = clauses[0].refs.map{|vr| vr.key.compact*'-'}
|
185
|
+
trace :matching, "#{operation} fact type for #{clauses.size} clauses over (#{player_names*', '})" do
|
186
|
+
if any_matched # There's an existing fact type we must be objectifying
|
187
|
+
fact_type = objectify_existing_fact_type(existing_clauses[0].fact_type)
|
188
|
+
end
|
189
|
+
|
190
|
+
unless fact_type
|
191
|
+
fact_type = clauses[0].make_fact_type(@vocabulary)
|
192
|
+
clauses[0].make_reading(@vocabulary, fact_type)
|
193
|
+
clauses[0].make_embedded_constraints vocabulary
|
194
|
+
existing_clauses = [clauses[0]]
|
195
|
+
end
|
196
|
+
|
197
|
+
(clauses - existing_clauses).each do |clause|
|
198
|
+
clause.make_reading(@vocabulary, fact_type)
|
199
|
+
clause.make_embedded_constraints vocabulary
|
200
|
+
end
|
201
|
+
|
202
|
+
fact_type
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def objectify_existing_fact_type fact_type
|
207
|
+
raise "#{@name} cannot objectify fact type '#{fact_type.entity_type.name}' that's already objectified" if fact_type.entity_type
|
208
|
+
if @fact_type
|
209
|
+
raise "#{@name} cannot objectify '#{fact_type.default_reading}', it already objectifies '#{@fact_type.default_reading}'"
|
210
|
+
end
|
211
|
+
|
212
|
+
if fact_type.internal_presence_constraints.select{|pc| pc.max_frequency == 1}.size == 0
|
213
|
+
# If there's no existing uniqueness constraint over this fact type, make a spanning one.
|
214
|
+
pc = @constellation.PresenceConstraint(
|
215
|
+
:new,
|
216
|
+
:vocabulary => @vocabulary,
|
217
|
+
:name => @entity_type.name+"UQ",
|
218
|
+
:role_sequence => fact_type.preferred_reading.role_sequence,
|
219
|
+
:is_preferred_identifier => false, # We only get here when there is a reference mode on the entity type
|
220
|
+
:max_frequency => 1
|
221
|
+
)
|
222
|
+
trace :constraint, "Made new objectification PC GUID=#{pc.concept.guid} min=nil max=1 over #{fact_type.preferred_reading.role_sequence.describe}"
|
223
|
+
end
|
224
|
+
|
225
|
+
@fact_type = @entity_type.fact_type = fact_type
|
226
|
+
@entity_type.create_implicit_fact_types # REVISIT: Could there be readings for the implicit fact types here?
|
227
|
+
@fact_type
|
228
|
+
end
|
229
|
+
|
230
|
+
def add_supertype(supertype_name, not_identifying)
|
231
|
+
trace :supertype, "Adding #{not_identifying ? '' : 'identifying '}supertype #{supertype_name} to #{@entity_type.name}" do
|
232
|
+
supertype = @vocabulary.valid_entity_type_name(supertype_name) ||
|
233
|
+
@constellation.EntityType(@vocabulary, supertype_name, :concept => :new) # Should always already exist
|
234
|
+
|
235
|
+
# Did we already know about this supertyping?
|
236
|
+
return if @entity_type.all_type_inheritance_as_subtype.detect{|ti| ti.supertype == supertype}
|
237
|
+
|
238
|
+
# By default, the first supertype identifies this entity type
|
239
|
+
is_identifying_supertype = !not_identifying && @entity_type.all_type_inheritance_as_subtype.size == 0
|
240
|
+
|
241
|
+
inheritance_fact = @constellation.TypeInheritance(@entity_type, supertype, :concept => :new)
|
242
|
+
|
243
|
+
assimilation_pragmas = ['absorbed', 'separate', 'partitioned']
|
244
|
+
assimilations = @pragmas.select { |p| assimilation_pragmas.include? p}
|
245
|
+
@pragmas -= assimilation_pragmas
|
246
|
+
raise "Conflicting assimilation pragmas #{assimilations*', '}" if assimilations.size > 1
|
247
|
+
inheritance_fact.assimilation = assimilations[0]
|
248
|
+
|
249
|
+
# Create a reading:
|
250
|
+
sub_role = @constellation.Role(inheritance_fact, 0, :object_type => @entity_type, :concept => :new)
|
251
|
+
super_role = @constellation.Role(inheritance_fact, 1, :object_type => supertype, :concept => :new)
|
252
|
+
|
253
|
+
rs = @constellation.RoleSequence(:new)
|
254
|
+
@constellation.RoleRef(rs, 0, :role => sub_role)
|
255
|
+
@constellation.RoleRef(rs, 1, :role => super_role)
|
256
|
+
@constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}", :is_negative => false)
|
257
|
+
|
258
|
+
rs2 = @constellation.RoleSequence(:new)
|
259
|
+
@constellation.RoleRef(rs2, 0, :role => super_role)
|
260
|
+
@constellation.RoleRef(rs2, 1, :role => sub_role)
|
261
|
+
# Decide in which order to include is a/is an. Provide both, but in order.
|
262
|
+
n = 'aeioh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
|
263
|
+
@constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}", :is_negative => false)
|
264
|
+
|
265
|
+
if is_identifying_supertype
|
266
|
+
inheritance_fact.provides_identification = true
|
267
|
+
end
|
268
|
+
|
269
|
+
# Create uniqueness constraints over the subtyping fact type.
|
270
|
+
p1rs = @constellation.RoleSequence(:new)
|
271
|
+
@constellation.RoleRef(p1rs, 0).role = sub_role
|
272
|
+
pc1 = @constellation.PresenceConstraint(:new, :vocabulary => @vocabulary)
|
273
|
+
pc1.name = "#{@entity_type.name}MustHaveSupertype#{supertype.name}"
|
274
|
+
pc1.role_sequence = p1rs
|
275
|
+
pc1.is_mandatory = true # A subtype instance must have a supertype instance
|
276
|
+
pc1.min_frequency = 1
|
277
|
+
pc1.max_frequency = 1
|
278
|
+
pc1.is_preferred_identifier = false
|
279
|
+
trace :constraint, "Made new subtype PC GUID=#{pc1.concept.guid} min=1 max=1 over #{p1rs.describe}"
|
280
|
+
|
281
|
+
p2rs = @constellation.RoleSequence(:new)
|
282
|
+
constellation.RoleRef(p2rs, 0).role = super_role
|
283
|
+
pc2 = constellation.PresenceConstraint(:new, :vocabulary => @vocabulary)
|
284
|
+
pc2.name = "#{supertype.name}MayBeA#{@entity_type.name}"
|
285
|
+
pc2.role_sequence = p2rs
|
286
|
+
pc2.is_mandatory = false
|
287
|
+
pc2.min_frequency = 0
|
288
|
+
pc2.max_frequency = 1
|
289
|
+
# The supertype role often identifies the subtype:
|
290
|
+
pc2.is_preferred_identifier = inheritance_fact.provides_identification
|
291
|
+
trace :supertype, "identification of #{@entity_type.name} via supertype #{supertype.name} was #{inheritance_fact.provides_identification ? '' : 'not '}added"
|
292
|
+
trace :constraint, "Made new supertype PC GUID=#{pc2.concept.guid} min=1 max=1 over #{p2rs.describe}"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def make_entity_type_refmode_valuetypes(name, mode, parameters)
|
297
|
+
vt_name = "#{name}#{mode}"
|
298
|
+
vt = nil
|
299
|
+
trace :entity, "Preparing value type #{vt_name} for reference mode" do
|
300
|
+
# Find an existing ValueType called 'vt_name' or 'name vtname'
|
301
|
+
# or find/create the supertype '#{mode}' and the subtype
|
302
|
+
unless vt = @vocabulary.valid_object_type_name(vt_name) or
|
303
|
+
vt = @vocabulary.valid_object_type_name(vt_name = "#{name} #{mode}")
|
304
|
+
base_vt = @vocabulary.valid_value_type_name(mode) ||
|
305
|
+
@constellation.ValueType(@vocabulary, mode, :concept => :new)
|
306
|
+
vt = @constellation.ValueType(@vocabulary, vt_name, :supertype => base_vt, :concept => :new)
|
307
|
+
if parameters
|
308
|
+
length, scale = *parameters
|
309
|
+
vt.length = length if length
|
310
|
+
vt.scale = scale if scale
|
311
|
+
end
|
312
|
+
else
|
313
|
+
trace :entity, "Value type #{vt_name} already exists"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# REVISIT: If we do this, it gets emitted twice when we generate CQL.
|
318
|
+
# The generator should detect that the value_constraint is the same and not emit it.
|
319
|
+
#if (ranges = identification[:value_constraint])
|
320
|
+
# vt.value_constraint = value_constraint(ranges, identification[:enforcement])
|
321
|
+
#end
|
322
|
+
@reference_mode_value_type = vt
|
323
|
+
end
|
324
|
+
|
325
|
+
def complete_reference_mode_fact_type(fact_types)
|
326
|
+
return unless identifying_type = @reference_mode_value_type
|
327
|
+
|
328
|
+
# Find an existing fact type, if any:
|
329
|
+
entity_role = identifying_role = nil
|
330
|
+
fact_type = fact_types.detect do |ft|
|
331
|
+
identifying_role = ft.all_role.detect{|r| r.object_type == identifying_type } and
|
332
|
+
entity_role = ft.all_role.detect{|r| r.object_type == @entity_type }
|
333
|
+
end
|
334
|
+
|
335
|
+
# Create an identifying fact type if needed:
|
336
|
+
unless fact_type
|
337
|
+
fact_type = @constellation.FactType(:new)
|
338
|
+
fact_types << fact_type
|
339
|
+
entity_role = @constellation.Role(fact_type, 0, :object_type => @entity_type, :concept => :new)
|
340
|
+
identifying_role = @constellation.Role(fact_type, 1, :object_type => identifying_type, :concept => :new)
|
341
|
+
end
|
342
|
+
@identification[0].role = identifying_role
|
343
|
+
|
344
|
+
if (value_constraint = @identification[0].value_constraint)
|
345
|
+
# The value_constraint applies only to the value role, not to the underlying value type
|
346
|
+
# Decide whether this puts the value_constraint in the right place:
|
347
|
+
value_constraint.constellation = fact_type.constellation
|
348
|
+
identifying_role.role_value_constraint = value_constraint.compile
|
349
|
+
end
|
350
|
+
|
351
|
+
# Find all role sequences over the fact type's two roles
|
352
|
+
rss = entity_role.all_role_ref.select do |rr|
|
353
|
+
rr.role_sequence.all_role_ref.size == 2 &&
|
354
|
+
(rr.role_sequence.all_role_ref.to_a-[rr])[0].role == identifying_role
|
355
|
+
end.map{|rr| rr.role_sequence}
|
356
|
+
|
357
|
+
# Make a forward reading, if there is none already:
|
358
|
+
# Find or create RoleSequences for the forward and reverse readings:
|
359
|
+
rs01 = rss.select{|rs| rs.all_role_ref.sort_by{|rr| rr.ordinal}.map(&:role) == [entity_role, identifying_role] }[0]
|
360
|
+
if !rs01
|
361
|
+
rs01 = @constellation.RoleSequence(:new)
|
362
|
+
@constellation.RoleRef(rs01, 0, :role => entity_role)
|
363
|
+
@constellation.RoleRef(rs01, 1, :role => identifying_role)
|
364
|
+
end
|
365
|
+
if rs01.all_reading.empty?
|
366
|
+
@constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs01, :text => "{0} has {1}", :is_negative => false)
|
367
|
+
trace :mode, "Creating new forward reading '#{entity_role.object_type.name} has #{identifying_type.name}'"
|
368
|
+
else
|
369
|
+
trace :mode, "Using existing forward reading"
|
370
|
+
end
|
371
|
+
|
372
|
+
# Make a reverse reading if none exists
|
373
|
+
rs10 = rss.select{|rs| rs.all_role_ref.sort_by{|rr| rr.ordinal}.map(&:role) == [identifying_role, entity_role] }[0]
|
374
|
+
if !rs10
|
375
|
+
rs10 = @constellation.RoleSequence(:new)
|
376
|
+
@constellation.RoleRef(rs10, 0, :role => identifying_role)
|
377
|
+
@constellation.RoleRef(rs10, 1, :role => entity_role)
|
378
|
+
end
|
379
|
+
if rs10.all_reading.empty?
|
380
|
+
@constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs10, :text => "{0} is of {1}", :is_negative => false)
|
381
|
+
trace :mode, "Creating new reverse reading '#{identifying_type.name} is of #{entity_role.object_type.name}'"
|
382
|
+
else
|
383
|
+
trace :mode, "Using existing reverse reading"
|
384
|
+
end
|
385
|
+
|
386
|
+
# Entity must have one identifying instance. Find or create the role sequence, then create a PC if necessary
|
387
|
+
rs0 = entity_role.all_role_ref.select{|rr| rr.role_sequence.all_role_ref.size == 1}[0]
|
388
|
+
if rs0
|
389
|
+
rs0 = rs0.role_sequence
|
390
|
+
trace :mode, "Using existing EntityType role sequence"
|
391
|
+
else
|
392
|
+
rs0 = @constellation.RoleSequence(:new)
|
393
|
+
@constellation.RoleRef(rs0, 0, :role => entity_role)
|
394
|
+
trace :mode, "Creating new EntityType role sequence"
|
395
|
+
end
|
396
|
+
if (rs0.all_presence_constraint.size == 0)
|
397
|
+
constraint = @constellation.PresenceConstraint(
|
398
|
+
:new,
|
399
|
+
:name => '',
|
400
|
+
:vocabulary => @vocabulary,
|
401
|
+
:role_sequence => rs0,
|
402
|
+
:min_frequency => 1,
|
403
|
+
:max_frequency => 1,
|
404
|
+
:is_preferred_identifier => false,
|
405
|
+
:is_mandatory => true
|
406
|
+
)
|
407
|
+
trace :constraint, "Made new refmode PC GUID=#{constraint.concept.guid} min=1 max=1 over #{rs0.describe}"
|
408
|
+
else
|
409
|
+
trace :mode, "Using existing EntityType PresenceConstraint"
|
410
|
+
end
|
411
|
+
|
412
|
+
# Value Type must have a value type. Find or create the role sequence, then create a PC if necessary
|
413
|
+
trace :mode, "identifying_role has #{identifying_role.all_role_ref.size} attached sequences"
|
414
|
+
trace :mode, "identifying_role has #{identifying_role.all_role_ref.select{|rr| rr.role_sequence.all_role_ref.size == 1}.size} unary sequences"
|
415
|
+
rs1 = identifying_role.all_role_ref.select{|rr| rr.role_sequence.all_role_ref.size == 1 ? rr.role_sequence : nil }.compact[0]
|
416
|
+
if (!rs1)
|
417
|
+
rs1 = @constellation.RoleSequence(:new)
|
418
|
+
@constellation.RoleRef(rs1, 0, :role => identifying_role)
|
419
|
+
trace :mode, "Creating new ValueType role sequence"
|
420
|
+
else
|
421
|
+
rs1 = rs1.role_sequence
|
422
|
+
trace :mode, "Using existing ValueType role sequence"
|
423
|
+
end
|
424
|
+
if (rs1.all_presence_constraint.size == 0)
|
425
|
+
constraint = @constellation.PresenceConstraint(
|
426
|
+
:new,
|
427
|
+
:name => '',
|
428
|
+
:vocabulary => @vocabulary,
|
429
|
+
:role_sequence => rs1,
|
430
|
+
:min_frequency => 0,
|
431
|
+
:max_frequency => 1,
|
432
|
+
:is_preferred_identifier => true,
|
433
|
+
:is_mandatory => false
|
434
|
+
)
|
435
|
+
trace :constraint, "Made new refmode ValueType PC GUID=#{constraint.concept.guid} min=0 max=1 over #{rs1.describe}"
|
436
|
+
else
|
437
|
+
trace :mode, "Marking existing ValueType PresenceConstraint as preferred"
|
438
|
+
rs1.all_presence_constraint.single.is_preferred_identifier = true
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def to_s
|
443
|
+
"EntityType: #{super} #{
|
444
|
+
@supertypes.size > 0 ? "< #{@supertypes*','} " : ''
|
445
|
+
}#{
|
446
|
+
@identification.is_a?(ReferenceMode) ? @identification.to_s : @identification.inspect
|
447
|
+
}#{
|
448
|
+
@clauses.size > 0 ? " where #{@clauses.inspect}" : ''
|
449
|
+
}#{
|
450
|
+
@pragmas.size > 0 ? ", pragmas [#{@pragmas*','}]" : ''
|
451
|
+
};"
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|