activefacts 0.7.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/Manifest.txt +24 -2
- data/Rakefile +25 -3
- data/bin/afgen +1 -1
- data/bin/cql +13 -2
- data/css/offline.css +3 -0
- data/css/orm2.css +24 -0
- data/css/print.css +8 -0
- data/css/style-print.css +357 -0
- data/css/style.css +387 -0
- data/download.html +85 -0
- data/examples/CQL/Address.cql +3 -3
- data/examples/CQL/Blog.cql +13 -14
- data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
- data/examples/CQL/Death.cql +3 -2
- data/examples/CQL/Genealogy.cql +13 -11
- data/examples/CQL/Marriage.cql +2 -2
- data/examples/CQL/Metamodel.cql +136 -93
- data/examples/CQL/MultiInheritance.cql +2 -2
- data/examples/CQL/OilSupply.cql +14 -10
- data/examples/CQL/Orienteering.cql +22 -19
- data/examples/CQL/PersonPlaysGame.cql +3 -2
- data/examples/CQL/SchoolActivities.cql +4 -2
- data/examples/CQL/SimplestUnary.cql +1 -1
- data/examples/CQL/SubtypePI.cql +6 -7
- data/examples/CQL/Warehousing.cql +16 -19
- data/examples/CQL/unit.cql +584 -0
- data/examples/index.html +276 -0
- data/examples/intro.html +497 -0
- data/examples/local.css +20 -0
- data/index.html +96 -0
- data/lib/activefacts/api/concept.rb +48 -46
- data/lib/activefacts/api/constellation.rb +43 -23
- data/lib/activefacts/api/entity.rb +2 -2
- data/lib/activefacts/api/instance.rb +6 -2
- data/lib/activefacts/api/instance_index.rb +5 -0
- data/lib/activefacts/api/value.rb +8 -2
- data/lib/activefacts/api/vocabulary.rb +15 -10
- data/lib/activefacts/cql/CQLParser.treetop +109 -88
- data/lib/activefacts/cql/Concepts.treetop +32 -10
- data/lib/activefacts/cql/Context.treetop +34 -0
- data/lib/activefacts/cql/Expressions.treetop +9 -9
- data/lib/activefacts/cql/FactTypes.treetop +30 -31
- data/lib/activefacts/cql/Language/English.treetop +50 -0
- data/lib/activefacts/cql/LexicalRules.treetop +2 -1
- data/lib/activefacts/cql/Terms.treetop +117 -0
- data/lib/activefacts/cql/ValueTypes.treetop +152 -0
- data/lib/activefacts/cql/compiler.rb +1718 -0
- data/lib/activefacts/cql/parser.rb +124 -57
- data/lib/activefacts/generate/absorption.rb +1 -1
- data/lib/activefacts/generate/cql.rb +111 -100
- data/lib/activefacts/generate/cql/html.rb +5 -5
- data/lib/activefacts/generate/oo.rb +3 -3
- data/lib/activefacts/generate/ordered.rb +51 -19
- data/lib/activefacts/generate/ruby.rb +10 -8
- data/lib/activefacts/generate/sql/mysql.rb +14 -10
- data/lib/activefacts/generate/sql/server.rb +29 -24
- data/lib/activefacts/input/cql.rb +9 -1264
- data/lib/activefacts/input/orm.rb +213 -200
- data/lib/activefacts/persistence/columns.rb +11 -10
- data/lib/activefacts/persistence/index.rb +15 -18
- data/lib/activefacts/persistence/reference.rb +17 -17
- data/lib/activefacts/persistence/tables.rb +50 -51
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +79 -8
- data/lib/activefacts/vocabulary/metamodel.rb +183 -114
- data/spec/absorption_ruby_spec.rb +99 -0
- data/spec/absorption_spec.rb +3 -4
- data/spec/api/constellation.rb +1 -1
- data/spec/api/entity_type.rb +3 -1
- data/spec/api/instance.rb +4 -2
- data/spec/api/roles.rb +8 -6
- data/spec/api_spec.rb +1 -2
- data/spec/cql/context_spec.rb +71 -0
- data/spec/cql/samples_spec.rb +154 -0
- data/spec/cql/unit_spec.rb +375 -0
- data/spec/cql_cql_spec.rb +31 -21
- data/spec/cql_mysql_spec.rb +70 -0
- data/spec/cql_parse_spec.rb +15 -9
- data/spec/cql_ruby_spec.rb +27 -13
- data/spec/cql_sql_spec.rb +42 -16
- data/spec/cql_symbol_tables_spec.rb +2 -3
- data/spec/cqldump_spec.rb +7 -7
- data/spec/helpers/file_matcher.rb +39 -0
- data/spec/norma_cql_spec.rb +20 -12
- data/spec/norma_ruby_spec.rb +6 -3
- data/spec/norma_sql_spec.rb +6 -3
- data/spec/norma_tables_spec.rb +6 -4
- data/spec/spec_helper.rb +27 -8
- data/status.html +69 -0
- data/why.html +60 -0
- metadata +34 -11
- data/lib/activefacts/cql/DataTypes.treetop +0 -81
- data/spec/cql_unit_spec.rb +0 -330
data/examples/index.html
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
<!--#include virtual="/header.html" -->
|
2
|
+
<!--#include virtual="navbar.html" -->
|
3
|
+
|
4
|
+
<link rel="stylesheet" href="../css/offline.css" media="screen" type="text/css" />
|
5
|
+
<link rel="stylesheet" href="../css/print.css" media="print" type="text/css" />
|
6
|
+
|
7
|
+
<div id="sidebar"></div>
|
8
|
+
|
9
|
+
<div id="top" class="content">
|
10
|
+
|
11
|
+
<h2>ActiveFacts Example Models</h2>
|
12
|
+
<p class="offline noprint"><font size="-1">Copyright (c) 2007 Clifford Heath.</font></p>
|
13
|
+
|
14
|
+
<!-- Things to show only in the offline version -->
|
15
|
+
<div class="localnav offline noprint">
|
16
|
+
<ul>
|
17
|
+
<li id="nav-home"><a href="/ActiveFacts/" title="Home">Home</a></li>
|
18
|
+
<li><a title="CompanyDirectorEmployee" href="#CompanyDirectorEmployee"> Company and People </a></li>
|
19
|
+
<li><a title="Blog" href="#Blog"> Blog </a></li>
|
20
|
+
<li><a title="SchoolActivities" href="#SchoolActivities"> School Activities </a></li>
|
21
|
+
<li><a title="Orienteering" href="#Orienteering"> Orienteering </a></li>
|
22
|
+
<li><a title="Metamodel" href="#Metamodel"> Metamodel </a></li>
|
23
|
+
<li><a title="All Examples" href="#AllExamples"> Table of all examples </a></li>
|
24
|
+
</ul>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<p>The example models presented here were created as Object Role Models
|
28
|
+
using <a href="http://www.ormfoundation.org/files/">NORMA</a>. The
|
29
|
+
ActiveFacts generator <em>afgen</em> was used to convert them from ORM2
|
30
|
+
to CQL, SQL and Ruby code using ActiveFacts. They're all available here
|
31
|
+
as PNG images and in the other formats.
|
32
|
+
|
33
|
+
</p><p>
|
34
|
+
To make a start learning Object Role Modeling, read the
|
35
|
+
<a href="intro.html">Introduction to ORM2</a> or check the Resources page
|
36
|
+
for other sites.
|
37
|
+
|
38
|
+
</p><p>
|
39
|
+
The CQL code here formatted and laid out is <em>exactly</em> as it was generated.
|
40
|
+
It's interesting how cleverly the semantic clustering algorithm works!
|
41
|
+
|
42
|
+
</p><p class="offline">
|
43
|
+
If you're looking at a Subversion checkout of the project on Windows,
|
44
|
+
and have Visual Studio (pro) with NORMA installed, you can open
|
45
|
+
the solution <a href="norma/ExampleModels.sln">here</a>.
|
46
|
+
Otherwise, you won't be able to edit the ORM2 files, but never fear,
|
47
|
+
you can do all the same things by editing the CQL text.
|
48
|
+
</p>
|
49
|
+
|
50
|
+
<a name="CompanyDirectorEmployee"></a>
|
51
|
+
<h4>CompanyDirectorEmployee</h4>
|
52
|
+
<p>This is my main introductory model. It shows the relationships of a person
|
53
|
+
to a company of which they are a director or employee.
|
54
|
+
</p>
|
55
|
+
|
56
|
+
<a name="Blog"></a>
|
57
|
+
<h4>Blog</h4>
|
58
|
+
<p>The Blog model supports a hierarchy of topics, Posts by Authors, each Post
|
59
|
+
consisting of a series of styled Paragraphs. Comments may be made on individual
|
60
|
+
Paragraphs.
|
61
|
+
</p>
|
62
|
+
|
63
|
+
<a name="SchoolActivities"></a>
|
64
|
+
<h4>SchoolActivities</h4>
|
65
|
+
<p>This model represents students representing their school in school-sanctioned activities.
|
66
|
+
It includes the ternary StudentParticipation <em>Fact Type</em>, which represents a student's
|
67
|
+
participation in a school-sanctioned activity.
|
68
|
+
The model has constraints that allow a given Student to participate only in a given
|
69
|
+
Activity for a single School, only representing the School in which they're enrolled, and
|
70
|
+
only if the School sanctions that activity.
|
71
|
+
</p>
|
72
|
+
|
73
|
+
<a name="Orienteering"></a>
|
74
|
+
<h4>Orienteering </h4>
|
75
|
+
<p>A model for the sport of Orienteering, broken into three diagrams for
|
76
|
+
administration, registration and scoring. These diagrams also reflect areas
|
77
|
+
of interest to folk in different roles, an idea which could inspire the use
|
78
|
+
of the division as security domains. However, for an object showing on multiple
|
79
|
+
diagrams, ORM2 doesn't indicate with which diagram it has a primary affinity.
|
80
|
+
In CQL, it might be better to structure this as three separate <em>vocabularies</em>.
|
81
|
+
|
82
|
+
</p><p>
|
83
|
+
The author is involved in orienteering in his local association (which publishes a
|
84
|
+
program and results at <a href="http://street.orienteering.com.au">street.orienteering.com.au</a>
|
85
|
+
and the <a href="http://www.vicorienteering.asn.au/parkstreet/"> Victorian Orienteering
|
86
|
+
Association</a>) and he used this model to construct a scoring system using low-power
|
87
|
+
microprocessors distributed around the course.
|
88
|
+
</p>
|
89
|
+
|
90
|
+
<a name="Metamodel"></a>
|
91
|
+
<h4>Metamodel </h4>
|
92
|
+
<p>The Metamodel encodes the meaning of an ORM2 or CQL model. All features of the
|
93
|
+
ORM2 and CQL languages have a representation here, as well as a few CQL features
|
94
|
+
that haven't yet been implemented yet. The generated Ruby code is <em>used as the
|
95
|
+
intermediate form</em> inside the CQL language implementation.
|
96
|
+
</p>
|
97
|
+
|
98
|
+
<a name="AllExamples"></a>
|
99
|
+
|
100
|
+
<h3>Table of all examples</h3>
|
101
|
+
<!-- Table below here is generated automatically -->
|
102
|
+
<table class="examples" width="100%">
|
103
|
+
<tr>
|
104
|
+
<th>Model Name</th>
|
105
|
+
<td></td>
|
106
|
+
<td></td>
|
107
|
+
<td></td>
|
108
|
+
<th>Diagrams</th>
|
109
|
+
</tr>
|
110
|
+
|
111
|
+
<tr>
|
112
|
+
<td>Address</td>
|
113
|
+
<td><a href="CQL/Address.cql">CQL</a></td>
|
114
|
+
<td><a href="ruby/Address.rb">Ruby</a></td>
|
115
|
+
<td><a href="SQL/Address.sql">SQL</a></td>
|
116
|
+
<td>
|
117
|
+
<a href="images/Address.png">Address</a><br>
|
118
|
+
</tr>
|
119
|
+
|
120
|
+
<tr>
|
121
|
+
<td>Blog</td>
|
122
|
+
<td><a href="CQL/Blog.cql">CQL</a></td>
|
123
|
+
<td><a href="ruby/Blog.rb">Ruby</a></td>
|
124
|
+
<td><a href="SQL/Blog.sql">SQL</a></td>
|
125
|
+
<td>
|
126
|
+
<a href="images/Blog.png">Blog</a><br>
|
127
|
+
</tr>
|
128
|
+
|
129
|
+
<tr>
|
130
|
+
<td>CompanyDirectorEmployee</td>
|
131
|
+
<td><a href="CQL/CompanyDirectorEmployee.cql">CQL</a></td>
|
132
|
+
<td><a href="ruby/CompanyDirectorEmployee.rb">Ruby</a></td>
|
133
|
+
<td><a href="SQL/CompanyDirectorEmployee.sql">SQL</a></td>
|
134
|
+
<td>
|
135
|
+
<a href="images/CompanyDirectorEmployee.png">CompanyDirectorEmployee</a><br>
|
136
|
+
</tr>
|
137
|
+
|
138
|
+
<tr>
|
139
|
+
<td>Death</td>
|
140
|
+
<td><a href="CQL/Death.cql">CQL</a></td>
|
141
|
+
<td><a href="ruby/Death.rb">Ruby</a></td>
|
142
|
+
<td><a href="SQL/Death.sql">SQL</a></td>
|
143
|
+
<td>
|
144
|
+
<a href="images/Death.png">Death</a><br>
|
145
|
+
</tr>
|
146
|
+
|
147
|
+
<tr>
|
148
|
+
<td>Genealogy</td>
|
149
|
+
<td><a href="CQL/Genealogy.cql">CQL</a></td>
|
150
|
+
<td><a href="ruby/Genealogy.rb">Ruby</a></td>
|
151
|
+
<td><a href="SQL/Genealogy.sql">SQL</a></td>
|
152
|
+
<td>
|
153
|
+
<a href="images/Genealogy.png">Genealogy</a><br>
|
154
|
+
</tr>
|
155
|
+
|
156
|
+
<tr>
|
157
|
+
<td>Insurance</td>
|
158
|
+
<td><a href="CQL/Insurance.cql">CQL</a></td>
|
159
|
+
<td><a href="ruby/Insurance.rb">Ruby</a></td>
|
160
|
+
<td><a href="SQL/Insurance.sql">SQL</a></td>
|
161
|
+
<td>
|
162
|
+
<a href="images/Insurance/Claim.png">Claim</a><br>
|
163
|
+
<a href="images/Insurance/Insurance.png">Insurance</a><br>
|
164
|
+
<a href="images/Insurance/Person.png">Person</a><br>
|
165
|
+
<a href="images/Insurance/Policy.png">Policy</a><br>
|
166
|
+
</tr>
|
167
|
+
|
168
|
+
<tr>
|
169
|
+
<td>Marriage</td>
|
170
|
+
<td><a href="CQL/Marriage.cql">CQL</a></td>
|
171
|
+
<td><a href="ruby/Marriage.rb">Ruby</a></td>
|
172
|
+
<td><a href="SQL/Marriage.sql">SQL</a></td>
|
173
|
+
<td>
|
174
|
+
<a href="images/Marriage.png">Marriage</a><br>
|
175
|
+
</tr>
|
176
|
+
|
177
|
+
<tr>
|
178
|
+
<td>Metamodel</td>
|
179
|
+
<td><a href="CQL/Metamodel.cql">CQL</a></td>
|
180
|
+
<td><a href="ruby/Metamodel.rb">Ruby</a></td>
|
181
|
+
<td><a href="SQL/Metamodel.sql">SQL</a></td>
|
182
|
+
<td>
|
183
|
+
<a href="images/Metamodel/Constraints.png">Constraints</a><br>
|
184
|
+
<a href="images/Metamodel/Import.png">Import</a><br>
|
185
|
+
<a href="images/Metamodel/Objects.png">Objects</a><br>
|
186
|
+
<a href="images/Metamodel/Populations.png">Populations</a><br>
|
187
|
+
<a href="images/Metamodel/Values.png">Values</a><br>
|
188
|
+
</tr>
|
189
|
+
|
190
|
+
<tr>
|
191
|
+
<td>MultiInheritance</td>
|
192
|
+
<td><a href="CQL/MultiInheritance.cql">CQL</a></td>
|
193
|
+
<td><a href="ruby/MultiInheritance.rb">Ruby</a></td>
|
194
|
+
<td><a href="SQL/MultiInheritance.sql">SQL</a></td>
|
195
|
+
<td>
|
196
|
+
<a href="images/MultiInheritance.png">MultiInheritance</a><br>
|
197
|
+
</tr>
|
198
|
+
|
199
|
+
<tr>
|
200
|
+
<td>OilSupply</td>
|
201
|
+
<td><a href="CQL/OilSupply.cql">CQL</a></td>
|
202
|
+
<td><a href="ruby/OilSupply.rb">Ruby</a></td>
|
203
|
+
<td><a href="SQL/OilSupply.sql">SQL</a></td>
|
204
|
+
<td>
|
205
|
+
<a href="images/OilSupply.png">OilSupply</a><br>
|
206
|
+
</tr>
|
207
|
+
|
208
|
+
<tr>
|
209
|
+
<td>Orienteering</td>
|
210
|
+
<td><a href="CQL/Orienteering.cql">CQL</a></td>
|
211
|
+
<td><a href="ruby/Orienteering.rb">Ruby</a></td>
|
212
|
+
<td><a href="SQL/Orienteering.sql">SQL</a></td>
|
213
|
+
<td>
|
214
|
+
<a href="images/Orienteering/Administration.png">Administration</a><br>
|
215
|
+
<a href="images/Orienteering/Registration.png">Registration</a><br>
|
216
|
+
<a href="images/Orienteering/Scoring.png">Scoring</a><br>
|
217
|
+
</tr>
|
218
|
+
|
219
|
+
<tr>
|
220
|
+
<td>OrienteeringER</td>
|
221
|
+
<td><a href="CQL/OrienteeringER.cql">CQL</a></td>
|
222
|
+
<td><a href="ruby/OrienteeringER.rb">Ruby</a></td>
|
223
|
+
<td><a href="SQL/OrienteeringER.sql">SQL</a></td>
|
224
|
+
<td>
|
225
|
+
<a href="images/OrienteeringER.png">OrienteeringER</a><br>
|
226
|
+
</tr>
|
227
|
+
|
228
|
+
<tr>
|
229
|
+
<td>PersonPlaysGame</td>
|
230
|
+
<td><a href="CQL/PersonPlaysGame.cql">CQL</a></td>
|
231
|
+
<td><a href="ruby/PersonPlaysGame.rb">Ruby</a></td>
|
232
|
+
<td><a href="SQL/PersonPlaysGame.sql">SQL</a></td>
|
233
|
+
<td>
|
234
|
+
<a href="images/PersonPlaysGame.png">PersonPlaysGame</a><br>
|
235
|
+
</tr>
|
236
|
+
|
237
|
+
<tr>
|
238
|
+
<td>SchoolActivities</td>
|
239
|
+
<td><a href="CQL/SchoolActivities.cql">CQL</a></td>
|
240
|
+
<td><a href="ruby/SchoolActivities.rb">Ruby</a></td>
|
241
|
+
<td><a href="SQL/SchoolActivities.sql">SQL</a></td>
|
242
|
+
<td>
|
243
|
+
<a href="images/SchoolActivities.png">SchoolActivities</a><br>
|
244
|
+
</tr>
|
245
|
+
|
246
|
+
<tr>
|
247
|
+
<td>SimplestUnary</td>
|
248
|
+
<td><a href="CQL/SimplestUnary.cql">CQL</a></td>
|
249
|
+
<td><a href="ruby/SimplestUnary.rb">Ruby</a></td>
|
250
|
+
<td><a href="SQL/SimplestUnary.sql">SQL</a></td>
|
251
|
+
<td>
|
252
|
+
<a href="images/SimplestUnary.png">SimplestUnary</a><br>
|
253
|
+
</tr>
|
254
|
+
|
255
|
+
<tr>
|
256
|
+
<td>Warehousing</td>
|
257
|
+
<td><a href="CQL/Warehousing.cql">CQL</a></td>
|
258
|
+
<td><a href="ruby/Warehousing.rb">Ruby</a></td>
|
259
|
+
<td><a href="SQL/Warehousing.sql">SQL</a></td>
|
260
|
+
<td>
|
261
|
+
<a href="images/Warehousing.png">Warehousing</a><br>
|
262
|
+
</tr>
|
263
|
+
|
264
|
+
<tr>
|
265
|
+
<td>WindowInRoomInBldg</td>
|
266
|
+
<td><a href="CQL/WindowInRoomInBldg.cql">CQL</a></td>
|
267
|
+
<td><a href="ruby/WindowInRoomInBldg.rb">Ruby</a></td>
|
268
|
+
<td><a href="SQL/WindowInRoomInBldg.sql">SQL</a></td>
|
269
|
+
<td>
|
270
|
+
<a href="images/WindowInRoomInBldg.png">WindowInRoomInBldg</a><br>
|
271
|
+
</tr>
|
272
|
+
</table>
|
273
|
+
|
274
|
+
</div>
|
275
|
+
|
276
|
+
<!--#include virtual="/footer.html" -->
|
data/examples/intro.html
ADDED
@@ -0,0 +1,497 @@
|
|
1
|
+
<!--#include virtual="/header.html" -->
|
2
|
+
<!--#include virtual="navbar.html" -->
|
3
|
+
|
4
|
+
<link rel="stylesheet" href="../css/offline.css" media="screen" type="text/css" />
|
5
|
+
<link rel="stylesheet" href="../css/print.css" media="print" type="text/css" />
|
6
|
+
|
7
|
+
<div id="top" class="content">
|
8
|
+
<h2>Introduction to Object Role Modeling</h2>
|
9
|
+
<p class="offline" ><font size="-1">Copyright (c) 2007 Clifford Heath.</font></p>
|
10
|
+
|
11
|
+
<div class="localnav offline noprint">
|
12
|
+
<a href="#top"><strong>Example Models</strong></a><br/>
|
13
|
+
<a href="#CompanyDirectorEmployee"> Company </a><br/>
|
14
|
+
<a href="#SchoolActivities"> School Activities </a><br/>
|
15
|
+
<a href="#Death"> Death </a><br/>
|
16
|
+
<a href="#Address"> Address</a><br/>
|
17
|
+
<a href="#Orienteering"> Orienteering </a><br/>
|
18
|
+
<a href="#AllExamples"> Table of all examples </a><br/>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<p> Congratulations, you're just a few minutes away from knowing a more
|
22
|
+
agile way to design good database schemas. These models were created in ORM2
|
23
|
+
using <a href="http://www.ormfoundation.org/files/">NORMA</a>.
|
24
|
+
ORM2 terminology is <em>emphasised</em> on first use.
|
25
|
+
</p>
|
26
|
+
|
27
|
+
<a name="CompanyDirectorEmployee"></a>
|
28
|
+
<h3>CompanyDirectorEmployee</h3>
|
29
|
+
<p>This model shows the some of the relationships of a person to a company of
|
30
|
+
which they are a director or employee.
|
31
|
+
</p>
|
32
|
+
|
33
|
+
<table class="examples" width="100%">
|
34
|
+
<tr>
|
35
|
+
<td style='width:25%; border:0;'>View the generated:</td>
|
36
|
+
<td><a href="CQL/CompanyDirectorEmployee.cql">CQL</a></td>
|
37
|
+
<td><a href="ruby/CompanyDirectorEmployee.rb">Ruby</a></td>
|
38
|
+
<td><a href="SQL/CompanyDirectorEmployee.sql">SQL</a></td>
|
39
|
+
</tr>
|
40
|
+
</table>
|
41
|
+
|
42
|
+
<p><center>
|
43
|
+
<img src="images/CompanyDirectorEmployee.png" width="700" height="420" class="hangleft"></p>
|
44
|
+
</center>
|
45
|
+
</p>
|
46
|
+
|
47
|
+
<p>This model includes examples of the features of ORM2 you'll be seeing most often.</p>
|
48
|
+
<ul>
|
49
|
+
<li><i>Entity Types</i> (<b>Person</b>, <b>Company</b>) and <i>Value Types</i>
|
50
|
+
(Name, Date). Collectively, ActiveFacts calls them Concepts. Value types
|
51
|
+
have a dashed outline.
|
52
|
+
</li>
|
53
|
+
|
54
|
+
<li>Entity <i>Identification schemes</i>: Person is identified by the pair
|
55
|
+
of values (given-Name, family-Name), as indicated by the circle with two
|
56
|
+
lines across it. Company is identified by a single CompanyName, but since
|
57
|
+
this is a common pattern, it's included within the Company shape, which
|
58
|
+
is called a <b>reference mode</b>. Every Entity Type must have some
|
59
|
+
identification scheme or preferred identifier, which enables us to tell
|
60
|
+
the instances apart.
|
61
|
+
</li>
|
62
|
+
|
63
|
+
<li><i>Subtype:</i> <b>Employee</b> and <b>Manager</b> are subtypes.
|
64
|
+
Employee has a dashed arrow, because it has its own identification scheme,
|
65
|
+
it doesn't use Persons's scheme. An EntityType may have multiple supertypes,
|
66
|
+
and an instance may play any role of its supertypes (so we can record the
|
67
|
+
Birth-Date of an Employee, for example).
|
68
|
+
</li>
|
69
|
+
|
70
|
+
<li><i>Fact Types</i> define relationships between concepts. They're displayed
|
71
|
+
as a series of one or more <i>Role</i> boxes. For example, the fact type
|
72
|
+
"Person has given-Name" has two roles, played by Person and Name.
|
73
|
+
The <b>unary fact type</b> <i>Meeting is board meeting</i> indicates a
|
74
|
+
true/false condition with respect to a meeting.
|
75
|
+
</li>
|
76
|
+
|
77
|
+
<li><i>Objectified Fact Type:</i> <b>Directorship</b> and <b>Meeting</b> are
|
78
|
+
fact types that are also entities, and thus can play other roles, such as
|
79
|
+
the role of Directorship in "Directorship began on appointment-Date".
|
80
|
+
</li>
|
81
|
+
|
82
|
+
<li><i>Fact Readings</i> are displayed as text under each fact type. A fact
|
83
|
+
type may have many readings, though only the preferred one is shown on the
|
84
|
+
diagram. Binary fact types may display a reading for each direction. An
|
85
|
+
arrow is displayed if a single reading is to be read in the opposite order
|
86
|
+
from the obvious one.
|
87
|
+
</li>
|
88
|
+
|
89
|
+
<li><i>Uniqueness constraints</i>: The horizontal lines above the fact types
|
90
|
+
are uniqueness constraints (UC). A UC covering a single role allows that
|
91
|
+
role's player to occur only once all the instances of that fact type (what
|
92
|
+
we call the <b>population</b> of that fact type). A UC that covers two or
|
93
|
+
more roles allows any combination of those players, but only once for each
|
94
|
+
combination. An </b>external</b> UC may connect to roles in more than one
|
95
|
+
fact type, and is shown as a circle with a line across it. If any of the
|
96
|
+
UC lines is doubles, that UC acts as a preferred identifier for the indicated
|
97
|
+
entity type.
|
98
|
+
</li>
|
99
|
+
|
100
|
+
<li><i>Mandatory constraint</i>: The heavy dot on some role connectors (like
|
101
|
+
the one on the Company role box of Director) is a mandatory constraint that
|
102
|
+
requires every company to have at least one director. A mandatory constraint
|
103
|
+
may also be shown as a hollow dot, which says <i>should</i>, rather than
|
104
|
+
<i>must</i>.
|
105
|
+
</li>
|
106
|
+
|
107
|
+
<li><i>External Mandatory constraint</i>: The circle containing a crossed dot
|
108
|
+
which is joined to "is ceo" and "Employee is supervised by Manager" is a
|
109
|
+
mandatory exclusion constraint. The cross (X) indicates exclusion (only one)
|
110
|
+
and the dot indicates mandatory (at least one). Both types may also occur
|
111
|
+
separately.
|
112
|
+
</li>
|
113
|
+
|
114
|
+
<li><i>Ring constraint</i>: A Ring constraint is attached to the "Employee is
|
115
|
+
supervised by Manager". This one is acyclic, indicating that an Employee
|
116
|
+
may not supervise themselves, or anyone who supervises them, and so on.
|
117
|
+
There are a number of other types of ring constraints.
|
118
|
+
</li>
|
119
|
+
|
120
|
+
<li><i>Role Value Restriction</i>: The Date value recorded for a person's birth
|
121
|
+
date may not be before the year 1900. Other Date values don't have this
|
122
|
+
same restriction.
|
123
|
+
</li>
|
124
|
+
|
125
|
+
</ul>
|
126
|
+
|
127
|
+
<br clear="all">
|
128
|
+
<a name="SchoolActivities">
|
129
|
+
</a>
|
130
|
+
<h3>SchoolActivities
|
131
|
+
</h3>
|
132
|
+
<p>This model represents students representing their school in school-sanctioned activities.
|
133
|
+
</p>
|
134
|
+
|
135
|
+
<p align="center"><img src="images/SchoolActivities.png"></p>
|
136
|
+
|
137
|
+
<p>
|
138
|
+
The model has three interesting constraints:
|
139
|
+
<dl>
|
140
|
+
<dt> Uniqueness over <em>Student represents School in Activity</em>:</dt>
|
141
|
+
<dd> Allows a given Student may only participate in a given Activity for a single School</dd>
|
142
|
+
<dt> <b>Subset</b> from Participation to Enrollment</dt>
|
143
|
+
<dd> Only for the School in which they're enrolled</dd>
|
144
|
+
<dt> <b>Subset</b> from Participation to Sanctions</dt>
|
145
|
+
<dd> Only if the School sanctions that activity.</dd>
|
146
|
+
</p>
|
147
|
+
|
148
|
+
<p> The rules of <em>elementarity</em> require that every fact type having N roles has
|
149
|
+
a uniqueness constraint over either N or N-1 roles, and never over fewer roles.
|
150
|
+
This is satisfied here by the StudentParticipation having a uniqueness constraint
|
151
|
+
over two of its three roles.
|
152
|
+
</p>
|
153
|
+
|
154
|
+
<p>
|
155
|
+
The subset constraints work like this. The student is enrolled in exactly one school,
|
156
|
+
and the set of (School, Student) pairs is a superset of the (School, Student) pair
|
157
|
+
inside the StudentParticipation ternary. In simple terms, this requires that each
|
158
|
+
student who participates in some activity may only do so as a representative of the
|
159
|
+
school in which they're enrolled, and not for any other school. The other subset
|
160
|
+
constraint requires that such participation may only occur for an activity that is
|
161
|
+
sanctioned by that school. The direction of the subset arrow is consistent with the
|
162
|
+
arrows used in sub-typing, as shown in the next case.
|
163
|
+
</p>
|
164
|
+
|
165
|
+
<br clear="all">
|
166
|
+
<a name="Death"></a>
|
167
|
+
<h3>Death</h3>
|
168
|
+
<img src="images/Death.png" align="right">
|
169
|
+
This example is more a curiosity than anything else, serving to illustrate a case
|
170
|
+
that you might not otherwise think was possible. Person may play a unary role in
|
171
|
+
the fact type "is dead". If they are in fact dead, we might wish to
|
172
|
+
record some facts about the event of their death, so we nest (objectify) the
|
173
|
+
"is dead" fact type as a new entity, Death. This allows us to
|
174
|
+
associate their death with a value indicating the cause of death. The uniqueness
|
175
|
+
constraint requires that we record only one cause of death.
|
176
|
+
<p>
|
177
|
+
|
178
|
+
<br clear="all">
|
179
|
+
<a name="Address">
|
180
|
+
</a>
|
181
|
+
<h3>
|
182
|
+
Address
|
183
|
+
</h3>
|
184
|
+
|
185
|
+
<p>The Address model shows how a mass of fine semantic detail can be turned into
|
186
|
+
an efficient relational database structure, through the miracle of <em>absorption</em>.
|
187
|
+
</p>
|
188
|
+
|
189
|
+
<table class="examples" width="100%">
|
190
|
+
<tr>
|
191
|
+
<td style='width:25%; border:0;'>View the generated:</td>
|
192
|
+
<td><a href="CQL/Address.cql">CQL</a></td>
|
193
|
+
<td><a href="ruby/Address.rb">Ruby</a></td>
|
194
|
+
<td><a href="SQL/Address.sql">SQL</a></td>
|
195
|
+
</tr>
|
196
|
+
</table>
|
197
|
+
</p>
|
198
|
+
|
199
|
+
<p align="center"><img src="images/Address.png" class="hangleft">
|
200
|
+
|
201
|
+
<p>Note that the preferred identifier for Street covers all its functional roles.
|
202
|
+
Essentially that means that whereever we have a Street, all the attributes of that
|
203
|
+
street must be provided; so we can include them in more than one table without
|
204
|
+
breaking the relational <a href="http://en.wikipedia.org/wiki/Third_normal_form">Third
|
205
|
+
Normal Form</a>, which would introduce unsafe duplication.
|
206
|
+
|
207
|
+
</p><p>
|
208
|
+
The same thing pertains for Address, so we can have an Address in both the Company
|
209
|
+
and Person tables without breaking normalisation. Here's the generated SQL created
|
210
|
+
by <em>afgen</em>:
|
211
|
+
|
212
|
+
</p> <p>
|
213
|
+
<img src="images/Address.SQL.png" align="left">
|
214
|
+
</p>
|
215
|
+
<br clear="all">
|
216
|
+
|
217
|
+
<p>Note also that the SQL doesn't enforce the uniqueness constraint requiring
|
218
|
+
that each Address occurs once only. There's no need to. Each Address is
|
219
|
+
identified by its roles, but more than one person may live at the same address,
|
220
|
+
and a company might have its HQ there too. If you want all the distinct addresses,
|
221
|
+
you can write an SQL UNION query for that.
|
222
|
+
</p>
|
223
|
+
|
224
|
+
<p>If you want an Address table anyhow, you can mark it as <em>independent</em>,
|
225
|
+
which is an instruction to the absorption process that an instance of this entity
|
226
|
+
type might exist apart from any other roles it might play. Independent entity types
|
227
|
+
are shown in ORM2 as having an exclamation point <b>!</b> after their name. In
|
228
|
+
order to represent an independent entity type in SQL, a separate table is always
|
229
|
+
needed.
|
230
|
+
</p>
|
231
|
+
|
232
|
+
<p> If you were to add a functional role to Address that's not part of its
|
233
|
+
identifier, the rules of normalisation gazump absorption and Address will become
|
234
|
+
an independent table. In that case, Company and Person would have each a large
|
235
|
+
multi-part foreign key into the new Address table, so it might be preferable to
|
236
|
+
introduce a <i>surrogate key</i> in the form of a database-allocated unique integer
|
237
|
+
into the address entity, and mark that key as the preferred identifier instead.
|
238
|
+
This can be done with <em>minimal</em> change to the semantic model, so any queries
|
239
|
+
you have will probably still <b>just work</b> - this is a key advantage of the
|
240
|
+
semantic approach.
|
241
|
+
</p>
|
242
|
+
|
243
|
+
<p>
|
244
|
+
Note that the absorption process must know <b>all</b> the entity types, fact types,
|
245
|
+
and constraints for the model. If you add a single fact type or change a constraint,
|
246
|
+
that might require that some concepts are represented in new tables, or different ones,
|
247
|
+
and these changes can <b>cascade</b>, so that a small change in the conceptual model
|
248
|
+
requires a large database migration. If you've written your program using the
|
249
|
+
Constellation API, you'll have <em>minimal rework</em>.
|
250
|
+
</p>
|
251
|
+
|
252
|
+
<br clear="all">
|
253
|
+
<h3>
|
254
|
+
<a name="Orienteering">
|
255
|
+
</a>
|
256
|
+
Orienteering
|
257
|
+
</h3>
|
258
|
+
<p>Here's a three-part model of a system for the sport of Orienteering. The
|
259
|
+
three parts cover administration, registration and scoring respectively, which
|
260
|
+
also happens to map areas of interest to folk in different roles. Any feature
|
261
|
+
may show on more than one diagram, and the ORM2 rules don't indicate that such
|
262
|
+
an element has a special affinity with any diagram on which it appears.
|
263
|
+
Concepts that appear in more than one place are shown with a drop-shadow. The
|
264
|
+
diagrams are presented without much comment, and after that, the absorbed
|
265
|
+
(compound) form is shown as an entity-relationship diagram. </p>
|
266
|
+
|
267
|
+
<a name="Administration"></a>
|
268
|
+
<h3>
|
269
|
+
Administration
|
270
|
+
</h3>
|
271
|
+
<p align="center"><img src="images/Orienteering/Administration.png">
|
272
|
+
</p>
|
273
|
+
|
274
|
+
<p>
|
275
|
+
A couple of new features are shown here. <i>Number</i> has a <b>Value restriction</b>,
|
276
|
+
which is similar to the role value restrictions we saw before, except it applies
|
277
|
+
to all <i>Number</i>s.
|
278
|
+
</p>
|
279
|
+
|
280
|
+
<p>Also here you'll see an external mandatory constraint without an exclusive
|
281
|
+
cross. This one requires that an event is either part of a series, or has a name,
|
282
|
+
or <b>both</b>. If and only if the event is part of a series, it must also have a
|
283
|
+
number in that series - this is shown by the <b>equality constraint</b>, a
|
284
|
+
circle with an = sign in it. </p>
|
285
|
+
<br clear="all">
|
286
|
+
</p>
|
287
|
+
|
288
|
+
<a name="Registration"></a>
|
289
|
+
<h3>
|
290
|
+
Registration
|
291
|
+
</h3>
|
292
|
+
|
293
|
+
<p align="center"><img src="images/Orienteering/Registration.png"></p>
|
294
|
+
|
295
|
+
<br clear="all">
|
296
|
+
<a name="Scoring"></a>
|
297
|
+
</p>
|
298
|
+
<h3>
|
299
|
+
Scoring
|
300
|
+
</h3>
|
301
|
+
|
302
|
+
<p align="center"><img src="images/Orienteering/Scoring.png">
|
303
|
+
<table class="examples" width="100%">
|
304
|
+
<tr>
|
305
|
+
<td style='width:25%; border:0;'>View the generated:</td>
|
306
|
+
<td><a href="CQL/Orienteering.cql">CQL</a></td>
|
307
|
+
<td><a href="ruby/Orienteering.rb">Ruby</a></td>
|
308
|
+
<td><a href="SQL/Orienteering.sql">SQL</a></td>
|
309
|
+
</tr>
|
310
|
+
</table>
|
311
|
+
</p>
|
312
|
+
|
313
|
+
<p>This model also shows the resultant score for a given Entrant's
|
314
|
+
Entry to a given Event. This value will be computed from the times
|
315
|
+
of their visits to punches during the event, according to a scoring
|
316
|
+
method as advised. The asterisks indicate that the role value is
|
317
|
+
derived from other values in the model, or that some values may
|
318
|
+
be derived and others now.
|
319
|
+
|
320
|
+
<br clear="all">
|
321
|
+
</p>
|
322
|
+
|
323
|
+
<div class="noprint">
|
324
|
+
<a name="AllExamples"></a>
|
325
|
+
|
326
|
+
<h3>Table of all examples</h3>
|
327
|
+
<!-- Table below here is generated automatically -->
|
328
|
+
<table class="examples" width="100%">
|
329
|
+
<tr>
|
330
|
+
<td>Address</td>
|
331
|
+
<td><a href="CQL/Address.cql">CQL</a></td>
|
332
|
+
<td><a href="ruby/Address.rb">Ruby</a></td>
|
333
|
+
<td><a href="SQL/Address.sql">SQL</a></td>
|
334
|
+
<td>
|
335
|
+
<a href="images/Address.png">Address</a><br>
|
336
|
+
</tr>
|
337
|
+
|
338
|
+
<a name="Blog"></a>
|
339
|
+
<tr>
|
340
|
+
<td>Blog</td>
|
341
|
+
<td><a href="CQL/Blog.cql">CQL</a></td>
|
342
|
+
<td><a href="ruby/Blog.rb">Ruby</a></td>
|
343
|
+
<td><a href="SQL/Blog.sql">SQL</a></td>
|
344
|
+
<td>
|
345
|
+
<a href="images/Blog.png">Blog</a><br>
|
346
|
+
</tr>
|
347
|
+
|
348
|
+
<tr>
|
349
|
+
<td>CompanyDirectorEmployee</td>
|
350
|
+
<td><a href="CQL/CompanyDirectorEmployee.cql">CQL</a></td>
|
351
|
+
<td><a href="ruby/CompanyDirectorEmployee.rb">Ruby</a></td>
|
352
|
+
<td><a href="SQL/CompanyDirectorEmployee.sql">SQL</a></td>
|
353
|
+
<td>
|
354
|
+
<a href="images/CompanyDirectorEmployee.png">CompanyDirectorEmployee</a><br>
|
355
|
+
</tr>
|
356
|
+
|
357
|
+
<tr>
|
358
|
+
<td>Death</td>
|
359
|
+
<td><a href="CQL/Death.cql">CQL</a></td>
|
360
|
+
<td><a href="ruby/Death.rb">Ruby</a></td>
|
361
|
+
<td><a href="SQL/Death.sql">SQL</a></td>
|
362
|
+
<td>
|
363
|
+
<a href="images/Death.png">Death</a><br>
|
364
|
+
</tr>
|
365
|
+
|
366
|
+
<tr>
|
367
|
+
<td>Genealogy</td>
|
368
|
+
<td><a href="CQL/Genealogy.cql">CQL</a></td>
|
369
|
+
<td><a href="ruby/Genealogy.rb">Ruby</a></td>
|
370
|
+
<td><a href="SQL/Genealogy.sql">SQL</a></td>
|
371
|
+
<td>
|
372
|
+
<a href="images/Genealogy.png">Genealogy</a><br>
|
373
|
+
</tr>
|
374
|
+
|
375
|
+
<tr>
|
376
|
+
<td>Insurance</td>
|
377
|
+
<td><a href="CQL/Insurance.cql">CQL</a></td>
|
378
|
+
<td><a href="ruby/Insurance.rb">Ruby</a></td>
|
379
|
+
<td><a href="SQL/Insurance.sql">SQL</a></td>
|
380
|
+
<td>
|
381
|
+
<a href="images/Insurance/Claim.png">Claim</a><br>
|
382
|
+
<a href="images/Insurance/Insurance.png">Insurance</a><br>
|
383
|
+
<a href="images/Insurance/Person.png">Person</a><br>
|
384
|
+
<a href="images/Insurance/Policy.png">Policy</a><br>
|
385
|
+
</tr>
|
386
|
+
|
387
|
+
<tr>
|
388
|
+
<td>Marriage</td>
|
389
|
+
<td><a href="CQL/Marriage.cql">CQL</a></td>
|
390
|
+
<td><a href="ruby/Marriage.rb">Ruby</a></td>
|
391
|
+
<td><a href="SQL/Marriage.sql">SQL</a></td>
|
392
|
+
<td>
|
393
|
+
<a href="images/Marriage.png">Marriage</a><br>
|
394
|
+
</tr>
|
395
|
+
|
396
|
+
<a name="Metamodel"></a>
|
397
|
+
<tr>
|
398
|
+
<td>Metamodel</td>
|
399
|
+
<td><a href="CQL/Metamodel.cql">CQL</a></td>
|
400
|
+
<td><a href="ruby/Metamodel.rb">Ruby</a></td>
|
401
|
+
<td><a href="SQL/Metamodel.sql">SQL</a></td>
|
402
|
+
<td>
|
403
|
+
<a href="images/Metamodel/Constraints.png">Constraints</a><br>
|
404
|
+
<a href="images/Metamodel/Import.png">Import</a><br>
|
405
|
+
<a href="images/Metamodel/Objects.png">Objects</a><br>
|
406
|
+
<a href="images/Metamodel/Populations.png">Populations</a><br>
|
407
|
+
<a href="images/Metamodel/Values.png">Values</a><br>
|
408
|
+
</tr>
|
409
|
+
|
410
|
+
<tr>
|
411
|
+
<td>MultiInheritance</td>
|
412
|
+
<td><a href="CQL/MultiInheritance.cql">CQL</a></td>
|
413
|
+
<td><a href="ruby/MultiInheritance.rb">Ruby</a></td>
|
414
|
+
<td><a href="SQL/MultiInheritance.sql">SQL</a></td>
|
415
|
+
<td>
|
416
|
+
<a href="images/MultiInheritance.png">MultiInheritance</a><br>
|
417
|
+
</tr>
|
418
|
+
|
419
|
+
<tr>
|
420
|
+
<td>OilSupply</td>
|
421
|
+
<td><a href="CQL/OilSupply.cql">CQL</a></td>
|
422
|
+
<td><a href="ruby/OilSupply.rb">Ruby</a></td>
|
423
|
+
<td><a href="SQL/OilSupply.sql">SQL</a></td>
|
424
|
+
<td>
|
425
|
+
<a href="images/OilSupply.png">OilSupply</a><br>
|
426
|
+
</tr>
|
427
|
+
|
428
|
+
<tr>
|
429
|
+
<td>Orienteering</td>
|
430
|
+
<td><a href="CQL/Orienteering.cql">CQL</a></td>
|
431
|
+
<td><a href="ruby/Orienteering.rb">Ruby</a></td>
|
432
|
+
<td><a href="SQL/Orienteering.sql">SQL</a></td>
|
433
|
+
<td>
|
434
|
+
<a href="images/Orienteering/Administration.png">Administration</a><br>
|
435
|
+
<a href="images/Orienteering/Registration.png">Registration</a><br>
|
436
|
+
<a href="images/Orienteering/Scoring.png">Scoring</a><br>
|
437
|
+
</tr>
|
438
|
+
|
439
|
+
<tr>
|
440
|
+
<td>OrienteeringER</td>
|
441
|
+
<td><a href="CQL/OrienteeringER.cql">CQL</a></td>
|
442
|
+
<td><a href="ruby/OrienteeringER.rb">Ruby</a></td>
|
443
|
+
<td><a href="SQL/OrienteeringER.sql">SQL</a></td>
|
444
|
+
<td>
|
445
|
+
<a href="images/OrienteeringER.png">OrienteeringER</a><br>
|
446
|
+
</tr>
|
447
|
+
|
448
|
+
<tr>
|
449
|
+
<td>PersonPlaysGame</td>
|
450
|
+
<td><a href="CQL/PersonPlaysGame.cql">CQL</a></td>
|
451
|
+
<td><a href="ruby/PersonPlaysGame.rb">Ruby</a></td>
|
452
|
+
<td><a href="SQL/PersonPlaysGame.sql">SQL</a></td>
|
453
|
+
<td>
|
454
|
+
<a href="images/PersonPlaysGame.png">PersonPlaysGame</a><br>
|
455
|
+
</tr>
|
456
|
+
|
457
|
+
<tr>
|
458
|
+
<td>SchoolActivities</td>
|
459
|
+
<td><a href="CQL/SchoolActivities.cql">CQL</a></td>
|
460
|
+
<td><a href="ruby/SchoolActivities.rb">Ruby</a></td>
|
461
|
+
<td><a href="SQL/SchoolActivities.sql">SQL</a></td>
|
462
|
+
<td>
|
463
|
+
<a href="images/SchoolActivities.png">SchoolActivities</a><br>
|
464
|
+
</tr>
|
465
|
+
|
466
|
+
<tr>
|
467
|
+
<td>SimplestUnary</td>
|
468
|
+
<td><a href="CQL/SimplestUnary.cql">CQL</a></td>
|
469
|
+
<td><a href="ruby/SimplestUnary.rb">Ruby</a></td>
|
470
|
+
<td><a href="SQL/SimplestUnary.sql">SQL</a></td>
|
471
|
+
<td>
|
472
|
+
<a href="images/SimplestUnary.png">SimplestUnary</a><br>
|
473
|
+
</tr>
|
474
|
+
|
475
|
+
<tr>
|
476
|
+
<td>Warehousing</td>
|
477
|
+
<td><a href="CQL/Warehousing.cql">CQL</a></td>
|
478
|
+
<td><a href="ruby/Warehousing.rb">Ruby</a></td>
|
479
|
+
<td><a href="SQL/Warehousing.sql">SQL</a></td>
|
480
|
+
<td>
|
481
|
+
<a href="images/Warehousing.png">Warehousing</a><br>
|
482
|
+
</tr>
|
483
|
+
|
484
|
+
<tr>
|
485
|
+
<td>WindowInRoomInBldg</td>
|
486
|
+
<td><a href="CQL/WindowInRoomInBldg.cql">CQL</a></td>
|
487
|
+
<td><a href="ruby/WindowInRoomInBldg.rb">Ruby</a></td>
|
488
|
+
<td><a href="SQL/WindowInRoomInBldg.sql">SQL</a></td>
|
489
|
+
<td>
|
490
|
+
<a href="images/WindowInRoomInBldg.png">WindowInRoomInBldg</a><br>
|
491
|
+
</tr>
|
492
|
+
</table>
|
493
|
+
</div>
|
494
|
+
|
495
|
+
</div>
|
496
|
+
|
497
|
+
<!--#include virtual="/footer.html" -->
|