activefacts 1.6.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +60 -0
- data/Rakefile +3 -80
- data/activefacts.gemspec +36 -0
- data/bin/afgen +4 -2
- data/bin/cql +5 -1
- data/lib/activefacts.rb +3 -12
- data/lib/activefacts/{vocabulary/query_evaluator.rb → query/evaluator.rb} +0 -0
- data/lib/activefacts/version.rb +2 -2
- metadata +48 -296
- data/History.txt +0 -4
- data/LICENSE +0 -19
- data/Manifest.txt +0 -165
- data/README.rdoc +0 -81
- data/css/offline.css +0 -3
- data/css/orm2.css +0 -124
- data/css/print.css +0 -8
- data/css/style-print.css +0 -357
- data/css/style.css +0 -387
- data/download.html +0 -110
- data/examples/CQL/Address.cql +0 -44
- data/examples/CQL/Blog.cql +0 -54
- data/examples/CQL/CompanyDirectorEmployee.cql +0 -56
- data/examples/CQL/Death.cql +0 -17
- data/examples/CQL/Diplomacy.cql +0 -48
- data/examples/CQL/Genealogy.cql +0 -98
- data/examples/CQL/Insurance.cql +0 -320
- data/examples/CQL/Marriage.cql +0 -18
- data/examples/CQL/Metamodel.cql +0 -493
- data/examples/CQL/Monogamy.cql +0 -24
- data/examples/CQL/MultiInheritance.cql +0 -22
- data/examples/CQL/NonRoleId.cql +0 -14
- data/examples/CQL/OddIdentifier.cql +0 -18
- data/examples/CQL/OilSupply.cql +0 -53
- data/examples/CQL/OneToOnes.cql +0 -17
- data/examples/CQL/Orienteering.cql +0 -111
- data/examples/CQL/PersonPlaysGame.cql +0 -18
- data/examples/CQL/RedundantDependency.cql +0 -34
- data/examples/CQL/SchoolActivities.cql +0 -33
- data/examples/CQL/SeparateSubtype.cql +0 -30
- data/examples/CQL/ServiceDirector.cql +0 -276
- data/examples/CQL/SimplestUnary.cql +0 -12
- data/examples/CQL/Supervision.cql +0 -34
- data/examples/CQL/WaiterTips.cql +0 -33
- data/examples/CQL/Warehousing.cql +0 -101
- data/examples/CQL/WindowInRoomInBldg.cql +0 -28
- data/examples/CQL/unit.cql +0 -474
- data/examples/index.html +0 -420
- data/examples/intro.html +0 -327
- data/examples/local.css +0 -24
- data/index.html +0 -111
- data/lib/activefacts/cql.rb +0 -35
- data/lib/activefacts/cql/CQLParser.treetop +0 -158
- data/lib/activefacts/cql/Context.treetop +0 -48
- data/lib/activefacts/cql/Expressions.treetop +0 -67
- data/lib/activefacts/cql/FactTypes.treetop +0 -358
- data/lib/activefacts/cql/Language/English.treetop +0 -315
- data/lib/activefacts/cql/LexicalRules.treetop +0 -253
- data/lib/activefacts/cql/ObjectTypes.treetop +0 -210
- data/lib/activefacts/cql/Rakefile +0 -14
- data/lib/activefacts/cql/Terms.treetop +0 -183
- data/lib/activefacts/cql/ValueTypes.treetop +0 -202
- data/lib/activefacts/cql/compiler.rb +0 -156
- data/lib/activefacts/cql/compiler/clause.rb +0 -1137
- data/lib/activefacts/cql/compiler/constraint.rb +0 -581
- data/lib/activefacts/cql/compiler/entity_type.rb +0 -457
- data/lib/activefacts/cql/compiler/expression.rb +0 -443
- data/lib/activefacts/cql/compiler/fact.rb +0 -390
- data/lib/activefacts/cql/compiler/fact_type.rb +0 -421
- data/lib/activefacts/cql/compiler/query.rb +0 -106
- data/lib/activefacts/cql/compiler/shared.rb +0 -161
- data/lib/activefacts/cql/compiler/value_type.rb +0 -174
- data/lib/activefacts/cql/nodes.rb +0 -49
- data/lib/activefacts/cql/parser.rb +0 -241
- data/lib/activefacts/dependency_analyser.rb +0 -182
- data/lib/activefacts/generate/absorption.rb +0 -70
- data/lib/activefacts/generate/composition.rb +0 -118
- data/lib/activefacts/generate/cql.rb +0 -714
- data/lib/activefacts/generate/dm.rb +0 -279
- data/lib/activefacts/generate/help.rb +0 -64
- data/lib/activefacts/generate/helpers/inject.rb +0 -16
- data/lib/activefacts/generate/helpers/oo.rb +0 -162
- data/lib/activefacts/generate/helpers/ordered.rb +0 -605
- data/lib/activefacts/generate/helpers/rails.rb +0 -57
- data/lib/activefacts/generate/html/glossary.rb +0 -461
- data/lib/activefacts/generate/json.rb +0 -337
- data/lib/activefacts/generate/null.rb +0 -32
- data/lib/activefacts/generate/rails/models.rb +0 -246
- data/lib/activefacts/generate/rails/schema.rb +0 -216
- data/lib/activefacts/generate/records.rb +0 -46
- data/lib/activefacts/generate/ruby.rb +0 -133
- data/lib/activefacts/generate/sql/mysql.rb +0 -280
- data/lib/activefacts/generate/sql/server.rb +0 -273
- data/lib/activefacts/generate/stats.rb +0 -69
- data/lib/activefacts/generate/text.rb +0 -27
- data/lib/activefacts/generate/topics.rb +0 -265
- data/lib/activefacts/generate/traits/datavault.rb +0 -241
- data/lib/activefacts/generate/traits/oo.rb +0 -73
- data/lib/activefacts/generate/traits/ordered.rb +0 -33
- data/lib/activefacts/generate/traits/ruby.rb +0 -210
- data/lib/activefacts/generate/transform/datavault.rb +0 -266
- data/lib/activefacts/generate/transform/surrogate.rb +0 -214
- data/lib/activefacts/generate/version.rb +0 -26
- data/lib/activefacts/input/cql.rb +0 -43
- data/lib/activefacts/input/orm.rb +0 -1636
- data/lib/activefacts/mapping/rails.rb +0 -132
- data/lib/activefacts/persistence.rb +0 -15
- data/lib/activefacts/persistence/columns.rb +0 -446
- data/lib/activefacts/persistence/foreignkey.rb +0 -187
- data/lib/activefacts/persistence/index.rb +0 -240
- data/lib/activefacts/persistence/object_type.rb +0 -198
- data/lib/activefacts/persistence/reference.rb +0 -434
- data/lib/activefacts/persistence/tables.rb +0 -380
- data/lib/activefacts/registry.rb +0 -11
- data/lib/activefacts/support.rb +0 -132
- data/lib/activefacts/vocabulary.rb +0 -9
- data/lib/activefacts/vocabulary/extensions.rb +0 -1348
- data/lib/activefacts/vocabulary/metamodel.rb +0 -570
- data/lib/activefacts/vocabulary/verbaliser.rb +0 -804
- data/script/txt2html +0 -71
- data/spec/absorption_spec.rb +0 -95
- data/spec/cql/comparison_spec.rb +0 -89
- data/spec/cql/context_spec.rb +0 -94
- data/spec/cql/contractions_spec.rb +0 -224
- data/spec/cql/deontic_spec.rb +0 -88
- data/spec/cql/entity_type_spec.rb +0 -320
- data/spec/cql/expressions_spec.rb +0 -66
- data/spec/cql/fact_type_matching_spec.rb +0 -338
- data/spec/cql/french_spec.rb +0 -21
- data/spec/cql/parser/bad_literals_spec.rb +0 -86
- data/spec/cql/parser/constraints_spec.rb +0 -19
- data/spec/cql/parser/entity_types_spec.rb +0 -106
- data/spec/cql/parser/expressions_spec.rb +0 -199
- data/spec/cql/parser/fact_types_spec.rb +0 -44
- data/spec/cql/parser/literals_spec.rb +0 -312
- data/spec/cql/parser/pragmas_spec.rb +0 -89
- data/spec/cql/parser/value_types_spec.rb +0 -42
- data/spec/cql/role_matching_spec.rb +0 -148
- data/spec/cql/samples_spec.rb +0 -244
- data/spec/cql_cql_spec.rb +0 -73
- data/spec/cql_dm_spec.rb +0 -136
- data/spec/cql_mysql_spec.rb +0 -69
- data/spec/cql_parse_spec.rb +0 -34
- data/spec/cql_ruby_spec.rb +0 -73
- data/spec/cql_sql_spec.rb +0 -72
- data/spec/cql_symbol_tables_spec.rb +0 -261
- data/spec/cqldump_spec.rb +0 -170
- data/spec/helpers/array_matcher.rb +0 -23
- data/spec/helpers/ctrl_c_support.rb +0 -52
- data/spec/helpers/diff_matcher.rb +0 -39
- data/spec/helpers/file_matcher.rb +0 -34
- data/spec/helpers/parse_to_ast_matcher.rb +0 -80
- data/spec/helpers/string_matcher.rb +0 -30
- data/spec/helpers/test_parser.rb +0 -15
- data/spec/norma_cql_spec.rb +0 -66
- data/spec/norma_ruby_spec.rb +0 -62
- data/spec/norma_ruby_sql_spec.rb +0 -107
- data/spec/norma_sql_spec.rb +0 -57
- data/spec/norma_tables_spec.rb +0 -95
- data/spec/ruby_api_spec.rb +0 -23
- data/spec/spec_helper.rb +0 -35
- data/spec/transform_surrogate_spec.rb +0 -59
- data/status.html +0 -138
- data/why.html +0 -60
data/lib/activefacts/registry.rb
DELETED
data/lib/activefacts/support.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Support code.
|
3
|
-
#
|
4
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
5
|
-
#
|
6
|
-
|
7
|
-
# Return all duplicate objects in the array (using hash-equality)
|
8
|
-
class Array
|
9
|
-
def duplicates(&b)
|
10
|
-
inject({}) do |h,e|
|
11
|
-
h[e] ||= 0
|
12
|
-
h[e] += 1
|
13
|
-
h
|
14
|
-
end.reject do |k,v|
|
15
|
-
v == 1
|
16
|
-
end.keys
|
17
|
-
end
|
18
|
-
|
19
|
-
if RUBY_VERSION =~ /^1\.8/
|
20
|
-
# Fake up Ruby 1.9's Array#index method, mostly
|
21
|
-
alias_method :__orig_index, :index
|
22
|
-
def index *a, &b
|
23
|
-
if a.size == 0
|
24
|
-
raise "Not faking Enumerator for #{RUBY_VERSION}" if !b
|
25
|
-
(0...size).detect{|i| return i if b.call(self[i]) }
|
26
|
-
else
|
27
|
-
__orig_index(*a, &b)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# If any element, or sequence of elements, repeats immediately, delete the repetition.
|
33
|
-
# Note that this doesn't remove all re-occurrences of a subsequence, only consecutive ones.
|
34
|
-
# The compare_block allows a custom equality comparison.
|
35
|
-
def elide_repeated_subsequences &compare_block
|
36
|
-
compare_block ||= lambda{|a,b| a == b}
|
37
|
-
i = 0
|
38
|
-
while i < size # Need to re-evaluate size on each loop - the array shrinks.
|
39
|
-
j = i
|
40
|
-
#puts "Looking for repetitions of #{self[i]}@[#{i}]"
|
41
|
-
while tail = self[j+1..-1] and k = tail.index {|e| compare_block.call(e, self[i]) }
|
42
|
-
length = j+1+k-i
|
43
|
-
#puts "Found at #{j+1+k} (subsequence of length #{j+1+k-i}), will need to repeat to #{j+k+length}"
|
44
|
-
if j+k+1+length <= size && compare_block[self[i, length], self[j+k+1, length]]
|
45
|
-
#puts "Subsequence from #{i}..#{j+k} repeats immediately at #{j+k+1}..#{j+k+length}"
|
46
|
-
slice!(j+k+1, length)
|
47
|
-
j = i
|
48
|
-
else
|
49
|
-
j += k+1
|
50
|
-
end
|
51
|
-
end
|
52
|
-
i += 1
|
53
|
-
end
|
54
|
-
self
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
class String
|
59
|
-
class Words
|
60
|
-
def initialize words
|
61
|
-
@words = words
|
62
|
-
end
|
63
|
-
|
64
|
-
def map(&b)
|
65
|
-
@words.map(&b)
|
66
|
-
end
|
67
|
-
|
68
|
-
def to_s
|
69
|
-
titlecase
|
70
|
-
end
|
71
|
-
|
72
|
-
def titlewords
|
73
|
-
@words.map do |word|
|
74
|
-
word[0].upcase+word[1..-1].downcase
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def titlecase
|
79
|
-
titlewords.join('')
|
80
|
-
end
|
81
|
-
|
82
|
-
def capwords
|
83
|
-
@words.map do |word|
|
84
|
-
word[0].upcase+word[1..-1]
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def capcase
|
89
|
-
capwords.join('')
|
90
|
-
end
|
91
|
-
|
92
|
-
def camelwords
|
93
|
-
count = 0
|
94
|
-
@words.map do |word|
|
95
|
-
if (count += 1) == 1
|
96
|
-
word
|
97
|
-
else
|
98
|
-
word[0].upcase+word[1..-1].downcase
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def camelcase
|
104
|
-
camelwords.join('')
|
105
|
-
end
|
106
|
-
|
107
|
-
def snakewords
|
108
|
-
@words.map do |w|
|
109
|
-
w.downcase
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def snakecase
|
114
|
-
snakewords.join('_')
|
115
|
-
end
|
116
|
-
|
117
|
-
def to_a
|
118
|
-
@words
|
119
|
-
end
|
120
|
-
|
121
|
-
def +(words)
|
122
|
-
Words.new(@words + Array(words))
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def words
|
127
|
-
Words.new(
|
128
|
-
self.split(/(?:[^[:alnum:]]+|(?<=[[:alnum:]])(?=[[:upper:]][[:lower:]]))/).reject{|w| w == '' }
|
129
|
-
)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
@@ -1,9 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Vocabulary Metamodel.
|
3
|
-
# The ActiveFacts Vocabulary API is generated from Metamodel.orm with extensions:
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
-
#
|
7
|
-
require 'activefacts/vocabulary/metamodel'
|
8
|
-
require 'activefacts/vocabulary/extensions'
|
9
|
-
require 'activefacts/vocabulary/verbaliser'
|
@@ -1,1348 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Vocabulary Metamodel.
|
3
|
-
# Extensions to the ActiveFacts Vocabulary classes (which are generated from the Metamodel)
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
-
#
|
7
|
-
module ActiveFacts
|
8
|
-
module Metamodel
|
9
|
-
class Vocabulary
|
10
|
-
def finalise
|
11
|
-
constellation.FactType.values.each do |fact_type|
|
12
|
-
if c = fact_type.check_and_add_spanning_uniqueness_constraint
|
13
|
-
trace :constraint, "Checking for existence of at least one uniqueness constraint over the roles of #{fact_type.default_reading.inspect}"
|
14
|
-
fact_type.check_and_add_spanning_uniqueness_constraint = nil
|
15
|
-
c.call
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# This name does not yet exist (at least not as we expect it to).
|
21
|
-
# If it in fact does exist (but as the wrong type), complain.
|
22
|
-
# If it doesn't exist, but its name would cause existing fact type
|
23
|
-
# readings to be re-interpreted to a different meaning, complain.
|
24
|
-
# Otherwise return nil.
|
25
|
-
def check_valid_nonexistent_object_type_name name
|
26
|
-
if ot = valid_object_type_name(name)
|
27
|
-
raise "Cannot redefine #{ot.class.basename} #{name}"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def valid_object_type_name name
|
32
|
-
# Raise an exception if adding this name to the vocabulary would create anomalies
|
33
|
-
anomaly = constellation.Reading.detect do |r_key, reading|
|
34
|
-
expanded = reading.expand do |role_ref, *words|
|
35
|
-
words.map! do |w|
|
36
|
-
case
|
37
|
-
when w == nil
|
38
|
-
w
|
39
|
-
when w[0...name.size] == name
|
40
|
-
'_ok_'+w
|
41
|
-
when w[-name.size..-1] == name
|
42
|
-
w[-1]+'_ok_'
|
43
|
-
else
|
44
|
-
w
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
words
|
49
|
-
end
|
50
|
-
expanded =~ %r{\b#{name}\b}
|
51
|
-
end
|
52
|
-
raise "Adding new term '#{name}' would create anomalous re-interpretation of '#{anomaly.expand}'" if anomaly
|
53
|
-
@constellation.ObjectType[[identifying_role_values, name]]
|
54
|
-
end
|
55
|
-
|
56
|
-
# If this entity type exists, ok, otherwise check it's ok to add it
|
57
|
-
def valid_entity_type_name name
|
58
|
-
@constellation.EntityType[[identifying_role_values, name]] or
|
59
|
-
check_valid_nonexistent_object_type_name name
|
60
|
-
end
|
61
|
-
|
62
|
-
# If this entity type exists, ok, otherwise check it's ok to add it
|
63
|
-
def valid_value_type_name name
|
64
|
-
@constellation.ValueType[[identifying_role_values, name]] or
|
65
|
-
check_valid_nonexistent_object_type_name name
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class Concept
|
70
|
-
def describe
|
71
|
-
case
|
72
|
-
when object_type; "#{object_type.class.basename} #{object_type.name.inspect}"
|
73
|
-
when fact_type; "FactType #{fact_type.default_reading.inspect}"
|
74
|
-
when role; "Role in #{role.fact_type.describe(role)}"
|
75
|
-
when constraint; constraint.describe
|
76
|
-
when instance; "Instance #{instance.verbalise}"
|
77
|
-
when fact; "Fact #{fact.verbalise}"
|
78
|
-
when query; query.describe
|
79
|
-
when context_note; "ContextNote#{context_note.verbalise}"
|
80
|
-
when unit; "Unit #{unit.describe}"
|
81
|
-
when population; "Population: #{population.name}"
|
82
|
-
else
|
83
|
-
raise "ROGUE CONCEPT OF NO TYPE"
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def embodied_as
|
88
|
-
case
|
89
|
-
when object_type; object_type
|
90
|
-
when fact_type; fact_type
|
91
|
-
when role; role
|
92
|
-
when constraint; constraint
|
93
|
-
when instance; instance
|
94
|
-
when fact; fact
|
95
|
-
when query; query
|
96
|
-
when context_note; context_note
|
97
|
-
when unit; unit
|
98
|
-
when population; population
|
99
|
-
else
|
100
|
-
raise "ROGUE CONCEPT OF NO TYPE"
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
# Return an array of all Concepts that must be defined before this concept can be defined:
|
105
|
-
def precursors
|
106
|
-
case body = embodied_as
|
107
|
-
when ActiveFacts::Metamodel::ValueType
|
108
|
-
[ body.supertype, body.unit ] +
|
109
|
-
body.all_value_type_parameter.map{|f| f.facet_value_type } +
|
110
|
-
body.all_value_type_parameter_restriction.map{|vr| vr.value.unit}
|
111
|
-
when ActiveFacts::Metamodel::EntityType
|
112
|
-
# You can't define the preferred_identifier fact types until you define the entity type,
|
113
|
-
# but the objects which play the identifying roles must be defined:
|
114
|
-
body.preferred_identifier.role_sequence.all_role_ref.map {|rr| rr.role.object_type } +
|
115
|
-
# You can't define the objectified fact type until you define the entity type:
|
116
|
-
# [ body.fact_type ] # If it's an objectification
|
117
|
-
body.all_type_inheritance_as_subtype.map{|ti| ti.supertype} # If it's a subtype
|
118
|
-
when FactType
|
119
|
-
body.all_role.map(&:object_type)
|
120
|
-
when Role # We don't consider roles as they cannot be separately defined
|
121
|
-
[]
|
122
|
-
when ActiveFacts::Metamodel::PresenceConstraint
|
123
|
-
body.role_sequence.all_role_ref.map do |rr|
|
124
|
-
rr.role.fact_type
|
125
|
-
end
|
126
|
-
when ActiveFacts::Metamodel::ValueConstraint
|
127
|
-
[ body.role ? body.role.fact_type : nil, body.value_type ] +
|
128
|
-
body.all_allowed_range.map do |ar|
|
129
|
-
[ ar.value_range.minimum_bound, ar.value_range.maximum_bound ].compact.map{|b| b.value.unit}
|
130
|
-
end
|
131
|
-
when ActiveFacts::Metamodel::SubsetConstraint
|
132
|
-
body.subset_role_sequence.all_role_ref.map{|rr| rr.role.fact_type } +
|
133
|
-
body.superset_role_sequence.all_role_ref.map{|rr| rr.role.fact_type }
|
134
|
-
when ActiveFacts::Metamodel::SetComparisonConstraint
|
135
|
-
body.all_set_comparison_roles.map{|scr| scr.role_sequence.all_role_ref.map{|rr| rr.role.fact_type } }
|
136
|
-
when ActiveFacts::Metamodel::RingConstraint
|
137
|
-
[ body.role.fact_type, body.other_role.fact_type ]
|
138
|
-
when Instance
|
139
|
-
[ body.population, body.object_type, body.value ? body.value.unit : nil ]
|
140
|
-
when Fact
|
141
|
-
[ body.population, body.fact_type ]
|
142
|
-
when Query
|
143
|
-
body.all_variable.map do |v|
|
144
|
-
[ v.object_type,
|
145
|
-
v.value ? v.value.unit : nil,
|
146
|
-
v.step ? v.step.fact_type : nil
|
147
|
-
] +
|
148
|
-
v.all_play.map{|p| p.role.fact_type }
|
149
|
-
end
|
150
|
-
when ContextNote
|
151
|
-
[]
|
152
|
-
when Unit
|
153
|
-
body.all_derivation_as_derived_unit.map{|d| d.base_unit }
|
154
|
-
when Population
|
155
|
-
[]
|
156
|
-
else
|
157
|
-
raise "ROGUE CONCEPT OF NO TYPE"
|
158
|
-
end.flatten.compact.uniq.map{|c| c.concept }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
class Topic
|
163
|
-
def precursors
|
164
|
-
# Precursors of a topic are the topics of all precursors of items in this topic
|
165
|
-
all_concept.map{|c| c.precursors }.flatten.uniq.map{|c| c.topic}.uniq-[self]
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
class Unit
|
170
|
-
def describe
|
171
|
-
'Unit' +
|
172
|
-
name +
|
173
|
-
(plural_name ? '/'+plural_name : '') +
|
174
|
-
'=' +
|
175
|
-
coefficient.to_s+'*' +
|
176
|
-
all_derivation_as_derived_unit.map do |derivation|
|
177
|
-
derivation.base_unit.name +
|
178
|
-
(derivation.exponent != 1 ? derivation.exponent.to_s : '')
|
179
|
-
end.join('') +
|
180
|
-
(offset ? ' + '+offset.to_s : '')
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
class Coefficient
|
185
|
-
def to_s
|
186
|
-
numerator.to_s +
|
187
|
-
(denominator != 1 ? '/' + denominator.to_s : '')
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
class FactType
|
192
|
-
attr_accessor :check_and_add_spanning_uniqueness_constraint
|
193
|
-
|
194
|
-
def all_reading_by_ordinal
|
195
|
-
all_reading.sort_by{|reading| reading.ordinal}
|
196
|
-
end
|
197
|
-
|
198
|
-
def preferred_reading negated = false
|
199
|
-
pr = all_reading_by_ordinal.detect{|r| !r.is_negative == !negated }
|
200
|
-
raise "No reading for (#{all_role.map{|r| r.object_type.name}*", "})" unless pr || negated
|
201
|
-
pr
|
202
|
-
end
|
203
|
-
|
204
|
-
def describe(highlight = nil)
|
205
|
-
(entity_type ? entity_type.name : "")+
|
206
|
-
'('+all_role.map{|role| role.describe(highlight) }*", "+')'
|
207
|
-
end
|
208
|
-
|
209
|
-
def default_reading(frequency_constraints = [], define_role_names = nil)
|
210
|
-
preferred_reading.expand(frequency_constraints, define_role_names)
|
211
|
-
end
|
212
|
-
|
213
|
-
# Does any role of this fact type participate in a preferred identifier?
|
214
|
-
def is_existential
|
215
|
-
return false if all_role.size > 2
|
216
|
-
all_role.detect do |role|
|
217
|
-
role.all_role_ref.detect do |rr|
|
218
|
-
rr.role_sequence.all_presence_constraint.detect do |pc|
|
219
|
-
pc.is_preferred_identifier
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def internal_presence_constraints
|
226
|
-
all_role.map do |r|
|
227
|
-
r.all_role_ref.map do |rr|
|
228
|
-
!rr.role_sequence.all_role_ref.detect{|rr1| rr1.role.fact_type != self } ?
|
229
|
-
rr.role_sequence.all_presence_constraint.to_a :
|
230
|
-
[]
|
231
|
-
end
|
232
|
-
end.flatten.compact.uniq
|
233
|
-
end
|
234
|
-
|
235
|
-
def implicit_boolean_type vocabulary
|
236
|
-
@constellation.ImplicitBooleanValueType[[vocabulary.identifying_role_values, "_ImplicitBooleanValueType"]] or
|
237
|
-
@constellation.ImplicitBooleanValueType(vocabulary.identifying_role_values, "_ImplicitBooleanValueType", :concept => [:new, :implication_rule => 'unary'])
|
238
|
-
end
|
239
|
-
|
240
|
-
# This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with phantom roles
|
241
|
-
def create_implicit_fact_type_for_unary
|
242
|
-
role = all_role.single
|
243
|
-
return if role.link_fact_type # Already exists
|
244
|
-
# NORMA doesn't create an implicit fact type here, rather the fact type has an implicit extra role, so looks like a binary
|
245
|
-
# We only do it when the unary fact type is not objectified
|
246
|
-
link_fact_type = @constellation.LinkFactType(:new, :implying_role => role)
|
247
|
-
link_fact_type.concept.implication_rule = 'unary'
|
248
|
-
entity_type = @entity_type || implicit_boolean_type(role.object_type.vocabulary)
|
249
|
-
phantom_role = @constellation.Role(link_fact_type, 0, :object_type => entity_type, :concept => :new)
|
250
|
-
end
|
251
|
-
|
252
|
-
def reading_preferably_starting_with_role role, negated = false
|
253
|
-
all_reading_by_ordinal.detect do |reading|
|
254
|
-
reading.text =~ /\{\d\}/ and
|
255
|
-
reading.role_sequence.all_role_ref_in_order[$1.to_i].role == role and
|
256
|
-
reading.is_negative == !!negated
|
257
|
-
end || preferred_reading(negated)
|
258
|
-
end
|
259
|
-
|
260
|
-
def all_role_in_order
|
261
|
-
all_role.sort_by{|r| r.ordinal}
|
262
|
-
end
|
263
|
-
|
264
|
-
def compatible_readings types_array
|
265
|
-
all_reading.select do |reading|
|
266
|
-
ok = true
|
267
|
-
reading.role_sequence.all_role_ref_in_order.each_with_index do |rr, i|
|
268
|
-
ok = false unless types_array[i].include?(rr.role.object_type)
|
269
|
-
end
|
270
|
-
ok
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
class Role
|
276
|
-
def describe(highlight = nil)
|
277
|
-
object_type.name + (self == highlight ? "*" : "")
|
278
|
-
end
|
279
|
-
|
280
|
-
# Is there are internal uniqueness constraint on this role only?
|
281
|
-
def unique
|
282
|
-
all_role_ref.detect{|rr|
|
283
|
-
rs = rr.role_sequence
|
284
|
-
rs.all_role_ref.size == 1 and
|
285
|
-
rs.all_presence_constraint.detect{|pc|
|
286
|
-
pc.max_frequency == 1
|
287
|
-
}
|
288
|
-
} ? true : false
|
289
|
-
end
|
290
|
-
|
291
|
-
def is_mandatory
|
292
|
-
return fact_type.implying_role.is_mandatory if fact_type.is_a?(LinkFactType)
|
293
|
-
all_role_ref.detect{|rr|
|
294
|
-
rs = rr.role_sequence
|
295
|
-
rs.all_role_ref.size == 1 and
|
296
|
-
rs.all_presence_constraint.detect{|pc|
|
297
|
-
pc.min_frequency and pc.min_frequency >= 1 and pc.is_mandatory
|
298
|
-
}
|
299
|
-
} ? true : false
|
300
|
-
end
|
301
|
-
|
302
|
-
def preferred_reference
|
303
|
-
fact_type.preferred_reading.role_sequence.all_role_ref.detect{|rr| rr.role == self }
|
304
|
-
end
|
305
|
-
|
306
|
-
# Return true if this role is functional (has only one instance wrt its player)
|
307
|
-
# A role in an objectified fact type is deemed to refer to the implicit role of the objectification.
|
308
|
-
def is_functional
|
309
|
-
fact_type.entity_type or
|
310
|
-
fact_type.all_role.size != 2 or
|
311
|
-
is_unique
|
312
|
-
end
|
313
|
-
|
314
|
-
def is_unique
|
315
|
-
all_role_ref.detect do |rr|
|
316
|
-
rr.role_sequence.all_role_ref.size == 1 and
|
317
|
-
rr.role_sequence.all_presence_constraint.detect do |pc|
|
318
|
-
pc.max_frequency == 1 and !pc.enforcement # Alethic uniqueness constraint
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
def name
|
324
|
-
role_name || object_type.name
|
325
|
-
end
|
326
|
-
|
327
|
-
end
|
328
|
-
|
329
|
-
class RoleRef
|
330
|
-
def describe
|
331
|
-
role_name
|
332
|
-
end
|
333
|
-
|
334
|
-
def preferred_reference
|
335
|
-
role.preferred_reference
|
336
|
-
end
|
337
|
-
|
338
|
-
def role_name(separator = "-")
|
339
|
-
return 'UNKNOWN' unless role
|
340
|
-
name_array =
|
341
|
-
if role.fact_type.all_role.size == 1
|
342
|
-
if role.fact_type.is_a?(LinkFactType)
|
343
|
-
"#{role.object_type.name} phantom for #{role.fact_type.role.object_type.name}"
|
344
|
-
else
|
345
|
-
role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.split(/\s/)
|
346
|
-
end
|
347
|
-
else
|
348
|
-
role.role_name || [leading_adjective, role.object_type.name, trailing_adjective].compact.map{|w| w.split(/\s/)}.flatten
|
349
|
-
end
|
350
|
-
return separator ? Array(name_array)*separator : Array(name_array)
|
351
|
-
end
|
352
|
-
|
353
|
-
def cql_leading_adjective
|
354
|
-
if leading_adjective
|
355
|
-
# 'foo' => "foo-"
|
356
|
-
# 'foo bar' => "foo- bar "
|
357
|
-
# 'foo-bar' => "foo-- bar "
|
358
|
-
# 'foo-bar baz' => "foo-- bar baz "
|
359
|
-
# 'bat foo-bar baz' => "bat- foo-bar baz "
|
360
|
-
leading_adjective.strip.
|
361
|
-
sub(/[- ]|$/, '-\0 ').sub(/ /, ' ').sub(/[^-]$/, '\0 ').sub(/- $/,'-')
|
362
|
-
else
|
363
|
-
''
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
def cql_trailing_adjective
|
368
|
-
if trailing_adjective
|
369
|
-
# 'foo' => "-foo"
|
370
|
-
# 'foo bar' => " foo -bar"
|
371
|
-
# 'foo-bar' => " foo --bar"
|
372
|
-
# 'foo-bar baz' => " foo-bar -baz"
|
373
|
-
# 'bat foo-bar baz' => " bat foo-bar -baz"
|
374
|
-
trailing_adjective.
|
375
|
-
strip.
|
376
|
-
sub(/(?<a>.*) (?<b>[^- ]+$)|(?<a>.*)(?<b>-[^- ]*)$|(?<a>)(?<b>.*)/) {
|
377
|
-
" #{$~[:a]} -#{$~[:b]}"
|
378
|
-
}.
|
379
|
-
sub(/^ *-/, '-') # A leading space is not needed if the hyphen is at the start
|
380
|
-
else
|
381
|
-
''
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
def cql_name
|
386
|
-
if role.fact_type.all_role.size == 1
|
387
|
-
role_name
|
388
|
-
elsif role.role_name
|
389
|
-
role.role_name
|
390
|
-
else
|
391
|
-
# Where an adjective has multiple words, the hyphen is inserted outside the outermost space, leaving the space
|
392
|
-
cql_leading_adjective +
|
393
|
-
role.object_type.name+
|
394
|
-
cql_trailing_adjective
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
|
-
class RoleSequence
|
400
|
-
def describe(highlighted_role_ref = nil)
|
401
|
-
"("+
|
402
|
-
all_role_ref.sort_by{|rr| rr.ordinal}.map{|rr| rr.describe + (highlighted_role_ref == rr ? '*' : '') }*", "+
|
403
|
-
")"
|
404
|
-
end
|
405
|
-
|
406
|
-
def all_role_ref_in_order
|
407
|
-
all_role_ref.sort_by{|rr| rr.ordinal}
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
class ObjectType
|
412
|
-
# Placeholder for the surrogate transform
|
413
|
-
attr_reader :injected_surrogate_role
|
414
|
-
|
415
|
-
def is_separate
|
416
|
-
is_independent or concept.all_concept_annotation.detect{|ca| ca.mapping_annotation == 'separate'}
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
class ValueType
|
421
|
-
def supertypes_transitive
|
422
|
-
[self] + (supertype ? supertype.supertypes_transitive : [])
|
423
|
-
end
|
424
|
-
|
425
|
-
def subtypes
|
426
|
-
all_value_type_as_supertype
|
427
|
-
end
|
428
|
-
|
429
|
-
def subtypes_transitive
|
430
|
-
[self] + subtypes.map{|st| st.subtypes_transitive}.flatten
|
431
|
-
end
|
432
|
-
|
433
|
-
def common_supertype(other)
|
434
|
-
return nil unless other.is_?(ActiveFacts::Metamodel::ValueType)
|
435
|
-
return self if other.supertypes_transitive.include?(self)
|
436
|
-
return other if supertypes_transitive.include(other)
|
437
|
-
nil
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
class EntityType
|
442
|
-
def identification_is_inherited
|
443
|
-
preferred_identifier and
|
444
|
-
preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) }
|
445
|
-
end
|
446
|
-
|
447
|
-
def assimilation
|
448
|
-
if rr = identification_is_inherited
|
449
|
-
rr.role.fact_type.assimilation
|
450
|
-
else
|
451
|
-
nil
|
452
|
-
end
|
453
|
-
end
|
454
|
-
|
455
|
-
def preferred_identifier
|
456
|
-
return @preferred_identifier if @preferred_identifier
|
457
|
-
if fact_type
|
458
|
-
# When compiling a fact instance, the delayed creation of a preferred identifier might be necessary
|
459
|
-
if c = fact_type.check_and_add_spanning_uniqueness_constraint
|
460
|
-
fact_type.check_and_add_spanning_uniqueness_constraint = nil
|
461
|
-
c.call
|
462
|
-
end
|
463
|
-
|
464
|
-
# For a nested fact type, the PI is a unique constraint over N or N-1 roles
|
465
|
-
fact_roles = Array(fact_type.all_role)
|
466
|
-
trace :pi, "Looking for PI on nested fact type #{name}" do
|
467
|
-
pi = catch :pi do
|
468
|
-
fact_roles[0,2].each{|r| # Try the first two roles of the fact type, that's enough
|
469
|
-
r.all_role_ref.map{|rr| # All role sequences that reference this role
|
470
|
-
role_sequence = rr.role_sequence
|
471
|
-
|
472
|
-
# The role sequence is only interesting if it cover only this fact's roles
|
473
|
-
# or roles of the objectification
|
474
|
-
next if role_sequence.all_role_ref.size < fact_roles.size-1 # Not enough roles
|
475
|
-
next if role_sequence.all_role_ref.size > fact_roles.size # Too many roles
|
476
|
-
next if role_sequence.all_role_ref.detect do |rsr|
|
477
|
-
if (of = rsr.role.fact_type) != fact_type
|
478
|
-
case of.all_role.size
|
479
|
-
when 1 # A unary FT must be played by the objectification of this fact type
|
480
|
-
next rsr.role.object_type != fact_type.entity_type
|
481
|
-
when 2 # A binary FT must have the objectification of this FT as the other player
|
482
|
-
other_role = (of.all_role-[rsr.role])[0]
|
483
|
-
next other_role.object_type != fact_type.entity_type
|
484
|
-
else
|
485
|
-
next true # A role in a ternary (or higher) cannot be usd in our identifier
|
486
|
-
end
|
487
|
-
end
|
488
|
-
rsr.role.fact_type != fact_type
|
489
|
-
end
|
490
|
-
|
491
|
-
# This role sequence is a candidate
|
492
|
-
pc = role_sequence.all_presence_constraint.detect{|c|
|
493
|
-
c.max_frequency == 1 && c.is_preferred_identifier
|
494
|
-
}
|
495
|
-
throw :pi, pc if pc
|
496
|
-
}
|
497
|
-
}
|
498
|
-
throw :pi, nil
|
499
|
-
end
|
500
|
-
trace :pi, "Got PI #{pi.name||pi.object_id} for nested #{name}" if pi
|
501
|
-
trace :pi, "Looking for PI on entity that nests this fact" unless pi
|
502
|
-
raise "Oops, pi for nested fact is #{pi.class}" unless !pi || pi.is_a?(ActiveFacts::Metamodel::PresenceConstraint)
|
503
|
-
return @preferred_identifier = pi if pi
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
|
-
trace :pi, "Looking for PI for ordinary entity #{name} with #{all_role.size} roles:" do
|
508
|
-
trace :pi, "Roles are in fact types #{all_role.map{|r| r.fact_type.describe(r)}*", "}"
|
509
|
-
pi = catch :pi do
|
510
|
-
all_supertypes = supertypes_transitive
|
511
|
-
trace :pi, "PI roles must be played by one of #{all_supertypes.map(&:name)*", "}" if all_supertypes.size > 1
|
512
|
-
all_role.each{|role|
|
513
|
-
next unless role.unique || fact_type
|
514
|
-
ftroles = Array(role.fact_type.all_role)
|
515
|
-
|
516
|
-
# Skip roles in ternary and higher fact types, they're objectified, and in unaries, they can't identify us.
|
517
|
-
next if ftroles.size != 2
|
518
|
-
|
519
|
-
trace :pi, "Considering role in #{role.fact_type.describe(role)}"
|
520
|
-
|
521
|
-
# Find the related role which must be included in any PI:
|
522
|
-
# Note this works with unary fact types:
|
523
|
-
pi_role = ftroles[ftroles[0] != role ? 0 : -1]
|
524
|
-
|
525
|
-
next if ftroles.size == 2 && pi_role.object_type == self
|
526
|
-
trace :pi, " Considering #{pi_role.object_type.name} as a PI role"
|
527
|
-
|
528
|
-
# If this is an identifying role, the PI is a PC whose role_sequence spans the role.
|
529
|
-
# Walk through all role_sequences that span this role, and test each:
|
530
|
-
pi_role.all_role_ref.each{|rr|
|
531
|
-
role_sequence = rr.role_sequence # A role sequence that includes a possible role
|
532
|
-
|
533
|
-
trace :pi, " Considering role sequence #{role_sequence.describe}"
|
534
|
-
|
535
|
-
# All roles in this role_sequence must be in fact types which
|
536
|
-
# (apart from that role) only have roles played by the original
|
537
|
-
# entity type or a supertype.
|
538
|
-
#trace :pi, " All supertypes #{all_supertypes.map{|st| "#{st.object_id}=>#{st.name}"}*", "}"
|
539
|
-
if role_sequence.all_role_ref.detect{|rsr|
|
540
|
-
fact_type = rsr.role.fact_type
|
541
|
-
trace :pi, " Role Sequence touches #{fact_type.describe(pi_role)}"
|
542
|
-
|
543
|
-
fact_type_roles = fact_type.all_role
|
544
|
-
trace :pi, " residual is #{fact_type_roles.map{|r| r.object_type.name}.inspect} minus #{rsr.role.object_type.name}"
|
545
|
-
residual_roles = fact_type_roles-[rsr.role]
|
546
|
-
residual_roles.detect{|rfr|
|
547
|
-
trace :pi, " Checking residual role #{rfr.object_type.object_id}=>#{rfr.object_type.name}"
|
548
|
-
# This next line looks right, but breaks things. Find out what and why:
|
549
|
-
# !rfr.unique or
|
550
|
-
!all_supertypes.include?(rfr.object_type)
|
551
|
-
}
|
552
|
-
}
|
553
|
-
trace :pi, " Discounting this role_sequence because it includes alien roles"
|
554
|
-
next
|
555
|
-
end
|
556
|
-
|
557
|
-
# Any presence constraint over this role sequence is a candidate
|
558
|
-
rr.role_sequence.all_presence_constraint.detect{|pc|
|
559
|
-
# Found it!
|
560
|
-
if pc.is_preferred_identifier
|
561
|
-
trace :pi, "found PI #{pc.name||pc.object_id}, is_preferred_identifier=#{pc.is_preferred_identifier.inspect} over #{pc.role_sequence.describe}"
|
562
|
-
throw :pi, pc
|
563
|
-
end
|
564
|
-
}
|
565
|
-
}
|
566
|
-
}
|
567
|
-
throw :pi, nil
|
568
|
-
end
|
569
|
-
raise "Oops, pi for entity is #{pi.class}" if pi && !pi.is_a?(ActiveFacts::Metamodel::PresenceConstraint)
|
570
|
-
trace :pi, "Got PI #{pi.name||pi.object_id} for #{name}" if pi
|
571
|
-
|
572
|
-
if !pi
|
573
|
-
if (supertype = identifying_supertype)
|
574
|
-
# This shouldn't happen now, as an identifying supertype is connected by a fact type
|
575
|
-
# that has a uniqueness constraint marked as the preferred identifier.
|
576
|
-
#trace :pi, "PI not found for #{name}, looking in supertype #{supertype.name}"
|
577
|
-
#pi = supertype.preferred_identifier
|
578
|
-
#return nil
|
579
|
-
elsif fact_type
|
580
|
-
possible_pi = nil
|
581
|
-
fact_type.all_role.each{|role|
|
582
|
-
role.all_role_ref.each{|role_ref|
|
583
|
-
# Discount role sequences that contain roles not in this fact type:
|
584
|
-
next if role_ref.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type != fact_type }
|
585
|
-
role_ref.role_sequence.all_presence_constraint.each{|pc|
|
586
|
-
next unless pc.max_frequency == 1
|
587
|
-
possible_pi = pc
|
588
|
-
next unless pc.is_preferred_identifier
|
589
|
-
pi = pc
|
590
|
-
break
|
591
|
-
}
|
592
|
-
break if pi
|
593
|
-
}
|
594
|
-
break if pi
|
595
|
-
}
|
596
|
-
if !pi && possible_pi
|
597
|
-
trace :pi, "Using existing PC as PI for #{name}"
|
598
|
-
pi = possible_pi
|
599
|
-
end
|
600
|
-
else
|
601
|
-
byebug
|
602
|
-
trace :pi, "No PI found for #{name}"
|
603
|
-
end
|
604
|
-
end
|
605
|
-
raise "No PI found for #{name}" unless pi
|
606
|
-
@preferred_identifier = pi
|
607
|
-
end
|
608
|
-
end
|
609
|
-
|
610
|
-
# An array of all direct subtypes:
|
611
|
-
def subtypes
|
612
|
-
# REVISIT: There's no sorting here. Should there be?
|
613
|
-
all_type_inheritance_as_supertype.map{|ti| ti.subtype }
|
614
|
-
end
|
615
|
-
|
616
|
-
def subtypes_transitive
|
617
|
-
[self] + subtypes.map{|st| st.subtypes_transitive}.flatten.uniq
|
618
|
-
end
|
619
|
-
|
620
|
-
def all_supertype_inheritance
|
621
|
-
all_type_inheritance_as_subtype.sort_by{|ti|
|
622
|
-
[ti.provides_identification ? 0 : 1, ti.supertype.name]
|
623
|
-
}
|
624
|
-
end
|
625
|
-
|
626
|
-
# An array all direct supertypes
|
627
|
-
def supertypes
|
628
|
-
all_supertype_inheritance.map{|ti|
|
629
|
-
ti.supertype
|
630
|
-
}
|
631
|
-
end
|
632
|
-
|
633
|
-
# An array of self followed by all supertypes in order:
|
634
|
-
def supertypes_transitive
|
635
|
-
([self] + all_type_inheritance_as_subtype.map{|ti|
|
636
|
-
ti.supertype.supertypes_transitive
|
637
|
-
}).flatten.uniq
|
638
|
-
end
|
639
|
-
|
640
|
-
# A subtype does not have a identifying_supertype if it defines its own identifier
|
641
|
-
def identifying_supertype
|
642
|
-
trace "Looking for identifying_supertype of #{name}"
|
643
|
-
all_type_inheritance_as_subtype.detect{|ti|
|
644
|
-
trace "considering supertype #{ti.supertype.name}"
|
645
|
-
next unless ti.provides_identification
|
646
|
-
trace "found identifying supertype of #{name}, it's #{ti.supertype.name}"
|
647
|
-
return ti.supertype
|
648
|
-
}
|
649
|
-
trace "Failed to find identifying supertype of #{name}"
|
650
|
-
return nil
|
651
|
-
end
|
652
|
-
|
653
|
-
def common_supertype(other)
|
654
|
-
return nil unless other.is_?(ActiveFacts::Metamodel::EntityType)
|
655
|
-
candidates = supertypes_transitive & other.supertypes_transitive
|
656
|
-
return candidates[0] if candidates.size <= 1
|
657
|
-
candidates[0] # REVISIT: This might not be the closest supertype
|
658
|
-
end
|
659
|
-
|
660
|
-
# This entity type has just objectified a fact type. Create the necessary ImplicitFactTypes with phantom roles
|
661
|
-
def create_implicit_fact_types
|
662
|
-
fact_type.all_role.map do |role|
|
663
|
-
next if role.link_fact_type # Already exists
|
664
|
-
link_fact_type = @constellation.LinkFactType(:new, :implying_role => role)
|
665
|
-
link_fact_type.concept.implication_rule = 'objectification'
|
666
|
-
phantom_role = @constellation.Role(link_fact_type, 0, :object_type => self, :concept => :new)
|
667
|
-
# We could create a copy of the visible external role here, but there's no need yet...
|
668
|
-
# Nor is there a need for a presence constraint, readings, etc.
|
669
|
-
link_fact_type
|
670
|
-
end
|
671
|
-
end
|
672
|
-
end
|
673
|
-
|
674
|
-
class Reading
|
675
|
-
# The frequency_constraints array here, if supplied, may provide for each role either:
|
676
|
-
# * a PresenceConstraint to be verbalised against the relevant role, or
|
677
|
-
# * a String, used as a definite or indefinite article on the relevant role, or
|
678
|
-
# * an array containing two strings (an article and a super-type name)
|
679
|
-
# The order in the array is the same as the reading's role-sequence.
|
680
|
-
# REVISIT: This should probably be changed to be the fact role sequence.
|
681
|
-
#
|
682
|
-
# define_role_names here is false (use defined names), true (define names) or nil (neither)
|
683
|
-
def expand(frequency_constraints = [], define_role_names = nil, literals = [], &subscript_block)
|
684
|
-
expanded = "#{text}"
|
685
|
-
role_refs = role_sequence.all_role_ref.sort_by{|role_ref| role_ref.ordinal}
|
686
|
-
(0...role_refs.size).each{|i|
|
687
|
-
role_ref = role_refs[i]
|
688
|
-
role = role_ref.role
|
689
|
-
l_adj = "#{role_ref.leading_adjective}".sub(/(\b-\b|.\b|.\Z)/, '\1-').sub(/\b--\b/,'-- ').sub(/- /,'- ')
|
690
|
-
l_adj = nil if l_adj == ""
|
691
|
-
# Double the space to compensate for space removed below
|
692
|
-
# REVISIT: hyphenated trailing adjectives are not correctly represented here
|
693
|
-
t_adj = "#{role_ref.trailing_adjective}".sub(/(\b.|\A.)/, '-\1').sub(/ -/,' -')
|
694
|
-
t_adj = nil if t_adj == ""
|
695
|
-
|
696
|
-
expanded.gsub!(/\{#{i}\}/) do
|
697
|
-
role_ref = role_refs[i]
|
698
|
-
if role_ref.role
|
699
|
-
player = role_ref.role.object_type
|
700
|
-
role_name = role.role_name
|
701
|
-
role_name = nil if role_name == ""
|
702
|
-
if role_name && define_role_names == false
|
703
|
-
l_adj = t_adj = nil # When using role names, don't add adjectives
|
704
|
-
end
|
705
|
-
|
706
|
-
freq_con = frequency_constraints[i]
|
707
|
-
freq_con = freq_con.frequency if freq_con && freq_con.is_a?(ActiveFacts::Metamodel::PresenceConstraint)
|
708
|
-
if freq_con.is_a?(Array)
|
709
|
-
freq_con, player_name = *freq_con
|
710
|
-
else
|
711
|
-
player_name = player.name
|
712
|
-
end
|
713
|
-
else
|
714
|
-
# We have an unknown role. The reading cannot be correctly expanded
|
715
|
-
player_name = "UNKNOWN"
|
716
|
-
role_name = nil
|
717
|
-
freq_con = nil
|
718
|
-
end
|
719
|
-
|
720
|
-
literal = literals[i]
|
721
|
-
words = [
|
722
|
-
freq_con ? freq_con : nil,
|
723
|
-
l_adj,
|
724
|
-
define_role_names == false && role_name ? role_name : player_name,
|
725
|
-
t_adj,
|
726
|
-
define_role_names && role_name && player_name != role_name ? "(as #{role_name})" : nil,
|
727
|
-
# Can't have both a literal and a value constraint, but we don't enforce that here:
|
728
|
-
literal ? literal : nil
|
729
|
-
]
|
730
|
-
if (subscript_block)
|
731
|
-
words = subscript_block.call(role_ref, *words)
|
732
|
-
end
|
733
|
-
words.compact*" "
|
734
|
-
end
|
735
|
-
}
|
736
|
-
expanded.gsub!(/ ?- ?/, '-') # Remove single spaces around adjectives
|
737
|
-
#trace "Expanded '#{expanded}' using #{frequency_constraints.inspect}"
|
738
|
-
expanded
|
739
|
-
end
|
740
|
-
|
741
|
-
def words_and_role_refs
|
742
|
-
text.
|
743
|
-
scan(/(?: |\{[0-9]+\}|[^{} ]+)/). # split up the text into words
|
744
|
-
reject{|s| s==' '}. # Remove white space
|
745
|
-
map do |frag| # and go through the bits
|
746
|
-
if frag =~ /\{([0-9]+)\}/
|
747
|
-
role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i}
|
748
|
-
else
|
749
|
-
frag
|
750
|
-
end
|
751
|
-
end
|
752
|
-
end
|
753
|
-
|
754
|
-
# Return the array of the numbers of the RoleRefs inserted into this reading from the role_sequence
|
755
|
-
def role_numbers
|
756
|
-
text.scan(/\{(\d)\}/).flatten.map{|m| Integer(m) }
|
757
|
-
end
|
758
|
-
|
759
|
-
def expand_with_final_presence_constraint &b
|
760
|
-
# Arrange the roles in order they occur in this reading:
|
761
|
-
role_refs = role_sequence.all_role_ref_in_order
|
762
|
-
role_numbers = text.scan(/\{(\d)\}/).flatten.map{|m| Integer(m) }
|
763
|
-
roles = role_numbers.map{|m| role_refs[m].role }
|
764
|
-
fact_constraints = fact_type.internal_presence_constraints
|
765
|
-
|
766
|
-
# Find the constraints that constrain frequency over each role we can verbalise:
|
767
|
-
frequency_constraints = []
|
768
|
-
roles.each do |role|
|
769
|
-
frequency_constraints <<
|
770
|
-
if (role == roles.last) # On the last role of the reading, emit any presence constraint
|
771
|
-
constraint = fact_constraints.
|
772
|
-
detect do |c| # Find a UC that spans all other Roles
|
773
|
-
c.is_a?(ActiveFacts::Metamodel::PresenceConstraint) &&
|
774
|
-
roles-c.role_sequence.all_role_ref.map(&:role) == [role]
|
775
|
-
end
|
776
|
-
constraint && constraint.frequency
|
777
|
-
else
|
778
|
-
nil
|
779
|
-
end
|
780
|
-
end
|
781
|
-
|
782
|
-
expand(frequency_constraints) { |*a| b && b.call(*a) }
|
783
|
-
end
|
784
|
-
end
|
785
|
-
|
786
|
-
class ValueConstraint
|
787
|
-
def describe
|
788
|
-
as_cql
|
789
|
-
end
|
790
|
-
|
791
|
-
def as_cql
|
792
|
-
"restricted to "+
|
793
|
-
( if regular_expression
|
794
|
-
'/' + regular_expression + '/'
|
795
|
-
else
|
796
|
-
'{' + all_allowed_range_sorted.map{|ar| ar.to_s(false) }*', ' + '}'
|
797
|
-
end
|
798
|
-
)
|
799
|
-
end
|
800
|
-
|
801
|
-
def all_allowed_range_sorted
|
802
|
-
all_allowed_range.sort_by{|ar|
|
803
|
-
((min = ar.value_range.minimum_bound) && min.value.literal) ||
|
804
|
-
((max = ar.value_range.maximum_bound) && max.value.literal)
|
805
|
-
}
|
806
|
-
end
|
807
|
-
|
808
|
-
def to_s
|
809
|
-
if all_allowed_range.size > 1
|
810
|
-
"[" +
|
811
|
-
all_allowed_range_sorted.map { |ar| ar.to_s(true) }*", " +
|
812
|
-
"]"
|
813
|
-
else
|
814
|
-
all_allowed_range.single.to_s
|
815
|
-
end
|
816
|
-
end
|
817
|
-
end
|
818
|
-
|
819
|
-
class AllowedRange
|
820
|
-
def to_s(infinity = true)
|
821
|
-
min = value_range.minimum_bound
|
822
|
-
max = value_range.maximum_bound
|
823
|
-
# Open-ended string ranges will fail in Ruby
|
824
|
-
|
825
|
-
if min = value_range.minimum_bound
|
826
|
-
min = min.value
|
827
|
-
if min.is_literal_string
|
828
|
-
min_literal = min.literal.inspect.gsub(/\A"|"\Z/,"'") # Escape string characters
|
829
|
-
else
|
830
|
-
min_literal = min.literal
|
831
|
-
end
|
832
|
-
else
|
833
|
-
min_literal = infinity ? "-Infinity" : ""
|
834
|
-
end
|
835
|
-
if max = value_range.maximum_bound
|
836
|
-
max = max.value
|
837
|
-
if max.is_literal_string
|
838
|
-
max_literal = max.literal.inspect.gsub(/\A"|"\Z/,"'") # Escape string characters
|
839
|
-
else
|
840
|
-
max_literal = max.literal
|
841
|
-
end
|
842
|
-
else
|
843
|
-
max_literal = infinity ? "Infinity" : ""
|
844
|
-
end
|
845
|
-
|
846
|
-
min_literal +
|
847
|
-
(min_literal != (max&&max_literal) ? (".." + max_literal) : "")
|
848
|
-
end
|
849
|
-
end
|
850
|
-
|
851
|
-
class Value
|
852
|
-
def to_s
|
853
|
-
if is_literal_string
|
854
|
-
"'"+
|
855
|
-
literal.
|
856
|
-
inspect. # Use Ruby's inspect to generate necessary escapes
|
857
|
-
gsub(/\A"|"\Z/,''). # Remove surrounding quotes
|
858
|
-
gsub(/'/, "\\'") + # Escape any single quotes
|
859
|
-
"'"
|
860
|
-
else
|
861
|
-
literal
|
862
|
-
end +
|
863
|
-
(unit ? " " + unit.name : "")
|
864
|
-
end
|
865
|
-
|
866
|
-
def inspect
|
867
|
-
to_s
|
868
|
-
end
|
869
|
-
end
|
870
|
-
|
871
|
-
class PresenceConstraint
|
872
|
-
def frequency
|
873
|
-
min = min_frequency
|
874
|
-
max = max_frequency
|
875
|
-
[
|
876
|
-
((min && min > 0 && min != max) ? "at least #{min == 1 ? "one" : min.to_s}" : nil),
|
877
|
-
((max && min != max) ? "at most #{max == 1 ? "one" : max.to_s}" : nil),
|
878
|
-
((max && min == max) ? "#{max == 1 ? "one" : "exactly "+max.to_s}" : nil)
|
879
|
-
].compact * " and "
|
880
|
-
end
|
881
|
-
|
882
|
-
def describe
|
883
|
-
min = min_frequency
|
884
|
-
max = max_frequency
|
885
|
-
'PresenceConstraint over '+role_sequence.describe + " occurs " + frequency + " time#{(min&&min>1)||(max&&max>1) ? 's' : ''}"
|
886
|
-
end
|
887
|
-
end
|
888
|
-
|
889
|
-
class SubsetConstraint
|
890
|
-
def describe
|
891
|
-
'SubsetConstraint(' +
|
892
|
-
subset_role_sequence.describe
|
893
|
-
' < ' +
|
894
|
-
superset_role_sequence.describe +
|
895
|
-
')'
|
896
|
-
end
|
897
|
-
end
|
898
|
-
|
899
|
-
class SetComparisonConstraint
|
900
|
-
def describe
|
901
|
-
self.class.basename+'(' +
|
902
|
-
all_set_comparison_roles.map do |scr|
|
903
|
-
scr.role_sequence.describe
|
904
|
-
end*',' +
|
905
|
-
')'
|
906
|
-
end
|
907
|
-
end
|
908
|
-
|
909
|
-
class RingConstraint
|
910
|
-
def describe
|
911
|
-
'RingConstraint(' +
|
912
|
-
ring_type.to_s+': ' +
|
913
|
-
role.describe+', ' +
|
914
|
-
other_role.describe+' in ' +
|
915
|
-
role.fact_type.default_reading +
|
916
|
-
')'
|
917
|
-
end
|
918
|
-
end
|
919
|
-
|
920
|
-
class TypeInheritance
|
921
|
-
def describe(role = nil)
|
922
|
-
"#{subtype.name} is a kind of #{supertype.name}"
|
923
|
-
end
|
924
|
-
|
925
|
-
def supertype_role
|
926
|
-
(roles = all_role.to_a)[0].object_type == supertype ? roles[0] : roles[1]
|
927
|
-
end
|
928
|
-
|
929
|
-
def subtype_role
|
930
|
-
(roles = all_role.to_a)[0].object_type == subtype ? roles[0] : roles[1]
|
931
|
-
end
|
932
|
-
end
|
933
|
-
|
934
|
-
class Step
|
935
|
-
def describe
|
936
|
-
"Step " +
|
937
|
-
"#{is_optional ? 'maybe ' : ''}" +
|
938
|
-
(is_unary_step ? '(unary) ' : "from #{input_play.describe} ") +
|
939
|
-
"#{is_disallowed ? 'not ' : ''}" +
|
940
|
-
"to #{output_plays.map(&:describe)*', '}" +
|
941
|
-
(objectification_variable ? ", objectified as #{objectification_variable.describe}" : '') +
|
942
|
-
" '#{fact_type.default_reading}'"
|
943
|
-
end
|
944
|
-
|
945
|
-
def input_play
|
946
|
-
all_play.detect{|p| p.is_input}
|
947
|
-
end
|
948
|
-
|
949
|
-
def output_plays
|
950
|
-
all_play.reject{|p| p.is_input}
|
951
|
-
end
|
952
|
-
|
953
|
-
def is_unary_step
|
954
|
-
# Preserve this in case we have to use a real variable for the phantom
|
955
|
-
all_play.size == 1
|
956
|
-
end
|
957
|
-
|
958
|
-
def is_objectification_step
|
959
|
-
!!objectification_variable
|
960
|
-
end
|
961
|
-
|
962
|
-
def external_fact_type
|
963
|
-
fact_type.is_a?(LinkFactType) ? fact_type.role.fact_type : fact_type
|
964
|
-
end
|
965
|
-
end
|
966
|
-
|
967
|
-
class Variable
|
968
|
-
def describe
|
969
|
-
object_type.name +
|
970
|
-
(subscript ? "(#{subscript})" : '') +
|
971
|
-
" Var#{ordinal}" +
|
972
|
-
(value ? ' = '+value.to_s : '')
|
973
|
-
end
|
974
|
-
|
975
|
-
def all_step
|
976
|
-
all_play.map(&:step).uniq
|
977
|
-
end
|
978
|
-
end
|
979
|
-
|
980
|
-
class Play
|
981
|
-
def describe
|
982
|
-
"#{role.object_type.name} Var#{variable.ordinal}" +
|
983
|
-
(role_ref ? " (projected)" : "")
|
984
|
-
end
|
985
|
-
end
|
986
|
-
|
987
|
-
class Query
|
988
|
-
def describe
|
989
|
-
steps_shown = {}
|
990
|
-
'Query(' +
|
991
|
-
all_variable.sort_by{|var| var.ordinal}.map do |variable|
|
992
|
-
variable.describe + ': ' +
|
993
|
-
variable.all_step.map do |step|
|
994
|
-
next if steps_shown[step]
|
995
|
-
steps_shown[step] = true
|
996
|
-
step.describe
|
997
|
-
end.compact.join(',')
|
998
|
-
end.join('; ') +
|
999
|
-
')'
|
1000
|
-
end
|
1001
|
-
|
1002
|
-
def show
|
1003
|
-
steps_shown = {}
|
1004
|
-
trace :query, "Displaying full contents of Query #{concept.guid}" do
|
1005
|
-
all_variable.sort_by{|var| var.ordinal}.each do |variable|
|
1006
|
-
trace :query, "#{variable.describe}" do
|
1007
|
-
variable.all_step.
|
1008
|
-
each do |step|
|
1009
|
-
next if steps_shown[step]
|
1010
|
-
steps_shown[step] = true
|
1011
|
-
trace :query, "#{step.describe}"
|
1012
|
-
end
|
1013
|
-
variable.all_play.each do |play|
|
1014
|
-
trace :query, "role of #{play.describe} in '#{play.role.fact_type.default_reading}'"
|
1015
|
-
end
|
1016
|
-
end
|
1017
|
-
end
|
1018
|
-
end
|
1019
|
-
end
|
1020
|
-
|
1021
|
-
def all_step
|
1022
|
-
all_variable.map{|var| var.all_step.to_a}.flatten.uniq
|
1023
|
-
end
|
1024
|
-
|
1025
|
-
# Check all parts of this query for validity
|
1026
|
-
def validate
|
1027
|
-
show
|
1028
|
-
return
|
1029
|
-
|
1030
|
-
# Check the variables:
|
1031
|
-
steps = []
|
1032
|
-
variables = all_variable.sort_by{|var| var.ordinal}
|
1033
|
-
variables.each_with_index do |variable, i|
|
1034
|
-
raise "Variable #{i} should have ordinal #{variable.ordinal}" unless variable.ordinal == i
|
1035
|
-
raise "Variable #{i} has missing object_type" unless variable.object_type
|
1036
|
-
variable.all_play do |play|
|
1037
|
-
raise "Variable for #{object_type.name} includes role played by #{play.object_type.name}" unless play.object_type == object_type
|
1038
|
-
end
|
1039
|
-
steps += variable.all_step
|
1040
|
-
end
|
1041
|
-
steps.uniq!
|
1042
|
-
|
1043
|
-
# Check the steps:
|
1044
|
-
steps.each do |step|
|
1045
|
-
raise "Step has missing fact type" unless step.fact_type
|
1046
|
-
raise "Step has missing input node" unless step.input_play
|
1047
|
-
raise "Step has missing output node" unless step.output_play
|
1048
|
-
if (role = input_play).role.fact_type != fact_type or
|
1049
|
-
(role = output_play).role.fact_type != fact_type
|
1050
|
-
raise "Step has role #{role.describe} which doesn't belong to the fact type '#{fact_type.default_reading}' it traverses"
|
1051
|
-
end
|
1052
|
-
end
|
1053
|
-
|
1054
|
-
# REVISIT: Do a connectivity check
|
1055
|
-
end
|
1056
|
-
end
|
1057
|
-
|
1058
|
-
class LinkFactType
|
1059
|
-
def default_reading
|
1060
|
-
# There are two cases, where role is in a unary fact type, and where the fact type is objectified
|
1061
|
-
# If a unary fact type is objectified, only the LinkFactType for the objectification is asserted
|
1062
|
-
if objectification = implying_role.fact_type.entity_type
|
1063
|
-
"#{objectification.name} involves #{implying_role.object_type.name}"
|
1064
|
-
else
|
1065
|
-
implying_role.fact_type.default_reading+" Boolean" # Must be a unary FT
|
1066
|
-
end
|
1067
|
-
end
|
1068
|
-
|
1069
|
-
def add_reading implicit_reading
|
1070
|
-
@readings ||= []
|
1071
|
-
@readings << implicit_reading
|
1072
|
-
end
|
1073
|
-
|
1074
|
-
def all_reading
|
1075
|
-
@readings ||=
|
1076
|
-
[ ImplicitReading.new(
|
1077
|
-
self,
|
1078
|
-
implying_role.fact_type.entity_type ? "{0} involves {1}" : implying_role.fact_type.default_reading+" Boolean"
|
1079
|
-
)
|
1080
|
-
] +
|
1081
|
-
Array(implying_role.fact_type.entity_type ? ImplicitReading.new(self, "{1} is involved in {0}") : nil)
|
1082
|
-
end
|
1083
|
-
|
1084
|
-
def reading_preferably_starting_with_role role, negated = false
|
1085
|
-
all_reading[role == implying_role ? 1 : 0]
|
1086
|
-
end
|
1087
|
-
|
1088
|
-
# This is only used for debugging, from RoleRef#describe
|
1089
|
-
class ImplicitReading
|
1090
|
-
attr_accessor :fact_type, :text
|
1091
|
-
attr_reader :is_negative # Never true
|
1092
|
-
|
1093
|
-
def initialize(fact_type, text)
|
1094
|
-
@fact_type = fact_type
|
1095
|
-
@text = text
|
1096
|
-
end
|
1097
|
-
|
1098
|
-
class ImplicitReadingRoleSequence
|
1099
|
-
class ImplicitReadingRoleRef
|
1100
|
-
attr_reader :role
|
1101
|
-
attr_reader :role_sequence
|
1102
|
-
def initialize(role, role_sequence)
|
1103
|
-
@role = role
|
1104
|
-
@role_sequence = role_sequence
|
1105
|
-
end
|
1106
|
-
def variable; nil; end
|
1107
|
-
def play; nil; end
|
1108
|
-
def leading_adjective; nil; end
|
1109
|
-
def trailing_adjective; nil; end
|
1110
|
-
def describe
|
1111
|
-
@role.object_type.name
|
1112
|
-
end
|
1113
|
-
end
|
1114
|
-
|
1115
|
-
def initialize roles
|
1116
|
-
@role_refs = roles.map{|role| ImplicitReadingRoleRef.new(role, self) }
|
1117
|
-
end
|
1118
|
-
|
1119
|
-
def all_role_ref
|
1120
|
-
@role_refs
|
1121
|
-
end
|
1122
|
-
def all_role_ref_in_order
|
1123
|
-
@role_refs
|
1124
|
-
end
|
1125
|
-
def describe
|
1126
|
-
'('+@role_refs.map(&:describe)*', '+')'
|
1127
|
-
end
|
1128
|
-
def all_reading
|
1129
|
-
[]
|
1130
|
-
end
|
1131
|
-
end
|
1132
|
-
|
1133
|
-
def role_sequence
|
1134
|
-
ImplicitReadingRoleSequence.new([@fact_type.implying_role, @fact_type.all_role.single])
|
1135
|
-
end
|
1136
|
-
|
1137
|
-
def ordinal; 0; end
|
1138
|
-
|
1139
|
-
def expand
|
1140
|
-
text.gsub(/\{([01])\}/) do
|
1141
|
-
if $1 == '0'
|
1142
|
-
fact_type.all_role[0].object_type.name
|
1143
|
-
else
|
1144
|
-
fact_type.implying_role.object_type.name
|
1145
|
-
end
|
1146
|
-
end
|
1147
|
-
end
|
1148
|
-
end
|
1149
|
-
end
|
1150
|
-
|
1151
|
-
# Some queries must be over the proximate roles, some over the counterpart roles.
|
1152
|
-
# Return the common superclass of the appropriate roles, and the actual roles
|
1153
|
-
def self.plays_over roles, options = :both # Or :proximate, :counterpart
|
1154
|
-
# If we can stay inside this objectified FT, there's no query:
|
1155
|
-
roles = Array(roles) # To be safe, in case we get a role collection proxy
|
1156
|
-
return nil if roles.size == 1 or
|
1157
|
-
options != :counterpart && roles.map{|role| role.fact_type}.uniq.size == 1
|
1158
|
-
proximate_sups, counterpart_sups, obj_sups, counterpart_roles, objectification_roles =
|
1159
|
-
*roles.inject(nil) do |d_c_o, role|
|
1160
|
-
object_type = role.object_type
|
1161
|
-
fact_type = role.fact_type
|
1162
|
-
|
1163
|
-
proximate_role_supertypes = object_type.supertypes_transitive
|
1164
|
-
|
1165
|
-
# A role in an objectified fact type may indicate either the objectification or the counterpart player.
|
1166
|
-
# This could be ambiguous. Figure out both and prefer the counterpart over the objectification.
|
1167
|
-
counterpart_role_supertypes =
|
1168
|
-
if fact_type.all_role.size > 2
|
1169
|
-
possible_roles = fact_type.all_role.select{|r| d_c_o && d_c_o[1].include?(r.object_type) }
|
1170
|
-
if possible_roles.size == 1 # Only one candidate matches the types of the possible variables
|
1171
|
-
counterpart_role = possible_roles[0]
|
1172
|
-
d_c_o[1] # No change
|
1173
|
-
else
|
1174
|
-
# puts "#{constraint_type} #{name}: Awkward, try counterpart-role query on a >2ary '#{fact_type.default_reading}'"
|
1175
|
-
# Try all roles; hopefully we don't have two roles with a matching candidate here:
|
1176
|
-
# Find which role is compatible with the existing supertypes, if any
|
1177
|
-
if d_c_o
|
1178
|
-
st = nil
|
1179
|
-
counterpart_role =
|
1180
|
-
fact_type.all_role.detect{|r| ((st = r.object_type.supertypes_transitive) & d_c_o[1]).size > 0}
|
1181
|
-
st
|
1182
|
-
else
|
1183
|
-
counterpart_role = nil # This can't work, we don't have any basis for a decision (must be objectification)
|
1184
|
-
[]
|
1185
|
-
end
|
1186
|
-
#fact_type.all_role.map{|r| r.object_type.supertypes_transitive}.flatten.uniq
|
1187
|
-
end
|
1188
|
-
else
|
1189
|
-
# Get the supertypes of the counterpart role (care with unaries):
|
1190
|
-
ftr = role.fact_type.all_role.to_a
|
1191
|
-
(counterpart_role = ftr[0] == role ? ftr[-1] : ftr[0]).object_type.supertypes_transitive
|
1192
|
-
end
|
1193
|
-
|
1194
|
-
if fact_type.entity_type
|
1195
|
-
objectification_role_supertypes =
|
1196
|
-
fact_type.entity_type.supertypes_transitive+object_type.supertypes_transitive
|
1197
|
-
objectification_role = role.link_fact_type.all_role.single # Find the phantom role here
|
1198
|
-
else
|
1199
|
-
objectification_role_supertypes = counterpart_role_supertypes
|
1200
|
-
objectification_role = counterpart_role
|
1201
|
-
end
|
1202
|
-
|
1203
|
-
if !d_c_o
|
1204
|
-
d_c_o = [proximate_role_supertypes, counterpart_role_supertypes, objectification_role_supertypes, [counterpart_role], [objectification_role]]
|
1205
|
-
#puts "role player supertypes starts #{d_c_o.map{|dco| dco.map(&:name).inspect}*' or '}"
|
1206
|
-
else
|
1207
|
-
#puts "continues #{[proximate_role_supertypes, counterpart_role_supertypes, objectification_role_supertypes]map{|dco| dco.map(&:name).inspect}*' or '}"
|
1208
|
-
d_c_o[0] &= proximate_role_supertypes
|
1209
|
-
d_c_o[1] &= counterpart_role_supertypes
|
1210
|
-
d_c_o[2] &= objectification_role_supertypes
|
1211
|
-
d_c_o[3] << (counterpart_role || objectification_role)
|
1212
|
-
d_c_o[4] << (objectification_role || counterpart_role)
|
1213
|
-
end
|
1214
|
-
d_c_o
|
1215
|
-
end # inject
|
1216
|
-
|
1217
|
-
# Discount a subtype step over an object type that's not a player here,
|
1218
|
-
# if we can use an objectification step to an object type that is:
|
1219
|
-
if counterpart_sups.size > 0 && obj_sups.size > 0 && counterpart_sups[0] != obj_sups[0]
|
1220
|
-
trace :query, "ambiguous query, could be over #{counterpart_sups[0].name} or #{obj_sups[0].name}"
|
1221
|
-
if !roles.detect{|r| r.object_type == counterpart_sups[0]} and roles.detect{|r| r.object_type == obj_sups[0]}
|
1222
|
-
trace :query, "discounting #{counterpart_sups[0].name} in favour of direct objectification"
|
1223
|
-
counterpart_sups = []
|
1224
|
-
end
|
1225
|
-
end
|
1226
|
-
|
1227
|
-
# Choose the first entry in the first non-empty supertypes list:
|
1228
|
-
if options != :counterpart && proximate_sups[0]
|
1229
|
-
[ proximate_sups[0], roles ]
|
1230
|
-
elsif !counterpart_sups.empty?
|
1231
|
-
[ counterpart_sups[0], counterpart_roles ]
|
1232
|
-
else
|
1233
|
-
[ obj_sups[0], objectification_roles ]
|
1234
|
-
end
|
1235
|
-
end
|
1236
|
-
|
1237
|
-
class Fact
|
1238
|
-
def verbalise(context = nil)
|
1239
|
-
reading = fact_type.preferred_reading
|
1240
|
-
reading_roles = reading.role_sequence.all_role_ref.sort_by{|rr| rr.ordinal}.map{|rr| rr.role }
|
1241
|
-
role_values_in_reading_order = all_role_value.sort_by{|rv| reading_roles.index(rv.role) }
|
1242
|
-
instance_verbalisations = role_values_in_reading_order.map do |rv|
|
1243
|
-
if rv.instance.value
|
1244
|
-
v = rv.instance.verbalise
|
1245
|
-
else
|
1246
|
-
if (c = rv.instance.object_type).is_a?(EntityType)
|
1247
|
-
if !c.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type == fact_type}
|
1248
|
-
v = rv.instance.verbalise
|
1249
|
-
end
|
1250
|
-
end
|
1251
|
-
end
|
1252
|
-
next nil unless v
|
1253
|
-
v.to_s.sub(/(#{rv.instance.object_type.name}|\S*)\s/,'')
|
1254
|
-
end
|
1255
|
-
reading.expand([], false, instance_verbalisations)
|
1256
|
-
end
|
1257
|
-
end
|
1258
|
-
|
1259
|
-
class Instance
|
1260
|
-
def verbalise(context = nil)
|
1261
|
-
return "#{object_type.name} #{value}" if object_type.is_a?(ValueType)
|
1262
|
-
|
1263
|
-
return "#{object_type.name} (in which #{fact.verbalise(context)})" if object_type.fact_type
|
1264
|
-
|
1265
|
-
# It's an entity that's not an objectified fact type
|
1266
|
-
|
1267
|
-
# If it has a simple identifier, there's no need to fully verbalise the identifying facts.
|
1268
|
-
# This recursive block returns either the identifying value or nil
|
1269
|
-
simple_identifier = proc do |instance|
|
1270
|
-
if instance.object_type.is_a?(ActiveFacts::Metamodel::ValueType)
|
1271
|
-
instance
|
1272
|
-
else
|
1273
|
-
pi = instance.object_type.preferred_identifier
|
1274
|
-
identifying_role_refs = pi.role_sequence.all_role_ref_in_order
|
1275
|
-
if identifying_role_refs.size != 1
|
1276
|
-
nil
|
1277
|
-
else
|
1278
|
-
role = identifying_role_refs[0].role
|
1279
|
-
my_role = (role.fact_type.all_role.to_a-[role])[0]
|
1280
|
-
identifying_fact = my_role.all_role_value.detect{|rv| rv.instance == self}.fact
|
1281
|
-
irv = identifying_fact.all_role_value.detect{|rv| rv.role == role}
|
1282
|
-
identifying_instance = irv.instance
|
1283
|
-
simple_identifier.call(identifying_instance)
|
1284
|
-
end
|
1285
|
-
end
|
1286
|
-
end
|
1287
|
-
|
1288
|
-
if (id = simple_identifier.call(self))
|
1289
|
-
"#{object_type.name} #{id.value}"
|
1290
|
-
else
|
1291
|
-
pi = object_type.preferred_identifier
|
1292
|
-
identifying_role_refs = pi.role_sequence.all_role_ref_in_order
|
1293
|
-
"#{object_type.name}" +
|
1294
|
-
" is identified by " + # REVISIT: Where the single fact type is TypeInheritance, we can shrink this
|
1295
|
-
identifying_role_refs.map do |rr|
|
1296
|
-
rr = rr.preferred_reference
|
1297
|
-
[ (l = rr.leading_adjective) ? l+"-" : nil,
|
1298
|
-
rr.role.role_name || rr.role.object_type.name,
|
1299
|
-
(t = rr.trailing_adjective) ? l+"-" : nil
|
1300
|
-
].compact*""
|
1301
|
-
end * " and " +
|
1302
|
-
" where " +
|
1303
|
-
identifying_role_refs.map do |rr| # Go through the identifying roles and emit the facts that define them
|
1304
|
-
instance_role = object_type.all_role.detect{|r| r.fact_type == rr.role.fact_type}
|
1305
|
-
identifying_fact = all_role_value.detect{|rv| rv.fact.fact_type == rr.role.fact_type}.fact
|
1306
|
-
#counterpart_role = (rr.role.fact_type.all_role.to_a-[instance_role])[0]
|
1307
|
-
#identifying_instance = counterpart_role.all_role_value.detect{|rv| rv.fact == identifying_fact}.instance
|
1308
|
-
identifying_fact.verbalise(context)
|
1309
|
-
end*", "
|
1310
|
-
end
|
1311
|
-
|
1312
|
-
end
|
1313
|
-
end
|
1314
|
-
|
1315
|
-
class ContextNote
|
1316
|
-
def verbalise(context=nil)
|
1317
|
-
as_cql
|
1318
|
-
end
|
1319
|
-
|
1320
|
-
def as_cql
|
1321
|
-
' (' +
|
1322
|
-
( if all_context_according_to
|
1323
|
-
'according to '
|
1324
|
-
all_context_according_to.map do |act|
|
1325
|
-
act.agent.agent_name+', '
|
1326
|
-
end.join('')
|
1327
|
-
end
|
1328
|
-
) +
|
1329
|
-
context_note_kind.gsub(/_/, ' ') +
|
1330
|
-
' ' +
|
1331
|
-
discussion +
|
1332
|
-
( if agreement
|
1333
|
-
', as agreed ' +
|
1334
|
-
(agreement.date ? ' on '+agreement.date.iso8601.inspect+' ' : '') +
|
1335
|
-
'by '
|
1336
|
-
agreement.all_context_agreed_by.map do |acab|
|
1337
|
-
acab.agent.agent_name+', '
|
1338
|
-
end.join('')
|
1339
|
-
else
|
1340
|
-
''
|
1341
|
-
end
|
1342
|
-
) +
|
1343
|
-
')'
|
1344
|
-
end
|
1345
|
-
end
|
1346
|
-
|
1347
|
-
end
|
1348
|
-
end
|