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
@@ -1,26 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Generators.
|
3
|
-
# Provides version number from the --version option
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
-
#
|
7
|
-
require 'activefacts/persistence'
|
8
|
-
|
9
|
-
module ActiveFacts
|
10
|
-
module Generate
|
11
|
-
# Generate nothing from an ActiveFacts vocabulary. This is useful to check the file can be read ok.
|
12
|
-
# Invoke as
|
13
|
-
# afgen --null <file>.cql
|
14
|
-
class VERSION
|
15
|
-
private
|
16
|
-
def initialize(vocabulary, *options)
|
17
|
-
puts ActiveFacts::VERSION
|
18
|
-
exit 0
|
19
|
-
end
|
20
|
-
|
21
|
-
public
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
ActiveFacts::Registry.generator('version', ActiveFacts::Generate::VERSION)
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# Compile a CQL file into an ActiveFacts vocabulary.
|
2
|
-
#
|
3
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
4
|
-
#
|
5
|
-
require 'activefacts/vocabulary'
|
6
|
-
require 'activefacts/cql/parser'
|
7
|
-
require 'activefacts/cql/compiler'
|
8
|
-
|
9
|
-
module ActiveFacts
|
10
|
-
module Input #:nodoc:
|
11
|
-
# Compile CQL to an ActiveFacts vocabulary.
|
12
|
-
# Invoke as
|
13
|
-
# afgen --<generator> <file>.cql
|
14
|
-
class CQL
|
15
|
-
# Read the specified file
|
16
|
-
def self.readfile(filename)
|
17
|
-
if File.basename(filename, '.cql') == "-"
|
18
|
-
read(STDIN, "<standard input>")
|
19
|
-
else
|
20
|
-
File.open(filename) {|file|
|
21
|
-
read(file, filename)
|
22
|
-
}
|
23
|
-
end
|
24
|
-
rescue => e
|
25
|
-
# Augment the exception message, but preserve the backtrace
|
26
|
-
ne = StandardError.new("In #{filename} #{e.message.strip}")
|
27
|
-
ne.set_backtrace(e.backtrace)
|
28
|
-
raise ne
|
29
|
-
end
|
30
|
-
|
31
|
-
# Read the specified input stream
|
32
|
-
def self.read(file, filename = "stdin")
|
33
|
-
readstring(file.read, filename)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Read the specified input string
|
37
|
-
def self.readstring(str, filename = "string")
|
38
|
-
compiler = ActiveFacts::CQL::Compiler.new(filename)
|
39
|
-
compiler.compile(str)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,1636 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Vocabulary Input.
|
3
|
-
# Read a NORMA file into an ActiveFacts vocabulary
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
-
#
|
7
|
-
# This code uses variables prefixed with x_ when they refer to Rexml nodes.
|
8
|
-
# Every node having an id="..." is indexed in @x_by_id[] hash before processing.
|
9
|
-
# As we build ActiveFacts objects to match, we index those in @by_id[].
|
10
|
-
# Both these hashes may be looked up by any of the ref="..." values in the file.
|
11
|
-
#
|
12
|
-
require 'nokogiri'
|
13
|
-
require 'activefacts/vocabulary'
|
14
|
-
|
15
|
-
module Nokogiri
|
16
|
-
module XML
|
17
|
-
class Node
|
18
|
-
def elements
|
19
|
-
children.select{|n|
|
20
|
-
Nokogiri::XML::Element === n
|
21
|
-
}
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
module ActiveFacts
|
28
|
-
module Input
|
29
|
-
# Compile a NORMA (.orm) file to an ActiveFacts vocabulary.
|
30
|
-
# Invoke as
|
31
|
-
# afgen --<generator> <file>.orm
|
32
|
-
# This parser uses Rexml so it's very slow.
|
33
|
-
class ORM
|
34
|
-
module Gravity
|
35
|
-
%w{NW N NE W C E SW S SE}.each_with_index { |dir, i| const_set(dir, i) }
|
36
|
-
end
|
37
|
-
|
38
|
-
DataTypeMapping = {
|
39
|
-
"FixedLengthText" => "Char",
|
40
|
-
"VariableLengthText" => "String",
|
41
|
-
"LargeLengthText" => "Text",
|
42
|
-
"SignedIntegerNumeric" => "Signed Integer(32)",
|
43
|
-
"SignedSmallIntegerNumeric" => "Signed Integer(16)",
|
44
|
-
"SignedLargeIntegerNumeric" => "Signed Integer(64)",
|
45
|
-
"UnsignedIntegerNumeric" => "Unsigned Integer(32)",
|
46
|
-
"UnsignedTinyIntegerNumeric" => "Unsigned Integer(8)",
|
47
|
-
"UnsignedSmallIntegerNumeric" => "Unsigned Integer(16)",
|
48
|
-
"UnsignedLargeIntegerNumeric" => "Unsigned Integer(64)",
|
49
|
-
"AutoCounterNumeric" => "Auto Counter",
|
50
|
-
"FloatingPointNumeric" => "Real(64)",
|
51
|
-
"SinglePrecisionFloatingPointNumeric" => "Real(32)",
|
52
|
-
"DoublePrecisionFloatingPointNumeric" => "Real(32)",
|
53
|
-
"DecimalNumeric" => "Decimal",
|
54
|
-
"MoneyNumeric" => "Money",
|
55
|
-
"FixedLengthRawData" => "Blob",
|
56
|
-
"VariableLengthRawData" => "Blob",
|
57
|
-
"LargeLengthRawData" => "Blob",
|
58
|
-
"PictureRawData" => "Image",
|
59
|
-
"OleObjectRawData" => "Blob",
|
60
|
-
"AutoTimestampTemporal" => "Auto Time Stamp",
|
61
|
-
"TimeTemporal" => "Time",
|
62
|
-
"DateTemporal" => "Date",
|
63
|
-
"DateAndTimeTemporal" => "Date Time",
|
64
|
-
"TrueOrFalseLogical" => "Boolean",
|
65
|
-
"YesOrNoLogical" => "Boolean",
|
66
|
-
"RowIdOther" => "Guid",
|
67
|
-
"ObjectIdOther" => "Guid"
|
68
|
-
}
|
69
|
-
RESERVED_WORDS = %w{
|
70
|
-
and but each each either false if maybe no none not one or some that true where
|
71
|
-
}
|
72
|
-
|
73
|
-
private
|
74
|
-
def self.readfile(filename, *options)
|
75
|
-
if File.basename(filename, '.orm') == "-"
|
76
|
-
self.read(STDIN, "<standard input>", options)
|
77
|
-
else
|
78
|
-
File.open(filename) {|file|
|
79
|
-
self.read(file, filename, *options)
|
80
|
-
}
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.read(file, filename = "stdin", *options)
|
85
|
-
ORM.new(file, filename, *options).read
|
86
|
-
end
|
87
|
-
|
88
|
-
def initialize(file, filename = "stdin", *options)
|
89
|
-
@file = file
|
90
|
-
@filename = filename
|
91
|
-
@options = options
|
92
|
-
end
|
93
|
-
|
94
|
-
public
|
95
|
-
def read #:nodoc:
|
96
|
-
begin
|
97
|
-
@document = Nokogiri::XML(@file)
|
98
|
-
rescue => e
|
99
|
-
puts "Failed to parse XML in #{@filename}: #{e.inspect}"
|
100
|
-
end
|
101
|
-
|
102
|
-
# Find the Vocabulary and do some setup:
|
103
|
-
root = @document.root
|
104
|
-
#p((root.methods-0.methods).sort.grep(/name/))
|
105
|
-
if root.name == "ORM2" && root.namespace.prefix == "ormRoot"
|
106
|
-
x_models = root.xpath('orm:ORMModel')
|
107
|
-
throw "No vocabulary found" unless x_models.size == 1
|
108
|
-
@x_model = x_models[0]
|
109
|
-
elsif root.name == "ORMModel"
|
110
|
-
p @document.children.map(&:name)
|
111
|
-
@x_model = @document.children[0]
|
112
|
-
else
|
113
|
-
throw "NORMA model not found in #{@filename}"
|
114
|
-
end
|
115
|
-
|
116
|
-
read_vocabulary
|
117
|
-
@vocabulary
|
118
|
-
end
|
119
|
-
|
120
|
-
private
|
121
|
-
|
122
|
-
def read_vocabulary
|
123
|
-
@constellation = ActiveFacts::API::Constellation.new(ActiveFacts::Metamodel)
|
124
|
-
vocabulary_name = @x_model['Name']
|
125
|
-
@vocabulary = @constellation.Vocabulary(vocabulary_name)
|
126
|
-
|
127
|
-
# Find all elements having an "id" attribute and index them
|
128
|
-
x_identified = @x_model.xpath(".//*[@id]")
|
129
|
-
@x_by_id = x_identified.inject({}){|h, x|
|
130
|
-
id = x['id']
|
131
|
-
h[id] = x
|
132
|
-
h
|
133
|
-
}
|
134
|
-
|
135
|
-
# Everything we build will be indexed here:
|
136
|
-
@by_id = {}
|
137
|
-
|
138
|
-
list_subtypes
|
139
|
-
read_entity_types
|
140
|
-
read_value_types
|
141
|
-
read_fact_types
|
142
|
-
read_nested_types
|
143
|
-
read_subtypes
|
144
|
-
read_roles
|
145
|
-
complete_nested_types
|
146
|
-
read_constraints
|
147
|
-
read_instances if @options.include?("instances")
|
148
|
-
read_diagrams if @options.include?("diagrams")
|
149
|
-
end
|
150
|
-
|
151
|
-
def id_of(x)
|
152
|
-
x['id'][1..-1]
|
153
|
-
end
|
154
|
-
|
155
|
-
def read_entity_types
|
156
|
-
# get and process all the entity types:
|
157
|
-
entity_types = []
|
158
|
-
x_entity_types = @x_model.xpath("orm:Objects/orm:EntityType")
|
159
|
-
x_entity_types.each do |x|
|
160
|
-
id = x['id']
|
161
|
-
name = (x['Name'] || "").gsub(/\s+/,' ').gsub(/-/,'_').strip
|
162
|
-
name = nil if name.size == 0
|
163
|
-
entity_type =
|
164
|
-
@by_id[id] =
|
165
|
-
trace :orm, "Asserting new EntityType #{name.inspect}" do
|
166
|
-
@vocabulary.valid_entity_type_name(name) ||
|
167
|
-
@constellation.EntityType(@vocabulary, name, :concept => id_of(x))
|
168
|
-
end
|
169
|
-
entity_types << entity_type
|
170
|
-
independent = x['IsIndependent']
|
171
|
-
entity_type.is_independent = true if independent && independent == 'true'
|
172
|
-
personal = x['IsPersonal']
|
173
|
-
entity_type.pronoun = 'personal' if personal && personal == 'true'
|
174
|
-
# x_pref = x.xpath("orm:PreferredIdentifier")[0]
|
175
|
-
# if x_pref
|
176
|
-
# pi_id = x_pref['ref']
|
177
|
-
# @pref_id_for[pi_id] = x
|
178
|
-
# end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def read_value_types
|
183
|
-
# Now the value types:
|
184
|
-
value_types = []
|
185
|
-
x_value_types = @x_model.xpath("orm:Objects/orm:ValueType")
|
186
|
-
@value_type_id_read = {}
|
187
|
-
x_value_types.each{|x|
|
188
|
-
next if x['IsImplicitBooleanValue']
|
189
|
-
value_types << read_value_type(x)
|
190
|
-
}
|
191
|
-
end
|
192
|
-
|
193
|
-
def read_value_type x
|
194
|
-
id = x['id']
|
195
|
-
return if @value_type_id_read[id] # Don't read the same value type twice
|
196
|
-
@value_type_id_read[id] = true
|
197
|
-
|
198
|
-
name = (x['Name'] || "").gsub(/\s+/,' ').gsub(/-/,'_').strip
|
199
|
-
name = nil if name.size == 0
|
200
|
-
|
201
|
-
cdt = x.xpath('orm:ConceptualDataType')[0]
|
202
|
-
scale = cdt['Scale']
|
203
|
-
scale = scale != "" && scale.to_i
|
204
|
-
length = cdt['Length']
|
205
|
-
length = length != "" && length.to_i
|
206
|
-
length = nil if length <= 0
|
207
|
-
base_type = @x_by_id[cdt['ref']]
|
208
|
-
type_name = "#{base_type.name}"
|
209
|
-
type_name.sub!(/^orm:/,'')
|
210
|
-
|
211
|
-
type_name.sub!(/DataType\Z/,'')
|
212
|
-
if t = DataTypeMapping[type_name]
|
213
|
-
existing = @constellation.ObjectType[[@vocabulary.identifying_role_values, t]]
|
214
|
-
if !existing || existing.is_a?(ActiveFacts::Metamodel::ValueType)
|
215
|
-
# There's no type conflict, so use the mapped name.
|
216
|
-
type_name = t
|
217
|
-
end
|
218
|
-
end
|
219
|
-
trace :orm, "Using #{type_name.inspect} as supertype for new #{name}"
|
220
|
-
if !length and type_name =~ /\(([0-9]+)\)/
|
221
|
-
length = $1.to_i
|
222
|
-
end
|
223
|
-
type_name = type_name.sub(/\(([0-9]*)\)/,'')
|
224
|
-
|
225
|
-
subtype_roles = x.xpath("orm:PlayedRoles/orm:SubtypeMetaRole")
|
226
|
-
value_super_type = nil
|
227
|
-
if !subtype_roles.empty?
|
228
|
-
subtype_role_id = subtype_roles[0]['ref']
|
229
|
-
subtype_role = @x_by_id[subtype_role_id]
|
230
|
-
subtyping_fact_roles = subtype_role.parent
|
231
|
-
supertype_id = subtyping_fact_roles.xpath("orm:SupertypeMetaRole/orm:RolePlayer")[0]['ref']
|
232
|
-
x_supertype = @x_by_id[supertype_id]
|
233
|
-
read_value_type x_supertype unless @value_type_id_read[supertype_id]
|
234
|
-
supertype = @by_id[supertype_id]
|
235
|
-
supertype_name = x_supertype['Name']
|
236
|
-
raise "Supertype of #{name} is post-defined but recursiving processing failed" unless supertype
|
237
|
-
raise "Supertype #{supertype_name} of #{name} is not a value type" unless supertype.kind_of? ActiveFacts::Metamodel::ValueType
|
238
|
-
trace :orm, "Asserting new ValueType #{supertype_name.inspect} for supertype" do
|
239
|
-
value_super_type =
|
240
|
-
@vocabulary.valid_value_type_name(supertype_name) ||
|
241
|
-
@constellation.ValueType(@vocabulary, supertype_name, :concept => id_of(x_supertype))
|
242
|
-
end
|
243
|
-
else
|
244
|
-
# REVISIT: Need to handle standard types better here:
|
245
|
-
value_super_type =
|
246
|
-
if type_name != name
|
247
|
-
trace :orm, "Asserting new ValueType #{type_name.inspect} for supertype" do
|
248
|
-
@vocabulary.valid_value_type_name(type_name) ||
|
249
|
-
@constellation.ValueType(@vocabulary.identifying_role_values, type_name, :concept => :new)
|
250
|
-
end
|
251
|
-
else
|
252
|
-
nil
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
vt =
|
257
|
-
trace :orm, "Asserting new ValueType #{name.inspect}" do
|
258
|
-
@by_id[id] =
|
259
|
-
@vocabulary.valid_value_type_name(name) ||
|
260
|
-
@constellation.ValueType(@vocabulary.identifying_role_values, name, :concept => id_of(x))
|
261
|
-
end
|
262
|
-
vt.supertype = value_super_type
|
263
|
-
vt.length = length if length
|
264
|
-
vt.scale = scale if scale && scale != 0
|
265
|
-
independent = x['IsIndependent']
|
266
|
-
vt.is_independent = true if independent && independent == 'true'
|
267
|
-
personal = x['IsPersonal']
|
268
|
-
vt.pronoun = 'personal' if personal && personal == 'true'
|
269
|
-
|
270
|
-
x_vr = x.xpath("orm:ValueRestriction/orm:ValueConstraint")
|
271
|
-
x_vr.each{|vr|
|
272
|
-
x_ranges = vr.xpath("orm:ValueRanges/orm:ValueRange")
|
273
|
-
next if x_ranges.size == 0
|
274
|
-
vt.value_constraint = @by_id[vr['id']] = @constellation.ValueConstraint(id_of(vr))
|
275
|
-
x_ranges.each{|x_range|
|
276
|
-
v_range = value_range(x_range)
|
277
|
-
ar = @constellation.AllowedRange(vt.value_constraint, v_range)
|
278
|
-
}
|
279
|
-
}
|
280
|
-
vt
|
281
|
-
end
|
282
|
-
|
283
|
-
def assert_value(val, string)
|
284
|
-
if val
|
285
|
-
@constellation.Value(val.to_s, string, nil)
|
286
|
-
else
|
287
|
-
nil
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def value_range(x_range)
|
292
|
-
min = x_range['MinValue']
|
293
|
-
max = x_range['MaxValue']
|
294
|
-
|
295
|
-
strings = is_literal_string(min) || is_literal_string(max)
|
296
|
-
# ValueRange takes a minimum and/or a maximum Bound, each takes value and whether inclusive
|
297
|
-
@constellation.ValueRange(
|
298
|
-
min.length > 0 ? @constellation.Bound(assert_value(min, strings), true) : nil,
|
299
|
-
max.length > 0 ? @constellation.Bound(assert_value(max, strings), true) : nil)
|
300
|
-
end
|
301
|
-
|
302
|
-
def read_fact_types
|
303
|
-
# Handle the fact types:
|
304
|
-
facts = []
|
305
|
-
@x_facts = @x_model.xpath("orm:Facts/orm:Fact")
|
306
|
-
trace :orm, "Reading fact types" do
|
307
|
-
@x_facts.each{|x|
|
308
|
-
id = x['id']
|
309
|
-
name = x['Name'] || x['_Name']
|
310
|
-
name = "<unnamed>" if !name
|
311
|
-
name = "" if !name || name.size == 0
|
312
|
-
# Note that the new metamodel doesn't have a name for a facttype unless it's objectified
|
313
|
-
|
314
|
-
trace :orm, "FactType #{name || id}"
|
315
|
-
facts << @by_id[id] = fact_type = @constellation.FactType(id_of(x))
|
316
|
-
}
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
def list_subtypes
|
321
|
-
@x_subtypes = @x_model.xpath("orm:Facts/orm:SubtypeFact")
|
322
|
-
if @document.namespaces['xmlns:oialtocdb']
|
323
|
-
oialtocdb = @document.xpath("ormRoot:ORM2/oialtocdb:MappingCustomization")
|
324
|
-
@x_mappings = oialtocdb.xpath(".//oialtocdb:AssimilationMappings/oialtocdb:AssimilationMapping/oialtocdb:FactType")
|
325
|
-
else
|
326
|
-
@x_mappings = []
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
def read_subtypes
|
331
|
-
# Handle the subtype fact types:
|
332
|
-
facts = []
|
333
|
-
|
334
|
-
trace :orm, "Reading sub-types" do
|
335
|
-
@x_subtypes.each{|x|
|
336
|
-
id = x['id']
|
337
|
-
name = (x['Name'] || x['_Name'] || '').gsub(/\s+/,' ').gsub(/-/,'_').strip
|
338
|
-
name = nil if name.size == 0
|
339
|
-
trace :orm, "FactType #{name || id}"
|
340
|
-
|
341
|
-
x_subtype_role = x.xpath('orm:FactRoles/orm:SubtypeMetaRole')[0]
|
342
|
-
subtype_role_id = x_subtype_role['id']
|
343
|
-
subtype_id = x_subtype_role.xpath('orm:RolePlayer')[0]['ref']
|
344
|
-
subtype = @by_id[subtype_id]
|
345
|
-
# REVISIT: Provide a way in the metamodel of handling Partition, (and mapping choices that vary for each supertype?)
|
346
|
-
|
347
|
-
x_supertype_role = x.xpath('orm:FactRoles/orm:SupertypeMetaRole')[0]
|
348
|
-
supertype_role_id = x_supertype_role['id']
|
349
|
-
supertype_id = x_supertype_role.xpath('orm:RolePlayer')[0]['ref']
|
350
|
-
supertype = @by_id[supertype_id]
|
351
|
-
|
352
|
-
throw "For Subtype fact #{name}, the supertype #{supertype_id} was not found" if !supertype
|
353
|
-
throw "For Subtype fact #{name}, the subtype #{subtype_id} was not found" if !subtype
|
354
|
-
trace :orm, "#{subtype.name} is a subtype of #{supertype.name}"
|
355
|
-
|
356
|
-
# We already handled ValueType subtyping:
|
357
|
-
next if subtype.kind_of? ActiveFacts::Metamodel::ValueType or
|
358
|
-
supertype.kind_of? ActiveFacts::Metamodel::ValueType
|
359
|
-
|
360
|
-
inheritance_fact = @constellation.TypeInheritance(subtype, supertype, :concept => id_of(x))
|
361
|
-
if x["IsPrimary"] == "true" or # Old way
|
362
|
-
x["PreferredIdentificationPath"] == "true" # Newer
|
363
|
-
trace :orm, "#{supertype.name} is primary supertype of #{subtype.name}"
|
364
|
-
inheritance_fact.provides_identification = true
|
365
|
-
end
|
366
|
-
mapping = @x_mappings.detect{ |m| m['ref'] == id }
|
367
|
-
mapping_choice = mapping ? mapping.parent['AbsorptionChoice'] : 'Absorbed'
|
368
|
-
inheritance_fact.assimilation = mapping_choice.downcase.sub(/partition/, 'partitioned') if mapping_choice != 'Absorbed'
|
369
|
-
facts << @by_id[id] = inheritance_fact
|
370
|
-
|
371
|
-
# Create the new Roles so we can find constraints on them:
|
372
|
-
subtype_role = @by_id[subtype_role_id] = @constellation.Role(inheritance_fact, 0, :object_type => subtype, :concept => id_of(x_subtype_role))
|
373
|
-
supertype_role = @by_id[supertype_role_id] = @constellation.Role(inheritance_fact, 1, :object_type => supertype, :concept => id_of(x_supertype_role))
|
374
|
-
|
375
|
-
# Create readings, so constraints can be verbalised for example:
|
376
|
-
rs = @constellation.RoleSequence(:new)
|
377
|
-
@constellation.RoleRef(rs, 0, :role => subtype_role)
|
378
|
-
@constellation.RoleRef(rs, 1, :role => supertype_role)
|
379
|
-
@constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}", :is_negative => false)
|
380
|
-
@constellation.Reading(inheritance_fact, 1, :role_sequence => rs, :text => "{0} is a subtype of {1}", :is_negative => false)
|
381
|
-
|
382
|
-
rs2 = @constellation.RoleSequence(:new)
|
383
|
-
@constellation.RoleRef(rs2, 0, :role => supertype_role)
|
384
|
-
@constellation.RoleRef(rs2, 1, :role => subtype_role)
|
385
|
-
n = 'aeioh'.include?(subtype_role.object_type.name.downcase[0]) ? 1 : 0
|
386
|
-
@constellation.Reading(inheritance_fact, 2+n, :role_sequence => rs2, :text => "{0} is a {1}", :is_negative => false)
|
387
|
-
@constellation.Reading(inheritance_fact, 3-n, :role_sequence => rs2, :text => "{0} is an {1}", :is_negative => false)
|
388
|
-
}
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
def read_nested_types
|
393
|
-
# Process NestedTypes, but ignore ones having a NestedPredicate with IsImplied="true"
|
394
|
-
# We'll ignore the fact roles (and constraints) that implied objectifications have.
|
395
|
-
# This happens for all ternaries and higher order facts
|
396
|
-
@nested_types = []
|
397
|
-
x_nested_types = @x_model.xpath("orm:Objects/orm:ObjectifiedType")
|
398
|
-
trace :orm, "Reading objectified types" do
|
399
|
-
x_nested_types.each{|x|
|
400
|
-
id = x['id']
|
401
|
-
name = (x['Name'] || "").gsub(/\s+/,' ').gsub(/-/,'_').strip
|
402
|
-
name = nil if name.size == 0
|
403
|
-
|
404
|
-
x_fact_type = x.xpath('orm:NestedPredicate')[0]
|
405
|
-
is_implied = x_fact_type['IsImplied'] == "true"
|
406
|
-
|
407
|
-
fact_id = x_fact_type['ref']
|
408
|
-
fact_type = @by_id[fact_id]
|
409
|
-
next unless fact_type # "Nested fact #{fact_id} not found; objectification of a derived fact type?"
|
410
|
-
|
411
|
-
trace :orm, "NestedType #{name} is #{id}, nests #{fact_type.concept.guid}"
|
412
|
-
@nested_types <<
|
413
|
-
@by_id[id] =
|
414
|
-
nested_type = @vocabulary.valid_entity_type_name(name) ||
|
415
|
-
@constellation.EntityType(@vocabulary, name, :concept => id_of(x))
|
416
|
-
independent = x['IsIndependent']
|
417
|
-
nested_type.is_independent = true if independent && independent == 'true' && !is_implied
|
418
|
-
nested_type.concept.implication_rule = 'objectification' if is_implied
|
419
|
-
nested_type.fact_type = fact_type
|
420
|
-
}
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
def complete_nested_types
|
425
|
-
@nested_types.each do |nested_type|
|
426
|
-
# Create the phantom roles here. These will be used later when we create objectification steps,
|
427
|
-
# but for now there's nothing we import from NORMA which requires objectification steps.
|
428
|
-
# Consequently there's no need to index them against NORMA's phantom roles.
|
429
|
-
nested_type.create_implicit_fact_types
|
430
|
-
end
|
431
|
-
end
|
432
|
-
|
433
|
-
def read_roles
|
434
|
-
trace :orm, "Reading roles and readings" do
|
435
|
-
@x_facts.each{|x|
|
436
|
-
id = x['id']
|
437
|
-
fact_type = @by_id[id]
|
438
|
-
fact_name = x['Name'] || x['_Name'] || ''
|
439
|
-
#fact_name.gsub!(/\s/,'')
|
440
|
-
fact_name = nil if fact_name == ''
|
441
|
-
|
442
|
-
x_fact_roles = x.xpath('orm:FactRoles/*')
|
443
|
-
|
444
|
-
# Deal with FactRoles (Roles):
|
445
|
-
trace :orm, "Reading fact roles" do
|
446
|
-
x_fact_roles.each{|x|
|
447
|
-
name = (x['Name'] || "").gsub(/\s+/,' ').gsub(/-/,'_').strip
|
448
|
-
name = nil if name.size == 0
|
449
|
-
|
450
|
-
# _IsMandatory = x['_IsMandatory']
|
451
|
-
# _Multiplicity = x['_Multiplicity]
|
452
|
-
id = x['id']
|
453
|
-
rp = x.xpath('orm:RolePlayer')[0]
|
454
|
-
raise "Invalid ORM file; fact has missing player (RolePlayer id=#{id})" unless rp
|
455
|
-
ref = rp['ref']
|
456
|
-
|
457
|
-
# Find the object_type that plays the role:
|
458
|
-
object_type = @by_id[ref]
|
459
|
-
|
460
|
-
# Skip implicit roles added by NORMA to make unaries into binaries.
|
461
|
-
# This would make constraints over the deleted roles impossible,
|
462
|
-
# so as a SPECIAL CASE we index the unary role by the id of the
|
463
|
-
# implicit role. That means care is needed when handling unary FTs.
|
464
|
-
if (ox = @x_by_id[ref]) && ox['IsImplicitBooleanValue']
|
465
|
-
x_other_role = x.parent.xpath('orm:Role').reject{|x_role|
|
466
|
-
x_role == x
|
467
|
-
}[0]
|
468
|
-
other_role_id = x_other_role["id"]
|
469
|
-
other_role = @by_id[other_role_id]
|
470
|
-
trace :orm, "Indexing unary FT role #{other_role_id} by implicit boolean role #{id}"
|
471
|
-
@by_id[id] = other_role
|
472
|
-
|
473
|
-
# The role name of the ignored role is the one that applies:
|
474
|
-
role_name = x['Name']
|
475
|
-
other_role.role_name = role_name if role_name && role_name != ''
|
476
|
-
|
477
|
-
@by_id.delete(ref) # and de-index it from our list
|
478
|
-
next
|
479
|
-
end
|
480
|
-
if !object_type
|
481
|
-
throw "RolePlayer for '#{name}' #{ref} in fact type #{x.parent.parent['_Name']} was not found"
|
482
|
-
end
|
483
|
-
|
484
|
-
trace :orm, "#{@vocabulary.name}, RoleName=#{x['Name'].inspect} played by object_type=#{object_type.name}"
|
485
|
-
throw "Role is played by #{object_type.class} not ObjectType" if !(@constellation.vocabulary.object_type(:ObjectType) === object_type)
|
486
|
-
|
487
|
-
trace :orm, "Creating role #{name} nr#{fact_type.all_role.size} of #{fact_type.concept.guid} played by #{object_type.name}"
|
488
|
-
|
489
|
-
role = @by_id[id] = @constellation.Role(fact_type, fact_type.all_role.size, :object_type => object_type, :concept => id_of(x))
|
490
|
-
role.role_name = name if name && name != object_type.name
|
491
|
-
trace :orm, "Fact #{fact_name} (id #{fact_type.concept.guid}) role #{x['Name']} is played by #{object_type.name}, role is #{role.concept.guid}"
|
492
|
-
|
493
|
-
x_vr = x.xpath("orm:ValueRestriction/orm:RoleValueConstraint")
|
494
|
-
x_vr.each{|vr|
|
495
|
-
x_ranges = vr.xpath("orm:ValueRanges/orm:ValueRange")
|
496
|
-
next if x_ranges.size == 0
|
497
|
-
role.role_value_constraint = @by_id[vr['id']] = @constellation.ValueConstraint(id_of(vr))
|
498
|
-
x_ranges.each{|x_range|
|
499
|
-
v_range = value_range(x_range)
|
500
|
-
ar = @constellation.AllowedRange(role.role_value_constraint, v_range)
|
501
|
-
}
|
502
|
-
}
|
503
|
-
|
504
|
-
trace :orm, "Adding Role #{role.role_name || role.object_type.name} to #{fact_type.describe}"
|
505
|
-
#fact_type.add_role(role)
|
506
|
-
trace :orm, "Role #{role} is #{id}"
|
507
|
-
}
|
508
|
-
end
|
509
|
-
|
510
|
-
# Deal with Readings:
|
511
|
-
trace :orm, "Reading fact readings" do
|
512
|
-
x_reading_orders = x.xpath('orm:ReadingOrders/*')
|
513
|
-
x_reading_orders.each{|x|
|
514
|
-
x_role_sequence = x.xpath('orm:RoleSequence/*')
|
515
|
-
x_readings = x.xpath('orm:Readings/orm:Reading/orm:Data')
|
516
|
-
|
517
|
-
# Build an array of the Roles needed:
|
518
|
-
role_array = x_role_sequence.map{|x| @by_id[x['ref']] }
|
519
|
-
|
520
|
-
trace :orm, "Reading #{x_readings.map(&:text).inspect}"
|
521
|
-
role_sequence = get_role_sequence(role_array)
|
522
|
-
|
523
|
-
#role_sequence.all_role_ref.each_with_index{|rr, i|
|
524
|
-
# # REVISIT: rr.leading_adjective = ...; Add adjectives here
|
525
|
-
# }
|
526
|
-
|
527
|
-
x_readings.each_with_index{|x, i|
|
528
|
-
reading = @constellation.Reading(fact_type, fact_type.all_reading.size, :is_negative => false)
|
529
|
-
reading.role_sequence = role_sequence
|
530
|
-
# REVISIT: The downcase here only needs to be the initial letter of each word, but be safe:
|
531
|
-
reading.text = extract_adjectives(x.text, role_sequence)
|
532
|
-
}
|
533
|
-
}
|
534
|
-
end
|
535
|
-
}
|
536
|
-
end
|
537
|
-
end
|
538
|
-
|
539
|
-
def extract_adjectives(text, role_sequence)
|
540
|
-
all_role_refs = role_sequence.all_role_ref.sort_by{|rr| rr.ordinal}
|
541
|
-
(0...all_role_refs.size).each{|i|
|
542
|
-
role_ref = all_role_refs[i]
|
543
|
-
role = role_ref.role
|
544
|
-
|
545
|
-
word = '\b[A-Za-z_][A-Za-z0-9_]+\b'
|
546
|
-
leading_adjectives_re = "#{word}-+(?: +#{word})*"
|
547
|
-
trailing_adjectives_re = "(?:#{word} +)*-+#{word}"
|
548
|
-
role_with_adjectives_re =
|
549
|
-
%r| ?(#{leading_adjectives_re})? *\{#{i}\} *(#{trailing_adjectives_re})? ?|
|
550
|
-
|
551
|
-
# A hyphenated pre-bound reading looks like this:
|
552
|
-
# <orm:Data>{0} has pre-- bound {1}</orm:Data>
|
553
|
-
|
554
|
-
text.gsub!(role_with_adjectives_re) {
|
555
|
-
# REVISIT: Don't want to strip all spaces here any more:
|
556
|
-
#puts "text=#{text.inspect}, la=#{$1.inspect}, ta=#{$2.inspect}" if $1 || $2
|
557
|
-
la = ($1||'').gsub(/\s+/,' ') # Strip duplicate spaces
|
558
|
-
ta = ($2||'').gsub(/\s+/,' ')
|
559
|
-
# When we have "aaa-bbb" we want "aaa bbb"
|
560
|
-
# When we have "aaa- bbb" we want "aaa bbb"
|
561
|
-
# When we have "aaa-- bbb" we want "aaa-bbb"
|
562
|
-
la = la.sub(/(-)?- ?/,'\1').strip
|
563
|
-
ta = ta.sub(/ ?(-)?-/,'\1').strip
|
564
|
-
#puts "Setting leading adj #{la.inspect} from #{text.inspect} for #{role_ref.role.object_type.name}" if la != ""
|
565
|
-
# REVISIT: Dunno what's up here, but removing the "if" test makes this chuck exceptions:
|
566
|
-
role_ref.leading_adjective = la if la != ""
|
567
|
-
role_ref.trailing_adjective = ta if ta != ""
|
568
|
-
|
569
|
-
#puts "Reading '#{text}' has role #{i} adjectives '#{la}' '#{ta}'" if la != "" || ta != ""
|
570
|
-
|
571
|
-
" {#{i}} "
|
572
|
-
}
|
573
|
-
}
|
574
|
-
text.sub!(/\s\s*/, ' ') # Compress extra spaces
|
575
|
-
text.strip!
|
576
|
-
text.downcase! # Check for reserved words and object type names *after* downcasing
|
577
|
-
elided = ''
|
578
|
-
text.gsub!(/( |[a-z]+(-[a-z]+)+|-?\b[A-Za-z_][A-Za-z0-9_]*\b-?|\{\d\})|./) do |w|
|
579
|
-
case w
|
580
|
-
when /[A-Za-z]/
|
581
|
-
if RESERVED_WORDS.include?(w)
|
582
|
-
$stderr.puts "Masking reserved word '#{w}' in reading '#{text}'"
|
583
|
-
next "_#{w}"
|
584
|
-
elsif @constellation.ObjectType[[[@vocabulary.name], w]]
|
585
|
-
$stderr.puts "Masking object type name '#{w}' in reading '#{text}'"
|
586
|
-
next "_#{w}"
|
587
|
-
elsif all_role_refs.detect{|rr| rr.role.role_name == w}
|
588
|
-
$stderr.puts "Masking role name '#{w}' in reading '#{text}'"
|
589
|
-
next "_#{w}"
|
590
|
-
end
|
591
|
-
next w
|
592
|
-
when /\{\d\}/
|
593
|
-
next w
|
594
|
-
when / /
|
595
|
-
next w
|
596
|
-
else
|
597
|
-
elided << w
|
598
|
-
next ''
|
599
|
-
end
|
600
|
-
end
|
601
|
-
$stderr.puts "Elided illegal characters '#{elided}' from reading #{text.inspect}" unless elided.empty?
|
602
|
-
text
|
603
|
-
end
|
604
|
-
|
605
|
-
def get_role_sequence(role_array)
|
606
|
-
# puts "Getting RoleSequence [#{role_array.map{|r| "#{r.object_type.name} (role #{r.concept.guid})" }*", "}]"
|
607
|
-
|
608
|
-
# Look for an existing RoleSequence
|
609
|
-
# REVISIT: This searches all role sequences. Perhaps we could narrow it down first instead?
|
610
|
-
role_sequence = @constellation.RoleSequence.values.detect{|c|
|
611
|
-
#puts "Checking RoleSequence [#{c.all_role_ref.map{|rr| rr.role.object_type.name}*", "}]"
|
612
|
-
role_array == c.all_role_ref.sort_by{|rr| rr.ordinal}.map{|rr| rr.role }
|
613
|
-
}
|
614
|
-
# puts "Found matching RoleSequence!" if role_sequence
|
615
|
-
return role_sequence if role_sequence
|
616
|
-
|
617
|
-
# Make a new RoleSequence:
|
618
|
-
role_sequence = @constellation.RoleSequence(:new) unless role_sequence
|
619
|
-
role_array.each_with_index do |r, i|
|
620
|
-
role_ref = @constellation.RoleRef(role_sequence, i)
|
621
|
-
role_ref.role = r
|
622
|
-
end
|
623
|
-
|
624
|
-
role_sequence
|
625
|
-
end
|
626
|
-
|
627
|
-
def map_roles(x_roles, why = nil)
|
628
|
-
role_array = x_roles.map do |x|
|
629
|
-
id = x['ref']
|
630
|
-
role = @by_id[id]
|
631
|
-
if (why && !role)
|
632
|
-
# We didn't make Implied objects, so some constraints are unconnectable
|
633
|
-
x_role = @x_by_id[id]
|
634
|
-
x_player = x_role.xpath('orm:RolePlayer')[0]
|
635
|
-
x_object = @x_by_id[x_player['ref']]
|
636
|
-
x_nests = nil
|
637
|
-
if (x_object.name.to_s == 'ObjectifiedType')
|
638
|
-
x_nests = x_object.xpath('orm:NestedPredicate')[0]
|
639
|
-
implied = x_nests['IsImplied']
|
640
|
-
# x_fact is the fact of which the role player is an objectification, not the fact this role belongs to
|
641
|
-
x_fact = @x_by_id[x_nests['ref']]
|
642
|
-
end
|
643
|
-
|
644
|
-
# This might have been a role of an ImpliedFact, which makes it safe to ignore.
|
645
|
-
next if 'ImpliedFact' == x_role.parent.parent.name
|
646
|
-
|
647
|
-
# Talk about why this wasn't found - this shouldn't happen.
|
648
|
-
if (!x_nests || !implied)
|
649
|
-
#puts "="*60
|
650
|
-
# We skip creating TypeInheritance implied fact types for ValueType inheritance
|
651
|
-
return nil if x_role.name = 'orm:SubtypeMetaRole' or x_role.name = 'orm:SupertypeMetaRole'
|
652
|
-
raise "Skipping #{why}, #{x_role.name} #{id} not found"
|
653
|
-
|
654
|
-
if (x_nests)
|
655
|
-
puts "Role is on #{implied ? "implied " : ""}objectification #{x_object}"
|
656
|
-
puts "which objectifies #{x_fact}"
|
657
|
-
end
|
658
|
-
puts x_object.to_s
|
659
|
-
end
|
660
|
-
end
|
661
|
-
role
|
662
|
-
end
|
663
|
-
return nil if role_array.include?(nil)
|
664
|
-
|
665
|
-
get_role_sequence(role_array)
|
666
|
-
end
|
667
|
-
|
668
|
-
def read_constraints
|
669
|
-
@constraints_by_rs = {}
|
670
|
-
|
671
|
-
read_mandatory_constraints
|
672
|
-
read_uniqueness_constraints
|
673
|
-
read_exclusion_constraints
|
674
|
-
read_subset_constraints
|
675
|
-
read_ring_constraints
|
676
|
-
read_equality_constraints
|
677
|
-
read_frequency_constraints
|
678
|
-
read_residual_mandatory_constraints
|
679
|
-
end
|
680
|
-
|
681
|
-
def read_mandatory_constraints
|
682
|
-
x_mandatory_constraints = @x_model.xpath("orm:Constraints/orm:MandatoryConstraint")
|
683
|
-
@mandatory_constraints_by_rs = {}
|
684
|
-
@mandatory_constraint_rs_by_id = {}
|
685
|
-
trace :orm, "Scanning mandatory constraints" do
|
686
|
-
x_mandatory_constraints.each{|x|
|
687
|
-
name = x["Name"] || ''
|
688
|
-
name = nil if name.size == 0
|
689
|
-
|
690
|
-
# As of Feb 2008, all NORMA ValueTypes have an implied mandatory constraint.
|
691
|
-
next if x.xpath("orm:ImpliedByObjectType").size > 0
|
692
|
-
|
693
|
-
x_roles = x.xpath("orm:RoleSequence/orm:Role")
|
694
|
-
role_sequence = map_roles(x_roles, "mandatory constraint #{name}")
|
695
|
-
next if !role_sequence
|
696
|
-
|
697
|
-
trace :orm, "New MC #{x['Name']} over #{role_sequence.describe}"
|
698
|
-
@mandatory_constraints_by_rs[role_sequence] = x
|
699
|
-
@mandatory_constraint_rs_by_id[x['id']] = role_sequence
|
700
|
-
}
|
701
|
-
end
|
702
|
-
end
|
703
|
-
|
704
|
-
# Mandatory constraints that didn't get merged with an exclusion constraint or a uniqueness constraint are simple mandatories
|
705
|
-
def read_residual_mandatory_constraints
|
706
|
-
trace :orm, "Processing non-absorbed mandatory constraints" do
|
707
|
-
@mandatory_constraints_by_rs.each { |role_sequence, x|
|
708
|
-
id = x['id']
|
709
|
-
# Create a simply-mandatory PresenceConstraint for each mandatory constraint
|
710
|
-
name = x["Name"] || ''
|
711
|
-
name = nil if name.size == 0
|
712
|
-
#puts "Residual Mandatory #{name}: #{role_sequence.to_s}"
|
713
|
-
|
714
|
-
if (players = role_sequence.all_role_ref.map{|rr| rr.role.object_type}).uniq.size > 1
|
715
|
-
join_over, = *ActiveFacts::Metamodel.plays_over(role_sequence.all_role_ref.map{|rr| rr.role}, :proximate)
|
716
|
-
raise "Mandatory join constraint #{name} has incompatible players #{players.map{|o| o.name}.inspect}" unless join_over
|
717
|
-
if players.detect{|p| p != join_over}
|
718
|
-
trace :query, "subtyping step simple mandatory constraint #{name} over #{join_over.name}"
|
719
|
-
players.each_with_index do |player, i|
|
720
|
-
next if player != join_over
|
721
|
-
# REVISIT: We don't need to make a subtyping step here (from join_over to player)
|
722
|
-
end
|
723
|
-
end
|
724
|
-
end
|
725
|
-
|
726
|
-
pc = @constellation.PresenceConstraint(id_of(x))
|
727
|
-
pc.vocabulary = @vocabulary
|
728
|
-
pc.name = name
|
729
|
-
pc.role_sequence = role_sequence
|
730
|
-
pc.is_mandatory = true
|
731
|
-
pc.min_frequency = 1
|
732
|
-
pc.max_frequency = nil
|
733
|
-
pc.is_preferred_identifier = false
|
734
|
-
|
735
|
-
(@constraints_by_rs[role_sequence] ||= []) << pc
|
736
|
-
@by_id[id] = pc
|
737
|
-
}
|
738
|
-
end
|
739
|
-
end
|
740
|
-
|
741
|
-
def read_uniqueness_constraints
|
742
|
-
x_uniqueness_constraints = @x_model.xpath("orm:Constraints/orm:UniquenessConstraint")
|
743
|
-
trace :orm, "Reading uniqueness constraints" do
|
744
|
-
x_uniqueness_constraints.each{|x|
|
745
|
-
name = x["Name"] || ''
|
746
|
-
name = nil if name.size == 0
|
747
|
-
uc_id = x["id"]
|
748
|
-
x_pi = x.xpath("orm:PreferredIdentifierFor")[0]
|
749
|
-
pi = x_pi ? @by_id[eref = x_pi['ref']] : nil
|
750
|
-
|
751
|
-
# Skip uniqueness constraints on implied object_types
|
752
|
-
next if x_pi && !pi
|
753
|
-
|
754
|
-
# Get the RoleSequence:
|
755
|
-
x_roles = x.xpath("orm:RoleSequence/orm:Role")
|
756
|
-
next if x_roles.size == 0
|
757
|
-
role_sequence = map_roles(x_roles, "uniqueness constraint #{name}")
|
758
|
-
next if !role_sequence
|
759
|
-
#puts "uc: #{role_sequence.all_role_ref.map{|rr|rr.role.fact_type.default_reading}*', '}"
|
760
|
-
|
761
|
-
# Check for a query
|
762
|
-
if (fact_types = role_sequence.all_role_ref.map{|rr| rr.role.fact_type}).uniq.size > 1
|
763
|
-
join_over, = *ActiveFacts::Metamodel.plays_over(role_sequence.all_role_ref.map{|rr| rr.role}, :counterpart)
|
764
|
-
|
765
|
-
players = role_sequence.all_role_ref.map{|rr| rr.role.object_type.name}.uniq
|
766
|
-
unless join_over
|
767
|
-
if x.xpath("orm:RoleSequence/orm:JoinRule").size > 0
|
768
|
-
$stderr.puts "Ignoring Join #{name} because its query is not understood"
|
769
|
-
next
|
770
|
-
end
|
771
|
-
raise "Uniqueness join constraint #{name} has incompatible players #{players.inspect}"
|
772
|
-
end
|
773
|
-
subtyping = players.size > 1 ? 'subtyping ' : ''
|
774
|
-
# REVISIT: Create the Query, the Variable for join_over, and steps from each role_ref to join_over
|
775
|
-
trace :query, "#{subtyping}join uniqueness constraint over #{join_over.name} in #{fact_types.map(&:default_reading)*', '}"
|
776
|
-
end
|
777
|
-
|
778
|
-
# There is an implicit uniqueness constraint when any object plays a unary. Skip it.
|
779
|
-
if (x_roles.size == 1 &&
|
780
|
-
(id = x_roles[0]['ref']) &&
|
781
|
-
(x_role = @x_by_id[id]) &&
|
782
|
-
(nodes = x_role.parent.elements).size == 2 &&
|
783
|
-
(sibling = nodes[1]) &&
|
784
|
-
(ib_id = sibling.elements[0]['ref']) &&
|
785
|
-
(ib = @x_by_id[ib_id]) &&
|
786
|
-
ib['IsImplicitBooleanValue'])
|
787
|
-
unary_identifier = true
|
788
|
-
end
|
789
|
-
|
790
|
-
mc_id = nil
|
791
|
-
|
792
|
-
if (mc = @mandatory_constraints_by_rs[role_sequence])
|
793
|
-
# Remove absorbed mandatory constraints, leaving residual ones.
|
794
|
-
trace :orm, "Absorbing MC #{mc['Name']} over #{role_sequence.describe}"
|
795
|
-
@mandatory_constraints_by_rs.delete(role_sequence)
|
796
|
-
mc_id = mc['id']
|
797
|
-
@mandatory_constraint_rs_by_id.delete(mc['id'])
|
798
|
-
elsif (fts = role_sequence.all_role_ref.map{|rr| rr.role.fact_type}.uniq).size == 1 and
|
799
|
-
fts[0].entity_type
|
800
|
-
# this uniqueness constraint is an internal UC on an objectified fact type,
|
801
|
-
# so the covered roles are always mandatory (wrt the OFT)
|
802
|
-
# That is, the phantom roles are mandatory, even if the visible roles are not.
|
803
|
-
mc = true
|
804
|
-
else
|
805
|
-
trace :orm, "No MC to absorb over #{role_sequence.describe}"
|
806
|
-
end
|
807
|
-
|
808
|
-
# A TypeInheritance fact type has a uniqueness constraint on each role.
|
809
|
-
# If this UC is on the supertype and identifies the subtype, it's preferred:
|
810
|
-
is_supertype_constraint =
|
811
|
-
(rr = role_sequence.all_role_ref.single) &&
|
812
|
-
(role = rr.role) &&
|
813
|
-
(fact_type = role.fact_type) &&
|
814
|
-
fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) &&
|
815
|
-
role.object_type == fact_type.supertype &&
|
816
|
-
fact_type.provides_identification
|
817
|
-
|
818
|
-
pc = @constellation.PresenceConstraint(id_of(x))
|
819
|
-
pc.vocabulary = @vocabulary
|
820
|
-
pc.name = name
|
821
|
-
pc.role_sequence = role_sequence
|
822
|
-
pc.is_mandatory = true if mc
|
823
|
-
pc.min_frequency = mc ? 1 : 0
|
824
|
-
pc.max_frequency = 1
|
825
|
-
pc.is_preferred_identifier = true if pi || unary_identifier || is_supertype_constraint
|
826
|
-
trace :orm, "#{name} covers #{role_sequence.describe} has min=#{pc.min_frequency}, max=1, preferred=#{pc.is_preferred_identifier.inspect}"
|
827
|
-
|
828
|
-
trace :orm, role_sequence.all_role_ref.to_a[0].role.fact_type.describe + " is subject to " + pc.describe if role_sequence.all_role_ref.all?{|r| r.role.fact_type.is_a? ActiveFacts::Metamodel::TypeInheritance }
|
829
|
-
|
830
|
-
(@constraints_by_rs[role_sequence] ||= []) << pc
|
831
|
-
@by_id[uc_id] = pc
|
832
|
-
@by_id[mc_id] = pc if mc_id
|
833
|
-
}
|
834
|
-
end
|
835
|
-
end
|
836
|
-
|
837
|
-
def subtype_step(query, ti)
|
838
|
-
subtype_node = query.all_variable.detect{|jn| jn.object_type == ti.subtype } ||
|
839
|
-
@constellation.Variable(query, query.all_variable.size, :object_type => ti.subtype)
|
840
|
-
supertype_node = query.all_variable.detect{|jn| jn.object_type == ti.supertype } ||
|
841
|
-
@constellation.Variable(query, query.all_variable.size, :object_type => ti.supertype)
|
842
|
-
step = @constellation.Step(:guid => :new, :fact_type => ti)
|
843
|
-
rs = @constellation.RoleSequence(:new)
|
844
|
-
@constellation.RoleRef(rs, 0, :role => ti.subtype_role)
|
845
|
-
sub_play = @constellation.Play(:step => step, :variable => subtype_node, :role => ti.subtype_role)
|
846
|
-
@constellation.RoleRef(rs, 1, :role => ti.supertype_role)
|
847
|
-
sup_play = @constellation.Play(:step => step, :variable => supertype_node, :role => ti.supertype_role, :is_input => true)
|
848
|
-
trace :query, "New subtyping step #{step.describe}"
|
849
|
-
step
|
850
|
-
end
|
851
|
-
|
852
|
-
# Make as many steps as it takes to get from subtype to supertype
|
853
|
-
def subtype_steps(query, subtype, supertype)
|
854
|
-
primary_ti = nil
|
855
|
-
other_ti = nil
|
856
|
-
subtype.all_type_inheritance_as_subtype.each do |ti|
|
857
|
-
next unless ti.supertype.supertypes_transitive.include? supertype
|
858
|
-
if ti.provides_identification
|
859
|
-
primary_ti ||= ti
|
860
|
-
else
|
861
|
-
other_ti ||= ti
|
862
|
-
end
|
863
|
-
end
|
864
|
-
ti = primary_ti || other_ti
|
865
|
-
# Make supertype steps first:
|
866
|
-
(ti.supertype == supertype ? [] : subtype_steps(query, ti.supertype, supertype)) +
|
867
|
-
[subtype_step(query, ti)]
|
868
|
-
end
|
869
|
-
|
870
|
-
# If there's a query, build it and return a new RoleSequence containing the projected roles:
|
871
|
-
def query_over_role_sequence(role_sequence, join_over, joined_roles, end_points)
|
872
|
-
# Skip if there's no query here (sequence join nor end-point subset join)
|
873
|
-
role_refs = role_sequence.all_role_ref_in_order
|
874
|
-
if !join_over and !role_refs.detect{|rr| rr.role.object_type != end_points[rr.ordinal]}
|
875
|
-
# No sequence join nor end_point join here
|
876
|
-
return role_sequence
|
877
|
-
end
|
878
|
-
|
879
|
-
# A RoleSequence for the actual query end-points
|
880
|
-
replacement_rs = @constellation.RoleSequence(:new)
|
881
|
-
|
882
|
-
query = @constellation.Query(:new)
|
883
|
-
variable = nil
|
884
|
-
query_role = nil
|
885
|
-
role_refs.zip(joined_roles||[]).each_with_index do |(role_ref, joined_role), i|
|
886
|
-
|
887
|
-
# Each role_ref is to an object joined via joined_role to variable (or which will be the variable)
|
888
|
-
|
889
|
-
# Create a variable for the actual end-point (supertype of the constrained roles)
|
890
|
-
end_point = end_points[i]
|
891
|
-
unless end_point
|
892
|
-
raise "In #{constraint_type} #{name}, there is a faulty or non-translated query"
|
893
|
-
end
|
894
|
-
trace :query, "Variable #{query.all_variable.size} is for #{end_point.name}"
|
895
|
-
end_node = @constellation.Variable(query, query.all_variable.size, :object_type => end_point)
|
896
|
-
|
897
|
-
# We're going to rewrite the constraint to constrain the supertype roles, but assume they're the same:
|
898
|
-
role_node = end_node
|
899
|
-
end_role = role_ref.role
|
900
|
-
|
901
|
-
# Create subtyping steps at the end-point, if needed:
|
902
|
-
projecting_play = nil
|
903
|
-
constrained_play = nil
|
904
|
-
if (subtype = role_ref.role.object_type) != end_point
|
905
|
-
trace :query, "Making subtyping steps from #{subtype.name} to #{end_point.name}" do
|
906
|
-
# There may be more than one supertyping level. Make the steps:
|
907
|
-
subtyping_steps = subtype_steps(query, subtype, end_point)
|
908
|
-
step = subtyping_steps[0]
|
909
|
-
#constrained_play = subtyping_steps[-1].all_play.detect{|p| p.role.object_type == end_point}
|
910
|
-
subtyping_steps.detect{|s| constrained_play = s.all_play.detect{|p| p.role.object_type == end_point}}
|
911
|
-
|
912
|
-
# Replace the constrained role and node with the supertype ones:
|
913
|
-
end_node = query.all_variable.detect{|jn| jn.object_type == end_point }
|
914
|
-
#projecting_play = step.all_play.detect{|p| p.role.object_type == subtype}
|
915
|
-
subtyping_steps.detect{|s| projecting_play = s.all_play.detect{|p| p.role.object_type == subtype}}
|
916
|
-
end_role = step.fact_type.all_role.detect{|r| r.object_type == end_point }
|
917
|
-
role_node = query.all_variable.detect{|jn| jn.object_type == role_ref.role.object_type }
|
918
|
-
end
|
919
|
-
end
|
920
|
-
|
921
|
-
if end_role.object_type != end_node.object_type
|
922
|
-
raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, end role mismatch"
|
923
|
-
end
|
924
|
-
|
925
|
-
if join_over
|
926
|
-
if !variable # Create the Variable when processing the first role
|
927
|
-
trace :query, "Variable #{query.all_variable.size} is over #{join_over.name}"
|
928
|
-
variable = @constellation.Variable(query, query.all_variable.size, :object_type => join_over)
|
929
|
-
end
|
930
|
-
trace :query, "Making step from #{end_point.name} to #{join_over.name}" do
|
931
|
-
rs = @constellation.RoleSequence(:new)
|
932
|
-
# Detect the fact type over which we're stepping (may involve objectification)
|
933
|
-
raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, object type mismatch" if role_ref.role.object_type != role_node.object_type
|
934
|
-
step = @constellation.Step(:guid => :new, :fact_type => joined_role.fact_type)
|
935
|
-
@constellation.RoleRef(rs, 0, :role => role_ref.role)
|
936
|
-
role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role, :is_input => true)
|
937
|
-
# Make the projected RoleRef:
|
938
|
-
rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => role_ref.role, :play => role_play)
|
939
|
-
raise "Internal error in #{constraint_type} #{name}: making illegal reference to variable, joined_role mismatch" if joined_role.object_type != variable.object_type
|
940
|
-
@constellation.RoleRef(rs, 1, :role => joined_role)
|
941
|
-
join_play = @constellation.Play(:step => step, :variable => variable, :role => joined_role)
|
942
|
-
trace :query, "New step #{step.describe}"
|
943
|
-
end
|
944
|
-
else
|
945
|
-
trace :query, "Need step for non-join_over role #{end_point.name} #{role_ref.describe} in #{role_ref.role.fact_type.default_reading}"
|
946
|
-
if (roles = role_ref.role.fact_type.all_role.to_a).size > 1
|
947
|
-
# Here we have an end join (step already created) but no sequence join
|
948
|
-
if variable
|
949
|
-
raise "Internal error in #{constraint_type} #{name}: making illegal step" if role_ref.role.object_type != role_node.object_type
|
950
|
-
step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
|
951
|
-
join_play = @constellation.Play(:step => step, :variable => variable, :role => query_role, :is_input => true)
|
952
|
-
role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role)
|
953
|
-
# Make the projected RoleRef:
|
954
|
-
rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => role_ref.role, :play => role_play)
|
955
|
-
roles -= [query_role, role_ref.role]
|
956
|
-
roles.each do |incidental_role|
|
957
|
-
jn = @constellation.Variable(query, query.all_variable.size, :object_type => incidental_role.object_type)
|
958
|
-
play = @constellation.Play(:step => step, :variable => jn, :role => incidental_role, :step => step)
|
959
|
-
end
|
960
|
-
else
|
961
|
-
|
962
|
-
if role_sequence.all_role_ref.size > 1
|
963
|
-
variable = role_node
|
964
|
-
query_role = role_ref.role
|
965
|
-
|
966
|
-
# Make the projected RoleRef:
|
967
|
-
rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => constrained_play.role, :play => constrained_play)
|
968
|
-
else
|
969
|
-
# We enter this fact type (requiring that a role be played) but don't exit it.
|
970
|
-
# I think this can only happen where we have subtyping steps, above.
|
971
|
-
|
972
|
-
# There's no query in this role sequence, so we'd drop off the bottom without doing the right things. Why?
|
973
|
-
# Without this case, Supervision.orm omits "that runs Company" from the exclusion constraint, and I'm not sure why.
|
974
|
-
# I think the "then" code causes it to drop out the bottom without making the step (which is otherwise made in every case, see CompanyDirectorEmployee for example)
|
975
|
-
step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
|
976
|
-
|
977
|
-
# p constrained_play.role.object_type.name
|
978
|
-
# p projecting_play.role.object_type.name
|
979
|
-
# debugger
|
980
|
-
|
981
|
-
role_play = @constellation.Play(:step => step, :variable => role_node, :role => role_ref.role, :is_input => true)
|
982
|
-
|
983
|
-
# Make the projected RoleRef:
|
984
|
-
rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => constrained_play.role, :play => constrained_play)
|
985
|
-
|
986
|
-
# role_ref.role.fact_type.all_role.each do |role|
|
987
|
-
# next if role == role_play.role
|
988
|
-
# next if role_sequence.all_role_ref.detect{|rr| rr.role == role}
|
989
|
-
# jn = @constellation.Variable(query, query.all_variable.size, :object_type => role.object_type)
|
990
|
-
# play = @constellation.Play(:step => step, :variable => jn, :role => role)
|
991
|
-
# if role == role_ref.role
|
992
|
-
# # Make the projected RoleRef:
|
993
|
-
# rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => role, :play => play)
|
994
|
-
# end
|
995
|
-
# end
|
996
|
-
|
997
|
-
end
|
998
|
-
end
|
999
|
-
else
|
1000
|
-
# Unary fact type, make a Step from and to the constrained_play
|
1001
|
-
step = @constellation.Step(:guid => :new, :fact_type => role_ref.role.fact_type)
|
1002
|
-
play = @constellation.Play(:step => step, :variable => constrained_play.variable, :role => role_ref.role, :is_input => true)
|
1003
|
-
# Make the projected RoleRef:
|
1004
|
-
rr = @constellation.RoleRef(replacement_rs, replacement_rs.all_role_ref.size, :role => role_ref.role, :play => play)
|
1005
|
-
end
|
1006
|
-
end
|
1007
|
-
end
|
1008
|
-
raise "hell" if replacement_rs.all_role_ref.size != role_sequence.all_role_ref.size
|
1009
|
-
|
1010
|
-
# Thoroughly check that this is a valid query
|
1011
|
-
query.validate
|
1012
|
-
trace :query, "Query has projected nodes #{replacement_rs.describe}"
|
1013
|
-
replacement_rs
|
1014
|
-
end
|
1015
|
-
|
1016
|
-
# Equality and subset join constraints involve two or more role sequences,
|
1017
|
-
# and the respective roles from each sequence must be compatible,
|
1018
|
-
# Compatibility might involve subtyping steps but not objectification steps
|
1019
|
-
# to the respective end-point (constrained object type).
|
1020
|
-
# Also, all roles in each sequence constitute a join over a single
|
1021
|
-
# object type, which might involve subtyping or objectification steps.
|
1022
|
-
#
|
1023
|
-
def make_queries(constraint_type, name, role_sequences)
|
1024
|
-
begin
|
1025
|
-
# Get the object types constrained for each position in the role sequences.
|
1026
|
-
# Supertyping steps may be needed to reach them.
|
1027
|
-
end_points = [] # An array of the common supertype for matching role_refs across the sequences
|
1028
|
-
end_step_needed = [] # An array of booleans indicating whether any role_sequence requires subtyping steps
|
1029
|
-
role_sequences[0].all_role_ref.size.times do |i|
|
1030
|
-
role_refs = role_sequences.map{|rs| rs.all_role_ref.detect{|rr| rr.ordinal == i}}
|
1031
|
-
if (fact_types = role_refs.map{|rr| rr.role.fact_type}).uniq.size == 1
|
1032
|
-
# $stderr.puts(role_sequences.map{|rs| rs.all_role_ref.map{|rr| rr.role.fact_type.describe(rr.role)}}.inspect)
|
1033
|
-
raise "In #{constraint_type} #{name} role sequence #{i}, there is a faulty join involving just 1 fact type: '#{fact_types[0].default_reading}'"
|
1034
|
-
end
|
1035
|
-
if (players = role_refs.map{|rr| rr.role.object_type}).uniq.size == 1
|
1036
|
-
# All roles in this set are played by the same object type
|
1037
|
-
end_point = players[0]
|
1038
|
-
end_step_needed[i] = false
|
1039
|
-
else
|
1040
|
-
# Can the players be joined using subtyping steps?
|
1041
|
-
common_supertypes = players[1..-1].
|
1042
|
-
inject(players[0].supertypes_transitive) do |remaining, player|
|
1043
|
-
remaining & player.supertypes_transitive
|
1044
|
-
end
|
1045
|
-
end_point = common_supertypes[0]
|
1046
|
-
|
1047
|
-
raise "constrained roles of #{constraint_type} #{name} are incompatible (#{players.map(&:name)*', '})" if common_supertypes.size == 0
|
1048
|
-
end_step_needed[i] = true
|
1049
|
-
end
|
1050
|
-
end_points[i] = end_point
|
1051
|
-
end
|
1052
|
-
|
1053
|
-
# For each role_sequence, find the object type over which the join is implied (nil if no join)
|
1054
|
-
sequence_join_over = []
|
1055
|
-
if role_sequences[0].all_role_ref.size > 1 # There are queries within each sequence.
|
1056
|
-
sequence_join_over = []
|
1057
|
-
sequence_joined_roles = []
|
1058
|
-
role_sequences.map do |rs|
|
1059
|
-
join_over, joined_roles = *ActiveFacts::Metamodel.plays_over(rs.all_role_ref.map{|rr| rr.role})
|
1060
|
-
sequence_join_over << join_over
|
1061
|
-
sequence_joined_roles << joined_roles
|
1062
|
-
end
|
1063
|
-
end
|
1064
|
-
|
1065
|
-
# If there are no queries, we can drop out here.
|
1066
|
-
if sequence_join_over.compact.empty? && !end_step_needed.detect{|e| e}
|
1067
|
-
return true
|
1068
|
-
end
|
1069
|
-
|
1070
|
-
trace :query, "#{constraint_type} join constraint #{name} over #{role_sequences.map{|rs|rs.describe}*', '}"
|
1071
|
-
|
1072
|
-
query = nil
|
1073
|
-
trace :query, "#{constraint_type} join constraint #{name} constrains #{
|
1074
|
-
end_points.zip(end_step_needed).map{|(p,j)| (p ? p.name : 'NULL')+(j ? ' & subtypes':'')}*', '
|
1075
|
-
}#{
|
1076
|
-
if role_sequences[0].all_role_ref.size > 1
|
1077
|
-
", joined over #{
|
1078
|
-
sequence_join_over.zip(sequence_joined_roles).map{|o, roles|
|
1079
|
-
(o ? o.name : '(none)') +
|
1080
|
-
(roles ? " to (#{roles.map{|role| role ? role.fact_type.default_reading : 'null'}*','})" : '')
|
1081
|
-
}*', '}"
|
1082
|
-
else
|
1083
|
-
''
|
1084
|
-
end
|
1085
|
-
}" do
|
1086
|
-
|
1087
|
-
# There may be one query per role sequence:
|
1088
|
-
role_sequences.zip(sequence_join_over||[], sequence_joined_roles||[]).map do |role_sequence, join_over, joined_roles|
|
1089
|
-
position = role_sequences.index(role_sequence)
|
1090
|
-
replacement_rs = query_over_role_sequence(role_sequence, join_over, joined_roles, end_points)
|
1091
|
-
if role_sequence != replacement_rs
|
1092
|
-
role_sequences[position] = replacement_rs
|
1093
|
-
end
|
1094
|
-
end
|
1095
|
-
|
1096
|
-
return true
|
1097
|
-
end
|
1098
|
-
rescue => e
|
1099
|
-
debugger if trace :debug
|
1100
|
-
$stderr.puts "// #{e.to_s}: #{e.backtrace[0]}"
|
1101
|
-
return false
|
1102
|
-
end
|
1103
|
-
|
1104
|
-
end
|
1105
|
-
|
1106
|
-
def read_exclusion_constraints
|
1107
|
-
x_exclusion_constraints = @x_model.xpath("orm:Constraints/orm:ExclusionConstraint")
|
1108
|
-
trace :orm, "Reading #{x_exclusion_constraints.size} exclusion constraints" do
|
1109
|
-
x_exclusion_constraints.each{|x|
|
1110
|
-
id = x['id']
|
1111
|
-
name = x["Name"] || ''
|
1112
|
-
name = nil if name.size == 0
|
1113
|
-
x_mandatory = (m = x.xpath("orm:ExclusiveOrMandatoryConstraint")[0]) &&
|
1114
|
-
@x_by_id[mc_id = m['ref']]
|
1115
|
-
role_sequences =
|
1116
|
-
x.xpath("orm:RoleSequences/orm:RoleSequence").map{|x_rs|
|
1117
|
-
x_role_refs = x_rs.xpath("orm:Role")
|
1118
|
-
map_roles(
|
1119
|
-
x_role_refs , # .map{|xr| @x_by_id[xr['ref']] },
|
1120
|
-
"exclusion constraint #{name}"
|
1121
|
-
)
|
1122
|
-
}
|
1123
|
-
if x_mandatory
|
1124
|
-
# Remove absorbed mandatory constraints, leaving residual ones.
|
1125
|
-
mc_rs = @mandatory_constraint_rs_by_id[mc_id]
|
1126
|
-
@mandatory_constraint_rs_by_id.delete(mc_id)
|
1127
|
-
@mandatory_constraints_by_rs.delete(mc_rs)
|
1128
|
-
end
|
1129
|
-
|
1130
|
-
if role_sequences.compact.size != role_sequences.size # Role sequence missing; includes a derived fact type role
|
1131
|
-
trace :orm, "skipped exclusion constraint #{id}, missing role sequence"
|
1132
|
-
next
|
1133
|
-
end
|
1134
|
-
|
1135
|
-
unless make_queries('exclusion', name+(x_mandatory ? '/'+x_mandatory['Name'] : ''), role_sequences)
|
1136
|
-
trace :orm, "skipped exclusion constraint #{id}, can't make_queries"
|
1137
|
-
next
|
1138
|
-
end
|
1139
|
-
|
1140
|
-
ec = @constellation.SetExclusionConstraint(id_of(x))
|
1141
|
-
ec.vocabulary = @vocabulary
|
1142
|
-
ec.name = name
|
1143
|
-
# ec.enforcement =
|
1144
|
-
role_sequences.each_with_index do |rs, i|
|
1145
|
-
@constellation.SetComparisonRoles(ec, i, :role_sequence => rs)
|
1146
|
-
end
|
1147
|
-
ec.is_mandatory = true if x_mandatory
|
1148
|
-
@by_id[id] = ec
|
1149
|
-
@by_id[mc_id] = ec if mc_id
|
1150
|
-
}
|
1151
|
-
end
|
1152
|
-
end
|
1153
|
-
|
1154
|
-
def read_equality_constraints
|
1155
|
-
x_equality_constraints = @x_model.xpath("orm:Constraints/orm:EqualityConstraint")
|
1156
|
-
trace :orm, "Reading equality constraints" do
|
1157
|
-
x_equality_constraints.each{|x|
|
1158
|
-
id = x['id']
|
1159
|
-
name = x["Name"] || ''
|
1160
|
-
name = nil if name.size == 0
|
1161
|
-
role_sequences =
|
1162
|
-
x.xpath("orm:RoleSequences/orm:RoleSequence").map{|x_rs|
|
1163
|
-
x_role_refs = x_rs.xpath("orm:Role")
|
1164
|
-
map_roles(
|
1165
|
-
x_role_refs , # .map{|xr| @x_by_id[xr['ref']] },
|
1166
|
-
"equality constraint #{name}"
|
1167
|
-
)
|
1168
|
-
}
|
1169
|
-
|
1170
|
-
# Role sequence missing; includes a derived fact type role
|
1171
|
-
next if role_sequences.compact.size != role_sequences.size
|
1172
|
-
|
1173
|
-
next unless make_queries('equality', name, role_sequences)
|
1174
|
-
|
1175
|
-
ec = @constellation.SetEqualityConstraint(id_of(x))
|
1176
|
-
ec.vocabulary = @vocabulary
|
1177
|
-
ec.name = name
|
1178
|
-
# ec.enforcement =
|
1179
|
-
role_sequences.each_with_index do |rs, i|
|
1180
|
-
@constellation.SetComparisonRoles(ec, i, :role_sequence => rs)
|
1181
|
-
end
|
1182
|
-
@by_id[id] = ec
|
1183
|
-
}
|
1184
|
-
end
|
1185
|
-
end
|
1186
|
-
|
1187
|
-
def read_subset_constraints
|
1188
|
-
x_subset_constraints = @x_model.xpath("orm:Constraints/orm:SubsetConstraint")
|
1189
|
-
trace :orm, "Reading subset constraints" do
|
1190
|
-
x_subset_constraints.each{|x|
|
1191
|
-
id = x['id']
|
1192
|
-
name = x["Name"] || ''
|
1193
|
-
name = nil if name.size == 0
|
1194
|
-
role_sequences =
|
1195
|
-
x.xpath("orm:RoleSequences/orm:RoleSequence").map{|x_rs|
|
1196
|
-
x_role_refs = x_rs.xpath("orm:Role")
|
1197
|
-
map_roles(
|
1198
|
-
x_role_refs , # .map{|xr| @x_by_id[xr['ref']] },
|
1199
|
-
"equality constraint #{name}"
|
1200
|
-
)
|
1201
|
-
}
|
1202
|
-
next if role_sequences.compact.size != role_sequences.size # Role sequence missing; includes a derived fact type role
|
1203
|
-
next unless make_queries('subset', name, role_sequences)
|
1204
|
-
|
1205
|
-
ec = @constellation.SubsetConstraint(id_of(x))
|
1206
|
-
ec.vocabulary = @vocabulary
|
1207
|
-
ec.name = name
|
1208
|
-
# ec.enforcement =
|
1209
|
-
ec.subset_role_sequence = role_sequences[0]
|
1210
|
-
ec.superset_role_sequence = role_sequences[1]
|
1211
|
-
@by_id[id] = ec
|
1212
|
-
}
|
1213
|
-
end
|
1214
|
-
end
|
1215
|
-
|
1216
|
-
def read_ring_constraints
|
1217
|
-
x_ring_constraints = @x_model.xpath("orm:Constraints/orm:RingConstraint")
|
1218
|
-
trace :orm, "Reading ring constraints" do
|
1219
|
-
x_ring_constraints.each{|x|
|
1220
|
-
id = x['id']
|
1221
|
-
name = x["Name"] || ''
|
1222
|
-
name = nil if name.size == 0
|
1223
|
-
ring_type = x["Type"]
|
1224
|
-
|
1225
|
-
from, to = *x.xpath("orm:RoleSequence/orm:Role").
|
1226
|
-
map do |xr|
|
1227
|
-
@by_id[xr['ref']]
|
1228
|
-
end
|
1229
|
-
next unless from && to # Roles missing; covers a derived fact type
|
1230
|
-
if from.object_type != to.object_type
|
1231
|
-
join_over, = *ActiveFacts::Metamodel.plays_over([from, to], :counterpart)
|
1232
|
-
raise "Ring constraint has incompatible players #{from.object_type.name}, #{to.object_type.name}" if !join_over
|
1233
|
-
trace :query, "join ring constraint over #{join_over.name}"
|
1234
|
-
end
|
1235
|
-
rc = @constellation.RingConstraint(id_of(x))
|
1236
|
-
rc.vocabulary = @vocabulary
|
1237
|
-
rc.name = name
|
1238
|
-
# rc.enforcement =
|
1239
|
-
rc.role = from
|
1240
|
-
rc.other_role = to
|
1241
|
-
rc.ring_type = ring_type.gsub(/PurelyReflexive/,'Reflexive')
|
1242
|
-
@by_id[id] = rc
|
1243
|
-
}
|
1244
|
-
end
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
def read_frequency_constraints
|
1248
|
-
x_frequency_constraints = @x_model.xpath("orm:Constraints/orm:FrequencyConstraint")
|
1249
|
-
trace :orm, "Reading frequency constraints" do
|
1250
|
-
x_frequency_constraints.each do |x_frequency_constraint|
|
1251
|
-
id = x_frequency_constraint['id']
|
1252
|
-
min_frequency = x_frequency_constraint["MinFrequency"].to_i
|
1253
|
-
min_frequency = nil if min_frequency == 0
|
1254
|
-
max_frequency = x_frequency_constraint["MaxFrequency"].to_i
|
1255
|
-
max_frequency = nil if max_frequency == 0
|
1256
|
-
x_roles = x_frequency_constraint.xpath("orm:RoleSequence/orm:Role")
|
1257
|
-
role = @by_id[x_roles[0]["ref"]]
|
1258
|
-
role_sequence = @constellation.RoleSequence(:new)
|
1259
|
-
role_ref = @constellation.RoleRef(role_sequence, 0, :role => role)
|
1260
|
-
next unless role # Role missing; belongs to a derived fact type
|
1261
|
-
trace :orm, "FrequencyConstraint(min #{min_frequency.inspect} max #{max_frequency.inspect} over #{role.fact_type.describe(role)} #{id} role ref = #{x_roles[0]["ref"]}"
|
1262
|
-
@by_id[id] = @constellation.PresenceConstraint(
|
1263
|
-
id_of(x_frequency_constraint),
|
1264
|
-
:vocabulary => @vocabulary,
|
1265
|
-
:name => name = x_frequency_constraint["Name"] || '',
|
1266
|
-
:role_sequence => role_sequence,
|
1267
|
-
:is_mandatory => false,
|
1268
|
-
:min_frequency => min_frequency,
|
1269
|
-
:max_frequency => max_frequency,
|
1270
|
-
:is_preferred_identifier => false
|
1271
|
-
)
|
1272
|
-
end
|
1273
|
-
end
|
1274
|
-
end
|
1275
|
-
|
1276
|
-
def read_instances
|
1277
|
-
trace :orm, "Reading sample data" do
|
1278
|
-
population = @constellation.Population(@vocabulary, "sample", :concept => :new)
|
1279
|
-
|
1280
|
-
# Value instances first, then entities then facts:
|
1281
|
-
|
1282
|
-
x_values = @x_model.xpath("orm:Objects/orm:ValueType/orm:Instances/orm:ValueTypeInstance/orm:Value")
|
1283
|
-
#pp x_values.map{|v| [ v.parent['id'], v.text ] }
|
1284
|
-
trace :orm, "Reading sample values" do
|
1285
|
-
x_values.each{|v|
|
1286
|
-
id = v.parent['id']
|
1287
|
-
# Get details of the ValueType:
|
1288
|
-
xvt = v.parent.parent.parent
|
1289
|
-
vt_id = xvt['id']
|
1290
|
-
vtname = xvt['Name'] || ''
|
1291
|
-
#vtname.gsub!(/\s/,'')
|
1292
|
-
vtname = nil if vtname.size == 0
|
1293
|
-
vt = @by_id[vt_id]
|
1294
|
-
throw "ValueType #{vtname} not found" unless vt
|
1295
|
-
|
1296
|
-
i = @constellation.Instance(id_of(v.parent), :population => population, :object_type => vt, :value => [v.text, is_literal_string(v.text), nil])
|
1297
|
-
@by_id[id] = i
|
1298
|
-
# show_xmlobj(v)
|
1299
|
-
}
|
1300
|
-
end
|
1301
|
-
|
1302
|
-
# Use the "id" attribute of EntityTypeInstance
|
1303
|
-
x_entities = @x_model.xpath("orm:Objects/orm:EntityType/orm:Instances/orm:EntityTypeInstance")
|
1304
|
-
#pp x_entities
|
1305
|
-
# x_entities.each{|v| show_xmlobj(v) }
|
1306
|
-
last_et_id = nil
|
1307
|
-
last_et = nil
|
1308
|
-
et = nil
|
1309
|
-
trace :orm, "Reading sample entities" do
|
1310
|
-
x_entities.each{|v|
|
1311
|
-
id = v['id']
|
1312
|
-
|
1313
|
-
# Get details of the EntityType:
|
1314
|
-
xet = v.parent.parent
|
1315
|
-
et_id = xet['id']
|
1316
|
-
if (et_id != last_et_id)
|
1317
|
-
etname = xet['Name'] || ''
|
1318
|
-
#etname.gsub!(/\s/,'')
|
1319
|
-
etname = nil if etname.size == 0
|
1320
|
-
last_et = et = @by_id[et_id]
|
1321
|
-
last_et_id = et_id
|
1322
|
-
throw "EntityType #{etname} not found" unless et
|
1323
|
-
end
|
1324
|
-
|
1325
|
-
instance = @constellation.Instance(id_of(v), :population => population, :object_type => et, :value => nil)
|
1326
|
-
@by_id[id] = instance
|
1327
|
-
trace :orm, "Made new EntityType #{etname}"
|
1328
|
-
}
|
1329
|
-
end
|
1330
|
-
|
1331
|
-
# The EntityType instances have implicit facts for the PI facts.
|
1332
|
-
# These are in the ORM file, but instead of using those,
|
1333
|
-
# We create implicit PI facts after all the instances.
|
1334
|
-
entity_count = 0
|
1335
|
-
pi_fact_count = 0
|
1336
|
-
trace :orm, "Creating identifying facts for entities" do
|
1337
|
-
x_entities.each do |v|
|
1338
|
-
id = v['id']
|
1339
|
-
instance = @by_id[id]
|
1340
|
-
et = @by_id[v.parent.parent['id']]
|
1341
|
-
next unless (preferred_id = et.preferred_identifier)
|
1342
|
-
|
1343
|
-
trace :orm, "Create identifying facts using #{preferred_id}"
|
1344
|
-
|
1345
|
-
# Collate the referenced objects by role:
|
1346
|
-
role_instances = v.elements[0].elements.inject({}){|h, v|
|
1347
|
-
etri = @x_by_id[v['ref']]
|
1348
|
-
x_role_id = etri.parent.parent['id']
|
1349
|
-
role = @by_id[x_role_id]
|
1350
|
-
object = @by_id[object_id = etri['ref']]
|
1351
|
-
h[role] = object
|
1352
|
-
h
|
1353
|
-
}
|
1354
|
-
|
1355
|
-
# Create an instance of each required fact type, for compound identification:
|
1356
|
-
identifying_fact_types =
|
1357
|
-
preferred_id.role_sequence.all_role_ref.map { |rr| rr.role.fact_type }.uniq
|
1358
|
-
identifying_fact_types.
|
1359
|
-
each do |ft|
|
1360
|
-
trace :orm, "For FactType #{ft}" do
|
1361
|
-
fact = @constellation.Fact(:new, :population => population, :fact_type => ft)
|
1362
|
-
fact_roles = ft.all_role.map do |role|
|
1363
|
-
if role.object_type == et
|
1364
|
-
object = instance
|
1365
|
-
else
|
1366
|
-
object = role_instances[role]
|
1367
|
-
trace :orm, "instance for role #{role} is #{object}"
|
1368
|
-
end
|
1369
|
-
@constellation.RoleValue(:instance => object, :population => population, :fact => fact, :role => role)
|
1370
|
-
end
|
1371
|
-
end
|
1372
|
-
pi_fact_count += 1
|
1373
|
-
end
|
1374
|
-
|
1375
|
-
entity_count += 1
|
1376
|
-
end
|
1377
|
-
end
|
1378
|
-
trace :orm, "Created #{pi_fact_count} facts to identify #{entity_count} entities"
|
1379
|
-
|
1380
|
-
# Use the "ref" attribute of FactTypeRoleInstance:
|
1381
|
-
x_fact_roles = @x_model.xpath("orm:Facts/orm:Fact/orm:Instances/orm:FactTypeInstance/orm:RoleInstances/orm:FactTypeRoleInstance")
|
1382
|
-
|
1383
|
-
# REVISIT: This presumably duplicates the identifying fact instances for the above entities. Hmmm.
|
1384
|
-
last_id = nil
|
1385
|
-
fact = nil
|
1386
|
-
fact_roles = []
|
1387
|
-
trace :orm, "Reading sample facts" do
|
1388
|
-
x_fact_roles.each do |v|
|
1389
|
-
fact_type_id = v.parent.parent.parent.parent['id']
|
1390
|
-
id = v.parent.parent['id']
|
1391
|
-
fact_type = @by_id[fact_type_id]
|
1392
|
-
throw "Fact type #{fact_type_id} not found" unless fact_type
|
1393
|
-
|
1394
|
-
# Create initial and subsequent Fact objects:
|
1395
|
-
fact = @constellation.Fact(:new, :population => population, :fact_type => fact_type) unless fact && last_id == id
|
1396
|
-
last_id = id
|
1397
|
-
|
1398
|
-
# REVISIT: This doesn't handle instances of objectified fact types (where a RoleValue.instance objectifies Fact)
|
1399
|
-
|
1400
|
-
x_role_instance = @x_by_id[v['ref']]
|
1401
|
-
x_role_id = x_role_instance.parent.parent['id']
|
1402
|
-
role = @by_id[x_role_id]
|
1403
|
-
throw "Role not found for instance #{x_role_id}" unless role
|
1404
|
-
instance_id = x_role_instance['ref']
|
1405
|
-
instance = @by_id[instance_id]
|
1406
|
-
throw "Instance not found for FactRole #{instance_id}" unless instance
|
1407
|
-
@constellation.RoleValue(:instance => instance, :population => population, :fact => fact, :role => role)
|
1408
|
-
end
|
1409
|
-
end
|
1410
|
-
|
1411
|
-
end
|
1412
|
-
end
|
1413
|
-
|
1414
|
-
def read_diagrams
|
1415
|
-
x_diagrams = @document.root.xpath("ormDiagram:ORMDiagram")
|
1416
|
-
trace :orm, "Reading diagrams" do
|
1417
|
-
x_diagrams.each do |x|
|
1418
|
-
name = (x["Name"] || '').strip
|
1419
|
-
diagram = @constellation.ORMDiagram(@vocabulary, name)
|
1420
|
-
trace :diagram, "Starting to read diagram #{name}"
|
1421
|
-
shapes = x.xpath("ormDiagram:Shapes/*")
|
1422
|
-
trace :orm, "Reading shapes" do
|
1423
|
-
shapes.map do |x_shape|
|
1424
|
-
x_subject = x_shape.xpath("ormDiagram:Subject")[0]
|
1425
|
-
subject = @by_id[x_subject["ref"]]
|
1426
|
-
is_expanded = v = x_shape['IsExpanded'] and v == 'true'
|
1427
|
-
bounds = x_shape['AbsoluteBounds']
|
1428
|
-
case shape_type = x_shape.name
|
1429
|
-
when 'FactTypeShape'
|
1430
|
-
if subject
|
1431
|
-
read_fact_type_shape diagram, x_shape, is_expanded, bounds, subject
|
1432
|
-
# else REVISIT: probably a derived fact type
|
1433
|
-
end
|
1434
|
-
when 'ExternalConstraintShape', 'FrequencyConstraintShape'
|
1435
|
-
# REVISIT: The offset might depend on the constraint type. This is right for subset and other round ones.
|
1436
|
-
location = convert_location(bounds, Gravity::C)
|
1437
|
-
shape = @constellation.ConstraintShape(
|
1438
|
-
:guid => id_of(x_shape), :orm_diagram => diagram, :location => location, :is_expanded => is_expanded,
|
1439
|
-
:constraint => subject
|
1440
|
-
)
|
1441
|
-
when 'RingConstraintShape'
|
1442
|
-
# REVISIT: The offset might depend on the ring constraint type. This is right for basic round ones.
|
1443
|
-
location = convert_location(bounds, Gravity::C)
|
1444
|
-
shape = @constellation.RingConstraintShape(
|
1445
|
-
:guid => id_of(x_shape), :orm_diagram => diagram, :location => location, :is_expanded => is_expanded,
|
1446
|
-
:constraint => subject
|
1447
|
-
)
|
1448
|
-
shape.fact_type_shape = subject.role.fact_type.all_fact_type_shape.to_a[0]
|
1449
|
-
when 'ModelNoteShape'
|
1450
|
-
# REVISIT: Add model notes
|
1451
|
-
when 'ObjectTypeShape'
|
1452
|
-
location = convert_location(bounds, Gravity::C)
|
1453
|
-
# $stderr.puts "#{subject.name}: bounds=#{bounds} -> location = (#{location.x}, #{location.y})"
|
1454
|
-
shape = @constellation.ObjectTypeShape(
|
1455
|
-
:guid => id_of(x_shape), :orm_diagram => diagram, :location => location, :is_expanded => is_expanded,
|
1456
|
-
:object_type => subject,
|
1457
|
-
:location => location
|
1458
|
-
)
|
1459
|
-
else
|
1460
|
-
raise "Unknown shape #{x_shape.name}"
|
1461
|
-
end
|
1462
|
-
end
|
1463
|
-
end
|
1464
|
-
end
|
1465
|
-
end
|
1466
|
-
end
|
1467
|
-
|
1468
|
-
def read_fact_type_shape diagram, x_shape, is_expanded, bounds, fact_type
|
1469
|
-
display_role_names_setting = v = x_shape["DisplayRoleNames"] and
|
1470
|
-
case v
|
1471
|
-
when 'Off'; 'false'
|
1472
|
-
when 'On'; 'true'
|
1473
|
-
else nil
|
1474
|
-
end
|
1475
|
-
rotation_setting = v = x_shape['DisplayOrientation'] and
|
1476
|
-
case v
|
1477
|
-
when 'VerticalRotatedLeft'; 'left'
|
1478
|
-
when 'VerticalRotatedRight'; 'right'
|
1479
|
-
else nil
|
1480
|
-
end
|
1481
|
-
|
1482
|
-
# Location of a fact type is the centre of the row of role boxes
|
1483
|
-
offs_x = 11
|
1484
|
-
offs_y = -12
|
1485
|
-
if fact_type.entity_type
|
1486
|
-
offs_x -= 12
|
1487
|
-
offs_y -= 9 if !fact_type.entity_type.concept.implication_rule # .implication_rule_name == 'objectification'
|
1488
|
-
end
|
1489
|
-
|
1490
|
-
location = convert_location(bounds, Gravity::S, offs_x, offs_y)
|
1491
|
-
|
1492
|
-
# $stderr.puts "#{fact_type.describe}: bounds=#{bounds} -> location = (#{location.x}, #{location.y})"
|
1493
|
-
|
1494
|
-
trace :orm, "REVISIT: Can't place rotated fact type correctly on diagram yet" if rotation_setting
|
1495
|
-
|
1496
|
-
trace :orm, "fact type at #{location.x},#{location.y} has display_role_names_setting=#{display_role_names_setting.inspect}, rotation_setting=#{rotation_setting.inspect}"
|
1497
|
-
shape = @constellation.FactTypeShape(
|
1498
|
-
:guid => id_of(x_shape),
|
1499
|
-
:orm_diagram => diagram,
|
1500
|
-
:location => location,
|
1501
|
-
:is_expanded => is_expanded,
|
1502
|
-
:display_role_names_setting => display_role_names_setting,
|
1503
|
-
:rotation_setting => rotation_setting,
|
1504
|
-
:fact_type => fact_type
|
1505
|
-
)
|
1506
|
-
# Create RoleDisplay objects if necessary
|
1507
|
-
x_role_display = x_shape.xpath("ormDiagram:RoleDisplayOrder/ormDiagram:Role")
|
1508
|
-
# print "Fact type '#{fact_type.preferred_reading.expand}' (#{fact_type.all_role.map{|r|r.object_type.name}*' '})"
|
1509
|
-
if x_role_display.size > 0
|
1510
|
-
trace :orm, " has roleDisplay (#{x_role_display.map{|rd| @by_id[rd['ref']].object_type.name}*','})'"
|
1511
|
-
x_role_display.each_with_index do |rd, ordinal|
|
1512
|
-
role_display = @constellation.RoleDisplay(shape, ordinal, :role => @by_id[rd['ref']])
|
1513
|
-
end
|
1514
|
-
else
|
1515
|
-
# Decide whether to create all RoleDisplay objects for this fact type, which is in role order
|
1516
|
-
# Omitting this here might lead to incomplete RoleDisplay sequences,
|
1517
|
-
# because each RoleNameShape or ValueConstraintShape creates just one.
|
1518
|
-
trace :orm, " has no roleDisplay"
|
1519
|
-
end
|
1520
|
-
|
1521
|
-
relative_shapes = x_shape.xpath('ormDiagram:RelativeShapes/*')
|
1522
|
-
relative_shapes.each do |xr_shape|
|
1523
|
-
location = convert_location(xr_shape['AbsoluteBounds'])
|
1524
|
-
case xr_shape.name
|
1525
|
-
when 'ObjectifiedFactTypeNameShape'
|
1526
|
-
@constellation.ObjectifiedFactTypeNameShape(:guid => id_of(xr_shape), :fact_type_shape => shape, :orm_diagram => diagram, :location => location, :is_expanded => false)
|
1527
|
-
when 'ReadingShape'
|
1528
|
-
begin
|
1529
|
-
@constellation.ReadingShape(:guid => id_of(xr_shape), :fact_type_shape => shape, :orm_diagram => diagram, :location => location, :is_expanded => false, :reading => fact_type.preferred_reading)
|
1530
|
-
rescue =>e
|
1531
|
-
debugger
|
1532
|
-
@constellation.ReadingShape(:guid => id_of(xr_shape), :fact_type_shape => shape, :orm_diagram => diagram, :location => location, :is_expanded => false, :reading => fact_type.preferred_reading)
|
1533
|
-
end
|
1534
|
-
when 'RoleNameShape'
|
1535
|
-
role = @by_id[xr_shape.xpath("ormDiagram:Subject")[0]['ref']]
|
1536
|
-
role_display = role_display_for_role(shape, x_role_display, role)
|
1537
|
-
trace :orm, "Fact type '#{fact_type.preferred_reading.expand}' has #{xr_shape.name}"
|
1538
|
-
@constellation.RoleNameShape(
|
1539
|
-
:guid => id_of(xr_shape), :orm_diagram => diagram, :location => location, :is_expanded => false,
|
1540
|
-
:role_display => role_display
|
1541
|
-
)
|
1542
|
-
when 'ValueConstraintShape'
|
1543
|
-
vc_subject_id = xr_shape.xpath("ormDiagram:Subject")[0]['ref']
|
1544
|
-
constraint = @by_id[vc_subject_id]
|
1545
|
-
trace :orm, "Fact type '#{fact_type.preferred_reading.expand}' has #{xr_shape.name} for #{constraint.inspect}"
|
1546
|
-
|
1547
|
-
role_display = role_display_for_role(shape, x_role_display, constraint.role)
|
1548
|
-
trace :orm, "ValueConstraintShape is on #{role_display.ordinal}'th role (by #{x_role_display.size > 0 ? 'role_display' : 'fact roles'})"
|
1549
|
-
@constellation.ValueConstraintShape(
|
1550
|
-
:guid => id_of(xr_shape), :orm_diagram => diagram, :location => location, :is_expanded => false,
|
1551
|
-
:constraint => constraint,
|
1552
|
-
:object_type_shape => nil, # This constraint is relative to a Fact Type, so must be on a role
|
1553
|
-
:role_display => role_display
|
1554
|
-
)
|
1555
|
-
else raise "Unknown relative shape #{xr_shape.name}"
|
1556
|
-
end
|
1557
|
-
end
|
1558
|
-
end
|
1559
|
-
|
1560
|
-
# Find or create the RoleDisplay for this role in this fact_type_shape, given (possibly empty) x_role_display nodes:
|
1561
|
-
def role_display_for_role(fact_type_shape, x_role_display, role)
|
1562
|
-
if x_role_display.size == 0
|
1563
|
-
# There's no x_role_display, which means the roles are in displayed
|
1564
|
-
# the same order as in the fact type. However, we need a RoleDisplay
|
1565
|
-
# to attach a ReadingShape or ValueConstraintShape, so make them all.
|
1566
|
-
fact_type_shape.fact_type.all_role.each{|r| @constellation.RoleDisplay(fact_type_shape, r.ordinal, :role => r) }
|
1567
|
-
role_ordinal = fact_type_shape.fact_type.all_role_in_order.index(role)
|
1568
|
-
else
|
1569
|
-
role_ordinal = x_role_display.map{|rd| @by_id[rd['ref']]}.index(role)
|
1570
|
-
end
|
1571
|
-
role_display = @constellation.RoleDisplay(fact_type_shape, role_ordinal, :role => role)
|
1572
|
-
end
|
1573
|
-
|
1574
|
-
DIAGRAM_SCALE = 96*1.5
|
1575
|
-
def convert_location(bounds, gravity = Gravity::C, xoffs = 0, yoffs = 0)
|
1576
|
-
return nil unless bounds
|
1577
|
-
# Bounds is top, left, width, height in inches
|
1578
|
-
bf = bounds.split(/, /).map{|b|b.to_f}
|
1579
|
-
sizefrax = [
|
1580
|
-
[0, 0], [1, 0], [2, 0],
|
1581
|
-
[0, 1], [1, 1], [2, 2],
|
1582
|
-
[0, 2], [1, 2], [2, 2],
|
1583
|
-
]
|
1584
|
-
|
1585
|
-
x = (DIAGRAM_SCALE * (bf[0]+bf[2]*sizefrax[gravity][0]/2)).round + xoffs
|
1586
|
-
y = (DIAGRAM_SCALE * (bf[1]+bf[3]*sizefrax[gravity][1]/2)).round + yoffs
|
1587
|
-
@constellation.Location(x, y)
|
1588
|
-
end
|
1589
|
-
|
1590
|
-
# Detect numeric data and denote it as a string:
|
1591
|
-
def is_literal_string(value)
|
1592
|
-
value =~ /[^ \d.]/
|
1593
|
-
end
|
1594
|
-
|
1595
|
-
def read_rest
|
1596
|
-
puts "Reading Implied Facts (not yet)"
|
1597
|
-
=begin
|
1598
|
-
x_implied_facts = @x_model.xpath("orm:Facts/orm:ImpliedFact")
|
1599
|
-
pp x_implied_facts
|
1600
|
-
=end
|
1601
|
-
puts "Reading Data Types (not yet)"
|
1602
|
-
=begin
|
1603
|
-
x_datatypes = @x_model.xpath("orm:DataTypes/*")
|
1604
|
-
pp x_datatypes
|
1605
|
-
=end
|
1606
|
-
puts "Reading Reference Mode Kinds (not yet)"
|
1607
|
-
=begin
|
1608
|
-
x_refmodekinds = @x_model.xpath("orm:ReferenceModeKinds/*")
|
1609
|
-
pp x_refmodekinds
|
1610
|
-
=end
|
1611
|
-
end
|
1612
|
-
|
1613
|
-
def show_xmlobj(x, indent = "")
|
1614
|
-
parentage = []
|
1615
|
-
p = x
|
1616
|
-
while (p)
|
1617
|
-
parentage.unshift(p)
|
1618
|
-
p = p.parent
|
1619
|
-
end
|
1620
|
-
#parentage = parentage.shift
|
1621
|
-
puts "#{indent}#{x.name} object has heritage {"
|
1622
|
-
parentage.each{|p|
|
1623
|
-
next if REXML::Document === p
|
1624
|
-
puts "#{indent}\t#{p.name}#{
|
1625
|
-
}#{(n = p['Name']) ? " Name='#{n}'" : ""
|
1626
|
-
}#{(id = p['id']) ? " #{id}" : ""
|
1627
|
-
}#{(ref = p['ref']) ? " -> #{ref}" : ""
|
1628
|
-
}#{/\S/ === ((text = p.text)) ? " "+text.inspect : ""
|
1629
|
-
}"
|
1630
|
-
show_xmlobj(@x_by_id[ref], "\t#{indent}") if ref
|
1631
|
-
}
|
1632
|
-
puts "#{indent}}"
|
1633
|
-
end
|
1634
|
-
end
|
1635
|
-
end
|
1636
|
-
end
|