openlogic-rdf 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/AUTHORS +3 -0
  2. data/CREDITS +9 -0
  3. data/README +361 -0
  4. data/UNLICENSE +24 -0
  5. data/VERSION +1 -0
  6. data/bin/rdf +18 -0
  7. data/etc/doap.nt +62 -0
  8. data/lib/df.rb +1 -0
  9. data/lib/rdf/cli.rb +200 -0
  10. data/lib/rdf/format.rb +383 -0
  11. data/lib/rdf/mixin/countable.rb +39 -0
  12. data/lib/rdf/mixin/durable.rb +31 -0
  13. data/lib/rdf/mixin/enumerable.rb +637 -0
  14. data/lib/rdf/mixin/indexable.rb +26 -0
  15. data/lib/rdf/mixin/inferable.rb +5 -0
  16. data/lib/rdf/mixin/mutable.rb +191 -0
  17. data/lib/rdf/mixin/queryable.rb +265 -0
  18. data/lib/rdf/mixin/readable.rb +15 -0
  19. data/lib/rdf/mixin/type_check.rb +21 -0
  20. data/lib/rdf/mixin/writable.rb +152 -0
  21. data/lib/rdf/model/graph.rb +263 -0
  22. data/lib/rdf/model/list.rb +731 -0
  23. data/lib/rdf/model/literal/boolean.rb +121 -0
  24. data/lib/rdf/model/literal/date.rb +73 -0
  25. data/lib/rdf/model/literal/datetime.rb +72 -0
  26. data/lib/rdf/model/literal/decimal.rb +86 -0
  27. data/lib/rdf/model/literal/double.rb +189 -0
  28. data/lib/rdf/model/literal/integer.rb +126 -0
  29. data/lib/rdf/model/literal/numeric.rb +184 -0
  30. data/lib/rdf/model/literal/time.rb +87 -0
  31. data/lib/rdf/model/literal/token.rb +47 -0
  32. data/lib/rdf/model/literal/xml.rb +39 -0
  33. data/lib/rdf/model/literal.rb +373 -0
  34. data/lib/rdf/model/node.rb +156 -0
  35. data/lib/rdf/model/resource.rb +28 -0
  36. data/lib/rdf/model/statement.rb +296 -0
  37. data/lib/rdf/model/term.rb +77 -0
  38. data/lib/rdf/model/uri.rb +570 -0
  39. data/lib/rdf/model/value.rb +133 -0
  40. data/lib/rdf/nquads.rb +152 -0
  41. data/lib/rdf/ntriples/format.rb +48 -0
  42. data/lib/rdf/ntriples/reader.rb +239 -0
  43. data/lib/rdf/ntriples/writer.rb +219 -0
  44. data/lib/rdf/ntriples.rb +104 -0
  45. data/lib/rdf/query/pattern.rb +329 -0
  46. data/lib/rdf/query/solution.rb +252 -0
  47. data/lib/rdf/query/solutions.rb +237 -0
  48. data/lib/rdf/query/variable.rb +221 -0
  49. data/lib/rdf/query.rb +404 -0
  50. data/lib/rdf/reader.rb +511 -0
  51. data/lib/rdf/repository.rb +389 -0
  52. data/lib/rdf/transaction.rb +161 -0
  53. data/lib/rdf/util/aliasing.rb +63 -0
  54. data/lib/rdf/util/cache.rb +139 -0
  55. data/lib/rdf/util/file.rb +38 -0
  56. data/lib/rdf/util/uuid.rb +36 -0
  57. data/lib/rdf/util.rb +6 -0
  58. data/lib/rdf/version.rb +19 -0
  59. data/lib/rdf/vocab/cc.rb +18 -0
  60. data/lib/rdf/vocab/cert.rb +13 -0
  61. data/lib/rdf/vocab/dc.rb +63 -0
  62. data/lib/rdf/vocab/dc11.rb +23 -0
  63. data/lib/rdf/vocab/doap.rb +45 -0
  64. data/lib/rdf/vocab/exif.rb +168 -0
  65. data/lib/rdf/vocab/foaf.rb +69 -0
  66. data/lib/rdf/vocab/geo.rb +13 -0
  67. data/lib/rdf/vocab/http.rb +26 -0
  68. data/lib/rdf/vocab/owl.rb +59 -0
  69. data/lib/rdf/vocab/rdfs.rb +17 -0
  70. data/lib/rdf/vocab/rsa.rb +12 -0
  71. data/lib/rdf/vocab/rss.rb +14 -0
  72. data/lib/rdf/vocab/sioc.rb +93 -0
  73. data/lib/rdf/vocab/skos.rb +36 -0
  74. data/lib/rdf/vocab/wot.rb +21 -0
  75. data/lib/rdf/vocab/xhtml.rb +9 -0
  76. data/lib/rdf/vocab/xsd.rb +58 -0
  77. data/lib/rdf/vocab.rb +215 -0
  78. data/lib/rdf/writer.rb +475 -0
  79. data/lib/rdf.rb +192 -0
  80. metadata +173 -0
@@ -0,0 +1,373 @@
1
+ module RDF
2
+ ##
3
+ # An RDF literal.
4
+ #
5
+ # Subclasses of {RDF::Literal} should define DATATYPE and GRAMMAR constants, which are used
6
+ # for identifying the appropriate class to use for a datatype URI and to perform lexical
7
+ # matching on the value.
8
+ #
9
+ # Literal comparison with other {RDF::Value} instances call {RDF::Value#type_error},
10
+ # which, returns false. Implementations wishing to have {RDF::TypeError} raised
11
+ # should mix-in {RDF::TypeCheck}. This is required for strict SPARQL conformance.
12
+ #
13
+ # Specific typed literals may have behavior different from the default implementation. See
14
+ # the following defined sub-classes for specific documentation. Additional sub-classes may
15
+ # be defined, and will interoperate by defining `DATATYPE` and `GRAMMAR` constants, in addition
16
+ # other required overrides of RDF::Literal behavior.
17
+ #
18
+ # * {RDF::Literal::Boolean}
19
+ # * {RDF::Literal::Date}
20
+ # * {RDF::Literal::DateTime}
21
+ # * {RDF::Literal::Decimal}
22
+ # * {RDF::Literal::Double}
23
+ # * {RDF::Literal::Integer}
24
+ # * {RDF::Literal::Time}
25
+ #
26
+ # @example Creating a plain literal
27
+ # value = RDF::Literal.new("Hello, world!")
28
+ # value.plain? #=> true`
29
+ #
30
+ # @example Creating a language-tagged literal (1)
31
+ # value = RDF::Literal.new("Hello!", :language => :en)
32
+ # value.has_language? #=> true
33
+ # value.language #=> :en
34
+ #
35
+ # @example Creating a language-tagged literal (2)
36
+ # RDF::Literal.new("Wazup?", :language => :"en-US")
37
+ # RDF::Literal.new("Hej!", :language => :sv)
38
+ # RDF::Literal.new("¡Hola!", :language => :es)
39
+ #
40
+ # @example Creating an explicitly datatyped literal
41
+ # value = RDF::Literal.new("2009-12-31", :datatype => RDF::XSD.date)
42
+ # value.has_datatype? #=> true
43
+ # value.datatype #=> RDF::XSD.date
44
+ #
45
+ # @example Creating an implicitly datatyped literal
46
+ # value = RDF::Literal.new(Date.today)
47
+ # value.has_datatype? #=> true
48
+ # value.datatype #=> RDF::XSD.date
49
+ #
50
+ # @example Creating implicitly datatyped literals
51
+ # RDF::Literal.new(false).datatype #=> XSD.boolean
52
+ # RDF::Literal.new(true).datatype #=> XSD.boolean
53
+ # RDF::Literal.new(123).datatype #=> XSD.integer
54
+ # RDF::Literal.new(9223372036854775807).datatype #=> XSD.integer
55
+ # RDF::Literal.new(3.1415).datatype #=> XSD.double
56
+ # RDF::Literal.new(Time.now).datatype #=> XSD.dateTime
57
+ # RDF::Literal.new(Date.new(2010)).datatype #=> XSD.date
58
+ # RDF::Literal.new(DateTime.new(2010)).datatype #=> XSD.dateTime
59
+ #
60
+ # @see http://www.w3.org/TR/rdf-concepts/#section-Literals
61
+ # @see http://www.w3.org/TR/rdf-concepts/#section-Datatypes-intro
62
+ class Literal
63
+
64
+ private
65
+ @@subclasses = [] # @private
66
+
67
+ ##
68
+ # @private
69
+ # @return [void]
70
+ def self.inherited(child)
71
+ @@subclasses << child
72
+ super
73
+ end
74
+
75
+ public
76
+
77
+ require 'rdf/model/literal/numeric'
78
+ require 'rdf/model/literal/boolean'
79
+ require 'rdf/model/literal/decimal'
80
+ require 'rdf/model/literal/integer'
81
+ require 'rdf/model/literal/double'
82
+ require 'rdf/model/literal/date'
83
+ require 'rdf/model/literal/datetime'
84
+ require 'rdf/model/literal/time'
85
+ require 'rdf/model/literal/token'
86
+ require 'rdf/model/literal/xml'
87
+
88
+ include RDF::Term
89
+
90
+ ##
91
+ # @private
92
+ # Return datatype class for uri, or nil if none is found
93
+ def self.datatyped_class(uri)
94
+ @@subclasses.detect {|klass| klass.const_defined?(:DATATYPE) && klass.const_get(:DATATYPE) == uri}
95
+ end
96
+
97
+ ##
98
+ # @private
99
+ def self.new(value, options = {})
100
+ klass = case
101
+ when !self.equal?(RDF::Literal)
102
+ self # subclasses can be directly constructed without type dispatch
103
+ when typed_literal = datatyped_class(RDF::URI(options[:datatype]))
104
+ typed_literal
105
+ else case value
106
+ when ::TrueClass then RDF::Literal::Boolean
107
+ when ::FalseClass then RDF::Literal::Boolean
108
+ when ::Integer then RDF::Literal::Integer
109
+ when ::Float then RDF::Literal::Double
110
+ when ::BigDecimal then RDF::Literal::Decimal
111
+ when ::DateTime then RDF::Literal::DateTime
112
+ when ::Date then RDF::Literal::Date
113
+ when ::Time then RDF::Literal::Time # FIXME: Ruby's Time class can represent datetimes as well
114
+ when ::Symbol then RDF::Literal::Token
115
+ else self
116
+ end
117
+ end
118
+ literal = klass.allocate
119
+ literal.send(:initialize, value, options)
120
+ literal.validate! if options[:validate]
121
+ literal.canonicalize! if options[:canonicalize]
122
+ literal
123
+ end
124
+
125
+ TRUE = RDF::Literal.new(true).freeze
126
+ FALSE = RDF::Literal.new(false).freeze
127
+ ZERO = RDF::Literal.new(0).freeze
128
+
129
+ # @return [Symbol] The language tag (optional).
130
+ attr_accessor :language
131
+
132
+ # @return [URI] The XML Schema datatype URI (optional).
133
+ attr_accessor :datatype
134
+
135
+ ##
136
+ # @param [Object] value
137
+ # @option options [Symbol] :language (nil)
138
+ # @option options [URI] :datatype (nil)
139
+ def initialize(value, options = {})
140
+ @object = value
141
+ @string = options[:lexical] if options[:lexical]
142
+ @string = value if !defined?(@string) && value.is_a?(String)
143
+ @language = options[:language].to_s.to_sym if options[:language]
144
+ @datatype = RDF::URI(options[:datatype]) if options[:datatype]
145
+ @datatype ||= self.class.const_get(:DATATYPE) if self.class.const_defined?(:DATATYPE)
146
+ end
147
+
148
+ ##
149
+ # Returns the value as a string.
150
+ #
151
+ # @return [String]
152
+ def value
153
+ @string || to_s
154
+ end
155
+
156
+ ##
157
+ # @return [Object]
158
+ def object
159
+ defined?(@object) ? @object : value
160
+ end
161
+
162
+ ##
163
+ # Returns `true`.
164
+ #
165
+ # @return [Boolean] `true` or `false`
166
+ def literal?
167
+ true
168
+ end
169
+
170
+ ##
171
+ # Returns `false`.
172
+ #
173
+ # @return [Boolean] `true` or `false`
174
+ def anonymous?
175
+ false
176
+ end
177
+
178
+ ##
179
+ # Returns a hash code for this literal.
180
+ #
181
+ # @return [Fixnum]
182
+ def hash
183
+ to_s.hash
184
+ end
185
+
186
+ ##
187
+ # Determins if `self` is the same term as `other`.
188
+ #
189
+ # @example
190
+ # RDF::Literal(1).eql?(RDF::Literal(1.0)) #=> false
191
+ #
192
+ # @param [Object] other
193
+ # @return [Boolean] `true` or `false`
194
+ def eql?(other)
195
+ self.equal?(other) ||
196
+ (self.class.eql?(other.class) &&
197
+ self.value.eql?(other.value) &&
198
+ self.language.to_s.downcase.eql?(other.language.to_s.downcase) &&
199
+ self.datatype.eql?(other.datatype))
200
+ end
201
+
202
+ ##
203
+ # Returns `true` if this literal is equivalent to `other` (with type check).
204
+ #
205
+ # @example
206
+ # RDF::Literal(1) == RDF::Literal(1.0) #=> true
207
+ #
208
+ # @param [Object] other
209
+ # @return [Boolean] `true` or `false`
210
+ #
211
+ # @see http://www.w3.org/TR/rdf-sparql-query/#func-RDFterm-equal
212
+ # @see http://www.w3.org/TR/rdf-concepts/#section-Literal-Equality
213
+ def ==(other)
214
+ case other
215
+ when Literal
216
+ case
217
+ when self.eql?(other)
218
+ true
219
+ when self.has_language? && self.language.to_s.downcase == other.language.to_s.downcase
220
+ # Literals with languages can compare if languages are identical
221
+ self.value == other.value
222
+ when (self.simple? || self.datatype == XSD.string) && (other.simple? || other.datatype == XSD.string)
223
+ self.value == other.value
224
+ when other.comperable_datatype?(self) || self.comperable_datatype?(other)
225
+ # Comoparing plain with undefined datatypes does not generate an error, but returns false
226
+ # From data-r2/expr-equal/eq-2-2.
227
+ false
228
+ else
229
+ type_error("unable to determine whether #{self.inspect} and #{other.inspect} are equivalent")
230
+ end
231
+ when String
232
+ self.plain? && self.value.eql?(other)
233
+ else false
234
+ end
235
+ end
236
+ alias_method :===, :==
237
+
238
+ ##
239
+ # Returns `true` if this is a plain literal.
240
+ #
241
+ # @return [Boolean] `true` or `false`
242
+ # @see http://www.w3.org/TR/rdf-concepts/#dfn-plain-literal
243
+ def plain?
244
+ language.nil? && datatype.nil?
245
+ end
246
+ alias_method :simple?, :plain?
247
+
248
+ ##
249
+ # Returns `true` if this is a language-tagged literal.
250
+ #
251
+ # @return [Boolean] `true` or `false`
252
+ # @see http://www.w3.org/TR/rdf-concepts/#dfn-plain-literal
253
+ def has_language?
254
+ !language.nil?
255
+ end
256
+ alias_method :language?, :has_language?
257
+
258
+ ##
259
+ # Returns `true` if this is a datatyped literal.
260
+ #
261
+ # @return [Boolean] `true` or `false`
262
+ # @see http://www.w3.org/TR/rdf-concepts/#dfn-typed-literal
263
+ def has_datatype?
264
+ !datatype.nil?
265
+ end
266
+ alias_method :datatype?, :has_datatype?
267
+ alias_method :typed?, :has_datatype?
268
+ alias_method :datatyped?, :has_datatype?
269
+
270
+ ##
271
+ # Returns `true` if the value adheres to the defined grammar of the
272
+ # datatype.
273
+ #
274
+ # @return [Boolean] `true` or `false`
275
+ # @since 0.2.1
276
+ def valid?
277
+ grammar = self.class.const_get(:GRAMMAR) rescue nil
278
+ grammar.nil? || !!(value =~ grammar)
279
+ end
280
+
281
+ ##
282
+ # Returns `true` if the value does not adhere to the defined grammar of
283
+ # the datatype.
284
+ #
285
+ # @return [Boolean] `true` or `false`
286
+ # @since 0.2.1
287
+ def invalid?
288
+ !valid?
289
+ end
290
+
291
+ ##
292
+ # Returns `true` if the literal has a datatype and the comparison should
293
+ # return false instead of raise a type error.
294
+ #
295
+ # This behavior is intuited from SPARQL data-r2/expr-equal/eq-2-2
296
+ # @return [Boolean]
297
+ def comperable_datatype?(other)
298
+ return false unless self.plain? || self.has_language?
299
+
300
+ case other
301
+ when RDF::Literal::Numeric, RDF::Literal::Boolean,
302
+ RDF::Literal::Date, RDF::Literal::Time, RDF::Literal::DateTime
303
+ # Invald types can be compared without raising a TypeError if literal has a language (open-eq-08)
304
+ !other.valid? && self.has_language?
305
+ else
306
+ case other.datatype
307
+ when XSD.string
308
+ true
309
+ when nil
310
+ # A different language will not generate a type error
311
+ other.has_language?
312
+ else
313
+ # An unknown datatype may not be used for comparison, unless it has a language? (open-eq-8)
314
+ self.has_language?
315
+ end
316
+ end
317
+ end
318
+
319
+ ##
320
+ # Validates the value using {#valid?}, raising an error if the value is
321
+ # invalid.
322
+ #
323
+ # @return [RDF::Literal] `self`
324
+ # @raise [ArgumentError] if the value is invalid
325
+ # @since 0.2.1
326
+ def validate!
327
+ raise ArgumentError, "#{to_s.inspect} is not a valid <#{datatype.to_s}> literal" if invalid?
328
+ self
329
+ end
330
+ alias_method :validate, :validate!
331
+
332
+ ##
333
+ # Returns a copy of this literal converted into its canonical lexical
334
+ # representation.
335
+ #
336
+ # Subclasses should override `#canonicalize!` as needed and appropriate,
337
+ # not this method.
338
+ #
339
+ # @return [RDF::Literal]
340
+ # @since 0.2.1
341
+ def canonicalize
342
+ self.dup.canonicalize!
343
+ end
344
+
345
+ ##
346
+ # Converts this literal into its canonical lexical representation.
347
+ #
348
+ # Subclasses should override this as needed and appropriate.
349
+ #
350
+ # @return [RDF::Literal] `self`
351
+ # @since 0.3.0
352
+ def canonicalize!
353
+ @language = @language.to_s.downcase.to_sym if @language
354
+ self
355
+ end
356
+
357
+ ##
358
+ # Returns the value as a string.
359
+ #
360
+ # @return [String]
361
+ def to_s
362
+ @object.to_s
363
+ end
364
+
365
+ ##
366
+ # Returns a developer-friendly representation of `self`.
367
+ #
368
+ # @return [String]
369
+ def inspect
370
+ sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, RDF::NTriples.serialize(self))
371
+ end
372
+ end # Literal
373
+ end # RDF
@@ -0,0 +1,156 @@
1
+ module RDF
2
+ ##
3
+ # An RDF blank node, also known as an anonymous or unlabeled node.
4
+ #
5
+ # @example Creating a blank node with an implicit identifier
6
+ # bnode = RDF::Node.new
7
+ #
8
+ # @example Creating a blank node with an UUID identifier
9
+ # bnode = RDF::Node.uuid
10
+ # bnode.to_s #=> "_:504c0a30-0d11-012d-3f50-001b63cac539"
11
+ #
12
+ class Node
13
+ include RDF::Resource
14
+
15
+ ##
16
+ # Returns a blank node with a random UUID-based identifier.
17
+ #
18
+ # @param [Hash{Symbol => Object}] options
19
+ # @option options [Regexp] :grammar (nil)
20
+ # a grammar specification that the generated UUID must match
21
+ # @return [RDF::Node]
22
+ def self.uuid(options = {})
23
+ case
24
+ when options[:grammar]
25
+ # The UUID is generated such that its initial part is guaranteed
26
+ # to match the given `grammar`, e.g. `/^[A-Za-z][A-Za-z0-9]*/`.
27
+ # Some RDF storage systems (e.g. AllegroGraph) require this.
28
+ # @see http://github.com/bendiken/rdf/pull/43
29
+ uuid = RDF::Util::UUID.generate(options) until uuid =~ options[:grammar]
30
+ else
31
+ uuid = RDF::Util::UUID.generate(options)
32
+ end
33
+ self.new(uuid)
34
+ end
35
+
36
+ ##
37
+ # Alias for `RDF::Node.new`, at the moment.
38
+ #
39
+ # @private
40
+ # @param [#to_s] id
41
+ # @return [RDF::Node]
42
+ # @since 0.2.0
43
+ def self.intern(id)
44
+ self.new(id)
45
+ end
46
+
47
+ ##
48
+ # Override #dup to remember original object.
49
+ # This allows .eql? to determine that two nodes
50
+ # are the same thing, and not different nodes
51
+ # instantiated with the same identifier.
52
+ # @return [RDF::Node]
53
+ def dup
54
+ node = super
55
+ node.original = self.original || self
56
+ node
57
+ end
58
+
59
+ ##
60
+ # Originally instantiated node, if any
61
+ # @return [RDF::Node]
62
+ attr_accessor :original
63
+
64
+ # @return [String]
65
+ attr_accessor :id
66
+
67
+ ##
68
+ # @param [#to_s] id
69
+ def initialize(id = nil)
70
+ @id = (id || "g#{__id__.to_i.abs}").to_s
71
+ end
72
+
73
+ ##
74
+ # Returns `true`.
75
+ #
76
+ # @return [Boolean]
77
+ def node?
78
+ true
79
+ end
80
+
81
+ ##
82
+ # Returns `true`.
83
+ #
84
+ # @return [Boolean]
85
+ def anonymous?
86
+ true
87
+ end
88
+
89
+ alias_method :unlabeled?, :anonymous?
90
+
91
+ ##
92
+ # Returns `false`.
93
+ #
94
+ # @return [Boolean]
95
+ def labeled?
96
+ !unlabeled?
97
+ end
98
+
99
+ ##
100
+ # Returns a hash code for this blank node.
101
+ #
102
+ # @return [Fixnum]
103
+ def hash
104
+ @id.hash
105
+ end
106
+
107
+ ##
108
+ # Determins if `self` is the same term as `other`.
109
+ #
110
+ # In this case, nodes must be the same object
111
+ #
112
+ # @param [Node] other
113
+ # @return [Boolean]
114
+ def eql?(other)
115
+ other.is_a?(RDF::Node) && (self.original || self).equal?(other.original || other)
116
+ end
117
+
118
+ ##
119
+ # Checks whether this blank node is equal to `other` (type checking).
120
+ #
121
+ # In this case, different nodes having the same id are considered the same.
122
+ #
123
+ # Per SPARQL data-r2/expr-equal/eq-2-2, numeric can't be compared with other types
124
+ #
125
+ # @param [Object] other
126
+ # @return [Boolean]
127
+ # @see http://www.w3.org/TR/rdf-sparql-query/#func-RDFterm-equal
128
+ def ==(other)
129
+ case other
130
+ when Literal
131
+ # If other is a Literal, reverse test to consolodate complex type checking logic
132
+ other == self
133
+ else
134
+ other.respond_to?(:node?) && other.node? && other.respond_to?(:id) && @id == other.id
135
+ end
136
+ end
137
+ alias_method :===, :==
138
+
139
+ ##
140
+ # Returns a string representation of this blank node.
141
+ #
142
+ # @return [String]
143
+ def to_s
144
+ "_:%s" % @id.to_s
145
+ end
146
+
147
+ ##
148
+ # Returns a symbol representation of this blank node.
149
+ #
150
+ # @return [Symbol]
151
+ # @since 0.2.0
152
+ def to_sym
153
+ @id.to_s.to_sym
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,28 @@
1
+ module RDF
2
+ ##
3
+ # An RDF resource.
4
+ module Resource
5
+ include RDF::Term
6
+
7
+ ##
8
+ # Instantiates an {RDF::Node} or an {RDF::URI}, depending on the given
9
+ # argument.
10
+ #
11
+ # @return [RDF::Resource]
12
+ def self.new(*args, &block)
13
+ case arg = args.shift
14
+ when Symbol then Node.intern(arg, *args, &block)
15
+ when /^_:(.*)$/ then Node.new($1, *args, &block)
16
+ else URI.new(arg, *args, &block)
17
+ end
18
+ end
19
+
20
+ ##
21
+ # Returns `true` to indicate that this value is a resource.
22
+ #
23
+ # @return [Boolean]
24
+ def resource?
25
+ true
26
+ end
27
+ end # Resource
28
+ end # RDF