saxon-rb 0.4.0-java → 0.7.2-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +429 -42
  3. data/.ruby-version +1 -1
  4. data/.yardopts +1 -0
  5. data/Gemfile +2 -2
  6. data/README.md +358 -10
  7. data/Rakefile +237 -7
  8. data/docs/templates/plugin.rb +73 -0
  9. 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
  10. data/lib/saxon-rb.rb +0 -0
  11. data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
  12. data/lib/saxon.rb +13 -0
  13. data/lib/saxon/axis_iterator.rb +8 -1
  14. data/lib/saxon/configuration.rb +16 -13
  15. data/lib/saxon/document_builder.rb +216 -5
  16. data/lib/saxon/feature_flags.rb +11 -0
  17. data/lib/saxon/feature_flags/errors.rb +8 -0
  18. data/lib/saxon/feature_flags/helpers.rb +15 -0
  19. data/lib/saxon/feature_flags/version.rb +100 -0
  20. data/lib/saxon/item_type.rb +129 -89
  21. data/lib/saxon/item_type/lexical_string_conversion.rb +214 -59
  22. data/lib/saxon/item_type/value_to_ruby.rb +25 -0
  23. data/lib/saxon/loader.rb +6 -1
  24. data/lib/saxon/nokogiri.rb +78 -0
  25. data/lib/saxon/occurrence_indicator.rb +32 -3
  26. data/lib/saxon/processor.rb +50 -5
  27. data/lib/saxon/qname.rb +37 -2
  28. data/lib/saxon/s9api.rb +5 -0
  29. data/lib/saxon/sequence_type.rb +131 -0
  30. data/lib/saxon/serializer.rb +3 -137
  31. data/lib/saxon/serializer/destination.rb +80 -0
  32. data/lib/saxon/serializer/object.rb +93 -0
  33. data/lib/saxon/serializer/output_properties.rb +83 -0
  34. data/lib/saxon/source.rb +207 -71
  35. data/lib/saxon/version.rb +7 -1
  36. data/lib/saxon/version/library.rb +89 -0
  37. data/lib/saxon/xdm.rb +7 -0
  38. data/lib/saxon/xdm/array.rb +16 -0
  39. data/lib/saxon/xdm/atomic_value.rb +10 -2
  40. data/lib/saxon/xdm/empty_sequence.rb +13 -0
  41. data/lib/saxon/xdm/external_object.rb +1 -0
  42. data/lib/saxon/xdm/function_item.rb +1 -0
  43. data/lib/saxon/xdm/item.rb +7 -0
  44. data/lib/saxon/xdm/map.rb +38 -0
  45. data/lib/saxon/xdm/node.rb +50 -1
  46. data/lib/saxon/xdm/sequence_like.rb +15 -0
  47. data/lib/saxon/xdm/value.rb +21 -5
  48. data/lib/saxon/xpath.rb +9 -0
  49. data/lib/saxon/xpath/compiler.rb +37 -2
  50. data/lib/saxon/xpath/executable.rb +53 -28
  51. data/lib/saxon/xpath/static_context.rb +25 -40
  52. data/lib/saxon/xpath/variable_declaration.rb +16 -49
  53. data/lib/saxon/xslt.rb +12 -0
  54. data/lib/saxon/xslt/compiler.rb +75 -6
  55. data/lib/saxon/xslt/evaluation_context.rb +30 -4
  56. data/lib/saxon/xslt/executable.rb +206 -29
  57. data/lib/saxon/xslt/invocation.rb +97 -0
  58. data/saxon-rb.gemspec +3 -3
  59. metadata +22 -10
  60. data/saxon.gemspec +0 -30
@@ -5,11 +5,19 @@ module Saxon
5
5
  # A collection of lamba-like objects for converting XDM::AtomicValues into
6
6
  # appropriate Ruby values
7
7
  module ValueToRuby
8
+ # Regexp patterns to assist in converting XDM typed values into Ruby
9
+ # native types
8
10
  module Patterns
11
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
9
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/
10
13
  end
11
14
 
15
+ # Helper class that converts XDM Date/Time strings into something Ruby's {Time} class can
16
+ # handle
12
17
  class DateTimeConvertor
18
+ # Convert an XDM Date/Time value into a native Ruby {Time} object.
19
+ # @return [Time, String] the converted Time, or the original XDM lexical
20
+ # string if conversion isn't possible
13
21
  def self.call(xdm_atomic_value)
14
22
  new(xdm_atomic_value).convert
15
23
  end
@@ -21,11 +29,15 @@ module Saxon
21
29
  @match = ValueToRuby::Patterns::DATE_TIME.match(@timestring)
22
30
  end
23
31
 
32
+ # Return a {Time} instance if possible, otherwise return the string value from the XDM.
33
+ # @return [Time, String] the converted Time, or the original string
24
34
  def convert
25
35
  return timestring if match.nil?
26
36
  Time.new(*integer_parts, decimal_part, tz_part)
27
37
  end
28
38
 
39
+ private
40
+
29
41
  def integer_parts
30
42
  match[1..5].map { |n| Integer(n, 10) }
31
43
  end
@@ -40,35 +52,48 @@ module Saxon
40
52
  end
41
53
  end
42
54
 
55
+ # A collection of Lambdas (or objects responding to +#call+) for
56
+ # converting XDM typed values to native Ruby types.
43
57
  module Convertors
58
+ # @see https://www.w3.org/TR/xmlschema-2/#integer
44
59
  INTEGER = INT = SHORT = LONG = UNSIGNED_INT = UNSIGNED_SHORT = UNSIGNED_LONG = \
45
60
  POSITIVE_INTEGER = NON_POSITIVE_INTEGER = NEGATIVE_INTEGER = NON_NEGATIVE_INTEGER = ->(xdm_atomic_value) {
46
61
  xdm_atomic_value.to_java.getValue
47
62
  }
63
+ # @see https://www.w3.org/TR/xmlschema-2/#decimal
48
64
  DECIMAL = ->(xdm_atomic_value) {
49
65
  BigDecimal(xdm_atomic_value.to_s)
50
66
  }
67
+ # @see https://www.w3.org/TR/xmlschema-2/#float
51
68
  FLOAT = DOUBLE = ->(xdm_atomic_value) {
52
69
  xdm_atomic_value.to_java.getValue
53
70
  }
71
+ # @see https://www.w3.org/TR/xmlschema-2/#dateTime
54
72
  DATE_TIME = DATE_TIME_STAMP = ValueToRuby::DateTimeConvertor
73
+ # @see https://www.w3.org/TR/xmlschema-2/#boolean
55
74
  BOOLEAN = ->(xdm_atomic_value) {
56
75
  xdm_atomic_value.to_java.getValue
57
76
  }
77
+ # @see https://www.w3.org/TR/xmlschema-2/#NOTATION
78
+ # @see https://www.w3.org/TR/xmlschema-2/#QName
58
79
  NOTATION = QNAME = ->(xdm_atomic_value) {
59
80
  Saxon::QName.new(xdm_atomic_value.to_java.getQNameValue)
60
81
  }
82
+ # @see https://www.w3.org/TR/xmlschema-2/#base64Binary
61
83
  BASE64_BINARY = ->(xdm_atomic_value) {
62
84
  Base64.decode64(xdm_atomic_value.to_s)
63
85
  }
86
+ # @see https://www.w3.org/TR/xmlschema-2/#hexBinary
64
87
  HEX_BINARY = ->(xdm_atomic_value) {
65
88
  bytes = []
66
89
  xdm_atomic_value.to_s.scan(/../) { |x| bytes << x.hex.chr(Encoding::ASCII_8BIT) }
67
90
  bytes.join
68
91
  }
92
+ # @see https://www.w3.org/TR/xmlschema-2/#byte
69
93
  BYTE = ->(xdm_atomic_value) {
70
94
  [xdm_atomic_value.to_java.getLongValue].pack('c')
71
95
  }
96
+ # @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
72
97
  UNSIGNED_BYTE = ->(xdm_atomic_value) {
73
98
  [xdm_atomic_value.to_java.getLongValue].pack('C')
74
99
  }
@@ -6,8 +6,11 @@ module Saxon
6
6
  module S9API
7
7
  end
8
8
 
9
+ # The mechanism for adding the JARs for either the bundled Saxon HE, or an
10
+ # external Saxon HE/PE/EE version, into the CLASSPATH and requiring them.
9
11
  module Loader
10
12
  LOAD_SEMAPHORE = Mutex.new
13
+ private_constant :LOAD_SEMAPHORE
11
14
 
12
15
  # Error raised if Saxon::Loader.load! is called but the path handed
13
16
  # in does not exist or is not a directory
@@ -16,6 +19,7 @@ module Saxon
16
19
  @path = path
17
20
  end
18
21
 
22
+ # returns an error message including the missing path
19
23
  def to_s
20
24
  "The path ('#{@path}') you supplied for the Saxon .jar files doesn't exist, sorry"
21
25
  end
@@ -28,6 +32,7 @@ module Saxon
28
32
  @path = path
29
33
  end
30
34
 
35
+ # returns an error message including the path looked in and jars we were looking for
31
36
  def to_s
32
37
  "One of saxon9he.jar, saxon9pe.jar, or saxon9ee.jar must be present in the path ('#{@path}') you supplied, sorry"
33
38
  end
@@ -45,7 +50,7 @@ module Saxon
45
50
  else
46
51
  if jars_not_on_classpath?
47
52
  if saxon_home.nil?
48
- require 'saxon_jars'
53
+ require 'saxon-rb_jars'
49
54
  else
50
55
  saxon_home = Pathname.new(saxon_home)
51
56
  raise NoJarsError, saxon_home unless saxon_home.directory?
@@ -0,0 +1,78 @@
1
+ require 'saxon/xslt'
2
+
3
+ module Saxon
4
+ module XSLT
5
+ class Executable
6
+ # Provided for Nokogiri API compatibility. Cannot use many XSLT 2 and 3
7
+ # features as a result.
8
+ #
9
+ # Transform the input document by applying templates as in XSLT 1. All
10
+ # parameters will be interpreted as Strings
11
+ #
12
+ # @param doc_node [Saxon::XDM::Node] the document to transform
13
+ # @param params [Hash, Array] a Hash of param name => value, or an Array
14
+ # of param names and values of the form +['param', 'value', 'param2',
15
+ # 'value']+. The array must be of an even-numbered length
16
+ # @return [Saxon::XDM::Value] the result document
17
+ def transform(doc_node, params = {})
18
+ apply_templates(doc_node, v1_parameters(params)).xdm_value
19
+ end
20
+
21
+ # Provided for Nokogiri API compatibility. Cannot use many XSLT 2 and 3
22
+ # features as a result.
23
+ #
24
+ # Transform the input document by applying templates as in XSLT 1, and
25
+ # then serializing the result to a string using the serialization options
26
+ # set in the XSLT stylesheet's +<xsl:output/>+.
27
+ #
28
+ # @param doc_node [Saxon::XDM::Node] the document to transform
29
+ # @param params [Hash, Array] a Hash of param name => value, or an Array
30
+ # of param names and values of the form +['param', 'value', 'param2',
31
+ # 'value']+. The array must be of an even-numbered length
32
+ # @return [String] a serialization of the the result document
33
+ def apply_to(doc_node, params = {})
34
+ apply_templates(doc_node, v1_parameters(params)).to_s
35
+ end
36
+
37
+ # Provided for Nokogiri API compatibility. Cannot use many XSLT 2 and 3
38
+ # features as a result.
39
+ #
40
+ # Serialize the input document to a string using the serialization options
41
+ # set in the XSLT stylesheet's +<xsl:output/>+.
42
+ #
43
+ # @param doc_node [Saxon::XDM::Node] the document to serialize
44
+ # @return [String] a serialization of the the input document
45
+ def serialize(doc_node)
46
+ s9_transformer = @s9_xslt_executable.load30
47
+ serializer = Saxon::Serializer::Object.new(s9_transformer.newSerializer)
48
+ serializer.serialize(doc_node.to_java)
49
+ end
50
+
51
+ private
52
+
53
+ def v1_parameters(params = [])
54
+ v1_params = v1_params_hash(params).map { |qname, value|
55
+ [Saxon::QName.resolve(qname), Saxon::XDM.AtomicValue(v1_param_value(value))]
56
+ }.to_h
57
+ return {} if v1_params.empty?
58
+ {global_parameters: v1_params}
59
+ end
60
+
61
+ def v1_params_hash(params = [])
62
+ return params if params.is_a?(Hash)
63
+ params.each_slice(2).map { |k,v|
64
+ raise ArgumentError.new("Odd number of values passed as params: #{params}") if v.nil?
65
+ [k, v]
66
+ }.to_h
67
+ end
68
+
69
+ def v1_param_value(value)
70
+ if /\A(['"]).+\1\z/.match(value)
71
+ value.slice(1..-2)
72
+ else
73
+ value
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,32 +1,61 @@
1
1
  require_relative 's9api'
2
2
 
3
3
  module Saxon
4
- # Provides simple access to Saxon's OccurrenceIndicator constants,
5
- # for use in declaring variable types
4
+ # Provides simple access to Saxon's OccurrenceIndicator constants, for
5
+ # declaring restrictions on the cardinality (length) of a sequence when, for
6
+ # example, defining a variable's type
6
7
  module OccurrenceIndicator
7
8
  class << self
9
+ # One thing
8
10
  def one
9
11
  @one ||= Saxon::S9API::OccurrenceIndicator::ONE
10
12
  end
11
13
 
14
+ # One or more things
12
15
  def one_or_more
13
16
  @one_or_more ||= Saxon::S9API::OccurrenceIndicator::ONE_OR_MORE
14
17
  end
15
18
 
19
+ # no things (the empty sequence)
16
20
  def zero
17
21
  @zero ||= Saxon::S9API::OccurrenceIndicator::ZERO
18
22
  end
19
23
 
24
+ # zero or more things
20
25
  def zero_or_more
21
26
  @zero_or_more ||= Saxon::S9API::OccurrenceIndicator::ZERO_OR_MORE
22
27
  end
23
28
 
29
+ # an optional thing
24
30
  def zero_or_one
25
31
  @zero_or_one ||= Saxon::S9API::OccurrenceIndicator::ZERO_OR_ONE
26
32
  end
27
33
 
34
+ # The list of valid occurence indicator names, as symbols. These
35
+ # correspond directly to the methods returning OccurrenceIndicators in
36
+ # this module.
37
+ #
38
+ # @return [Array<Symbol>] the indicator names
28
39
  def indicator_names
29
- @indicator_names ||= (public_methods(false) - Object.public_methods - [:indicator_names])
40
+ # .refine gets added to modules that have methods, so it's not in
41
+ # Module's public_methods list
42
+ @indicator_names ||= (public_methods(false) - Module.public_methods - [:indicator_names, :get_indicator, :refine])
43
+ end
44
+
45
+ # Return an OccurrenceIndicator given a name as a symbol. Passes through
46
+ # existing OccurrenceIndicator instances: this method is primarily for API
47
+ # use, most people will find it easier to directly call one of the
48
+ # methods, as in +OccurrenceIndicator.one+ rather than
49
+ # +OccurrenceIndicator.get_indicator(:one)+.
50
+ #
51
+ # @param indicator_name [Symbol, Saxon::S9API::OccurrenceIndicator] the name of the OccurrenceIndicator to return
52
+ # @return [Saxon::S9API::OccurrenceIndicator] the OccurrenceIndicator
53
+ def get_indicator(indicator_name)
54
+ return indicator_name if indicator_name.is_a?(Saxon::S9API::OccurrenceIndicator)
55
+ unless indicator_names.include?(indicator_name)
56
+ raise ArgumentError, "#{indicator_name.inspect} is not a valid indicator name (one of #{indicator_names.map(&:inspect).join(', ')})"
57
+ end
58
+ OccurrenceIndicator.send(indicator_name)
30
59
  end
31
60
  end
32
61
  end
@@ -4,6 +4,7 @@ require 'saxon/configuration'
4
4
  require 'saxon/document_builder'
5
5
  require 'saxon/xpath'
6
6
  require 'saxon/xslt'
7
+ require 'saxon/serializer'
7
8
 
8
9
  module Saxon
9
10
  # Saxon::Processor wraps the S9API::Processor object. This is the object
@@ -45,12 +46,14 @@ module Saxon
45
46
  @s9_processor = s9_processor
46
47
  end
47
48
 
48
- # Generate a new DocumentBuilder that uses this Processor.
49
- # Sharing DocumentBuilders across threads is not recommended/
49
+ # Generate a new DocumentBuilder that uses this Processor. Sharing
50
+ # DocumentBuilders across threads is not safe.
50
51
  #
52
+ # @yield A DocumentBuilder configuration DSL block, see
53
+ # {Saxon::DocumentBuilder.create}
51
54
  # @return [Saxon::DocumentBuilder] A new Saxon::DocumentBuilder
52
- def document_builder
53
- Saxon::DocumentBuilder.new(@s9_processor.newDocumentBuilder)
55
+ def document_builder(&block)
56
+ Saxon::DocumentBuilder.create(self, &block)
54
57
  end
55
58
 
56
59
  # Declare custom collations for use by XSLT, XPath, and XQuery processors
@@ -76,12 +79,23 @@ module Saxon
76
79
  # <tt>Processor</tt>. Sharing <tt>XSLT::Compiler</tt>s across threads is
77
80
  # fine as long as the static context is not changed.
78
81
  #
79
- # @yield An XPath compiler DSL block, see {Saxon::XSLT::Compiler.create}
82
+ # @yield An XSLT compiler DSL block, see {Saxon::XSLT::Compiler.create}
80
83
  # @return [Saxon::XSLT::Compiler] a new XSLT compiler
81
84
  def xslt_compiler(&block)
82
85
  Saxon::XSLT::Compiler.create(self, &block)
83
86
  end
84
87
 
88
+ # Generate a new +Serializer+ for directly serializing XDM Values that uses
89
+ # this +Processor+. +Serializer+s are effectively one-shot objects, and
90
+ # shouldn't be reused.
91
+ #
92
+ # @yield the block passed will be called bound to the serializer instance. See
93
+ # {Saxon::Serializer::Object.create}
94
+ # @return [Saxon::Serializer::Object]
95
+ def serializer(&block)
96
+ Saxon::Serializer::Object.create(self, &block)
97
+ end
98
+
85
99
  # @return [net.sf.saxon.s9api.Processor] The underlying Saxon processor
86
100
  def to_java
87
101
  @s9_processor
@@ -98,5 +112,36 @@ module Saxon
98
112
  def config
99
113
  @config ||= Saxon::Configuration.create(self)
100
114
  end
115
+
116
+ # Create a {DocumentBuilder} and construct a {Source} and parse some XML.
117
+ # The args are passed to {Saxon::Source.create}, and the returned {Source}
118
+ # is parsed using {DocumentBuilder#build}. If a {Source} is passed in, parse
119
+ # that. Any options in +opts+ will be ignored in that case.
120
+ #
121
+ # @param input [Saxon::Source, IO, File, String, Pathname, URI] the input to
122
+ # be turned into a {Source} and parsed.
123
+ # @param opts [Hash] for Source creation. See {Saxon::Source.create}.
124
+ # @return [Saxon::XDM::Node] the XML document
125
+ def XML(input, opts = {})
126
+ source = Source.create(input, opts)
127
+ document_builder.build(source)
128
+ end
129
+
130
+ # Construct a {Source} containing an XSLT stylesheet, create an
131
+ # {XSLT::Compiler}, and compile the source, returning the {XSLT::Executable}
132
+ # produced. If a {Source} is passed as +input+, then it will be passed
133
+ # through to the compiler and any source-related options in +opts+ will be
134
+ # ignored.
135
+ #
136
+ # @param input [Saxon::Source, IO, File, String, Pathname, URI] the input to
137
+ # be turned into a {Source} and parsed.
138
+ # @param opts [Hash] for Source creation. See {Saxon::Source.create}.
139
+ # @yield the block is executed as an {XSLT::EvaluationContext::DSL} instance
140
+ # and applied to the compiler
141
+ # @return [Saxon::XSLT::Executable] the XSLT Executable
142
+ def XSLT(input, opts = {}, &block)
143
+ source = Source.create(input, opts)
144
+ xslt_compiler(&block).compile(source)
145
+ end
101
146
  end
102
147
  end
@@ -3,16 +3,39 @@ require 'saxon/s9api'
3
3
  module Saxon
4
4
  # Represents QNames
5
5
  class QName
6
+ # Create a {QName} from a Clark-notation string.
7
+ #
8
+ # Clark-notation for QNames uses +{}+ to delimit the namespace, so for a
9
+ # QName not in a namespace it's simply +local-name+, and for one in a
10
+ # namespace it's +{http://example.org/ns}local-name+
11
+ #
12
+ # @param clark_string [String] A QName in Clark notation.
13
+ # @return [Saxon::QName] A QName
6
14
  def self.clark(clark_string)
7
15
  s9_qname = Saxon::S9API::QName.fromClarkName(clark_string)
8
16
  new(s9_qname)
9
17
  end
10
18
 
19
+ # Create a {QName} from an Expanded QName string.
20
+ #
21
+ # Expanded QNames uses +Q{}+ to delimit the namespace, so for a
22
+ # QName not in a namespace it's simply +Q{}local-name+ (or +local-name}), and for one in a
23
+ # namespace it's +Q{http://example.org/ns}local-name+
24
+ #
25
+ # @param eqname_string [String] A QName in Expanded QName notation.
26
+ # @return [Saxon::QName] A QName
11
27
  def self.eqname(eqname_string)
12
28
  s9_qname = Saxon::S9API::QName.fromEQName(eqname_string)
13
29
  new(s9_qname)
14
30
  end
15
31
 
32
+ # Create a {QName} from prefix, uri, and local name options
33
+ #
34
+ # @param opts [Hash]
35
+ # @option [String] :prefix the namespace prefix to use (optional, requires +:uri+ passed too)
36
+ # @option [String] :uri the namespace URI to use (only required for QNames in a namespace)
37
+ # @option [String] :local_name the local-part of the QName. Required.
38
+ # @return [Saxon::QName] the QName
16
39
  def self.create(opts = {})
17
40
  prefix = opts[:prefix]
18
41
  uri = opts[:uri]
@@ -32,7 +55,7 @@ module Saxon
32
55
  # it's an instance of the underlying Saxon Java QName, it'll be wrapped
33
56
  # into a {Saxon::QName}
34
57
  #
35
- # If the arg is a string, it's resolved by using {resolve_variable_name}
58
+ # If the arg is a string, it's resolved by using {resolve_qname_string}
36
59
  #
37
60
  # @param qname_or_string [String, Symbol, Saxon::QName] the qname to resolve
38
61
  # @param namespaces [Hash<String => String>] the set of namespaces as a hash of <tt>"prefix" => "namespace-uri"</tt>
@@ -48,7 +71,7 @@ module Saxon
48
71
  end
49
72
  end
50
73
 
51
- # Resolve a QName string of the form <tt>"prefix:local-name"</tt> into a
74
+ # Resolve a QName string of the form +"prefix:local-name"+ into a
52
75
  # {Saxon::QName} by looking up the namespace URI in a hash of
53
76
  # <tt>"prefix" => "namespace-uri"</tt>
54
77
  #
@@ -110,12 +133,21 @@ module Saxon
110
133
  @s9_qname.getEQName
111
134
  end
112
135
 
136
+ # Compare this QName with another. They compare equal if they have same URI
137
+ # and local name. Prefix is ignored.
138
+ #
139
+ # @param other [Saxon::QName] the QName to compare against
140
+ # @return [Boolean] whether the two compare equal
113
141
  def ==(other)
114
142
  return false unless other.is_a?(QName)
115
143
  s9_qname.equals(other.to_java)
116
144
  end
117
145
  alias_method :eql?, :==
118
146
 
147
+ # Compute a hash-code for this {QName}.
148
+ #
149
+ # Two {QNames}s with the same local name and URI will have the same hash code (and will compare using eql?).
150
+ # @see Object#hash
119
151
  def hash
120
152
  @hash ||= (local_name + uri).hash
121
153
  end
@@ -134,6 +166,8 @@ module Saxon
134
166
  s9_qname.to_s
135
167
  end
136
168
 
169
+ # Returns a more detailed string representation of the object, showing
170
+ # prefix, uri, and local_name instance variables
137
171
  def inspect
138
172
  "<Saxon::QName @prefix=#{prefix} @uri=#{uri} @local_name=#{local_name}>"
139
173
  end
@@ -145,6 +179,7 @@ module Saxon
145
179
  @qname_string, @prefix = qname_string, prefix
146
180
  end
147
181
 
182
+ # The error message reports the unbound prefix and complete QName
148
183
  def to_s
149
184
  "Namespace prefix ‘#{@prefix}’ for QName ‘#{@qname_string}’ is not bound to a URI"
150
185
  end
@@ -3,8 +3,13 @@ require 'saxon/loader'
3
3
  module Saxon
4
4
  module S9API
5
5
  CLASS_IMPORT_SEMAPHORE = Mutex.new
6
+ private_constant :CLASS_IMPORT_SEMAPHORE
6
7
 
7
8
  class << self
9
+ # Override the +const_missing+ hook in {S9API} so that we can delay
10
+ # loading the Saxon JARs until the user has had a chance to set an
11
+ # alternate location for them, if they don't want to use the bundled Saxon
12
+ # HE
8
13
  def const_missing(name)
9
14
  Saxon::Loader.load!
10
15
  begin