saxon-rb 0.6.0-java → 0.8.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ module Saxon
2
+ module FeatureFlags
3
+ # Helper methods to create feature restrictions in the library. To be mixed
4
+ # in to library classes.
5
+ module Helpers
6
+ # specify that the method named can only run if the version constraint is satisfied
7
+ #
8
+ # @param method_name [Symbol] the name of the method
9
+ # @param version_constraint [String] the version constraint (<tt>'>= 9.9'</tt>)
10
+ def requires_saxon_version(method_name, version_constraint)
11
+ Saxon::FeatureFlags::Version.create(self, method_name, version_constraint)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,100 @@
1
+ require_relative '../version/library'
2
+ require_relative 'errors'
3
+
4
+ module Saxon
5
+ module FeatureFlags
6
+ # Restrict a specific method to only work with the specified Saxon version
7
+ class Version
8
+ # Modify the method so that it will only run if the version constraint is
9
+ # satisfied.
10
+ #
11
+ # We can't know what version of Saxon is in use at code load time, only
12
+ # once {Saxon::Loader#load!} has been called, either explicitly or by
13
+ # calling a method which requires a Saxon Java object. Therefore, we have
14
+ # to check this the first time someone calls the method.
15
+ #
16
+ # Creating this check replaces the method on the target class with one
17
+ # that checks the version, and runs the original version if its constraint
18
+ # is satisfied.
19
+ #
20
+ # To avoid performing the check every time the method is called, once we
21
+ # know whether or not the constraint is satisfied, we assume that the
22
+ # verion of Saxon cannot be unloaded and a new one loaded in its place and
23
+ # replace our version checking method with the original (if the constraint
24
+ # is passed), or with one that simply +raise+s the constraint error.
25
+ #
26
+ # @param klass [Class] the class the method lives on
27
+ # @param method_name [Symbol] the name of the method to be constrained
28
+ # @param version_constraint [String] the version constraint
29
+ # (<tt>'>9.9.1.7'</tt> or <tt>'>= 9.9'</tt>)
30
+ def self.create(klass, method_name, version_constraint)
31
+ method = klass.instance_method(method_name)
32
+ version_check = new(version_constraint)
33
+
34
+
35
+ klass.send(:define_method, method_name, ->(*args) {
36
+ if version_check.ok?
37
+ klass.send(:define_method, method_name, method)
38
+ method.bind(self).call(*args)
39
+ else
40
+ klass.send(:define_method, method_name, ->(*args) {
41
+ raise UnavailableInThisSaxonVersionError
42
+ })
43
+ raise UnavailableInThisSaxonVersionError
44
+ end
45
+ })
46
+ end
47
+
48
+ # @return [String] the complete constraint string
49
+ attr_reader :constraint_string
50
+ # @return [Symbol] the extracted comparison operator
51
+ attr_reader :comparison_operator
52
+ # @return [String] the extracted version string
53
+ attr_reader :version_string
54
+
55
+ # Create a version constraint check from the supplied constraint string
56
+ #
57
+ # @param constraint_string [String] the version constraint
58
+ def initialize(constraint_string)
59
+ @constraint_string = constraint_string
60
+ @comparison_operator, @version_string = parse_version_constraint(constraint_string)
61
+ end
62
+
63
+ # Reports if the version constraint is satisfied or not.
64
+ #
65
+ # @return [Boolean] true if the constraint is satisfied, false otherwise
66
+ def ok?
67
+ Saxon::Version::Library.loaded_version.send(comparison_operator, constraint_version)
68
+ end
69
+
70
+ # Generates a {Saxon::Version::Library} representing the version specified
71
+ # in the constraint that can be compared with the loaded Saxon version
72
+ #
73
+ # @return [Saxon::Version::Library] the constraint version
74
+ def constraint_version
75
+ @constraint_version ||= begin
76
+ components = version_string.split('.').map { |n| Integer(n, 10) }
77
+ Saxon::Version::Library.new(version_string, components, 'HE')
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def parse_version_constraint(version_constraint)
84
+ op_string, version_string = version_constraint.split
85
+
86
+ comparison_operator = parse_operator_string(op_string)
87
+ [comparison_operator, version_string]
88
+ end
89
+
90
+ def parse_operator_string(op_string)
91
+ case op_string
92
+ when '~>'
93
+ :pessimistic_compare
94
+ else
95
+ op_string.to_sym
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -6,8 +6,49 @@ require_relative 'item_type/value_to_ruby'
6
6
  module Saxon
7
7
  # Represent XDM types abstractly
8
8
  class ItemType
9
- # Error raised when a Ruby class has no equivalent XDM type to be converted
10
- # into
9
+ # lazy-loading Hash so we can avoid eager-loading saxon Jars, which prevents
10
+ # using external Saxon Jars unless the user is more careful than they should
11
+ # have to be.
12
+ class LazyReadOnlyHash
13
+ include Enumerable
14
+
15
+ attr_reader :loaded_hash, :load_mutex, :init_block
16
+ private :loaded_hash, :load_mutex, :init_block
17
+
18
+ def initialize(&init_block)
19
+ @init_block = init_block
20
+ @load_mutex = Mutex.new
21
+ @loaded_hash = nil
22
+ end
23
+
24
+ def [](key)
25
+ ensure_loaded!
26
+ loaded_hash[key]
27
+ end
28
+
29
+ def fetch(*args, &block)
30
+ ensure_loaded!
31
+ loaded_hash.fetch(*args, &block)
32
+ end
33
+
34
+ def each(&block)
35
+ ensure_loaded!
36
+ loaded_hash.each(&block)
37
+ end
38
+
39
+ private
40
+
41
+ def ensure_loaded!
42
+ return true unless loaded_hash.nil?
43
+ load_mutex.synchronize do
44
+ return true unless loaded_hash.nil?
45
+ @loaded_hash = init_block.call
46
+ end
47
+ end
48
+ end
49
+
50
+ # Error raised when a Ruby class has no equivalent XDM type to
51
+ # be converted into
11
52
  class UnmappedRubyTypeError < StandardError
12
53
  def initialize(class_name)
13
54
  @class_name = class_name
@@ -54,85 +95,89 @@ module Saxon
54
95
  }.freeze
55
96
 
56
97
  # A mapping of QNames to XDM type constants
57
- QNAME_MAPPING = Hash[{
58
- 'anyAtomicType' => :ANY_ATOMIC_VALUE,
59
- 'anyURI' => :ANY_URI,
60
- 'base64Binary' => :BASE64_BINARY,
61
- 'boolean' => :BOOLEAN,
62
- 'byte' => :BYTE,
63
- 'date' => :DATE,
64
- 'dateTime' => :DATE_TIME,
65
- 'dateTimeStamp' => :DATE_TIME_STAMP,
66
- 'dayTimeDuration' => :DAY_TIME_DURATION,
67
- 'decimal' => :DECIMAL,
68
- 'double' => :DOUBLE,
69
- 'duration' => :DURATION,
70
- 'ENTITY' => :ENTITY,
71
- 'float' => :FLOAT,
72
- 'gDay' => :G_DAY,
73
- 'gMonth' => :G_MONTH,
74
- 'gMonthDay' => :G_MONTH_DAY,
75
- 'gYear' => :G_YEAR,
76
- 'gYearMonth' => :G_YEAR_MONTH,
77
- 'hexBinary' => :HEX_BINARY,
78
- 'ID' => :ID,
79
- 'IDREF' => :IDREF,
80
- 'int' => :INT,
81
- 'integer' => :INTEGER,
82
- 'language' => :LANGUAGE,
83
- 'long' => :LONG,
84
- 'Name' => :NAME,
85
- 'NCName' => :NCNAME,
86
- 'negativeInteger' => :NEGATIVE_INTEGER,
87
- 'NMTOKEN' => :NMTOKEN,
88
- 'nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
89
- 'nonPositiveInteger' => :NON_POSITIVE_INTEGER,
90
- 'normalizedString' => :NORMALIZED_STRING,
91
- 'NOTATION' => :NOTATION,
92
- 'numeric' => :NUMERIC,
93
- 'positiveInteger' => :POSITIVE_INTEGER,
94
- 'QName' => :QNAME,
95
- 'short' => :SHORT,
96
- 'string' => :STRING,
97
- 'time' => :TIME,
98
- 'token' => :TOKEN,
99
- 'unsignedByte' => :UNSIGNED_BYTE,
100
- 'unsignedInt' => :UNSIGNED_INT,
101
- 'unsignedLong' => :UNSIGNED_LONG,
102
- 'unsignedShort' => :UNSIGNED_SHORT,
103
- 'untypedAtomic' => :UNTYPED_ATOMIC,
104
- 'yearMonthDuration' => :YEAR_MONTH_DURATION
105
- }.map { |local_name, constant|
106
- qname = Saxon::QName.create({
107
- prefix: 'xs', uri: 'http://www.w3.org/2001/XMLSchema',
108
- local_name: local_name
109
- })
110
- [qname, constant]
111
- }].freeze
98
+ QNAME_MAPPING = LazyReadOnlyHash.new do
99
+ {
100
+ 'anyAtomicType' => :ANY_ATOMIC_VALUE,
101
+ 'anyURI' => :ANY_URI,
102
+ 'base64Binary' => :BASE64_BINARY,
103
+ 'boolean' => :BOOLEAN,
104
+ 'byte' => :BYTE,
105
+ 'date' => :DATE,
106
+ 'dateTime' => :DATE_TIME,
107
+ 'dateTimeStamp' => :DATE_TIME_STAMP,
108
+ 'dayTimeDuration' => :DAY_TIME_DURATION,
109
+ 'decimal' => :DECIMAL,
110
+ 'double' => :DOUBLE,
111
+ 'duration' => :DURATION,
112
+ 'ENTITY' => :ENTITY,
113
+ 'float' => :FLOAT,
114
+ 'gDay' => :G_DAY,
115
+ 'gMonth' => :G_MONTH,
116
+ 'gMonthDay' => :G_MONTH_DAY,
117
+ 'gYear' => :G_YEAR,
118
+ 'gYearMonth' => :G_YEAR_MONTH,
119
+ 'hexBinary' => :HEX_BINARY,
120
+ 'ID' => :ID,
121
+ 'IDREF' => :IDREF,
122
+ 'int' => :INT,
123
+ 'integer' => :INTEGER,
124
+ 'language' => :LANGUAGE,
125
+ 'long' => :LONG,
126
+ 'Name' => :NAME,
127
+ 'NCName' => :NCNAME,
128
+ 'negativeInteger' => :NEGATIVE_INTEGER,
129
+ 'NMTOKEN' => :NMTOKEN,
130
+ 'nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
131
+ 'nonPositiveInteger' => :NON_POSITIVE_INTEGER,
132
+ 'normalizedString' => :NORMALIZED_STRING,
133
+ 'NOTATION' => :NOTATION,
134
+ 'numeric' => :NUMERIC,
135
+ 'positiveInteger' => :POSITIVE_INTEGER,
136
+ 'QName' => :QNAME,
137
+ 'short' => :SHORT,
138
+ 'string' => :STRING,
139
+ 'time' => :TIME,
140
+ 'token' => :TOKEN,
141
+ 'unsignedByte' => :UNSIGNED_BYTE,
142
+ 'unsignedInt' => :UNSIGNED_INT,
143
+ 'unsignedLong' => :UNSIGNED_LONG,
144
+ 'unsignedShort' => :UNSIGNED_SHORT,
145
+ 'untypedAtomic' => :UNTYPED_ATOMIC,
146
+ 'yearMonthDuration' => :YEAR_MONTH_DURATION
147
+ }.map { |local_name, constant|
148
+ qname = Saxon::QName.create({
149
+ prefix: 'xs', uri: 'http://www.w3.org/2001/XMLSchema',
150
+ local_name: local_name
151
+ })
152
+ [qname, constant]
153
+ }.to_h.freeze
154
+ end
112
155
 
113
156
  # A mapping of type names/QNames to XDM type constants
114
- STR_MAPPING = {
115
- 'array(*)' => :ANY_ARRAY,
116
- 'item()' => :ANY_ITEM,
117
- 'map(*)' => :ANY_MAP,
118
- 'node()' => :ANY_NODE
119
- }.merge(
120
- Hash[QNAME_MAPPING.map { |qname, v| [qname.to_s, v] }]
121
- ).freeze
157
+ STR_MAPPING = LazyReadOnlyHash.new do
158
+ {
159
+ 'array(*)' => :ANY_ARRAY,
160
+ 'item()' => :ANY_ITEM,
161
+ 'map(*)' => :ANY_MAP,
162
+ 'node()' => :ANY_NODE
163
+ }.merge(
164
+ Hash[QNAME_MAPPING.map { |qname, v| [qname.to_s, v] }]
165
+ ).freeze
166
+ end
122
167
 
123
168
  # convertors to generate lexical strings for a given {ItemType}, as a hash keyed on the ItemType
124
- ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS = Hash[
169
+ ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS = LazyReadOnlyHash.new do
125
170
  LexicalStringConversion::Convertors.constants.map { |const|
126
171
  [S9API::ItemType.const_get(const), LexicalStringConversion::Convertors.const_get(const)]
127
- }
128
- ].freeze
172
+ }.to_h.freeze
173
+ end
129
174
 
130
175
  # convertors from {XDM::AtomicValue} to a ruby primitve value, as a hash keyed on the ItemType
131
- ATOMIC_VALUE_TO_RUBY_CONVERTORS = Hash[
176
+ ATOMIC_VALUE_TO_RUBY_CONVERTORS = LazyReadOnlyHash.new do
132
177
  ValueToRuby::Convertors.constants.map { |const|
133
178
  [S9API::ItemType.const_get(const), ValueToRuby::Convertors.const_get(const)]
134
- }
135
- ].freeze
179
+ }.to_h.freeze
180
+ end
136
181
 
137
182
  class << self
138
183
  # Get an appropriate {ItemType} for a Ruby type or given a type name as a
@@ -222,9 +222,9 @@ module Saxon
222
222
  # Pattern fragments that can be combined to help create the lexical space
223
223
  # patterns in {Patterns}
224
224
  module PatternFragments
225
+ # The time part of the XSD Duration format allows T0H1M1S, T1H1M, T1M1S, T1H1S, T1H, T1M, T1S
225
226
  TIME_DURATION = /(?:T
226
227
  (?:
227
- # The time part of the format allows T0H1M1S, T1H1M, T1M1S, T1H1S, T1H, T1M, T1S
228
228
  [0-9]+[HM]|
229
229
  [0-9]+(?:\.[0-9]+)?S|
230
230
  [0-9]+H[0-9]+M|
@@ -233,46 +233,78 @@ module Saxon
233
233
  [0-9]+H[0-9]+M[0-9]+(?:\.[0-9]+)?S
234
234
  )
235
235
  )?/x
236
+ # XSD Date
236
237
  DATE = /-?[0-9]{4}-[0-9]{2}-[0-9]{2}/
238
+ # XSD DateTime Time
237
239
  TIME = /[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?/
240
+ # XSD DateTime Timezone
238
241
  TIME_ZONE = /(?:[\-+][0-9]{2}:[0-9]{2}|Z)?/
242
+ # Valid first characers in an NCName
239
243
  NCNAME_START_CHAR = '[A-Z]|_|[a-z]|[\u{C0}-\u{D6}]|[\u{D8}-\u{F6}]|[\u{F8}-\u{2FF}]|[\u{370}-\u{37D}]|[\u{37F}-\u{1FFF}]|[\u{200C}-\u{200D}]|[\u{2070}-\u{218F}]|[\u{2C00}-\u{2FEF}]|[\u{3001}-\u{D7FF}]|[\u{F900}-\u{FDCF}]|[\u{FDF0}-\u{FFFD}]|[\u{10000}-\u{EFFFF}]'
244
+ # Valid first characters in an XML Name
240
245
  NAME_START_CHAR = ":|" + NCNAME_START_CHAR
246
+ # Valid characters within an NCName
241
247
  NCNAME_CHAR = NCNAME_START_CHAR + '|-|\.|[0-9]|\u{B7}|[\u{0300}-\u{036F}]|[\u{203F}-\u{2040}]'
248
+ # Valid characters within an XML Name
242
249
  NAME_CHAR = ":|" + NCNAME_CHAR
243
250
  end
244
251
 
245
252
  # A collection of lexical space patterns for XDM types
246
253
  module Patterns
254
+ # Construct a Regexp from an array of patterns
255
+ # @param patterns [Array<String>]
256
+ # @return [Regexp]
247
257
  def self.build(*patterns)
248
258
  Regexp.new((['\A'] + patterns.map(&:to_s) + ['\z']).join(''))
249
259
  end
260
+ # @see https://www.w3.org/TR/xmlschema-2/#date-lexical-representation
250
261
  DATE = build(PatternFragments::DATE, PatternFragments::TIME_ZONE)
262
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
251
263
  DATE_TIME = build(PatternFragments::DATE, 'T', PatternFragments::TIME, PatternFragments::TIME_ZONE)
264
+ # @see https://www.w3.org/TR/xmlschema-2/#time-lexical-repr
252
265
  TIME = build(PatternFragments::TIME, PatternFragments::TIME_ZONE)
266
+ # @see https://www.w3.org/TR/xmlschema-2/#duration-lexical-repr
253
267
  DURATION = build(/-?P(?!\z)(?:[0-9]+Y)?(?:[0-9]+M)?(?:[0-9]+D)?/, PatternFragments::TIME_DURATION)
268
+ # @see https://www.w3.org/TR/xmlschema-2/#duration-lexical-repr
254
269
  DAY_TIME_DURATION = build(/-?P(?!\z)(?:[0-9]+D)?/, PatternFragments::TIME_DURATION)
270
+ # @see https://www.w3.org/TR/xmlschema-2/#duration-lexical-repr
255
271
  YEAR_MONTH_DURATION = /\A-?P(?!\z)(?:[0-9]+Y)?(?:[0-9]+M)?\z/
272
+ # @see https://www.w3.org/TR/xmlschema-2/#gDay-lexical-repr
256
273
  G_DAY = build(/---([0-9]{2})/, PatternFragments::TIME_ZONE)
274
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonth-lexical-repr
257
275
  G_MONTH = build(/--([0-9]{2})/, PatternFragments::TIME_ZONE)
276
+ # @see https://www.w3.org/TR/xmlschema-2/#gYear-lexical-repr
258
277
  G_YEAR = build(/(-?[0-9]{4,})/, PatternFragments::TIME_ZONE)
278
+ # @see https://www.w3.org/TR/xmlschema-2/#gYearMonth-lexical-repr
259
279
  G_YEAR_MONTH = build(/-?([0-9]{4,})-([0-9]{2})/, PatternFragments::TIME_ZONE)
280
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonthDay-lexical-repr
260
281
  G_MONTH_DAY = build(/--([0-9]{2})-([0-9]{2})/, PatternFragments::TIME_ZONE)
282
+ # @see https://www.w3.org/TR/xmlschema-2/#integer-lexical-representation
261
283
  INTEGER = /\A[+-]?[0-9]+\z/
284
+ # @see https://www.w3.org/TR/xmlschema-2/#decimal-lexical-representation
262
285
  DECIMAL = /\A[+-]?[0-9]+(?:\.[0-9]+)?\z/
286
+ # @see https://www.w3.org/TR/xmlschema-2/#float-lexical-representation
263
287
  FLOAT = /\A(?:[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][0-9]+)?|-?INF|NaN)\z/
288
+ # @see https://www.w3.org/TR/xmlschema-2/#NCName
264
289
  NCNAME = build("(?:#{PatternFragments::NCNAME_START_CHAR})", "(?:#{PatternFragments::NCNAME_CHAR})*")
290
+ # @see https://www.w3.org/TR/xmlschema-2/#Name
265
291
  NAME = build("(?:#{PatternFragments::NAME_START_CHAR})", "(?:#{PatternFragments::NAME_CHAR})*")
292
+ # @see https://www.w3.org/TR/xmlschema-2/#token
266
293
  TOKEN = /\A[^\u0020\u000A\u000D\u0009]+(?: [^\u0020\u000A\u000D\u0009]+)*\z/
294
+ # @see https://www.w3.org/TR/xmlschema-2/#normalizedString
267
295
  NORMALIZED_STRING = /\A[^\u000A\u000D\u0009]+\z/
296
+ # @see https://www.w3.org/TR/xmlschema-2/#NMTOKEN
268
297
  NMTOKEN = build("(?:#{PatternFragments::NAME_CHAR})+")
298
+ # @see https://www.w3.org/TR/xmlschema-2/#language
269
299
  LANGUAGE = /\A[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*\z/
300
+ # @see https://www.w3.org/TR/xmlschema-2/#base64Binary
270
301
  BASE64_BINARY = /\A(?:(?:[A-Za-z0-9+\/] ?){4})*(?:(?:[A-Za-z0-9+\/] ?){3}[A-Za-z0-9+\/]|(?:[A-Za-z0-9+\/] ?){2}[AEIMQUYcgkosw048] ?=|[A-Za-z0-9+\/] ?[AQgw] ?= ?=)?\z/
271
302
  end
272
303
 
273
304
  # Convertors from Ruby values to lexical string representations for a
274
305
  # particular XDM type
275
306
  module Convertors
307
+ # @see https://www.w3.org/TR/xmlschema-2/#anyURI
276
308
  ANY_URI = ->(value, item_type) {
277
309
  uri_classes = [URI::Generic]
278
310
  case value
@@ -286,13 +318,17 @@ module Saxon
286
318
  end
287
319
  end
288
320
  }
321
+ # @see https://www.w3.org/TR/xmlschema-2/#base64Binary
289
322
  BASE64_BINARY = ->(value, item_type) {
290
323
  Base64.strict_encode64(value.to_s.force_encoding(Encoding::ASCII_8BIT))
291
324
  }
325
+ # @see https://www.w3.org/TR/xmlschema-2/#boolean
292
326
  BOOLEAN = ->(value, item_type) {
293
327
  value ? 'true' : 'false'
294
328
  }
329
+ # @see https://www.w3.org/TR/xmlschema-2/#byte
295
330
  BYTE = ByteConversion.new
331
+ # @see https://www.w3.org/TR/xmlschema-2/#date
296
332
  DATE = ->(value, item_type) {
297
333
  if value.respond_to?(:strftime)
298
334
  value.strftime('%F')
@@ -300,6 +336,7 @@ module Saxon
300
336
  LexicalStringConversion.validate(value, item_type, Patterns::DATE)
301
337
  end
302
338
  }
339
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime
303
340
  DATE_TIME = ->(value, item_type) {
304
341
  if value.respond_to?(:strftime)
305
342
  value.strftime('%FT%T%:z')
@@ -307,11 +344,15 @@ module Saxon
307
344
  LexicalStringConversion.validate(value, item_type, Patterns::DATE_TIME)
308
345
  end
309
346
  }
347
+ # @see https://www.w3.org/TR/xmlschema-2/#time
310
348
  TIME = ->(value, item_type) {
311
349
  LexicalStringConversion.validate(value, item_type, Patterns::TIME)
312
350
  }
351
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime
313
352
  DATE_TIME_STAMP = DATE_TIME
353
+ # @see https://www.w3.org/TR/xmlschema-2/#duration
314
354
  DAY_TIME_DURATION = DurationConversion.new(Patterns::DAY_TIME_DURATION)
355
+ # @see https://www.w3.org/TR/xmlschema-2/#decimal
315
356
  DECIMAL = ->(value, item_type) {
316
357
  case value
317
358
  when ::Integer
@@ -324,19 +365,25 @@ module Saxon
324
365
  LexicalStringConversion.validate(value, item_type, Patterns::DECIMAL)
325
366
  end
326
367
  }
368
+ # @see https://www.w3.org/TR/xmlschema-2/#double
327
369
  DOUBLE = FloatConversion.new(:single)
370
+ # @see https://www.w3.org/TR/xmlschema-2/#duration
328
371
  DURATION = DurationConversion.new(Patterns::DURATION)
372
+ # @see https://www.w3.org/TR/xmlschema-2/#float
329
373
  FLOAT = FloatConversion.new
374
+ # @see https://www.w3.org/TR/xmlschema-2/#gDay
330
375
  G_DAY = GDateConversion.new({
331
376
  bounds: 1..31,
332
377
  validation_pattern: Patterns::G_DAY,
333
378
  integer_formatter: ->(value) { '---%02d' }
334
379
  })
380
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonth
335
381
  G_MONTH = GDateConversion.new({
336
382
  bounds: 1..12,
337
383
  validation_pattern: Patterns::G_MONTH,
338
384
  integer_formatter: ->(value) { '--%02d' }
339
385
  })
386
+ # @see https://www.w3.org/TR/xmlschema-2/#gMonthDay
340
387
  G_MONTH_DAY = ->(value, item_type) {
341
388
  month_days = {
342
389
  1 => 31,
@@ -359,6 +406,7 @@ module Saxon
359
406
  raise Errors::RubyValueOutOfBounds.new(value, item_type) if day > month_days[month]
360
407
  formatted_value
361
408
  }
409
+ # @see https://www.w3.org/TR/xmlschema-2/#gYear
362
410
  G_YEAR = GDateConversion.new({
363
411
  bounds: ->(value) { value != 0 },
364
412
  validation_pattern: Patterns::G_YEAR,
@@ -366,6 +414,7 @@ module Saxon
366
414
  value.negative? ? '%05d' : '%04d'
367
415
  }
368
416
  })
417
+ # @see https://www.w3.org/TR/xmlschema-2/#gYearMonth
369
418
  G_YEAR_MONTH = ->(value, item_type) {
370
419
  formatted_value = LexicalStringConversion.validate(value, item_type, Patterns::G_YEAR_MONTH)
371
420
  year, month = Patterns::G_YEAR_MONTH.match(formatted_value).captures.take(2).map { |i|
@@ -376,43 +425,68 @@ module Saxon
376
425
  end
377
426
  value
378
427
  }
428
+ # @see https://www.w3.org/TR/xmlschema-2/#hexBinary
379
429
  HEX_BINARY = ->(value, item_type) {
380
430
  value.to_s.force_encoding(Encoding::ASCII_8BIT).each_byte.map { |b| b.to_s(16) }.join
381
431
  }
432
+ # @see https://www.w3.org/TR/xmlschema-2/#int
382
433
  INT = IntegerConversion.new(-2147483648, 2147483647)
434
+ # @see https://www.w3.org/TR/xmlschema-2/#integer
383
435
  INTEGER = IntegerConversion.new(nil, nil)
436
+ # @see https://www.w3.org/TR/xmlschema-2/#language
384
437
  LANGUAGE = ->(value, item_type) {
385
438
  LexicalStringConversion.validate(value, item_type, Patterns::LANGUAGE)
386
439
  }
440
+ # @see https://www.w3.org/TR/xmlschema-2/#long
387
441
  LONG = IntegerConversion.new(-9223372036854775808, 9223372036854775807)
442
+ # @see https://www.w3.org/TR/xmlschema-2/#Name
388
443
  NAME = ->(value, item_type) {
389
444
  LexicalStringConversion.validate(value, item_type, Patterns::NAME)
390
445
  }
446
+ # @see https://www.w3.org/TR/xmlschema-2/#ID
447
+ # @see https://www.w3.org/TR/xmlschema-2/#IDREF
448
+ # @see https://www.w3.org/TR/xmlschema-2/#ENTITY
449
+ # @see https://www.w3.org/TR/xmlschema-2/#NCName
391
450
  ID = IDREF = ENTITY = NCNAME = ->(value, item_type) {
392
451
  LexicalStringConversion.validate(value, item_type, Patterns::NCNAME)
393
452
  }
453
+ # @see https://www.w3.org/TR/xmlschema-2/#negativeInteger
394
454
  NEGATIVE_INTEGER = IntegerConversion.new(nil, -1)
455
+ # @see https://www.w3.org/TR/xmlschema-2/#NMTOKEN
395
456
  NMTOKEN = ->(value, item_type) {
396
457
  LexicalStringConversion.validate(value, item_type, Patterns::NMTOKEN)
397
458
  }
459
+ # @see https://www.w3.org/TR/xmlschema-2/#nonNegativeInteger
398
460
  NON_NEGATIVE_INTEGER = IntegerConversion.new(0, nil)
461
+ # @see https://www.w3.org/TR/xmlschema-2/#nonPositiveInteger
399
462
  NON_POSITIVE_INTEGER = IntegerConversion.new(nil, 0)
463
+ # @see https://www.w3.org/TR/xmlschema-2/#normalizedString
400
464
  NORMALIZED_STRING = ->(value, item_type) {
401
465
  LexicalStringConversion.validate(value, item_type, Patterns::NORMALIZED_STRING)
402
466
  }
467
+ # @see https://www.w3.org/TR/xmlschema-2/#positiveInteger
403
468
  POSITIVE_INTEGER = IntegerConversion.new(1, nil)
469
+ # @see https://www.w3.org/TR/xmlschema-2/#short
404
470
  SHORT = IntegerConversion.new(-32768, 32767)
405
471
  # STRING (It's questionable whether anything needs doing here)
472
+ # @see https://www.w3.org/TR/xmlschema-2/#token
406
473
  TOKEN = ->(value, item_type) {
407
474
  LexicalStringConversion.validate(value, item_type, Patterns::TOKEN)
408
475
  }
476
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
409
477
  UNSIGNED_BYTE = ByteConversion.new(:unsigned)
478
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedInt
410
479
  UNSIGNED_INT = IntegerConversion.new(0, 4294967295)
480
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedLong
411
481
  UNSIGNED_LONG = IntegerConversion.new(0, 18446744073709551615)
482
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedShort
412
483
  UNSIGNED_SHORT = IntegerConversion.new(0, 65535)
484
+ # @see https://www.w3.org/TR/xmlschema-2/#duration
413
485
  YEAR_MONTH_DURATION = ->(value, item_type) {
414
486
  LexicalStringConversion.validate(value, item_type, Patterns::YEAR_MONTH_DURATION)
415
487
  }
488
+ # @see https://www.w3.org/TR/xmlschema-2/#QName
489
+ # @see https://www.w3.org/TR/xmlschema-2/#NOTATION
416
490
  QNAME = NOTATION = ->(value, item_type) {
417
491
  raise Errors::UnconvertableNamespaceSensitveItemType
418
492
  }
@@ -430,6 +504,8 @@ module Saxon
430
504
  @value, @item_type = value, item_type
431
505
  end
432
506
 
507
+ # error message includes Ruby value and the XDM type conversion was
508
+ # being attempted to
433
509
  def to_s
434
510
  "Ruby value #{value.inspect} cannot be converted to an XDM #{item_type.type_name.to_s}"
435
511
  end
@@ -446,6 +522,7 @@ module Saxon
446
522
  @value, @item_type = value, item_type
447
523
  end
448
524
 
525
+ # error message includes Ruby value and the XDM type whose bounds it is outside of
449
526
  def to_s
450
527
  "Ruby value #{value.inspect} is outside the allowed bounds of an XDM #{item_type.type_name.to_s}"
451
528
  end