annotations2triannon 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.env_example +44 -0
  3. data/.travis.yml +15 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE +202 -0
  6. data/README.md +31 -0
  7. data/Rakefile +50 -0
  8. data/annotations2triannon.gemspec +58 -0
  9. data/bin/console +5 -0
  10. data/bin/ctags.rb +8 -0
  11. data/bin/dms.rb +175 -0
  12. data/bin/revs.rb +17 -0
  13. data/bin/revs_annotations2csv.sh +66 -0
  14. data/lib/annotations2triannon/annotation_list.rb +37 -0
  15. data/lib/annotations2triannon/configuration.rb +52 -0
  16. data/lib/annotations2triannon/iiif_annotation_list.rb +17 -0
  17. data/lib/annotations2triannon/iiif_collection.rb +56 -0
  18. data/lib/annotations2triannon/iiif_manifest.rb +32 -0
  19. data/lib/annotations2triannon/iiif_navigator.rb +172 -0
  20. data/lib/annotations2triannon/manifest.rb +86 -0
  21. data/lib/annotations2triannon/open_annotation.rb +262 -0
  22. data/lib/annotations2triannon/open_annotation_harvest.rb +37 -0
  23. data/lib/annotations2triannon/resource.rb +264 -0
  24. data/lib/annotations2triannon/revs.rb +263 -0
  25. data/lib/annotations2triannon/revs_db.rb +69 -0
  26. data/lib/annotations2triannon/shared_canvas_annotation_list.rb +18 -0
  27. data/lib/annotations2triannon/shared_canvas_manifest.rb +32 -0
  28. data/lib/annotations2triannon.rb +27 -0
  29. data/lib/rdf/vocab/Content.rb +112 -0
  30. data/lib/rdf/vocab/sc.rb +233 -0
  31. data/lib/requires.rb +69 -0
  32. data/log/.gitignore +4 -0
  33. data/spec/lib/annotations2triannon/configuration_spec.rb +24 -0
  34. data/spec/lib/annotations2triannon/open_annotation_spec.rb +176 -0
  35. data/spec/lib/annotations2triannon/resource_spec.rb +53 -0
  36. data/spec/lib/annotations2triannon_spec.rb +45 -0
  37. data/spec/spec_helper.rb +10 -0
  38. metadata +387 -0
@@ -0,0 +1,263 @@
1
+
2
+ require_relative 'revs_db'
3
+
4
+ module Annotations2triannon
5
+
6
+ class Revs
7
+
8
+ attr_accessor :db
9
+ attr_accessor :open_annotations
10
+
11
+ def initialize
12
+ @db = Annotations2triannon::RevsDb.new
13
+ end
14
+
15
+ def open_annotations
16
+ # convert all the annotations
17
+ user_id = nil
18
+ oa_list = []
19
+ @db.annotation_ids.each do |a|
20
+ # the annotation_ids are sorted by user, so there's no
21
+ # need to retrieve the user data for every annotation.
22
+ if user_id != a[:user_id]
23
+ user = user_rdf(a[:user_id])
24
+ end
25
+ annotation = @db.annotation(a[:id])
26
+ oa_list << annotation2oa(annotation, user)
27
+ end
28
+ oa_list
29
+ end
30
+
31
+ def open_annotations_as_jsonld
32
+ oa_list = open_annotations
33
+ oa_list.collect {|a| a.as_jsonld }
34
+ end
35
+
36
+ def open_annotation(id=nil)
37
+ # Not using a join, because some annotations may be anonymous.
38
+ # join = r.db.annotations.join_table(:inner, r.db.users, :id=>:user_id)
39
+ raise 'Invalid annotation ID' if id.nil?
40
+ # find and convert an annotation by id
41
+ annotation = @db.annotation(id)
42
+ raise "No annotation with id => #{id}" if annotation.nil?
43
+ user = user_rdf(annotation[:user_id])
44
+ annotation2oa(annotation, user)
45
+ end
46
+
47
+ def user_rdf(user_id)
48
+ user = @db.user(user_id)
49
+ user_id = sprintf 'revs_user_%04d', user[:id]
50
+ user_uri = RDF::URI.parse(user_id)
51
+ # avoid creation of blank nodes?
52
+ # user_node = RDF::Node.new(user_uri)
53
+ user_graph = RDF::Graph.new
54
+ user_graph.insert([user_uri, RDF.type, RDF::SCHEMA.Person])
55
+ # user_info = RDF::Literal.new("REVS user id: #{user[:id]}")
56
+ # user_graph.insert([user_uri, RDF::SCHEMA.description, user_info])
57
+ if user[:public]
58
+ unless user[:first_name].nil? || user[:first_name].empty?
59
+ # TODO: add language tags?
60
+ #fn = RDF::Literal.new(user[:first_name], :language => :en)
61
+ fn = RDF::Literal.new(user[:first_name])
62
+ user_graph.insert([user_uri, RDF::SCHEMA.givenName, fn])
63
+ end
64
+ unless user[:last_name].nil? || user[:last_name].empty?
65
+ #ln = RDF::Literal.new(user[:last_name], :language => :en)
66
+ ln = RDF::Literal.new(user[:last_name])
67
+ user_graph.insert([user_uri, RDF::SCHEMA.familyName, ln])
68
+ end
69
+ unless user[:bio].nil? || user[:bio].empty?
70
+ #description = RDF::Literal.new(user[:bio], :language => :en)
71
+ description = RDF::Literal.new(user[:bio])
72
+ user_graph.insert([user_uri, RDF::SCHEMA.description, description])
73
+ end
74
+ unless user[:email].nil? || user[:email].empty?
75
+ email = RDF::URI.parse('mailto:' + user[:email])
76
+ user_graph.insert([user_uri, RDF::SCHEMA.email, email])
77
+ end
78
+ unless user[:url].nil? || user[:url].empty?
79
+ url = user[:url]
80
+ url = url.start_with?('http://') ? url : 'http://' + url
81
+ url = RDF::URI.parse(url)
82
+ user_graph.insert([user_uri, RDF::SCHEMA.url, url])
83
+ end
84
+ unless user[:twitter].nil? || user[:twitter].empty?
85
+ url = user[:twitter]
86
+ unless (url.start_with? 'https://twitter.com') || (url.start_with? 'http://twitter.com')
87
+ url = 'https://twitter.com/' + url
88
+ end
89
+ url = RDF::URI.parse(url)
90
+ user_graph.insert([user_uri, RDF::SCHEMA.url, url])
91
+ end
92
+ end
93
+ {
94
+ :uri => user_uri,
95
+ # :node => user_node,
96
+ :graph => user_graph
97
+ }
98
+ end
99
+
100
+ # private
101
+
102
+ #
103
+ # Mapping a REVS annotation into an Open Annotation
104
+ #
105
+ def annotation2oa(annotation, user)
106
+
107
+ begin
108
+ # id --> part of URI for the annotation but, triannon POST will not accept an ID
109
+ annotation_id = sprintf 'revs_annotation_%04d', annotation[:id]
110
+
111
+ # convert the 'druid' into a PURL URI
112
+ purl = 'http://purl.stanford.edu/' + annotation[:druid]
113
+ purl_uri = RDF::URI.parse(purl)
114
+
115
+ # Commentary on the annotation json field
116
+ #
117
+ # > for each row of the annotation table (in mysql), can the 'shapes' array
118
+ # contain more than one entry?
119
+ #
120
+ # Shapes can currently only contain one entry, and are currently always
121
+ # rectangular. This data structure and shape implementation is a result of our
122
+ # use of the annotorious plugin, which is not guaranteed across projects or even
123
+ # in Revs in the long term.
124
+ #
125
+ # > if so, this suggests that a 'text' annotation might refer to more than
126
+ # one segment or region of a REVS image?
127
+ #
128
+ # Not at the moment. Not sure why it is an array. Perhaps so you can store
129
+ # multiple annotations about the same image in one row instead of many, but we
130
+ # do not do this for various reasons.
131
+ #
132
+ # > What is the 'context' field? Would you rather use the 'context' field than
133
+ # the 'src' field as the target of an open annotation (OA)?
134
+ #
135
+ # Context is just what annotorious uses for the src target. Again, we just used
136
+ # their vocabulary for ease of implementation. If we were to use a different
137
+ # back-end data store at some point, we could always transform into and out-of
138
+ # their specific json structure as needed.
139
+ #
140
+
141
+ # convert the 'json' field
142
+ annotation_json = JSON.parse(annotation[:json])
143
+ revs_uri = RDF::URI.parse(annotation_json['context'])
144
+ revs_img_src = annotation_json['src']
145
+ revs_img_uri = RDF::URI.parse(revs_img_src)
146
+ revs_fragments = []
147
+ annotation_json['shapes'].each do |shape|
148
+ # shapes are likely type 'rect'
149
+ if shape['type'] == 'rect'
150
+ # image annotation geometry
151
+ # x is % across from top left
152
+ # y is % down from top left
153
+ # width is % across from x
154
+ # height is % down from y
155
+ x = shape['geometry']['x'] * 100
156
+ y = shape['geometry']['y'] * 100
157
+ w = shape['geometry']['width'] * 100
158
+ h = shape['geometry']['height'] * 100
159
+ # media fragment: #xywh=percent:30.1,16.8,35.1,52.2
160
+ fragment = sprintf '#xywh=percent:%04.1f,%04.1f,%04.1f,%04.1f', x, y, w, h
161
+ revs_fragments << fragment
162
+ end
163
+ end
164
+ revs_img_graph = RDF::Graph.new
165
+ # revs_img_node = RDF::Node.new(revs_img_uri)
166
+ # revs_img_graph.insert([revs_img_node, RDF.type, RDF::Vocab::OA.SpecificResource])
167
+ # revs_img_graph.insert([revs_img_node, RDF::Vocab::OA.hasSource, revs_img_uri])
168
+ revs_img_graph.insert([revs_uri, RDF.type, RDF::Vocab::OA.SpecificResource])
169
+ revs_img_graph.insert([revs_uri, RDF::Vocab::OA.hasSource, revs_img_uri])
170
+ revs_img_graph.insert([revs_img_uri, RDF.type, RDF::DCMIType.Image])
171
+ # Note: it's most likely there is only one fragment in a REVS annotation.
172
+ revs_fragment_graphs = []
173
+ revs_fragments.each_with_index do |f, i|
174
+ # img_uri = RDF::URI.parse(revs_img_src + fragment)
175
+ # revs_img_uris << img_uri
176
+ f_id = sprintf '%s_fragment_%02d', annotation_id, i
177
+ f_uri = RDF::URI.parse(f_id)
178
+ f_graph = RDF::Graph.new
179
+ # avoid creation of blank nodes?
180
+ # f_node = RDF::Node.new(f_uri)
181
+ # f_graph.insert([f_node, RDF.type, RDF::Vocab::OA.FragmentSelector])
182
+ # f_graph.insert([f_node, RDF::DC.conformsTo, RDF::MA.MediaFragment])
183
+ # f_graph.insert([f_node, RDF.value, RDF::Literal.new(f)])
184
+ # revs_img_graph.insert([revs_img_node, RDF::Vocab::OA.hasSelector, f_node])
185
+ f_graph.insert([f_uri, RDF.type, RDF::Vocab::OA.FragmentSelector])
186
+ f_graph.insert([f_uri, RDF::DC.conformsTo, RDF::MA.MediaFragment])
187
+ f_graph.insert([f_uri, RDF.value, RDF::Literal.new(f)])
188
+ revs_img_graph.insert([revs_uri, RDF::Vocab::OA.hasSelector, f_uri])
189
+ revs_fragment_graphs << f_graph
190
+ end
191
+
192
+ # oa#hasBody
193
+ # text --> value of cnt:chars property of a ContentAsText body of the annotation
194
+ body_id = sprintf '%s_comment', annotation_id
195
+ body_uri = RDF::URI.parse(body_id)
196
+ # TODO: add a language tag?
197
+ #body_text = RDF::Literal.new(annotation[:text], :language => :en)
198
+ body_text = RDF::Literal.new(annotation[:text])
199
+ body_graph = RDF::Graph.new
200
+ # avoid creation of blank nodes?
201
+ # body_node = RDF::Node.uuid
202
+ # body_node = RDF::Node.new(annotation[:id])
203
+ body_graph.insert([body_uri, RDF.type, RDF::Content.ContentAsText])
204
+ body_graph.insert([body_uri, RDF.type, RDF::DCMIType.Text])
205
+ body_graph.insert([body_uri, RDF::Content.chars, body_text])
206
+ body_graph.insert([body_uri, RDF::Content.characterEncoding, 'UTF-8'])
207
+
208
+ # oa#annotatedAt
209
+ # created_at --> discard if updated_at is always present
210
+ # updated_at --> oa:annotatedAt
211
+ #
212
+ # > annotation[:created_at].class
213
+ # => Time
214
+ # > annotation[:created_at].utc
215
+ # => 2014-03-25 01:56:01 UTC
216
+ # > annotation[:created_at].to_i # unix time since epoch
217
+ # => 1395712561
218
+ # > [annotation[:created_at].utc, annotation[:updated_at].utc]
219
+ # => [2014-03-25 01:56:01 UTC, 2014-03-25 01:56:14 UTC]
220
+ #
221
+ # create an RDF literal with datatype, see
222
+ # http://rdf.greggkellogg.net/yard/RDF/Literal.html
223
+ # > RDF::Literal.new(annotation[:created_at]).datatype
224
+ # => #<RDF::Vocabulary::Term:0x3f86333d6ca8 URI:http://www.w3.org/2001/XMLSchema#time>
225
+ # However, this automatic conversion discards the date!
226
+ # > RDF::Literal.new(annotation[:created_at]).to_s
227
+ # => "01:56:01Z"
228
+ # So, an explicit datatype is required, i.e.:
229
+ # > RDF::Literal.new(annotation[:created_at], :datatype => RDF::XSD.dateTime).to_s
230
+ # => "2014-03-25T01:56:01Z"
231
+ created_datetime = RDF::Literal.new(annotation[:created_at].utc, :datatype => RDF::XSD.dateTime)
232
+ updated_datetime = RDF::Literal.new(annotation[:updated_at].utc, :datatype => RDF::XSD.dateTime)
233
+ annotation_datetime = updated_datetime #if annotation[:created_at].utc < annotation[:updated_at].utc
234
+
235
+ # Create and populate an Open Annotation instance.
236
+ oa = Annotations2triannon::OpenAnnotation.new
237
+ oa.insert_hasTarget(revs_uri)
238
+ oa.insert_hasTarget(purl_uri)
239
+ oa.insert_hasTarget(revs_img_uri)
240
+ # oa.insert_hasTarget(revs_img_node)
241
+ oa.graph.insert(revs_img_graph)
242
+ revs_fragment_graphs.each {|g| oa.graph.insert(g) }
243
+ # to enable the blank node, change body_graph to use body_node instead of body_uri
244
+ # oa.insert_hasBody(body_node)
245
+ oa.insert_hasBody(body_uri)
246
+ oa.graph.insert(body_graph)
247
+ oa.insert_annotatedAt(annotation_datetime)
248
+ # to enable the blank node, change user_graph to use user_node instead of user_uri
249
+ # oa.insert_annotatedBy(user[:node])
250
+ oa.insert_annotatedBy(user[:uri])
251
+ oa.graph.insert(user[:graph])
252
+ oa
253
+ rescue => e
254
+ puts e.message
255
+ # binding.pry
256
+ raise e
257
+ end
258
+ end
259
+
260
+ end
261
+
262
+ end
263
+
@@ -0,0 +1,69 @@
1
+ require 'logger'
2
+ require 'mysql2'
3
+ require 'sequel'
4
+ # An interface to the revs SQL database using Sequel
5
+ # @see http://sequel.jeremyevans.net/documentation.html Sequel RDoc
6
+ # @see http://sequel.jeremyevans.net/rdoc/files/README_rdoc.html Sequel README
7
+ # @see http://sequel.jeremyevans.net/rdoc/files/doc/code_order_rdoc.html Sequel code order
8
+
9
+ module Annotations2triannon
10
+
11
+ class RevsDb
12
+
13
+ @@log = Logger.new('log/revs_db.log')
14
+
15
+ attr_accessor :db
16
+ attr_accessor :db_config
17
+
18
+ def self.log_model_info(m)
19
+ @@log.info "table: #{m.table_name}, columns: #{m.columns}, pk: #{m.primary_key}"
20
+ end
21
+
22
+ def initialize
23
+ @db_config = {}
24
+ @db_config['host'] = ENV['REVS_DB_HOST'] || 'localhost'
25
+ @db_config['port'] = ENV['REVS_DB_PORT'] || '3306'
26
+ @db_config['user'] = ENV['REVS_DB_USER'] || 'revs'
27
+ @db_config['password'] = ENV['REVS_DB_PASS'] || ''
28
+ @db_config['database'] = ENV['REVS_DB_DATABASE'] || 'revs'
29
+ options = @db_config.merge(
30
+ {
31
+ :encoding => 'utf8',
32
+ :max_connections => 10,
33
+ :logger => @@log
34
+ })
35
+ @db = Sequel.mysql2(options)
36
+ @db.extension(:pagination)
37
+ # Ensure the connection is good on startup, raises exceptions on failure
38
+ @@log.info "#{@db} connected: #{@db.test_connection}"
39
+ end
40
+
41
+ def annotation(id)
42
+ @db[:annotations][:id => id]
43
+ end
44
+
45
+ def annotations
46
+ @db[:annotations]
47
+ end
48
+
49
+ def annotation_ids
50
+ @db[:annotations].order(:user_id).select(:user_id, :id)
51
+ end
52
+
53
+ def annotations_join_users
54
+ @db[:annotations].join_table(:inner, @db[:users], :id=>:user_id)
55
+ # @db[:annotations].join_table(:outer, @db[:users], :id=>:user_id)
56
+ end
57
+
58
+ def users
59
+ @db[:users]
60
+ end
61
+
62
+ def user(id)
63
+ @db[:users][:id => id]
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
@@ -0,0 +1,18 @@
1
+
2
+ module Annotations2triannon
3
+
4
+ # A filter to exclude any IIIF namespace content
5
+ class SharedCanvasAnnotationList < AnnotationList
6
+
7
+ def annotation_list?
8
+ sc_annotation_list?
9
+ end
10
+
11
+ def iiif_annotation_list?
12
+ false
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
@@ -0,0 +1,32 @@
1
+
2
+ module Annotations2triannon
3
+
4
+ # A filter to exclude any IIIF namespace content
5
+ class SharedCanvasManifest < Manifest
6
+
7
+ def manifest?
8
+ sc_manifest?
9
+ end
10
+
11
+ def iiif_manifest?
12
+ false
13
+ end
14
+
15
+ def annotation_lists
16
+ return @annotation_lists unless @annotation_lists.nil?
17
+ uris = collect_annotation_list_uris(query_sc_annotation_list)
18
+ @annotation_lists = uris.collect do |uri|
19
+ Annotations2triannon::AnnotationList.new(uri)
20
+ end
21
+ @annotation_lists
22
+ end
23
+
24
+ def iiif_annotation_lists
25
+ return @iiif_annotation_lists unless @iiif_annotation_lists.nil?
26
+ @iiif_annotation_lists = []
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
@@ -0,0 +1,27 @@
1
+ require 'requires'
2
+
3
+ module Annotations2triannon
4
+
5
+ AGENT = RDF::URI.parse('https://github.com/sul-dlss/annotations2triannon')
6
+
7
+ # configuration at the module level, see
8
+ # http://brandonhilkert.com/blog/ruby-gem-configuration-patterns/
9
+
10
+ class << self
11
+ attr_writer :configuration
12
+ end
13
+
14
+ def self.configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ def self.reset
19
+ @configuration = Configuration.new
20
+ end
21
+
22
+ def self.configure
23
+ yield(configuration)
24
+ end
25
+
26
+ end
27
+
@@ -0,0 +1,112 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # This file generated automatically using vocab-fetch from http://www.w3.org/2011/content
3
+ require 'rdf'
4
+ module RDF
5
+ class CONTENT < RDF::StrictVocabulary("http://www.w3.org/2011/content#")
6
+
7
+ # Class definitions
8
+ term :Content,
9
+ comment: %(The content.).freeze,
10
+ label: "Content".freeze,
11
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
12
+ term :ContentAsBase64,
13
+ comment: %(The base64 encoded content \(can be used for binary content\).).freeze,
14
+ label: "Base64 content".freeze,
15
+ subClassOf: "http://www.w3.org/2011/content#Content".freeze,
16
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
17
+ term :ContentAsText,
18
+ comment: %(The text content \(can be used for text content\).).freeze,
19
+ label: "Text content".freeze,
20
+ subClassOf: "http://www.w3.org/2011/content#Content".freeze,
21
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
22
+ term :ContentAsXML,
23
+ comment: %(The XML content \(can only be used for XML-wellformed content\).).freeze,
24
+ label: "XML content".freeze,
25
+ subClassOf: "http://www.w3.org/2011/content#Content".freeze,
26
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
27
+ term :DoctypeDecl,
28
+ comment: %(The document type declaration.).freeze,
29
+ label: "Document type declaration".freeze,
30
+ type: ["rdfs:Class".freeze, "owl:Class".freeze]
31
+
32
+ # Property definitions
33
+ property :bytes,
34
+ comment: %(The Base64 encoded byte sequence of the content.).freeze,
35
+ domain: "http://www.w3.org/2011/content#ContentAsBase64".freeze,
36
+ label: "Base64 encoded byte sequence".freeze,
37
+ range: "xsd:base64Binary".freeze,
38
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
39
+ property :characterEncoding,
40
+ comment: %(The character encoding used to create a character sequence from a byte sequence or vice versa.).freeze,
41
+ domain: "http://www.w3.org/2011/content#Content".freeze,
42
+ label: "Character encoding".freeze,
43
+ range: "rdfs:Literal".freeze,
44
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
45
+ property :chars,
46
+ comment: %(The character sequence of the text content.).freeze,
47
+ domain: "http://www.w3.org/2011/content#ContentAsText".freeze,
48
+ label: "Character sequence".freeze,
49
+ range: "rdfs:Literal".freeze,
50
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
51
+ property :declaredEncoding,
52
+ comment: %(The character encoding declared in the XML declaration.).freeze,
53
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
54
+ label: "XML character encoding".freeze,
55
+ range: "rdfs:Literal".freeze,
56
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
57
+ property :doctypeName,
58
+ comment: %(The document type name.).freeze,
59
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
60
+ label: "Document type name".freeze,
61
+ range: "rdfs:Literal".freeze,
62
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
63
+ property :dtDecl,
64
+ comment: %(The document type declaration.).freeze,
65
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
66
+ label: "Document type declaration".freeze,
67
+ range: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
68
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
69
+ property :internalSubset,
70
+ comment: %(The internal document type definition subset within the document type declarations.).freeze,
71
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
72
+ label: "Internal DTD subset".freeze,
73
+ range: "rdfs:Literal".freeze,
74
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
75
+ property :leadingMisc,
76
+ comment: %(The XML content preceding the document type declaration.).freeze,
77
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
78
+ label: "XML leading misc".freeze,
79
+ range: "rdfs:XMLLiteral".freeze,
80
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
81
+ property :publicId,
82
+ comment: %(The document type declarations's public identifier.).freeze,
83
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
84
+ label: "Public ID".freeze,
85
+ range: "rdfs:Literal".freeze,
86
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
87
+ property :rest,
88
+ comment: %(The XML content following the document type declaration.).freeze,
89
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
90
+ label: "XML rest".freeze,
91
+ range: "rdfs:XMLLiteral".freeze,
92
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
93
+ property :standalone,
94
+ comment: %(The standalone declaration in the XML declaration.).freeze,
95
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
96
+ label: "XML standalone document declaration".freeze,
97
+ range: "rdfs:Literal".freeze,
98
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
99
+ property :systemId,
100
+ comment: %(The document type declarations's system identifier \(typed: xsd:anyURI\)).freeze,
101
+ domain: "http://www.w3.org/2011/content#DoctypeDecl".freeze,
102
+ label: "System ID".freeze,
103
+ range: "xsd:anyURI".freeze,
104
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
105
+ property :version,
106
+ comment: %(The XML version declared in the XML declaration.).freeze,
107
+ domain: "http://www.w3.org/2011/content#ContentAsXML".freeze,
108
+ label: "XML version".freeze,
109
+ range: "rdfs:Literal".freeze,
110
+ type: ["rdf:Property".freeze, "owl:ObjectProperty".freeze]
111
+ end
112
+ end