activefacts 0.8.9 → 0.8.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gemtest +0 -0
  2. data/Manifest.txt +28 -33
  3. data/Rakefile +11 -12
  4. data/bin/cql +90 -46
  5. data/examples/CQL/Blog.cql +2 -1
  6. data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
  7. data/examples/CQL/Death.cql +1 -1
  8. data/examples/CQL/Diplomacy.cql +9 -9
  9. data/examples/CQL/Genealogy.cql +3 -2
  10. data/examples/CQL/Insurance.cql +10 -7
  11. data/examples/CQL/JoinEquality.cql +2 -2
  12. data/examples/CQL/Marriage.cql +1 -1
  13. data/examples/CQL/Metamodel.cql +73 -53
  14. data/examples/CQL/MetamodelNext.cql +89 -67
  15. data/examples/CQL/OneToOnes.cql +2 -2
  16. data/examples/CQL/ServiceDirector.cql +10 -5
  17. data/examples/CQL/Supervision.cql +3 -3
  18. data/examples/CQL/Tests.Test5.Load.cql +1 -1
  19. data/examples/CQL/Warehousing.cql +4 -2
  20. data/lib/activefacts/cql/CQLParser.treetop +26 -60
  21. data/lib/activefacts/cql/Context.treetop +12 -2
  22. data/lib/activefacts/cql/Expressions.treetop +14 -30
  23. data/lib/activefacts/cql/FactTypes.treetop +165 -110
  24. data/lib/activefacts/cql/Language/English.treetop +167 -54
  25. data/lib/activefacts/cql/LexicalRules.treetop +16 -2
  26. data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
  27. data/lib/activefacts/cql/Terms.treetop +57 -27
  28. data/lib/activefacts/cql/ValueTypes.treetop +39 -13
  29. data/lib/activefacts/cql/compiler.rb +5 -3
  30. data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
  31. data/lib/activefacts/cql/compiler/constraint.rb +178 -275
  32. data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
  33. data/lib/activefacts/cql/compiler/expression.rb +418 -0
  34. data/lib/activefacts/cql/compiler/fact.rb +146 -145
  35. data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
  36. data/lib/activefacts/cql/compiler/join.rb +159 -0
  37. data/lib/activefacts/cql/compiler/shared.rb +51 -23
  38. data/lib/activefacts/cql/compiler/value_type.rb +56 -2
  39. data/lib/activefacts/cql/parser.rb +15 -4
  40. data/lib/activefacts/generate/absorption.rb +7 -7
  41. data/lib/activefacts/generate/cql.rb +100 -37
  42. data/lib/activefacts/generate/oo.rb +28 -51
  43. data/lib/activefacts/generate/ordered.rb +60 -36
  44. data/lib/activefacts/generate/ruby.rb +6 -6
  45. data/lib/activefacts/generate/sql/server.rb +4 -4
  46. data/lib/activefacts/input/orm.rb +71 -53
  47. data/lib/activefacts/persistence.rb +1 -1
  48. data/lib/activefacts/persistence/columns.rb +27 -23
  49. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  50. data/lib/activefacts/persistence/index.rb +17 -17
  51. data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
  52. data/lib/activefacts/persistence/reference.rb +61 -36
  53. data/lib/activefacts/persistence/tables.rb +61 -59
  54. data/lib/activefacts/support.rb +54 -29
  55. data/lib/activefacts/version.rb +1 -1
  56. data/lib/activefacts/vocabulary/extensions.rb +99 -54
  57. data/lib/activefacts/vocabulary/metamodel.rb +43 -37
  58. data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
  59. data/spec/absorption_spec.rb +8 -8
  60. data/spec/cql/comparison_spec.rb +91 -0
  61. data/spec/cql/contractions_spec.rb +251 -0
  62. data/spec/cql/entity_type_spec.rb +319 -0
  63. data/spec/cql/expressions_spec.rb +63 -0
  64. data/spec/cql/fact_type_matching_spec.rb +283 -0
  65. data/spec/cql/french_spec.rb +21 -0
  66. data/spec/cql/parser/bad_literals_spec.rb +86 -0
  67. data/spec/cql/parser/constraints_spec.rb +19 -0
  68. data/spec/cql/parser/entity_types_spec.rb +106 -0
  69. data/spec/cql/parser/expressions_spec.rb +179 -0
  70. data/spec/cql/parser/fact_types_spec.rb +41 -0
  71. data/spec/cql/parser/literals_spec.rb +312 -0
  72. data/spec/cql/parser/pragmas_spec.rb +89 -0
  73. data/spec/cql/parser/value_types_spec.rb +42 -0
  74. data/spec/cql/role_matching_spec.rb +147 -0
  75. data/spec/cql/samples_spec.rb +9 -9
  76. data/spec/cql_cql_spec.rb +1 -1
  77. data/spec/cql_dm_spec.rb +116 -0
  78. data/spec/cql_mysql_spec.rb +1 -1
  79. data/spec/cql_ruby_spec.rb +1 -1
  80. data/spec/cql_sql_spec.rb +3 -3
  81. data/spec/cql_symbol_tables_spec.rb +30 -30
  82. data/spec/cqldump_spec.rb +4 -4
  83. data/spec/helpers/array_matcher.rb +32 -27
  84. data/spec/helpers/diff_matcher.rb +6 -26
  85. data/spec/helpers/file_matcher.rb +41 -32
  86. data/spec/helpers/parse_to_ast_matcher.rb +76 -0
  87. data/spec/helpers/string_matcher.rb +32 -31
  88. data/spec/norma_cql_spec.rb +1 -1
  89. data/spec/norma_ruby_spec.rb +1 -1
  90. data/spec/norma_ruby_sql_spec.rb +1 -1
  91. data/spec/norma_sql_spec.rb +3 -1
  92. data/spec/norma_tables_spec.rb +1 -1
  93. data/spec/ruby_api_spec.rb +23 -0
  94. data/spec/spec_helper.rb +5 -4
  95. metadata +66 -66
  96. data/examples/CQL/OrienteeringER.cql +0 -58
  97. data/lib/activefacts/api.rb +0 -44
  98. data/lib/activefacts/api/concept.rb +0 -410
  99. data/lib/activefacts/api/constellation.rb +0 -128
  100. data/lib/activefacts/api/entity.rb +0 -256
  101. data/lib/activefacts/api/instance.rb +0 -60
  102. data/lib/activefacts/api/instance_index.rb +0 -80
  103. data/lib/activefacts/api/numeric.rb +0 -167
  104. data/lib/activefacts/api/role.rb +0 -80
  105. data/lib/activefacts/api/role_proxy.rb +0 -70
  106. data/lib/activefacts/api/role_values.rb +0 -117
  107. data/lib/activefacts/api/standard_types.rb +0 -87
  108. data/lib/activefacts/api/support.rb +0 -65
  109. data/lib/activefacts/api/value.rb +0 -135
  110. data/lib/activefacts/api/vocabulary.rb +0 -82
  111. data/spec/api/autocounter.rb +0 -82
  112. data/spec/api/constellation.rb +0 -130
  113. data/spec/api/entity_type.rb +0 -103
  114. data/spec/api/instance.rb +0 -461
  115. data/spec/api/roles.rb +0 -124
  116. data/spec/api/value_type.rb +0 -112
  117. data/spec/api_spec.rb +0 -13
  118. data/spec/cql/matching_spec.rb +0 -517
  119. data/spec/cql/unit_spec.rb +0 -394
  120. data/spec/spec.opts +0 -1
@@ -136,6 +136,11 @@ module ActiveFacts
136
136
  value_type :length => 256
137
137
  end
138
138
 
139
+ class TransactionTiming < String
140
+ value_type
141
+ restrict 'assert', 'commit'
142
+ end
143
+
139
144
  class UnitId < AutoCounter
140
145
  value_type
141
146
  end
@@ -169,12 +174,12 @@ module ActiveFacts
169
174
 
170
175
  class ContextNote
171
176
  identified_by :context_note_id
172
- has_one :concept # See Concept.all_context_note
173
177
  has_one :constraint # See Constraint.all_context_note
174
178
  one_to_one :context_note_id, :mandatory => true # See ContextNoteId.context_note
175
179
  has_one :context_note_kind, :mandatory => true # See ContextNoteKind.all_context_note
176
180
  has_one :discussion, :mandatory => true # See Discussion.all_context_note
177
181
  has_one :fact_type # See FactType.all_context_note
182
+ has_one :object_type # See ObjectType.all_context_note
178
183
  end
179
184
 
180
185
  class Enforcement
@@ -201,9 +206,9 @@ module ActiveFacts
201
206
 
202
207
  class Instance
203
208
  identified_by :instance_id
204
- has_one :concept, :mandatory => true # See Concept.all_instance
205
209
  one_to_one :fact # See Fact.instance
206
210
  one_to_one :instance_id, :mandatory => true # See InstanceId.instance
211
+ has_one :object_type, :mandatory => true # See ObjectType.all_instance
207
212
  has_one :population, :mandatory => true # See Population.all_instance
208
213
  has_one :value # See Value.all_instance
209
214
  end
@@ -215,9 +220,10 @@ module ActiveFacts
215
220
 
216
221
  class JoinNode
217
222
  identified_by :join, :ordinal
218
- has_one :concept, :mandatory => true # See Concept.all_join_node
219
223
  has_one :join, :mandatory => true # See Join.all_join_node
224
+ has_one :object_type, :mandatory => true # See ObjectType.all_join_node
220
225
  has_one :ordinal, :mandatory => true # See Ordinal.all_join_node
226
+ has_one :role_name, :class => Name # See Name.all_join_node_as_role_name
221
227
  has_one :subscript # See Subscript.all_join_node
222
228
  has_one :value # See Value.all_join_node
223
229
  end
@@ -254,8 +260,8 @@ module ActiveFacts
254
260
  identified_by :fact_type, :ordinal
255
261
  has_one :fact_type, :mandatory => true # See FactType.all_role
256
262
  has_one :ordinal, :mandatory => true # See Ordinal.all_role
257
- has_one :concept, :mandatory => true # See Concept.all_role
258
- one_to_one :implicit_fact_type # See ImplicitFactType.role
263
+ one_to_one :implicit_fact_type, :counterpart => :implying_role # See ImplicitFactType.implying_role
264
+ has_one :object_type, :mandatory => true # See ObjectType.all_role
259
265
  has_one :role_name, :class => Name # See Name.all_role_as_role_name
260
266
  end
261
267
 
@@ -329,14 +335,6 @@ module ActiveFacts
329
335
  has_one :value, :mandatory => true # See Value.all_bound
330
336
  end
331
337
 
332
- class Concept
333
- identified_by :vocabulary, :name
334
- maybe :is_independent
335
- has_one :name, :mandatory => true # See Name.all_concept
336
- has_one :pronoun # See Pronoun.all_concept
337
- has_one :vocabulary, :mandatory => true # See Vocabulary.all_concept
338
- end
339
-
340
338
  class ConstraintShape < Shape
341
339
  has_one :constraint, :mandatory => true # See Constraint.all_constraint_shape
342
340
  end
@@ -367,10 +365,6 @@ module ActiveFacts
367
365
  has_one :vocabulary, :mandatory => true # See Vocabulary.all_diagram
368
366
  end
369
367
 
370
- class EntityType < Concept
371
- one_to_one :fact_type # See FactType.entity_type
372
- end
373
-
374
368
  class FactTypeShape < Shape
375
369
  has_one :display_role_names_setting # See DisplayRoleNamesSetting.all_fact_type_shape
376
370
  has_one :fact_type, :mandatory => true # See FactType.all_fact_type_shape
@@ -397,9 +391,17 @@ module ActiveFacts
397
391
  has_one :context_note, :mandatory => true # See ContextNote.all_model_note_shape
398
392
  end
399
393
 
394
+ class ObjectType
395
+ identified_by :vocabulary, :name
396
+ maybe :is_independent
397
+ has_one :name, :mandatory => true # See Name.all_object_type
398
+ has_one :pronoun # See Pronoun.all_object_type
399
+ has_one :vocabulary, :mandatory => true # See Vocabulary.all_object_type
400
+ end
401
+
400
402
  class ObjectTypeShape < Shape
401
- has_one :concept, :mandatory => true # See Concept.all_object_type_shape
402
403
  maybe :has_expanded_reference_mode
404
+ has_one :object_type, :mandatory => true # See ObjectType.all_object_type_shape
403
405
  end
404
406
 
405
407
  class ObjectifiedFactTypeNameShape < Shape
@@ -461,14 +463,6 @@ module ActiveFacts
461
463
  maybe :is_mandatory
462
464
  end
463
465
 
464
- class TypeInheritance < FactType
465
- identified_by :subtype, :supertype
466
- has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
467
- has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
468
- has_one :assimilation # See Assimilation.all_type_inheritance
469
- maybe :provides_identification
470
- end
471
-
472
466
  class ValueConstraintShape < ConstraintShape
473
467
  has_one :object_type_shape # See ObjectTypeShape.all_value_constraint_shape
474
468
  one_to_one :role_display # See RoleDisplay.value_constraint_shape
@@ -480,8 +474,8 @@ module ActiveFacts
480
474
  has_one :minimum_bound, :class => Bound # See Bound.all_value_range_as_minimum_bound
481
475
  end
482
476
 
483
- class ValueType < Concept
484
- maybe :is_auto_assigned
477
+ class ValueType < ObjectType
478
+ has_one :auto_assigned_transaction_timing, :class => TransactionTiming # See TransactionTiming.all_value_type_as_auto_assigned_transaction_timing
485
479
  has_one :length # See Length.all_value_type
486
480
  has_one :scale # See Scale.all_value_type
487
481
  has_one :supertype, :class => ValueType # See ValueType.all_value_type_as_supertype
@@ -495,20 +489,32 @@ module ActiveFacts
495
489
  has_one :value_range, :mandatory => true # See ValueRange.all_allowed_range
496
490
  end
497
491
 
492
+ class EntityType < ObjectType
493
+ one_to_one :fact_type # See FactType.entity_type
494
+ end
495
+
498
496
  class ImplicitBooleanValueType < ValueType
499
497
  end
500
498
 
501
- class Parameter
502
- identified_by :name, :value_type
503
- has_one :name, :mandatory => true # See Name.all_parameter
504
- has_one :value_type, :mandatory => true # See ValueType.all_parameter
499
+ class Facet
500
+ identified_by :value_type, :name
501
+ has_one :name, :mandatory => true # See Name.all_facet
502
+ has_one :value_type, :mandatory => true # See ValueType.all_facet
503
+ end
504
+
505
+ class TypeInheritance < FactType
506
+ identified_by :subtype, :supertype
507
+ has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
508
+ has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
509
+ has_one :assimilation # See Assimilation.all_type_inheritance
510
+ maybe :provides_identification
505
511
  end
506
512
 
507
- class ParamValue
508
- identified_by :value, :parameter
509
- has_one :parameter, :mandatory => true # See Parameter.all_param_value
510
- has_one :value, :mandatory => true # See Value.all_param_value
511
- has_one :value_type, :mandatory => true # See ValueType.all_param_value
513
+ class FacetValue
514
+ identified_by :value_type, :facet
515
+ has_one :facet, :mandatory => true # See Facet.all_facet_value
516
+ has_one :value, :mandatory => true # See Value.all_facet_value
517
+ has_one :value_type, :mandatory => true # See ValueType.all_facet_value
512
518
  end
513
519
 
514
520
  end
@@ -11,15 +11,15 @@ module ActiveFacts
11
11
  # * Maintains verbalisation context to expand readings using subscripting where needed
12
12
  # * Verbalises Joins by iteratively choosing a Join Step and expanding readings
13
13
  #
14
- # The verbalisation context consists of a set of Players, each for one Concept.
15
- # There may be more than one Player for the same Concept. If adjectives or role
14
+ # The verbalisation context consists of a set of Players, each for one ObjectType.
15
+ # There may be more than one Player for the same ObjectType. If adjectives or role
16
16
  # names don't make such duplicates unambiguous, subscripts will be generated.
17
17
  # Thus, the verbalisation context must be completely populated before subscript
18
18
  # generation, which must be before any Player name gets verbalised.
19
19
  #
20
20
  # When a Player occurs in a Join, it corresponds to one Join Node of that Join.
21
21
  # Each such Player has one or more JoinRoles, which refer to roles played by
22
- # that Concept. Where a join traverses two roles of a ternary fact type, there
22
+ # that ObjectType. Where a join traverses two roles of a ternary fact type, there
23
23
  # will be a residual node that has only a single JoinRole with no other meaning.
24
24
  # A JoinRole must be for exactly one Player, so is used to identify a Player.
25
25
  #
@@ -37,11 +37,11 @@ module ActiveFacts
37
37
  #
38
38
  # A constrained RoleSequence that has no explicit Join may have an implicit join,
39
39
  # as per ORM2, when the roles aren't in the same fact type. These implicit joins
40
- # are over only one Concept, by traversing a single FactType (and possibly,
40
+ # are over only one ObjectType, by traversing a single FactType (and possibly,
41
41
  # multiple TypeInheritance FactTypes) for each RoleRef. Note however that when
42
- # the Concept is an objectified Fact Type, the FactType traversed might be a
42
+ # the ObjectType is an objectified Fact Type, the FactType traversed might be a
43
43
  # phantom of the objectification. In the case of implicit joins, each Player is
44
- # identified by the projected RoleRef, except for the joined-over Concept whose
44
+ # identified by the projected RoleRef, except for the joined-over ObjectType whose
45
45
  # Player is... well, read the next paragraph!
46
46
  #
47
47
  # REVISIT: I believe that the foregoing paragraph is out of date, except with
@@ -54,7 +54,7 @@ module ActiveFacts
54
54
  # For a SetComparisonConstraint, there are two or more constrained RoleSequences.
55
55
  # The matching RoleRefs (by Ordinal position) are for joined players, that is,
56
56
  # one individual instance plays both roles. The RoleRefs must (now) be for the
57
- # same Concept (no implicit subtyping Join is allowed). Instead, the input modules
57
+ # same ObjectType (no implicit subtyping Join is allowed). Instead, the input modules
58
58
  # find the closest common supertype and create explicit JoinSteps so its roles
59
59
  # can be projected.
60
60
  #
@@ -99,9 +99,9 @@ module ActiveFacts
99
99
  end
100
100
 
101
101
  class Player
102
- attr_accessor :concept, :join_nodes_by_join, :subscript, :join_roles, :role_refs
103
- def initialize concept
104
- @concept = concept
102
+ attr_accessor :object_type, :join_nodes_by_join, :subscript, :join_roles, :role_refs
103
+ def initialize object_type
104
+ @object_type = object_type
105
105
  @join_nodes_by_join = {}
106
106
  @subscript = nil
107
107
  @join_roles = []
@@ -111,30 +111,41 @@ module ActiveFacts
111
111
  # What words are used (across all roles) for disambiguating the references to this player?
112
112
  # If more than one set of adjectives was used, this player must have been subject to loose binding.
113
113
  # This method is used to decide when subscripts aren't needed.
114
- def role_adjuncts
115
- adjuncts = @role_refs.map{|rr|
116
- [
117
- rr.leading_adjective,
118
- # rr.role.role_name,
119
- rr.trailing_adjective
120
- ].compact}.uniq.sort
114
+ def role_adjuncts matching
115
+ if matching == :loose
116
+ adjuncts = []
117
+ else
118
+ adjuncts = @role_refs.map{|rr|
119
+ [
120
+ rr.leading_adjective,
121
+ matching == :rolenames ? rr.role.role_name : nil,
122
+ rr.trailing_adjective
123
+ ].compact}.uniq.sort
124
+ end
125
+ adjuncts += [@join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]].compact
121
126
  adjuncts.flatten*"_"
122
127
  end
123
128
 
124
129
  def describe
125
- @concept.name + (@join_nodes_by_join.size > 0 ? " (in #{@join_nodes_by_join.size} joins)" : "")
130
+ @object_type.name + (@join_nodes_by_join.size > 0 ? " (in #{@join_nodes_by_join.size} joins)" : "")
126
131
  end
127
132
  end
128
133
 
129
134
  # Find or create a Player to which we can add this role_ref
130
135
  def player(ref)
131
- if ref.is_a?(ActiveFacts::Metamodel::JoinRole)
132
- @player_by_join_role[ref] or
133
- @players.push(p = Player.new(ref.role.concept)) && p
136
+ existing_player = if ref.is_a?(ActiveFacts::Metamodel::JoinRole)
137
+ @player_by_join_role[ref]
138
+ else
139
+ @player_by_role_ref[ref] or ref.join_role && @player_by_join_role[ref.join_role]
140
+ end
141
+ if existing_player
142
+ debug :player, "Using existing player for #{ref.role.object_type.name} #{ref.respond_to?(:role_sequence) && ref.role_sequence.all_reading.size > 0 ? ' in reading' : ''}in '#{ref.role.fact_type.default_reading}'"
143
+ return existing_player
134
144
  else
135
- @player_by_role_ref[ref] or
136
- ref.join_role && @player_by_join_role[ref.join_role] or
137
- @players.push(p = Player.new(ref.role.concept)) && p
145
+ debug :player, "Adding new player for #{ref.role.object_type.name} #{ref.respond_to?(:role_sequence) && ref.role_sequence.all_reading.size > 0 ? ' in reading' : ''}in '#{ref.role.fact_type.default_reading}'"
146
+ p = Player.new(ref.role.object_type)
147
+ @players.push(p)
148
+ p
138
149
  end
139
150
  end
140
151
 
@@ -142,7 +153,7 @@ module ActiveFacts
142
153
  return if player.join_roles.include?(join_role)
143
154
  jn = join_role.join_node
144
155
  if jn1 = player.join_nodes_by_join[jn.join] and jn1 != jn
145
- raise "Player for #{player.concept.name} may only have one join node per join, not #{jn1.concept.name} and #{jn.concept.name}"
156
+ raise "Player for #{player.object_type.name} may only have one join node per join, not #{jn1.object_type.name} and #{jn.object_type.name}"
146
157
  end
147
158
  player.join_nodes_by_join[jn.join] = jn
148
159
  @player_by_join_role[join_role] = player
@@ -155,7 +166,7 @@ module ActiveFacts
155
166
  if jr = role_ref.join_role
156
167
  add_join_role(player, jr)
157
168
  elsif !player.role_refs.include?(role_ref)
158
- debug :subscript, "Adding reference to player #{player.object_id} for #{role_ref.role.concept.name} in #{role_ref.role_sequence.describe} with #{role_ref.role_sequence.all_reading.size} readings"
169
+ debug :subscript, "Adding reference to player #{player.object_id} for #{role_ref.role.object_type.name} in #{role_ref.role_sequence.describe} with #{role_ref.role_sequence.all_reading.size} readings"
159
170
  player.role_refs.push(role_ref)
160
171
  @player_by_role_ref[role_ref] = player
161
172
  end
@@ -182,11 +193,7 @@ module ActiveFacts
182
193
  elsif role_name = role_ref.role.role_name and role_name != ''
183
194
  role_name
184
195
  else
185
- role_words = []
186
- role_words << preferred_role_ref.leading_adjective if preferred_role_ref.leading_adjective != ""
187
- role_words << preferred_role_ref.role.concept.name
188
- role_words << preferred_role_ref.trailing_adjective if preferred_role_ref.trailing_adjective != ""
189
- role_name = role_words.compact*"-"
196
+ role_name = preferred_role_ref.cql_name
190
197
  if p = player(preferred_role_ref) and p.subscript
191
198
  role_name += "(#{p.subscript})"
192
199
  end
@@ -211,10 +218,10 @@ module ActiveFacts
211
218
  # If any of these join_roles are for a known player, use that, else make a new player.
212
219
  existing_players = join_roles.map{|jr| @player_by_join_role[jr] }.compact.uniq
213
220
  if existing_players.size > 1
214
- raise "Can't join these roles to more than one existing player: #{existing_players.map{|p|p.concept.name}*', '}!"
221
+ raise "Can't join these roles to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
215
222
  end
216
223
  p = existing_players[0] || player(join_roles[0])
217
- debugger if join_roles.detect{|jr| jr.role.concept != p.concept }
224
+ debugger if join_roles.detect{|jr| jr.role.object_type != p.object_type }
218
225
  debug :subscript, "Joining roles to #{p.describe}" do
219
226
  join_roles.each do |jr|
220
227
  debug :subscript, "#{jr.describe}" do
@@ -233,17 +240,17 @@ module ActiveFacts
233
240
  existing_players =
234
241
  role_refs.map{|rr| @player_by_role_ref[rr] || @player_by_join_role[rr.join_role] }.compact.uniq
235
242
  if existing_players.size > 1
236
- raise "Can't join these role_refs to more than one existing player: #{existing_players.map{|p|p.concept.name}*', '}!"
243
+ raise "Can't join these role_refs to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
237
244
  end
238
245
  p = existing_players[0] || player(role_refs[0])
239
246
 
240
- debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.concept.name}.uniq*', '}" do
247
+ debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.object_type.name}.uniq*', '}" do
241
248
  role_refs.each do |rr|
242
- unless p.concept == rr.role.concept
249
+ unless p.object_type == rr.role.object_type
243
250
  # This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping join.
244
251
  # For now, explode only if there's no common supertype:
245
- if 0 == (p.concept.supertypes_transitive & rr.role.concept.supertypes_transitive).size
246
- raise "REVISIT: Internal error, trying to add role of #{rr.role.concept.name} to player #{p.concept.name}"
252
+ if 0 == (p.object_type.supertypes_transitive & rr.role.object_type.supertypes_transitive).size
253
+ raise "REVISIT: Internal error, trying to add role of #{rr.role.object_type.name} to player #{p.object_type.name}"
247
254
  end
248
255
  end
249
256
  add_role_player(p, rr)
@@ -251,21 +258,44 @@ module ActiveFacts
251
258
  end
252
259
  end
253
260
 
254
- def create_subscripts
261
+ # REVISIT: include_rolenames is a bit of a hack. Role names generally serve to disambiguate players,
262
+ # so subscripts wouldn't be needed, but where a constraint refers to a fact type which is defined with
263
+ # role names, those are considered. We should instead consider only the role names that are defined
264
+ # within the constraint, not in the underlying fact types. For now, this parameter is passed as true
265
+ # from all the object type verbalisations, and not from constraints.
266
+ def create_subscripts(matching = :normal)
255
267
  # Create subscripts, where necessary
256
268
  @players.each { |p| p.subscript = nil } # Wipe subscripts
257
269
  @players.
258
- map{|p| [p, p.concept] }.
259
- each do |player, concept|
270
+ map{|p| [p, p.object_type] }.
271
+ each do |player, object_type|
260
272
  next if player.subscript # Done previously
261
- dups = @players.select{|p| p.concept == concept && p.role_adjuncts == player.role_adjuncts }
273
+ dups = @players.select do |p|
274
+ p.object_type == object_type &&
275
+ p.role_adjuncts(matching) == player.role_adjuncts(matching)
276
+ end
262
277
  if dups.size == 1
263
- debug :subscript, "No subscript needed for #{concept.name}"
278
+ debug :subscript, "No subscript needed for #{object_type.name}"
264
279
  next
265
280
  end
266
- debug :subscript, "Applying subscripts to #{dups.size} occurrences of #{concept.name}" do
267
- dups.each_with_index do |player, index|
268
- player.subscript = index+1
281
+ debug :subscript, "Applying subscripts to #{dups.size} occurrences of #{object_type.name}" do
282
+ s = 0
283
+ dups.
284
+ sort_by{|p| # Guarantee stable numbering
285
+ p.role_adjuncts(:role_name) + ' ' +
286
+ # Tie-breaker:
287
+ p.role_refs.map{|rr| rr.role.fact_type.preferred_reading.text}.sort.to_s
288
+ }.
289
+ each do |player|
290
+ jrname = player.join_roles.map{|jr| jr.role_ref && jr.role_ref.role.role_name}.compact[0]
291
+ rname = (rr = player.role_refs[0]) && rr.role.role_name
292
+ if jrname and !rname
293
+ # puts "Oops: rolename #{rname.inspect} != #{jrname.inspect}" if jrname != rname
294
+ player.join_nodes_by_join.values.each{|jn| jn.role_name = jrname }
295
+ else
296
+ player.subscript = s+1
297
+ s += 1
298
+ end
269
299
  end
270
300
  end
271
301
  end
@@ -276,7 +306,7 @@ module ActiveFacts
276
306
  # and also define adjectives by using the hyphenated form (on at least the first occurrence).
277
307
  def expand_reading(reading, frequency_constraints = [], define_role_names = nil, value_constraints = [], &subscript_block)
278
308
  reading.expand(frequency_constraints, define_role_names, value_constraints) do |role_ref|
279
- (!(role_ref.role.role_name and define_role_names) and p = player(role_ref) and p.subscript) ? "(#{p.subscript})" : ""
309
+ (!(role_ref.role.role_name and define_role_names != nil) and p = player(role_ref) and p.subscript) ? "(#{p.subscript})" : ""
280
310
  end
281
311
  end
282
312
 
@@ -284,8 +314,8 @@ module ActiveFacts
284
314
  # REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the join.
285
315
  def role_refs_are_subtype_joined roles
286
316
  role_refs = roles.is_a?(Array) ? roles : roles.all_role_ref.to_a
287
- role_refs_by_concept = role_refs.inject({}) { |h, r| (h[r.role.concept] ||= []) << r; h }
288
- role_refs_by_concept.values.each { |rrs| role_refs_have_same_player(rrs) }
317
+ role_refs_by_object_type = role_refs.inject({}) { |h, r| (h[r.role.object_type] ||= []) << r; h }
318
+ role_refs_by_object_type.values.each { |rrs| role_refs_have_same_player(rrs) }
289
319
  end
290
320
 
291
321
  # These roles are the players in an implicit counterpart join in a Presence Constraint.
@@ -293,9 +323,11 @@ module ActiveFacts
293
323
  # Fix the CQL compiler to create proper joins for these presence constraints instead.
294
324
  def roles_have_same_player roles
295
325
  role_refs = roles.map do |role|
296
- pr = role.fact_type.preferred_reading
297
- pr.role_sequence.all_role_ref.detect{|rr| rr.role == role}
298
- end
326
+ role.fact_type.all_reading.map{|reading|
327
+ reading.role_sequence.all_role_ref.detect{|rr| rr.role == role}
328
+ } +
329
+ role.all_role_ref.select{|rr| rr.role_sequence.all_reading.size == 0 }
330
+ end.flatten.uniq
299
331
  role_refs_have_same_player(role_refs)
300
332
  end
301
333
 
@@ -316,10 +348,10 @@ module ActiveFacts
316
348
  prrs = fact_type.preferred_reading.role_sequence.all_role_ref
317
349
  residual_roles = fact_type.all_role.select{|r| !@role_refs.detect{|rr| rr.role == r} }
318
350
  residual_roles.each do |role|
319
- debug :subscript, "Adding residual role for #{role.concept.name} (in #{fact_type.default_reading}) not covered in role sequence"
351
+ debug :subscript, "Adding residual role for #{role.object_type.name} (in #{fact_type.default_reading}) not covered in role sequence"
320
352
  preferred_role_ref = prrs.detect{|rr| rr.role == role}
321
353
  if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
322
- raise "Adding DUPLICATE residual role for #{role.concept.name}"
354
+ raise "Adding DUPLICATE residual role for #{role.object_type.name}"
323
355
  end
324
356
  role_refs_have_same_player([prrs.detect{|rr| rr.role == role}])
325
357
  end
@@ -340,13 +372,13 @@ module ActiveFacts
340
372
  =begin
341
373
  # For each fact type traversed, register a player for each role *not* linked to this join
342
374
  # REVISIT: Using the preferred_reading role_ref is wrong here; the same preferred_reading might occur twice,
343
- # so the respective concept will need more than one Player and will be subscripted to keep them from being joined.
375
+ # so the respective object_type will need more than one Player and will be subscripted to keep them from being joined.
344
376
  # Accordingly, there must be a join step for each such role, and to enforce that, I raise an exception here on duplication.
345
377
  # This isn't needed now all JoinNodes have at least one JoinRole
346
378
 
347
379
  join_steps.map do |js|
348
380
  if js.fact_type.is_a?(ActiveFacts::Metamodel::ImplicitFactType)
349
- js.fact_type.role.fact_type
381
+ js.fact_type.implying_role.fact_type
350
382
  else
351
383
  js.fact_type
352
384
  end
@@ -358,10 +390,10 @@ module ActiveFacts
358
390
  prrs = fact_type.preferred_reading.role_sequence.all_role_ref
359
391
  residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.join_node && rr.join_node.join == join} }
360
392
  residual_roles.each do |r|
361
- debug :subscript, "Adding residual role for #{r.concept.name} (in #{fact_type.default_reading}) not covered in join"
393
+ debug :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in join"
362
394
  preferred_role_ref = prrs.detect{|rr| rr.role == r}
363
395
  if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
364
- raise "Adding DUPLICATE residual role for #{r.concept.name} not covered in join"
396
+ raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in join"
365
397
  end
366
398
  role_refs_have_same_player([preferred_role_ref])
367
399
  end
@@ -388,7 +420,7 @@ module ActiveFacts
388
420
  # Choose a reading that start with the (first) role which caused us to emit this fact type:
389
421
  reading = fact_type.reading_preferably_starting_with_role(role_by_fact_type[fact_type])
390
422
  if join_over and # Find a reading preferably starting with the joined_over role:
391
- joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.concept)}[0]
423
+ joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.object_type)}[0]
392
424
  reading = fact_type.reading_preferably_starting_with_role joined_role
393
425
 
394
426
  # Use the name of the joined_over object, not the role player, in case of a subtype join:
@@ -399,13 +431,11 @@ module ActiveFacts
399
431
  reading.role_sequence.all_role_ref.each do |rr|
400
432
  next unless player = @player_by_role_ref[rr]
401
433
  next unless subscript = player.subscript
402
- debug :subscript, "Need to apply subscript #{subscript} to #{rr.role.concept.name}"
434
+ debug :subscript, "Need to apply subscript #{subscript} to #{rr.role.object_type.name}"
403
435
  end
404
436
  player_by_role = {}
405
437
  @player_by_role_ref.keys.each{|rr| player_by_role[rr.role] = @player_by_role_ref[rr] if rr.role.fact_type == fact_type }
406
- #role_refs = @player_by_role_ref.keys.select{|rr| rr.role.fact_type == fact_type}
407
438
  expand_reading_text(nil, reading.text, reading.role_sequence, player_by_role)
408
- #reading.expand(name_substitutions)
409
439
  end
410
440
  joiner ? readings*joiner : readings
411
441
  end
@@ -421,37 +451,29 @@ module ActiveFacts
421
451
  text.gsub(/\{(\d)\}/) do
422
452
  role_ref = rrs[$1.to_i]
423
453
  # REVISIT: We may need to use the step's role_refs to expand the role players here, not the reading's one (extra adjectives?)
424
- # REVISIT: There's no way to get literals to be emitted here (value join step?)
454
+ # REVISIT: There's no way to get literals to be emitted here (value join step or query result?)
425
455
 
426
456
  player = player_by_role[role_ref.role]
427
457
 
428
- =begin
429
- rr = role_refs.detect{|rr| rr.role == role_ref.role} || role_ref
430
-
431
- player = @player_by_role_ref[rr] and subscript = player.subscript
432
- if !subscript and
433
- pp = @players.select{|p|p.concept == rr.role.concept} and
434
- pp.detect{|p|p.subscript}
435
- # raise "Internal error: Subscripted players (of the same concept #{pp[0].concept.name}) when this player isn't subscripted"
436
- end
437
- =end
438
-
439
- subscripted_player(role_ref, player && player.subscript) +
440
- objectification_verbalisation(role_ref.role.concept)
458
+ join_role_name = player && player.join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]
459
+ subscripted_player(role_ref, player && player.subscript, join_role_name) +
460
+ objectification_verbalisation(role_ref.role.object_type)
441
461
  end
442
462
  end
443
463
  end
444
464
 
445
- def subscripted_player role_ref, subscript = nil
446
- if subscript
447
- debug :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.concept.name}"
448
- end
449
- concept = role_ref.role.concept
450
- [
451
- role_ref.leading_adjective,
452
- concept.name,
453
- role_ref.trailing_adjective
454
- ].compact*' ' +
465
+ def subscripted_player role_ref, subscript = nil, join_role_name = nil
466
+ prr = @player_by_role_ref[role_ref]
467
+ subscript ||= prr.subscript if prr
468
+ debug :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.object_type.name}" if subscript
469
+ object_type = role_ref.role.object_type
470
+ (join_role_name ||
471
+ [
472
+ role_ref.leading_adjective,
473
+ object_type.name,
474
+ role_ref.trailing_adjective
475
+ ].compact*' '
476
+ ) +
455
477
  (subscript ? "(#{subscript})" : '')
456
478
  end
457
479
 
@@ -515,9 +537,9 @@ module ActiveFacts
515
537
  debug :join, "Chose new random step from #{join_steps.size}: #{next_step.describe}"
516
538
  if next_step.is_objectification_step
517
539
  # if this objectification plays any roles (other than its FT roles) in remaining steps, use one of those first:
518
- fact_type = next_step.fact_type.role.fact_type
519
- jn = [next_step.input_join_role.join_node, next_step.output_join_role.join_node].detect{|jn| jn.concept == fact_type.entity_type}
520
- sr = @join_steps_by_join_node[jn].reject{|t| t.fact_type.role and t.fact_type.role.fact_type == fact_type}
540
+ fact_type = next_step.fact_type.implying_role.fact_type
541
+ jn = [next_step.input_join_role.join_node, next_step.output_join_role.join_node].detect{|jn| jn.object_type == fact_type.entity_type}
542
+ sr = @join_steps_by_join_node[jn].reject{|t| r = t.fact_type.implying_role and r.fact_type == fact_type}
521
543
  next_step = sr[0] if sr.size > 0
522
544
  end
523
545
  return next_step
@@ -534,13 +556,13 @@ module ActiveFacts
534
556
  # This reading's RoleRef for that role:
535
557
  (role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]) &&
536
558
  # was that RoleRef for the upcoming node?
537
- role_ref.role.concept == next_node.concept
559
+ role_ref.role.object_type == next_node.object_type
538
560
  end
539
561
 
540
562
  def reading_starts_with_node(reading, next_node)
541
563
  reading.text =~ /^\{([0-9])\}/ and
542
564
  role_ref = reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i} and
543
- role_ref.role.concept == next_node.concept
565
+ role_ref.role.object_type == next_node.object_type
544
566
  end
545
567
 
546
568
  # The last reading we emitted ended with the object type name for next_node.
@@ -558,21 +580,21 @@ module ActiveFacts
558
580
  end
559
581
  next_reading
560
582
  end
561
- debug :join, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.concept.name}'"
583
+ debug :join, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.object_type.name}'"
562
584
  return [next_step, next_reading]
563
585
  end
564
586
 
565
- # REVISIT: There might be more than one objectification_verbalisation for a given concept. Need to get the Join Node here and emit an objectification step involving that node.
566
- def objectification_verbalisation(concept)
587
+ # REVISIT: There might be more than one objectification_verbalisation for a given object_type. Need to get the Join Node here and emit an objectification step involving that node.
588
+ def objectification_verbalisation(object_type)
567
589
  objectified_node = nil
568
- unless concept.is_a?(Metamodel::EntityType) and
569
- concept.fact_type and # Not objectified
590
+ unless object_type.is_a?(Metamodel::EntityType) and
591
+ object_type.fact_type and # Not objectified
570
592
  objectification_step = @join_steps.
571
593
  detect do |js|
572
594
  # The objectifying entity type should always be the input_join_node here, but be safe:
573
595
  js.is_objectification_step and
574
- (objectified_node = js.input_join_role.join_node).concept == concept ||
575
- (objectified_node = js.output_join_role.join_node).concept == concept
596
+ (objectified_node = js.input_join_role.join_node).object_type == object_type ||
597
+ (objectified_node = js.output_join_role.join_node).object_type == object_type
576
598
  end
577
599
  return ''
578
600
  end
@@ -586,7 +608,7 @@ module ActiveFacts
586
608
  @join_steps.
587
609
  detect{|js|
588
610
  js.is_objectification_step and
589
- js.input_join_role.join_node.concept == concept || js.output_join_role.join_node.concept == concept
611
+ js.input_join_role.join_node.object_type == object_type || js.output_join_role.join_node.object_type == object_type
590
612
  }
591
613
  steps << other_step
592
614
  debug :join, "Emitting objectification step allows deleting #{other_step.describe}"
@@ -601,9 +623,9 @@ module ActiveFacts
601
623
  end
602
624
  end
603
625
 
604
- # role_refs = steps.map{|step| [step.input_join_role.join_node, step.output_join_role.join_node].map{|jn| jn.all_role_ref.detect{|rr| rr.role.fact_type == concept.fact_type}}}.flatten.compact.uniq
626
+ # role_refs = steps.map{|step| [step.input_join_role.join_node, step.output_join_role.join_node].map{|jn| jn.all_role_ref.detect{|rr| rr.role.fact_type == object_type.fact_type}}}.flatten.compact.uniq
605
627
 
606
- reading = concept.fact_type.preferred_reading
628
+ reading = object_type.fact_type.preferred_reading
607
629
  " (where #{expand_reading_text(objectification_step, reading.text, reading.role_sequence, player_by_role)})"
608
630
  end
609
631
 
@@ -624,16 +646,19 @@ module ActiveFacts
624
646
  exit_node = @join_nodes.detect{|jn| jn.all_join_role.detect{|jr| jr.role == last_role_ref.role}}
625
647
  exit_step = nil
626
648
 
649
+ count = 0
627
650
  while other_step =
628
651
  @join_steps.
629
652
  detect{|js|
630
653
  next unless js.is_objectification_step
631
- next unless js.input_join_role.join_node.concept == fact_type.entity_type || js.output_join_role.join_node.concept == fact_type.entity_type
654
+ # REVISIT: This test is too weak: We need to ensure that the same join nodes are involved, not just the same object types:
655
+ next unless js.input_join_role.join_node.object_type == fact_type.entity_type || js.output_join_role.join_node.object_type == fact_type.entity_type
632
656
  exit_step = js if js.output_join_role.join_node == exit_node
633
657
  true
634
658
  }
635
659
  debug :join, "Emitting objectified FT allows deleting #{other_step.describe}"
636
660
  step_completed(other_step)
661
+ # raise "The objectification of '#{fact_type.default_reading}' should not cause the deletion of more than #{fact_type.all_role.size} other join steps" if (count += 1) > fact_type.all_role.size
637
662
  end
638
663
  [ reading, exit_step ? exit_step.input_join_role.join_node : exit_node, exit_step, last_is_contractable]
639
664
  end
@@ -657,7 +682,7 @@ module ActiveFacts
657
682
  end
658
683
 
659
684
  if next_step
660
- debug :join, "Chose #{next_step.describe} because it's contractable against last node #{next_node.concept.name} using #{next_reading.expand}"
685
+ debug :join, "Chose #{next_step.describe} because it's contractable against last node #{next_node.object_type.name} using #{next_reading.expand}"
661
686
 
662
687
  player_by_role =
663
688
  next_step.all_join_role.inject({}) {|h, jr| h[jr.role] = @player_by_join_role[jr]; h }
@@ -671,26 +696,26 @@ module ActiveFacts
671
696
 
672
697
  if next_step.is_unary_step
673
698
  # Objectified unaries get emitted as unaries, not as objectifications:
674
- # REVISIT: There must be a simpler way of finding the preferred reading here:
675
- rr = next_step.input_join_node.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ImplicitFactType) }
676
- next_reading = rr.role.fact_type.role.fact_type.preferred_reading
699
+ role = next_step.input_join_role.role
700
+ role = role.fact_type.implying_role if role.fact_type.is_a?(ImplicitFactType)
701
+ next_reading = role.fact_type.preferred_reading
677
702
  readings += " and " unless readings.empty?
678
703
  readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
679
704
  step_completed(next_step)
680
705
  elsif next_step.is_objectification_step
681
- fact_type = next_step.fact_type.role.fact_type
706
+ fact_type = next_step.fact_type.implying_role.fact_type
682
707
 
683
708
  # This objectification step is over an implicit fact type, so player_by_role won't have all the players
684
709
  # Add the players of other roles associated with steps from this objectified player.
685
710
  objectified_node = next_step.input_join_role.join_node
686
- raise "Assumption violated that the objectification is the input join role" unless objectified_node.concept.fact_type
711
+ raise "Assumption violated that the objectification is the input join role" unless objectified_node.object_type.fact_type
687
712
  objectified_node.all_join_step.map do |other_step|
688
713
  (other_step.all_incidental_join_role.to_a + [other_step.output_join_role]).map do |jr|
689
714
  player_by_role[jr.role] = @player_by_join_role[jr]
690
715
  end
691
716
  end
692
717
 
693
- if last_is_contractable and next_node.concept.is_a?(EntityType) and next_node.concept.fact_type == fact_type
718
+ if last_is_contractable and next_node.object_type.is_a?(EntityType) and next_node.object_type.fact_type == fact_type
694
719
  # The last reading we emitted ended with the name of the objectification of this fact type, so we can contract the objectification
695
720
  # REVISIT: Do we need to use player_by_role here (if this objectification is traversed twice and so is subscripted)
696
721
  readings += objectification_verbalisation(fact_type.entity_type)