saxon-rb 0.4.0-java → 0.7.2-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +429 -42
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/Gemfile +2 -2
- data/README.md +358 -10
- data/Rakefile +237 -7
- data/docs/templates/plugin.rb +73 -0
- data/lib/net/sf/saxon/Saxon-HE/{9.9.1-5/Saxon-HE-9.9.1-5.jar → 9.9.1-6/Saxon-HE-9.9.1-6.jar} +0 -0
- data/lib/saxon-rb.rb +0 -0
- data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
- data/lib/saxon.rb +13 -0
- data/lib/saxon/axis_iterator.rb +8 -1
- data/lib/saxon/configuration.rb +16 -13
- data/lib/saxon/document_builder.rb +216 -5
- data/lib/saxon/feature_flags.rb +11 -0
- data/lib/saxon/feature_flags/errors.rb +8 -0
- data/lib/saxon/feature_flags/helpers.rb +15 -0
- data/lib/saxon/feature_flags/version.rb +100 -0
- data/lib/saxon/item_type.rb +129 -89
- data/lib/saxon/item_type/lexical_string_conversion.rb +214 -59
- data/lib/saxon/item_type/value_to_ruby.rb +25 -0
- data/lib/saxon/loader.rb +6 -1
- data/lib/saxon/nokogiri.rb +78 -0
- data/lib/saxon/occurrence_indicator.rb +32 -3
- data/lib/saxon/processor.rb +50 -5
- data/lib/saxon/qname.rb +37 -2
- data/lib/saxon/s9api.rb +5 -0
- data/lib/saxon/sequence_type.rb +131 -0
- data/lib/saxon/serializer.rb +3 -137
- data/lib/saxon/serializer/destination.rb +80 -0
- data/lib/saxon/serializer/object.rb +93 -0
- data/lib/saxon/serializer/output_properties.rb +83 -0
- data/lib/saxon/source.rb +207 -71
- data/lib/saxon/version.rb +7 -1
- data/lib/saxon/version/library.rb +89 -0
- data/lib/saxon/xdm.rb +7 -0
- data/lib/saxon/xdm/array.rb +16 -0
- data/lib/saxon/xdm/atomic_value.rb +10 -2
- data/lib/saxon/xdm/empty_sequence.rb +13 -0
- data/lib/saxon/xdm/external_object.rb +1 -0
- data/lib/saxon/xdm/function_item.rb +1 -0
- data/lib/saxon/xdm/item.rb +7 -0
- data/lib/saxon/xdm/map.rb +38 -0
- data/lib/saxon/xdm/node.rb +50 -1
- data/lib/saxon/xdm/sequence_like.rb +15 -0
- data/lib/saxon/xdm/value.rb +21 -5
- data/lib/saxon/xpath.rb +9 -0
- data/lib/saxon/xpath/compiler.rb +37 -2
- data/lib/saxon/xpath/executable.rb +53 -28
- data/lib/saxon/xpath/static_context.rb +25 -40
- data/lib/saxon/xpath/variable_declaration.rb +16 -49
- data/lib/saxon/xslt.rb +12 -0
- data/lib/saxon/xslt/compiler.rb +75 -6
- data/lib/saxon/xslt/evaluation_context.rb +30 -4
- data/lib/saxon/xslt/executable.rb +206 -29
- data/lib/saxon/xslt/invocation.rb +97 -0
- data/saxon-rb.gemspec +3 -3
- metadata +22 -10
- data/saxon.gemspec +0 -30
@@ -0,0 +1,83 @@
|
|
1
|
+
module Saxon
|
2
|
+
module Serializer
|
3
|
+
# Manage access to the serialization properties of this serializer, with
|
4
|
+
# hash-like access.
|
5
|
+
#
|
6
|
+
# Properties can be set explicitly through this API, or via XSLT or XQuery
|
7
|
+
# serialization options like +<xsl:output>+.
|
8
|
+
#
|
9
|
+
# Properties set explicitly here will override properties set through the
|
10
|
+
# document by +<xsl:output>+.
|
11
|
+
module OutputProperties
|
12
|
+
# @return [Saxon::Serializer::OutputProperties] hash-like access to the Output Properties
|
13
|
+
def output_property
|
14
|
+
@output_property ||= OutputProperties::Accessor.new(s9_serializer)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
# The private wrapper class that manages getting and setting output
|
20
|
+
# properties on a Serializer in an idiomatic Ruby-like way.
|
21
|
+
class Accessor
|
22
|
+
# @api private
|
23
|
+
# Provides mapping between symbols and the underlying Saxon property
|
24
|
+
# instances
|
25
|
+
def self.output_properties
|
26
|
+
@output_properties ||= Hash[
|
27
|
+
Saxon::S9API::Serializer::Property.values.map { |property|
|
28
|
+
qname = property.getQName
|
29
|
+
key = [
|
30
|
+
qname.getPrefix,
|
31
|
+
qname.getLocalName.tr('-', '_')
|
32
|
+
].reject { |str| str == '' }.join('_').to_sym
|
33
|
+
[key, property]
|
34
|
+
}
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :s9_serializer
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
def initialize(s9_serializer)
|
42
|
+
@s9_serializer = s9_serializer
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to fetch
|
46
|
+
def [](property)
|
47
|
+
s9_serializer.getOutputProperty(resolved_property(property))
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to set
|
51
|
+
# @param [String] value The string value of the property
|
52
|
+
def []=(property, value)
|
53
|
+
s9_serializer.setOutputProperty(resolved_property(property), value)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @overload fetch(property)
|
57
|
+
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to fetch
|
58
|
+
# @overload fetch(property, default)
|
59
|
+
# @param property [Symbol, Saxon::S9API::Serializer::Property] The property to fetch
|
60
|
+
# @param default [Object] The value to return if the property is unset
|
61
|
+
def fetch(property, default = nil)
|
62
|
+
explicit_value = self[property]
|
63
|
+
if explicit_value.nil? && !default.nil?
|
64
|
+
default
|
65
|
+
else
|
66
|
+
explicit_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def resolved_property(property_key)
|
73
|
+
case property_key
|
74
|
+
when Symbol
|
75
|
+
self.class.output_properties.fetch(property_key)
|
76
|
+
else
|
77
|
+
property_key
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/saxon/source.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'java'
|
2
2
|
require 'saxon/jaxp'
|
3
3
|
require 'uri'
|
4
|
+
require 'open-uri'
|
4
5
|
require 'pathname'
|
5
6
|
|
6
7
|
module Saxon
|
@@ -8,10 +9,13 @@ module Saxon
|
|
8
9
|
# the XML bytestream in. Provides some extra methods to make handling closing
|
9
10
|
# the source and its inputstream after consumption more idiomatic
|
10
11
|
class Source
|
12
|
+
# Helper methods for getting Java-useful representations of source document
|
13
|
+
# strings and files
|
11
14
|
module Helpers
|
12
15
|
# Given a File, or IO object which will return either #path or
|
13
16
|
# #base_uri, return the #base_uri, if present, or the #path, if present, or
|
14
17
|
# nil
|
18
|
+
#
|
15
19
|
# @param [File, IO] io A File or IO
|
16
20
|
# object representing the input XML file or data, or a String containing
|
17
21
|
# the XML
|
@@ -23,30 +27,92 @@ module Saxon
|
|
23
27
|
io.path if io.respond_to?(:path)
|
24
28
|
end
|
25
29
|
|
26
|
-
# Given a File or IO return a Java InputStream
|
27
|
-
#
|
28
|
-
#
|
30
|
+
# Given a File or IO return a Java InputStream, or an InputStreamReader if
|
31
|
+
# the Encoding is explicitly specified (rather than inferred from the
|
32
|
+
# <?xml charset="..."?>) declaration in the source.
|
33
|
+
#
|
34
|
+
# @param io [File, IO, org.jruby.util.IOInputStream, java.io.InputStream]
|
35
|
+
# input to be converted to an input stream
|
36
|
+
# @param encoding [Encoding, String] the character encoding to be used to
|
37
|
+
# for the stream, overriding the XML parser.
|
29
38
|
# @return [java.io.InputStream] the wrapped input
|
30
|
-
def self.inputstream(io)
|
31
|
-
case io
|
39
|
+
def self.inputstream(io, encoding = nil)
|
40
|
+
stream = case io
|
32
41
|
when org.jruby.util.IOInputStream, java.io.InputStream
|
33
42
|
io
|
34
43
|
else
|
35
44
|
io.to_inputstream if io.respond_to?(:read)
|
36
45
|
end
|
46
|
+
|
47
|
+
return stream if encoding.nil?
|
48
|
+
java.io.InputStreamReader.new(stream, ruby_encoding_to_charset(encoding))
|
37
49
|
end
|
38
50
|
|
39
51
|
# Given a path return a Java File object
|
40
|
-
#
|
52
|
+
#
|
53
|
+
# @param path [String, Pathname] the path to the file
|
41
54
|
# @return [java.io.File] the Java File object
|
42
55
|
def self.file(path)
|
43
56
|
java.io.File.new(path.to_s)
|
44
57
|
end
|
58
|
+
|
59
|
+
# Given a file path and encoding, return a Java InputStreamReader object
|
60
|
+
# for the file.
|
61
|
+
#
|
62
|
+
# @param path [String, Pathname] the path to the file
|
63
|
+
# @param encoding [String, Encoding] the file's character encoding
|
64
|
+
# @return [java.io.InputStreamReader] a Java InputStreamReader object
|
65
|
+
# wrapping a FileInputStream for the file
|
66
|
+
def self.file_reader(path, encoding)
|
67
|
+
java.io.InputStreamReader.new(java.io.FileInputStream.new(file(path)), ruby_encoding_to_charset(encoding))
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return a File or Reader object for a file, depending on whether the
|
71
|
+
# encoding must be explicitly specified or not.
|
72
|
+
#
|
73
|
+
# @param path [String, Pathname] the path to the file
|
74
|
+
# @param encoding [String, Encoding] the file's character encoding
|
75
|
+
# @return [java.io.Reader] a Java Reader object
|
76
|
+
def self.file_or_reader(path, encoding = nil)
|
77
|
+
encoding.nil? ? file(path) : file_reader(path, encoding)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return a Reader object for the String with an explicitly set encoding.
|
81
|
+
# If the encoding is +ASCII_8BIT+ then a binary-mode StreamReader is
|
82
|
+
# returned, rather than a character Reader
|
83
|
+
#
|
84
|
+
# @param string [String] the string
|
85
|
+
# @param encoding [String, Encoding] the string's character encoding
|
86
|
+
# @return [java.io.InputStream, java.io.Reader] a Java InputStream or Reader object
|
87
|
+
def self.string_reader(string, encoding)
|
88
|
+
inputstream = StringIO.new(string).to_inputstream
|
89
|
+
encoding = ruby_encoding(encoding)
|
90
|
+
return inputstream if encoding == ::Encoding::ASCII_8BIT
|
91
|
+
java.io.InputStreamReader.new(inputstream, ruby_encoding_to_charset(encoding))
|
92
|
+
end
|
93
|
+
|
94
|
+
# Figure out the equivalent Java +Charset+ for a Ruby {Encoding}.
|
95
|
+
#
|
96
|
+
# @param encoding [String, Encoding] the encoding to find a +Charset+ for
|
97
|
+
def self.ruby_encoding_to_charset(encoding)
|
98
|
+
ruby_encoding(encoding).to_java.getEncoding.getCharset
|
99
|
+
end
|
100
|
+
|
101
|
+
# Given a String with an {Encoding} name or an {Encoding} instance, return
|
102
|
+
# an {Encoding} instance
|
103
|
+
#
|
104
|
+
# @param encoding [String, Encoding] the encoding or encoding name
|
105
|
+
# @return [Encoding] the encoding
|
106
|
+
def self.ruby_encoding(encoding)
|
107
|
+
encoding.nil? ? nil : ::Encoding.find(encoding)
|
108
|
+
end
|
45
109
|
end
|
46
110
|
|
111
|
+
# Lambda that checks if the given path exists and is a file
|
47
112
|
PathChecker = ->(path) {
|
48
113
|
File.file?(path)
|
49
114
|
}
|
115
|
+
# Lambda that checks if the given string is a valid URI
|
50
116
|
URIChecker = ->(uri) {
|
51
117
|
begin
|
52
118
|
URI.parse(uri)
|
@@ -56,75 +122,144 @@ module Saxon
|
|
56
122
|
end
|
57
123
|
}
|
58
124
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
125
|
+
class << self
|
126
|
+
# Generate a Saxon::Source given an IO-like
|
127
|
+
#
|
128
|
+
# @param [IO, File] io The IO-like containing XML to be parsed
|
129
|
+
# @param [Hash] opts
|
130
|
+
# @option opts [String] :base_uri The Base URI for the Source - an
|
131
|
+
# absolute URI or relative path that will be used to resolve relative
|
132
|
+
# URLs in the XML. Setting this will override any path or URI derived
|
133
|
+
# from the IO-like.
|
134
|
+
# @option opts [String, Encoding] :encoding The encoding of the source.
|
135
|
+
# Note that specifying this will force the parser to ignore the charset
|
136
|
+
# if it's set in the XML declaration of the source. Only really useful
|
137
|
+
# if there's a discrepancy between the source's declared and actual
|
138
|
+
# encoding. Defaults to the <?xml charset="..."?> declaration in the
|
139
|
+
# source.
|
140
|
+
# @return [Saxon::Source] the Saxon::Source wrapping the input
|
141
|
+
def from_io(io, opts = {})
|
142
|
+
base_uri = opts.fetch(:base_uri) { Helpers.base_uri(io) }
|
143
|
+
encoding = opts.fetch(:encoding, nil)
|
144
|
+
inputstream = Helpers.inputstream(io, encoding)
|
145
|
+
from_inputstream_or_reader(inputstream, base_uri)
|
146
|
+
end
|
74
147
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
148
|
+
# Generate a Saxon::Source given a path to a file
|
149
|
+
#
|
150
|
+
# @param [String, Pathname] path The path to the XML file to be parsed
|
151
|
+
# @param [Hash] opts
|
152
|
+
# @option opts [String] :base_uri The Base URI for the Source - an
|
153
|
+
# absolute URI or relative path that will be used to resolve relative
|
154
|
+
# URLs in the XML. Setting this will override the file path.
|
155
|
+
# @option opts [String, Encoding] :encoding The encoding of the source.
|
156
|
+
# Note that specifying this will force the parser to ignore the charset
|
157
|
+
# if it's set in the XML declaration of the source. Only really useful
|
158
|
+
# if there's a discrepancy between the source's declared and actual
|
159
|
+
# encoding. Defaults to the <?xml charset="..."?> declaration in the
|
160
|
+
# source.
|
161
|
+
# @return [Saxon::Source] the Saxon::Source wrapping the input
|
162
|
+
def from_path(path, opts = {})
|
163
|
+
encoding = opts.fetch(:encoding, nil)
|
164
|
+
return from_inputstream_or_reader(Helpers.file(path), opts[:base_uri]) if encoding.nil?
|
165
|
+
reader = Helpers.file_reader(path, encoding)
|
166
|
+
base_uri = opts.fetch(:base_uri) { File.expand_path(path) }
|
167
|
+
from_inputstream_or_reader(reader, base_uri)
|
168
|
+
end
|
88
169
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
170
|
+
# Generate a Saxon::Source given a URI
|
171
|
+
#
|
172
|
+
# @param [String, URI] uri The URI to the XML file to be parsed
|
173
|
+
# @param [Hash] opts
|
174
|
+
# @option opts [String] :base_uri The Base URI for the Source - an
|
175
|
+
# absolute URI or relative path that will be used to resolve relative
|
176
|
+
# URLs in the XML. Setting this will override the given URI.
|
177
|
+
# @option opts [String, Encoding] :encoding The encoding of the source.
|
178
|
+
# Note that specifying this will force the parser to ignore the charset
|
179
|
+
# if it's set in the XML declaration of the source. Only really useful
|
180
|
+
# if there's a discrepancy between the source's declared and actual
|
181
|
+
# encoding. Defaults to the <?xml charset="..."?> declaration in the
|
182
|
+
# source.
|
183
|
+
# @return [Saxon::Source] the Saxon::Source wrapping the input
|
184
|
+
def from_uri(uri, opts = {})
|
185
|
+
encoding = opts.fetch(:encoding, nil)
|
186
|
+
return from_io(open(uri), encoding: encoding) if encoding
|
187
|
+
from_inputstream_or_reader(uri.to_s, opts[:base_uri])
|
188
|
+
end
|
102
189
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
190
|
+
# Generate a Saxon::Source given a string containing XML
|
191
|
+
#
|
192
|
+
# @param [String] string The string containing XML to be parsed
|
193
|
+
# @param [Hash] opts
|
194
|
+
# @option opts [String] :base_uri The Base URI for the Source - an
|
195
|
+
# absolute URI or relative path that will be used to resolve relative
|
196
|
+
# URLs in the XML. This will be nil unless set.
|
197
|
+
# @option opts [String, Encoding] :encoding The encoding of the source.
|
198
|
+
# Note that specifying this will force the parser to ignore the charset
|
199
|
+
# if it's set in the XML declaration of the source. Only really useful
|
200
|
+
# if there's a discrepancy between the encoding of the string and the
|
201
|
+
# encoding of the source. Defaults to the encoding of the string, unless
|
202
|
+
# that is ASCII-8BIT, in which case the parser will use the
|
203
|
+
# <?xml charset="..."?> declaration in the source to pick the encoding.
|
204
|
+
# @return [Saxon::Source] the Saxon::Source wrapping the input
|
205
|
+
def from_string(string, opts = {})
|
206
|
+
encoding = opts.fetch(:encoding) { string.encoding }
|
207
|
+
reader = Helpers.string_reader(string, encoding)
|
208
|
+
from_inputstream_or_reader(reader, opts[:base_uri])
|
209
|
+
end
|
210
|
+
|
211
|
+
# Generate a Saxon::Source from one of the several inputs allowed.
|
212
|
+
#
|
213
|
+
# If possible the character encoding of the input source will be left to
|
214
|
+
# the XML parser to discover (from the <tt><?xml charset="..."?></tt> XML
|
215
|
+
# declaration).
|
216
|
+
#
|
217
|
+
# The Base URI for the source (its absolute path, or URI) can be set by
|
218
|
+
# passing in the +:base_uri+ option. This is the same thing as an XML
|
219
|
+
# document's 'System ID' - Base URI is the term most widely used in Ruby
|
220
|
+
# libraries for this, so that's what's used here.
|
221
|
+
#
|
222
|
+
# If the source's character encoding can't be correctly discovered by the
|
223
|
+
# parser from the XML declaration (<tt><?xml version="..."
|
224
|
+
# charset="..."?></tt> at the top of the document), then it can be passed
|
225
|
+
# as the +:encoding+ option.
|
226
|
+
#
|
227
|
+
# If an existing {Source} is passed in, simply return it.
|
228
|
+
#
|
229
|
+
# @param [Saxon::Source, IO, File, String, Pathname, URI] input The XML to be parsed
|
230
|
+
# @param [Hash] opts
|
231
|
+
# @option opts [String] :base_uri The Base URI for the Source - an
|
232
|
+
# absolute URI or relative path that will be used to resolve relative
|
233
|
+
# URLs in the XML. Setting this will override any path or URI derived
|
234
|
+
# from an IO, URI, or Path.
|
235
|
+
# @option opts [String, Encoding] :encoding The encoding of the source.
|
236
|
+
# Note that specifying this will force the parser to ignore the charset
|
237
|
+
# if it's set in the XML declaration of the source. Only really useful
|
238
|
+
# if there's a discrepancy between the source's declared and actual
|
239
|
+
# encoding. Defaults to the <?xml charset="..."?> declaration in the
|
240
|
+
# source.
|
241
|
+
# @return [Saxon::Source] the Saxon::Source wrapping the input
|
242
|
+
def create(input, opts = {})
|
243
|
+
case input
|
244
|
+
when Saxon::Source
|
245
|
+
input
|
246
|
+
when IO, File, java.io.InputStream, StringIO
|
247
|
+
from_io(input, opts)
|
248
|
+
when Pathname, PathChecker
|
249
|
+
from_path(input, opts)
|
250
|
+
when URIChecker
|
251
|
+
from_uri(input, opts)
|
252
|
+
else
|
253
|
+
from_string(input, opts)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
117
258
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
when Pathname, PathChecker
|
123
|
-
from_path(io_path_uri_or_string, opts)
|
124
|
-
when URIChecker
|
125
|
-
from_uri(io_path_uri_or_string, opts)
|
126
|
-
else
|
127
|
-
from_string(io_path_uri_or_string, opts)
|
259
|
+
def from_inputstream_or_reader(inputstream_or_reader, base_uri = nil)
|
260
|
+
stream_source = Saxon::JAXP::StreamSource.new(inputstream_or_reader)
|
261
|
+
stream_source.setSystemId(base_uri) if base_uri
|
262
|
+
new(stream_source, inputstream_or_reader)
|
128
263
|
end
|
129
264
|
end
|
130
265
|
|
@@ -183,5 +318,6 @@ module Saxon
|
|
183
318
|
end
|
184
319
|
end
|
185
320
|
|
321
|
+
# Error raised when trying to consume an already-consumed, and closed, Source
|
186
322
|
class SourceClosedError < Exception; end
|
187
323
|
end
|
data/lib/saxon/version.rb
CHANGED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'saxon/s9api'
|
2
|
+
|
3
|
+
module Saxon
|
4
|
+
module Version
|
5
|
+
# The version of the underlying Saxon library, which we need to discover at
|
6
|
+
# runtime based on what version is on the Classpath
|
7
|
+
class Library
|
8
|
+
# The loaded version of the Saxon Java library
|
9
|
+
#
|
10
|
+
# @return [Saxon::Version::Library] the version of the loaded library
|
11
|
+
def self.loaded_version
|
12
|
+
Saxon::Loader.load!
|
13
|
+
|
14
|
+
sv = Java::net.sf.saxon.Version
|
15
|
+
new(sv.getProductVersion, sv.getStructuredVersionNumber, sv.softwareEdition)
|
16
|
+
end
|
17
|
+
|
18
|
+
include Comparable
|
19
|
+
|
20
|
+
# @return [String] the version string (e.g. '9.9.1.6', '10.0')
|
21
|
+
attr_reader :version
|
22
|
+
# @return [String] the version components (e.g. <tt>[9, 9, 1, 6]</tt>, <tt>[10, 0]</tt>)
|
23
|
+
attr_reader :components
|
24
|
+
# @return [Symbol] the edition (+:he+, +:pe+, or +:ee+)
|
25
|
+
attr_reader :edition
|
26
|
+
|
27
|
+
# @param version [String] the version string
|
28
|
+
# @param components [Array<Integer>] the version components separated
|
29
|
+
# @param edition [String, Symbol] the name of the Saxon edition (e.g. +:he+, +'HE'+)
|
30
|
+
def initialize(version, components, edition)
|
31
|
+
@version = version.dup.freeze
|
32
|
+
@components = components.dup.freeze
|
33
|
+
@edition = edition.downcase.to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
# Comparison against another instance
|
37
|
+
#
|
38
|
+
# @param other [Saxon::Version::Library] the other version to compare against
|
39
|
+
# @return [Integer] -1 for less, 1 for greater, 0 for equal
|
40
|
+
def <=>(other)
|
41
|
+
return false unless other.is_a?(self.class)
|
42
|
+
|
43
|
+
n_components = [self.components.length, other.components.length].max
|
44
|
+
(0..(n_components - 1)).reduce(0, &comparator(other))
|
45
|
+
end
|
46
|
+
|
47
|
+
# Pessimistic comparison à la rubygems +~>+: do I satisfy the other
|
48
|
+
# version if considered as a pessimistic version constraint
|
49
|
+
#
|
50
|
+
# @param pessimistic_version [Saxon::Version::Library] the version to
|
51
|
+
# compare pessimistically
|
52
|
+
# @return [Boolean] do I satisfy the constraint?
|
53
|
+
def pessimistic_compare(pessimistic_version)
|
54
|
+
pessimistic_components = pessimistic_version.components
|
55
|
+
pessimistic_components = pessimistic_components + [0] if pessimistic_components.length == 1
|
56
|
+
locked = pessimistic_components[0..-2]
|
57
|
+
locked = locked.zip(components[0..locked.length])
|
58
|
+
variable = [pessimistic_components[-1], components[locked.length]]
|
59
|
+
|
60
|
+
locked_ok = locked.all? { |check, mine|
|
61
|
+
check == mine
|
62
|
+
}
|
63
|
+
|
64
|
+
return false unless locked_ok
|
65
|
+
|
66
|
+
check, mine = variable
|
67
|
+
mine >= check
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [String] the version string
|
71
|
+
def to_s
|
72
|
+
version
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def comparator(other)
|
78
|
+
->(cmp, i) {
|
79
|
+
return cmp unless cmp == 0
|
80
|
+
|
81
|
+
mine = self.components[i].nil? ? 0 : self.components[i]
|
82
|
+
theirs = other.components[i].nil? ? 0 : other.components[i]
|
83
|
+
|
84
|
+
mine <=> theirs
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|