saxon-rb 0.6.0-java → 0.7.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e9afbc5a2644034bc285b3c3cd4e78f852b715d7a096ff8f7a6386730501598
4
- data.tar.gz: fe9c135af6356690fcca7b4f1dc2d9656cce44018806c3b5f6c9f4b6407c576d
3
+ metadata.gz: 11d57a8924dff1a6f260b5496fcbc0c693d3bc4818b782acf0329950952ad1c6
4
+ data.tar.gz: 67353b0bd7ffc1a2d350019ee20c41713d69e1f5a5bbfa4c42a0d420456401c1
5
5
  SHA512:
6
- metadata.gz: c59e0f986b8bbf4de77b18feba99a61c8e724df537679b0e674bc142a0d2b476026bb01579dcd2ec6e9b4f246493db3b892fbe7e997848eb06bf5c7bd8e2f5ea
7
- data.tar.gz: 182d383aced898a376d37eaedb60ff80461910d3451e119f2bf6e97ebe37d8c64e600563ba968efec22f49ae02f025ec3725f00a2cb9af10813af8e20fc13100
6
+ metadata.gz: 26f99187a484b68f55bb9fd7e0e49cbd4dd87bf2d1b366ed178140c24965a9ef9d61185e26f904b5f4aaea2cf9722bd3a842995e32fda036c9b17e38c55d5cac
7
+ data.tar.gz: 837e3e4f0e4bbdd70f88ed21c595ea4b5416a615362ad663f84a4b23956349bbce813528b20e783f5b2664560337be30b964bceb756e1c5554656939dd71ecbf
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- jruby-9.2.6.0
1
+ jruby-9.2.9.0
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -e docs/templates/plugin.rb
data/README.md CHANGED
@@ -78,7 +78,7 @@ result_2 = transformer.call_template('main-template')
78
78
  processor = Saxon::Processor.create
79
79
  xpath = processor.xpath_compiler.compile('//element[@attr = $a:var]')
80
80
 
81
- matches = xpath.run(document_node)
81
+ matches = xpath.evaluate(document_node)
82
82
  ```
83
83
 
84
84
  ## Migrating from `saxon-xslt` (or Nokogiri)
@@ -0,0 +1,73 @@
1
+ include YARD
2
+ include Templates
3
+
4
+ module JavadocHtmlHelper
5
+ JAVA_TYPE_MATCHER = /\A(?:[a-z_$](?:[a-z0-9_$]*)\.)+[A-Z][A-Za-z_$]*/
6
+ RUBY_COLLECTION_TYPE_MATCHER = /\A(?:[A-Z][A-Za-z0-9_])(?:::[A-Z][A-Za-z0-9_]*)*</
7
+ SAXON_TYPE_MATCHER = /\A(?:net\.sf\.saxon|com\.saxonica)/
8
+
9
+ def format_types(typelist, brackets = true)
10
+ return unless typelist.is_a?(Array)
11
+ list = typelist.map { |type|
12
+ case type
13
+ when JAVA_TYPE_MATCHER
14
+ format_java_type(type)
15
+ else
16
+ super([type], false)
17
+ end
18
+ }
19
+ list.empty? ? "" : (brackets ? "(#{list.join(", ")})" : list.join(", "))
20
+ end
21
+
22
+ def format_java_type(type)
23
+ "<tt>" + linkify_saxon_type(type) + "</tt>"
24
+ end
25
+
26
+ def linkify_saxon_type(type)
27
+ case type
28
+ when SAXON_TYPE_MATCHER
29
+ link = url_for_java_object(type)
30
+ else
31
+ link = nil
32
+ end
33
+ link ? link_url(link, type, :title => h(type)) : type
34
+ end
35
+
36
+ def linkify(*args)
37
+ if args.first.is_a?(String)
38
+ case args.first
39
+ when JAVA_TYPE_MATCHER
40
+ link = url_for_java_object(args.first)
41
+ title = args.first
42
+ link ? link_url(link, title, :title => h(title)) : title
43
+ else
44
+ super
45
+ end
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def url_for(obj, anchor = nil, relative = true)
52
+ case obj
53
+ when JAVA_TYPE_MATCHER
54
+ url_for_java_object(obj, anchor, relative)
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ def url_for_java_object(obj, anchor = nil, relative = nil)
61
+ case obj
62
+ when SAXON_TYPE_MATCHER
63
+ package, _, klass = obj.rpartition(".")
64
+ "http://saxonica.com/documentation/index.html#!javadoc/#{package}/#{klass}"
65
+ else
66
+ path = obj.split(".").join("/")
67
+ "https://docs.oracle.com/javase/8/docs/api/index.html?#{path}.html"
68
+ end
69
+ end
70
+ end
71
+
72
+ Template.extra_includes << proc { |opts| JavadocHtmlHelper if opts.format == :html }
73
+ # Engine.register_template_path(File.dirname(__FILE__))
@@ -2,12 +2,13 @@ require 'saxon/s9api'
2
2
  require 'saxon/parse_options'
3
3
 
4
4
  module Saxon
5
- # Wraps the <tt>net.saxon.Configuration</tt> class. See
6
- # http://saxonica.com/documentation9.5/javadoc/net/sf/saxon/Configuration.html
7
- # for details of what configuration options are available and what values
8
- # they accept. See
9
- # http://saxonica.com/documentation9.5/javadoc/net/sf/saxon/lib/FeatureKeys.html
10
- # for details of the constant names used to access the values
5
+ # Wraps the <tt>net.sf.saxon.Configuration</tt> class.
6
+ #
7
+ # See {net.sf.saxon.Configuration} for details of what configuration options
8
+ # are available and what values they accept.
9
+ #
10
+ # See {net.sf.saxon.lib.FeatureKeys} for details of the constant names used to
11
+ # access the values
11
12
  class Configuration
12
13
  DEFAULT_SEMAPHORE = Mutex.new
13
14
  private_constant :DEFAULT_SEMAPHORE
@@ -60,22 +61,23 @@ module Saxon
60
61
  end
61
62
 
62
63
  # Get a configuration option value
63
- # See https://www.saxonica.com/html/documentation/javadoc/net/sf/saxon/lib/FeatureKeys.html
64
- # for details of the available options. Use the constant name as a string
65
- # or symbol as the option
64
+ #
65
+ # See {net.sf.saxon.lib.FeatureKeys} for details of the available options.
66
+ # Use the constant name as a string or symbol as the option, e.g.
67
+ # +:allow_multhreading+, +'ALLOW_MULTITHREADING'+, +'allow_multithreading'+.
66
68
  #
67
69
  # @param option [String, Symbol]
68
70
  # @return [Object] the value of the configuration option
69
71
  # @raise [NameError] if the option name does not exist
72
+ # @see net.sf.saxon.lib.FeatureKeys
70
73
  def [](option)
71
74
  @config.getConfigurationProperty(option_url(option))
72
75
  end
73
76
 
74
- # Get a configuration option value
75
- # See http://saxonica.com/documentation9.5/javadoc/net/sf/saxon/lib/FeatureKeys.html
76
- # for details of the available options. Use the constant name as a string
77
- # or symbol as the option
77
+ # Set a configuration option value. See {#[]} for details about the option
78
+ # names.
78
79
  #
80
+ # @see #[]
79
81
  # @param option [String, Symbol]
80
82
  # @param value [Object] the value of the configuration option
81
83
  # @return [Object] the value you passed in
@@ -4,11 +4,217 @@ module Saxon
4
4
  # Builds XDM objects from XML sources, for use in XSLT or for query and
5
5
  # access
6
6
  class DocumentBuilder
7
+ # Provides a simple configuraion DSL for DocumentBuilders.
8
+ # @see DocumentBuilder.create
9
+ class ConfigurationDSL
10
+ # @api private
11
+ #
12
+ # Create a new instance and +instance_exec+ the passed-in block against it
13
+ def self.define(document_builder, block)
14
+ new(document_builder).instance_exec(&block)
15
+ end
16
+
17
+ # @api private
18
+ def initialize(document_builder)
19
+ @document_builder = document_builder
20
+ end
21
+
22
+ # Sets line numbering on or off
23
+ #
24
+ # @see DocumentBuilder#line_numbering=
25
+ #
26
+ # @param value [Boolean] on (true) or off (false)
27
+ def line_numbering(value)
28
+ @document_builder.line_numbering = value
29
+ end
30
+
31
+ # Sets the base URI of documents created using this instance.
32
+ #
33
+ # @see DocumentBuilder.base_uri=
34
+ #
35
+ # @param value [String, URI::File, URI::HTTP] The (absolute) base URI to use
36
+ def base_uri(value)
37
+ @document_builder.base_uri = value
38
+ end
39
+
40
+ # Sets the base URI of documents created using this instance.
41
+ #
42
+ # @see DocumentBuilder.base_uri=
43
+ #
44
+ # @param value [String, URI::File, URI::HTTP] The (absolute) base URI to use
45
+ def whitespace_stripping_policy(value)
46
+ @document_builder.whitespace_stripping_policy = value
47
+ end
48
+
49
+ # Sets the base URI of documents created using this instance.
50
+ #
51
+ # @see DocumentBuilder.base_uri=
52
+ #
53
+ # @param value [String, URI::File, URI::HTTP] The (absolute) base URI to use
54
+ def dtd_validation(value)
55
+ @document_builder.dtd_validation = value
56
+ end
57
+ end
58
+
59
+
60
+ # Create a new DocumentBuilder that can be used to build new XML documents
61
+ # with the passed-in {Saxon::Processor}. If a block is passed in it's
62
+ # executed as a DSL for configuring the builder instance.
63
+ #
64
+ # @param processor [Saxon::Processor] the Processor
65
+ # @yield An DocumentBuilder configuration DSL block
66
+ # @return [Saxon::DocumentBuilder] the new instance
67
+ def self.create(processor, &block)
68
+ new(processor.to_java.newDocumentBuilder, &block)
69
+ end
70
+
71
+ attr_reader :s9_document_builder
72
+ private :s9_document_builder
73
+
7
74
  # @api private
8
75
  # @param [net.sf.saxon.s9api.DocumentBuilder] s9_document_builder The
9
- # Saxon DocumentBuilder instance to wrap
10
- def initialize(s9_document_builder)
76
+ # Saxon DocumentBuilder instance to wrap
77
+ def initialize(s9_document_builder, &block)
11
78
  @s9_document_builder = s9_document_builder
79
+ if block_given?
80
+ ConfigurationDSL.define(self, block)
81
+ end
82
+ end
83
+
84
+ # Report whether documents created using this instance will keep track of
85
+ # the line and column numbers of elements.
86
+ #
87
+ # @return [Boolean] whether line numbering will be tracked
88
+ def line_numbering?
89
+ s9_document_builder.isLineNumbering
90
+ end
91
+
92
+
93
+ # Switch tracking of line and column numbers for elements in documents
94
+ # created by this instance on or off
95
+ #
96
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setLineNumbering
97
+ #
98
+ # @param on_or_not [Boolean] whether or not to track line numbering
99
+ def line_numbering=(on_or_not)
100
+ s9_document_builder.setLineNumbering(on_or_not)
101
+ end
102
+
103
+ # Return the default base URI to be used when building documents using this
104
+ # instance. This value will be ignored if the source being parsed has an
105
+ # intrinsic base URI (e.g. a File).
106
+ #
107
+ # Returns +nil+ if no URI is set (the default).
108
+ #
109
+ # @return [nil, URI::File, URI::HTTP] the default base URI (or nil)
110
+ def base_uri
111
+ uri = s9_document_builder.getBaseURI
112
+ uri.nil? ? uri : URI(uri.to_s)
113
+ end
114
+
115
+ # Set the base URI of documents created using this instance. This value will
116
+ # be ignored if the source being parsed has an intrinsic base URI (e.g. a
117
+ # File)
118
+ #
119
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setBaseURI
120
+ #
121
+ # @param uri [String, URI::File, URI::HTTP] The (absolute) base URI to use
122
+ def base_uri=(uri)
123
+ s9_document_builder.setBaseURI(java.net.URI.new(uri.to_s))
124
+ end
125
+
126
+ # Return the Whitespace stripping policy for this instance. Returns one of
127
+ # the standard policy names as a symbol, or the custom Java
128
+ # WhitespaceStrippingPolicy if one was defined using
129
+ # +#whitespace_stripping_policy = ->(qname) { ... }+. (See
130
+ # {#whitespace_stripping_policy=} for more.)
131
+ #
132
+ # +:all+: All whitespace-only nodes will be discarded
133
+ #
134
+ # +:none+: No whitespace-only nodes will be discarded (the default if DTD or
135
+ # schema validation is not in effect)
136
+ #
137
+ # +:ignorable+: Whitespace-only nodes inside elements defined as
138
+ # element-only in the DTD or schema being used will be discarded (the
139
+ # default if DTD or schema validation is in effect)
140
+ #
141
+ # +:unspecified+: the default, which in practice means :ignorable if DTD or
142
+ # schema validation is in effect, and :none otherwise.
143
+ #
144
+ # @return [:all, :none, :ignorable, :unspecified, Proc]
145
+ def whitespace_stripping_policy
146
+ s9_policy = s9_document_builder.getWhitespaceStrippingPolicy
147
+ case s9_policy
148
+ when Saxon::S9API::WhitespaceStrippingPolicy::UNSPECIFIED
149
+ :unspecified
150
+ when Saxon::S9API::WhitespaceStrippingPolicy::NONE
151
+ :none
152
+ when Saxon::S9API::WhitespaceStrippingPolicy::IGNORABLE
153
+ :ignorable
154
+ when Saxon::S9API::WhitespaceStrippingPolicy::ALL
155
+ :all
156
+ else
157
+ s9_policy
158
+ end
159
+ end
160
+
161
+ # Set the whitespace stripping policy to be used for documents built with
162
+ # this instance.
163
+ #
164
+ # Possible values are:
165
+ #
166
+ # * One of the standard policies, as a symbol (+:all+, +:none+,
167
+ # +:ignorable+, +:unspecified+, see {#whitespace_stripping_policy}).
168
+ # * A Java +net.sf.saxon.s9api.WhitesapceStrippingPolicy+ instance
169
+ # * A Proc/lambda that is handed an element name as a {Saxon::QName}, and
170
+ # should return true (if whitespace should be stripped for this element)
171
+ # or false (it should not).
172
+ # @example
173
+ # whitespace_stripping_policy = ->(element_qname) {
174
+ # element_qname == Saxon::QName.clark("{http://example.org/}element-name")
175
+ # }
176
+ #
177
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setWhitespaceStrippingPolicy
178
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/WhitespaceStrippingPolicy
179
+ # @param policy [Symbol, Proc, Saxon::S9API::WhitespaceStrippingPolicy] the
180
+ # policy to use
181
+ def whitespace_stripping_policy=(policy)
182
+ case policy
183
+ when :unspecified, :none, :ignorable, :all
184
+ s9_policy = Saxon::S9API::WhitespaceStrippingPolicy.const_get(policy.to_s.upcase.to_sym)
185
+ when Proc
186
+ wrapped_policy = ->(s9_qname) {
187
+ policy.call(Saxon::QName.new(s9_qname))
188
+ }
189
+ s9_policy = Saxon::S9API::WhitespaceStrippingPolicy.makeCustomPolicy(wrapped_policy)
190
+ when Saxon::S9API::WhitespaceStrippingPolicy
191
+ s9_policy = policy
192
+ else
193
+ raise InvalidWhitespaceStrippingPolicyError, "#{policy.inspect} is not one of the allowed Symbols, or a custom policy"
194
+ end
195
+ s9_document_builder.setWhitespaceStrippingPolicy(s9_policy)
196
+ end
197
+
198
+ # @return [Boolean] whether DTD Validation is enabled
199
+ def dtd_validation?
200
+ s9_document_builder.isDTDValidation
201
+ end
202
+
203
+ # Switches DTD validation on or off.
204
+ #
205
+ # It's important to note that DTD validation only applies to documents that
206
+ # contain a +<!doctype>+, but switching DTD validation off doesn't stop the
207
+ # XML parser Saxon uses from trying to retrieve the DTD that's referenced,
208
+ # which can mean network requests. By default, the SAX parser Saxon uses
209
+ # (Xerces) doesn't make use of XML catalogs, which causes problems when documents reference a DTD with a relative path as in:
210
+ # <!DOCTYPE root-element SYSTEM "example.dtd">
211
+ # This can be controlled through a configuration option, however.
212
+ #
213
+ # @see https://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/DocumentBuilder@setDTDValidation
214
+ # @see https://www.saxonica.com/documentation9.9/index.html#!sourcedocs/controlling-parsing
215
+ # @param on [Boolean] whether DTD Validation should be enabled
216
+ def dtd_validation=(on)
217
+ s9_document_builder.setDTDValidation(on)
12
218
  end
13
219
 
14
220
  # @param [Saxon::Source] source The Saxon::Source containing the source
@@ -16,13 +222,18 @@ module Saxon
16
222
  # @return [Saxon::XDM::Node] The Saxon::XDM::Node representing the root of the
17
223
  # document tree
18
224
  def build(source)
19
- XDM::Node.new(@s9_document_builder.build(source.to_java))
225
+ XDM::Node.new(s9_document_builder.build(source.to_java))
20
226
  end
21
227
 
22
- # @return [net.sf.saxon.s9api.DocumentBuilder] The underlying Java Saxon
228
+ # @return [Java::NetSfSaxonS9api::DocumentBuilder] The underlying Java Saxon
23
229
  # DocumentBuilder instance
24
230
  def to_java
25
- @s9_document_builder
231
+ s9_document_builder
26
232
  end
27
233
  end
234
+
235
+ # Error raised when someone tries to set an invalid whitespace stripping
236
+ # policy on a {DocumentBuilder}
237
+ class InvalidWhitespaceStrippingPolicyError < RuntimeError
238
+ end
28
239
  end
@@ -0,0 +1,8 @@
1
+ module Saxon
2
+ module FeatureFlags
3
+ # Error raised if a feature is not available under the loaded version of
4
+ # Saxon
5
+ class UnavailableInThisSaxonVersionError < StandardError
6
+ end
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,11 @@
1
+ require_relative 'feature_flags/version'
2
+ require_relative 'feature_flags/helpers'
3
+
4
+ module Saxon
5
+ # Allows saxon-rb features to be switched off if they can't be used under one
6
+ # of the otherwise-supported Saxon versions
7
+ #
8
+ # @api private
9
+ module FeatureFlags
10
+ end
11
+ end
@@ -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
@@ -8,6 +8,7 @@ module Saxon
8
8
  # Regexp patterns to assist in converting XDM typed values into Ruby
9
9
  # native types
10
10
  module Patterns
11
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
11
12
  DATE_TIME = /\A(-?[0-9]{4,})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)(Z|[-+][0-9]{2}:[0-9]{2})?\z/
12
13
  end
13
14
 
@@ -54,34 +55,45 @@ module Saxon
54
55
  # A collection of Lambdas (or objects responding to +#call+) for
55
56
  # converting XDM typed values to native Ruby types.
56
57
  module Convertors
58
+ # @see https://www.w3.org/TR/xmlschema-2/#integer
57
59
  INTEGER = INT = SHORT = LONG = UNSIGNED_INT = UNSIGNED_SHORT = UNSIGNED_LONG = \
58
60
  POSITIVE_INTEGER = NON_POSITIVE_INTEGER = NEGATIVE_INTEGER = NON_NEGATIVE_INTEGER = ->(xdm_atomic_value) {
59
61
  xdm_atomic_value.to_java.getValue
60
62
  }
63
+ # @see https://www.w3.org/TR/xmlschema-2/#decimal
61
64
  DECIMAL = ->(xdm_atomic_value) {
62
65
  BigDecimal(xdm_atomic_value.to_s)
63
66
  }
67
+ # @see https://www.w3.org/TR/xmlschema-2/#float
64
68
  FLOAT = DOUBLE = ->(xdm_atomic_value) {
65
69
  xdm_atomic_value.to_java.getValue
66
70
  }
71
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime
67
72
  DATE_TIME = DATE_TIME_STAMP = ValueToRuby::DateTimeConvertor
73
+ # @see https://www.w3.org/TR/xmlschema-2/#boolean
68
74
  BOOLEAN = ->(xdm_atomic_value) {
69
75
  xdm_atomic_value.to_java.getValue
70
76
  }
77
+ # @see https://www.w3.org/TR/xmlschema-2/#NOTATION
78
+ # @see https://www.w3.org/TR/xmlschema-2/#QName
71
79
  NOTATION = QNAME = ->(xdm_atomic_value) {
72
80
  Saxon::QName.new(xdm_atomic_value.to_java.getQNameValue)
73
81
  }
82
+ # @see https://www.w3.org/TR/xmlschema-2/#base64Binary
74
83
  BASE64_BINARY = ->(xdm_atomic_value) {
75
84
  Base64.decode64(xdm_atomic_value.to_s)
76
85
  }
86
+ # @see https://www.w3.org/TR/xmlschema-2/#hexBinary
77
87
  HEX_BINARY = ->(xdm_atomic_value) {
78
88
  bytes = []
79
89
  xdm_atomic_value.to_s.scan(/../) { |x| bytes << x.hex.chr(Encoding::ASCII_8BIT) }
80
90
  bytes.join
81
91
  }
92
+ # @see https://www.w3.org/TR/xmlschema-2/#byte
82
93
  BYTE = ->(xdm_atomic_value) {
83
94
  [xdm_atomic_value.to_java.getLongValue].pack('c')
84
95
  }
96
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
85
97
  UNSIGNED_BYTE = ->(xdm_atomic_value) {
86
98
  [xdm_atomic_value.to_java.getLongValue].pack('C')
87
99
  }
data/lib/saxon/loader.rb CHANGED
@@ -19,6 +19,7 @@ module Saxon
19
19
  @path = path
20
20
  end
21
21
 
22
+ # returns an error message including the missing path
22
23
  def to_s
23
24
  "The path ('#{@path}') you supplied for the Saxon .jar files doesn't exist, sorry"
24
25
  end
@@ -31,6 +32,7 @@ module Saxon
31
32
  @path = path
32
33
  end
33
34
 
35
+ # returns an error message including the path looked in and jars we were looking for
34
36
  def to_s
35
37
  "One of saxon9he.jar, saxon9pe.jar, or saxon9ee.jar must be present in the path ('#{@path}') you supplied, sorry"
36
38
  end
@@ -46,12 +46,14 @@ module Saxon
46
46
  @s9_processor = s9_processor
47
47
  end
48
48
 
49
- # Generate a new DocumentBuilder that uses this Processor.
50
- # Sharing DocumentBuilders across threads is not recommended/
49
+ # Generate a new DocumentBuilder that uses this Processor. Sharing
50
+ # DocumentBuilders across threads is not safe.
51
51
  #
52
+ # @yield A DocumentBuilder configuration DSL block, see
53
+ # {Saxon::DocumentBuilder.create}
52
54
  # @return [Saxon::DocumentBuilder] A new Saxon::DocumentBuilder
53
- def document_builder
54
- Saxon::DocumentBuilder.new(@s9_processor.newDocumentBuilder)
55
+ def document_builder(&block)
56
+ Saxon::DocumentBuilder.create(self, &block)
55
57
  end
56
58
 
57
59
  # Declare custom collations for use by XSLT, XPath, and XQuery processors
@@ -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
data/lib/saxon/version.rb CHANGED
@@ -1,3 +1,9 @@
1
+ require 'saxon/s9api'
2
+
1
3
  module Saxon
2
- VERSION = "0.6.0"
4
+ # Provides the saxon-rb and underlying Saxon library versions
5
+ module Version
6
+ # The version of the saxon-rb gem (not of Saxon itself)
7
+ WRAPPER = "0.7.0"
8
+ end
3
9
  end
@@ -20,6 +20,7 @@ module Saxon
20
20
  # them imlpicitly through the XDM::AtomicValue creation process doesn't really
21
21
  # work. They need to be created explicitly and then handed in to be wrapped.
22
22
  class CannotCreateQNameFromString < StandardError
23
+ # returns an error message
23
24
  def to_s
24
25
  "QName XDM::AtomicValues must be created using an instance of Saxon::QName, not a string like 'prefix:name': Prefix URI binding is undefined at this point"
25
26
  end
@@ -29,7 +30,8 @@ module Saxon
29
30
  # isn't a way to create these outside of parsing an XML document within
30
31
  # Saxon, so attempting to do so raises this error.
31
32
  class NotationCannotBeDirectlyCreated < StandardError
32
- def to_s
33
+ # returns an error message
34
+ def to_s
33
35
  "xs:NOTATION XDM::AtomicValues cannot be directly created outside of XML parsing."
34
36
  end
35
37
  end
@@ -85,6 +85,36 @@ module Saxon
85
85
  def axis_iterator(axis)
86
86
  AxisIterator.new(self, axis)
87
87
  end
88
+
89
+ # Use Saxon's naive XDM Node serialisation to serialize the node and its
90
+ # descendants. Saxon uses a new Serializer with default options to
91
+ # serialize the node. In particular, if the Node was produced by an XSLT
92
+ # that used +<xsl:character-map>+ through +<xsl:output>+ to modify the
93
+ # contents, then they *WILL* *NOT* have been applied.
94
+ #
95
+ # +<xsl:output>+ has its effect at serialization time, not at XDM tree
96
+ # creation time, so it won't be applied until you serialize the document.
97
+ #
98
+ # Even then, unless you use a {Serializer} configured with the XSLT's
99
+ # +<xsl:output>+. We make a properly configured serializer available in
100
+ # the result of any XSLT transform (a {Saxon::XSLT::Invocation}), e.g.:
101
+ #
102
+ # result = xslt.apply_templates(input)
103
+ # result.to_s
104
+ # # or
105
+ # result.serialize('/path/to/output.xml')
106
+ #
107
+ # You can also get that {Serializer} directly from {XSLT::Executable},
108
+ # should you want to use the serialization options from a particular XSLT
109
+ # to serialize arbitrary XDM values:
110
+ #
111
+ # serializer = xslt.serializer
112
+ # serializer.serialize(an_xdm_value)
113
+ #
114
+ # @see http://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/XdmNode@toString
115
+ def to_s
116
+ s9_xdm_node.toString
117
+ end
88
118
  end
89
119
  end
90
120
  end
@@ -40,7 +40,7 @@ module Saxon
40
40
  # static context.
41
41
  #
42
42
  # @param processor [Saxon::Processor] the {Saxon::Processor} to use
43
- # @yield An XPath compiler DSL block
43
+ # @yield An XPath::StaticContext::DSL block
44
44
  # @return [Saxon::XPath::Compiler] the new compiler instance
45
45
  def self.create(processor, &block)
46
46
  static_context = XPath::StaticContext.define(block)
@@ -79,7 +79,7 @@ module Saxon
79
79
  # Compiler's static context. As with {.create}, passing a block gives
80
80
  # access to a DSL for setting up the compiler's static context.
81
81
  #
82
- # @yield An {XPath::StaticContext::DSL} block
82
+ # @yield An XPath::StaticContext::DSL block
83
83
  # @return [Saxon::XPath::Compiler] the new compiler instance
84
84
  def create(&block)
85
85
  new_static_context = static_context.define(block)
@@ -117,7 +117,12 @@ module Saxon
117
117
  DSL.define(block)
118
118
  end
119
119
 
120
- attr_reader :declared_variables, :declared_namespaces, :default_collation
120
+ # @return [String] The default collation URI as a String
121
+ attr_reader :default_collation
122
+ # @return [Hash<Saxon::QName => Saxon::XPath::VariableDeclaration] the declared variables
123
+ attr_reader :declared_variables
124
+ # @return [Hash<String => String>] the declared namespaces, as a prefix => uri hash
125
+ attr_reader :declared_namespaces
121
126
 
122
127
  # @return [Saxon::QName]
123
128
  # @overload resolve_variable_qname(qname)
@@ -143,7 +143,17 @@ module Saxon
143
143
  DSL.define(block)
144
144
  end
145
145
 
146
- attr_reader :default_collation, :static_parameters, :global_parameters, :initial_template_parameters, :initial_template_tunnel_parameters
146
+
147
+ # @return [String] The default collation URI as a String
148
+ attr_reader :default_collation
149
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the static parameters
150
+ attr_reader :static_parameters
151
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the global parameters
152
+ attr_reader :global_parameters
153
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the initial template parameters
154
+ attr_reader :initial_template_parameters
155
+ # @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the initial template parameters with tunnelling = "yes"
156
+ attr_reader :initial_template_tunnel_parameters
147
157
 
148
158
  # @api private
149
159
  # When passed a Proc, create a new EvaluationContext based on this one, with the same DSL available as in {.define}.
@@ -4,6 +4,7 @@ require_relative 'invocation'
4
4
  require_relative '../serializer'
5
5
  require_relative '../xdm'
6
6
  require_relative '../qname'
7
+ require_relative '../feature_flags'
7
8
 
8
9
  module Saxon
9
10
  module XSLT
@@ -53,6 +54,7 @@ module Saxon
53
54
  # prefix, you must use an explicit {Saxon::QName} to refer to it.
54
55
  class Executable
55
56
  extend Forwardable
57
+ extend Saxon::FeatureFlags::Helpers
56
58
 
57
59
  attr_reader :evaluation_context
58
60
  private :evaluation_context
@@ -162,6 +164,15 @@ module Saxon
162
164
  transformation(opts.reject { |k, v| k == :args }).call_function(function_name, args)
163
165
  end
164
166
 
167
+ # Create a {Serializer::Object} configured using the options that were set
168
+ # by +<xsl:output>+.
169
+ #
170
+ # @return [Saxon::Serializer::Object] the Serializer
171
+ def serializer
172
+ Saxon::Serializer::Object.new(@s9_xslt_executable.load30.newSerializer)
173
+ end
174
+ requires_saxon_version :serializer, '>= 9.9'
175
+
165
176
  # @return [net.sf.saxon.s9api.XsltExecutable] the underlying Saxon
166
177
  # +XsltExecutable+
167
178
  def to_java
@@ -203,6 +214,7 @@ module Saxon
203
214
  # Represents a loaded XSLT transformation ready to be applied against a
204
215
  # context node.
205
216
  class Transformation
217
+ # A list of valid option names for the transform
206
218
  VALID_OPTS = [:raw, :mode, :global_context_item, :global_parameters, :initial_template_parameters, :initial_template_tunnel_parameters]
207
219
 
208
220
  attr_reader :s9_transformer, :opts
@@ -297,11 +309,11 @@ module Saxon
297
309
  end
298
310
 
299
311
  def initial_template_parameters(parameters)
300
- s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters) , false)
312
+ s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters), false)
301
313
  end
302
314
 
303
315
  def initial_template_tunnel_parameters(parameters)
304
- s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters) , true)
316
+ s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters), true)
305
317
  end
306
318
  end
307
319
 
@@ -10,6 +10,7 @@ module Saxon
10
10
  # IO.
11
11
  class Invocation
12
12
  attr_reader :s9_transformer, :invocation_lambda
13
+ private :s9_transformer, :invocation_lambda
13
14
 
14
15
  # @api private
15
16
  def initialize(s9_transformer, invocation_lambda, raw)
@@ -65,7 +66,7 @@ module Saxon
65
66
 
66
67
  # Send the result of the transformation to the supplied Destination
67
68
  #
68
- # @param destination [net.sf.saxon.s9api.Destination] the Saxon
69
+ # @param s9_destination [net.sf.saxon.s9api.Destination] the Saxon
69
70
  # destination to use
70
71
  # @return [nil]
71
72
  def to_destination(s9_destination)
data/saxon-rb.gemspec CHANGED
@@ -5,7 +5,7 @@ require 'saxon/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'saxon-rb'
8
- spec.version = Saxon::VERSION
8
+ spec.version = Saxon::Version::WRAPPER
9
9
  spec.authors = ['Matt Patterson']
10
10
  spec.email = ['matt@werkstatt.io']
11
11
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saxon-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: java
6
6
  authors:
7
7
  - Matt Patterson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-19 00:00:00.000000000 Z
11
+ date: 2020-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,7 @@ files:
164
164
  - ".gitignore"
165
165
  - ".rspec"
166
166
  - ".ruby-version"
167
+ - ".yardopts"
167
168
  - CODE_OF_CONDUCT.md
168
169
  - Gemfile
169
170
  - LICENSE.txt
@@ -171,6 +172,7 @@ files:
171
172
  - Rakefile
172
173
  - bin/console
173
174
  - bin/setup
175
+ - docs/templates/plugin.rb
174
176
  - lib/net/sf/saxon/Saxon-HE/9.9.1-6/Saxon-HE-9.9.1-6.jar
175
177
  - lib/saxon-rb.rb
176
178
  - lib/saxon-rb_jars.rb
@@ -178,6 +180,10 @@ files:
178
180
  - lib/saxon/axis_iterator.rb
179
181
  - lib/saxon/configuration.rb
180
182
  - lib/saxon/document_builder.rb
183
+ - lib/saxon/feature_flags.rb
184
+ - lib/saxon/feature_flags/errors.rb
185
+ - lib/saxon/feature_flags/helpers.rb
186
+ - lib/saxon/feature_flags/version.rb
181
187
  - lib/saxon/item_type.rb
182
188
  - lib/saxon/item_type/lexical_string_conversion.rb
183
189
  - lib/saxon/item_type/value_to_ruby.rb
@@ -196,6 +202,7 @@ files:
196
202
  - lib/saxon/serializer/output_properties.rb
197
203
  - lib/saxon/source.rb
198
204
  - lib/saxon/version.rb
205
+ - lib/saxon/version/library.rb
199
206
  - lib/saxon/xdm.rb
200
207
  - lib/saxon/xdm/array.rb
201
208
  - lib/saxon/xdm/atomic_value.rb
@@ -238,8 +245,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
238
245
  version: '0'
239
246
  requirements:
240
247
  - jar net.sf.saxon, Saxon-HE, 9.9.1-6
241
- rubyforge_project:
242
- rubygems_version: 2.7.10
248
+ rubygems_version: 3.1.3
243
249
  signing_key:
244
250
  specification_version: 4
245
251
  summary: Saxon 9.9 for JRuby, with an idiomatic Ruby API