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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +19 -0
  8. data/Rakefile +6 -0
  9. data/activefacts-cql.gemspec +29 -0
  10. data/bin/setup +7 -0
  11. data/lib/activefacts/cql.rb +7 -0
  12. data/lib/activefacts/cql/.gitignore +0 -0
  13. data/lib/activefacts/cql/Rakefile +14 -0
  14. data/lib/activefacts/cql/compiler.rb +156 -0
  15. data/lib/activefacts/cql/compiler/clause.rb +1137 -0
  16. data/lib/activefacts/cql/compiler/constraint.rb +581 -0
  17. data/lib/activefacts/cql/compiler/entity_type.rb +457 -0
  18. data/lib/activefacts/cql/compiler/expression.rb +443 -0
  19. data/lib/activefacts/cql/compiler/fact.rb +390 -0
  20. data/lib/activefacts/cql/compiler/fact_type.rb +421 -0
  21. data/lib/activefacts/cql/compiler/query.rb +106 -0
  22. data/lib/activefacts/cql/compiler/shared.rb +161 -0
  23. data/lib/activefacts/cql/compiler/value_type.rb +174 -0
  24. data/lib/activefacts/cql/parser.rb +234 -0
  25. data/lib/activefacts/cql/parser/CQLParser.treetop +167 -0
  26. data/lib/activefacts/cql/parser/Context.treetop +48 -0
  27. data/lib/activefacts/cql/parser/Expressions.treetop +67 -0
  28. data/lib/activefacts/cql/parser/FactTypes.treetop +358 -0
  29. data/lib/activefacts/cql/parser/Language/English.treetop +315 -0
  30. data/lib/activefacts/cql/parser/Language/French.treetop +315 -0
  31. data/lib/activefacts/cql/parser/Language/Mandarin.treetop +304 -0
  32. data/lib/activefacts/cql/parser/LexicalRules.treetop +253 -0
  33. data/lib/activefacts/cql/parser/ObjectTypes.treetop +210 -0
  34. data/lib/activefacts/cql/parser/Terms.treetop +183 -0
  35. data/lib/activefacts/cql/parser/ValueTypes.treetop +202 -0
  36. data/lib/activefacts/cql/parser/nodes.rb +49 -0
  37. data/lib/activefacts/cql/require.rb +36 -0
  38. data/lib/activefacts/cql/verbaliser.rb +804 -0
  39. data/lib/activefacts/cql/version.rb +5 -0
  40. data/lib/activefacts/input/cql.rb +43 -0
  41. data/lib/rubygems_plugin.rb +12 -0
  42. 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