saxon-rb 0.7.0-java → 0.8.1-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.
@@ -0,0 +1,2 @@
1
+ --format progress
2
+ --color
@@ -1 +1 @@
1
- jruby-9.2.9.0
1
+ jruby-9.2.13.0
data/Rakefile CHANGED
@@ -25,7 +25,7 @@ task :circleci do
25
25
  end
26
26
  end
27
27
  def jruby_image_tags
28
- %w{9.2.9.0 9.1.17.0 9.2.10.0-SNAPSHOT-latest}
28
+ %w{9.2.9.0 9.1.17.0 9.2.10.0 9.2.11.1 9.2.12.0 9.2.13.0-SNAPSHOT-latest}
29
29
  end
30
30
 
31
31
  def jdk_image_tags
@@ -51,7 +51,7 @@ task :circleci do
51
51
 
52
52
  def codeclimate_jobs
53
53
  (alt_saxon_urls.keys << nil).map { |alt_saxon_url|
54
- ["9.2.9.0", "8-jdk-slim", alt_saxon_url]
54
+ ["9.2.12.0", "8-jdk-slim", alt_saxon_url]
55
55
  }
56
56
  end
57
57
 
@@ -190,12 +190,18 @@ task :circleci do
190
190
  def run_tests_step(opts)
191
191
  command = [
192
192
  "mkdir -p /tmp/test-results",
193
- "bundle exec rspec spec --profile 10 --format RspecJunitFormatter --out /tmp/test-results/rspec.xml --format progress"
193
+ "VERIFY_SAXON_LAZY_LOADING=1 bundle exec rspec spec/jar_loading_spec.rb --options .rspec-jar-loading --profile 10 --format RspecJunitFormatter --out /tmp/test-results/rspec-jar-loading.xml"
194
194
  ]
195
195
  if opts.fetch(:run_codeclimate)
196
196
  command.prepend("./cc-test-reporter before-build")
197
- command.append("if [ $? -eq 0 ]; then ./cc-test-reporter format-coverage -t simplecov -o \"cc-coverage#{"-alt-saxon" if opts.fetch(:alt_saxon_url)}.json\"; fi")
197
+ command.append("if [ $? -eq 0 ]; then ./cc-test-reporter format-coverage -t simplecov -o \"cc-coverage-jar-loading#{"-alt-saxon" if opts.fetch(:alt_saxon_url)}.json\"; fi")
198
198
  end
199
+ command.append("rm -rf coverage")
200
+ command.append("bundle exec rspec spec --profile 10 --format RspecJunitFormatter --out /tmp/test-results/rspec.xml --format progress")
201
+ if opts.fetch(:run_codeclimate)
202
+ command.append("if [ $? -eq 0 ]; then ./cc-test-reporter format-coverage -t simplecov -o \"cc-coverage-main#{"-alt-saxon" if opts.fetch(:alt_saxon_url)}.json\"; fi")
203
+ end
204
+
199
205
  {
200
206
  "run" => {
201
207
  "name" => "Run the tests" + (opts.fetch(:run_codeclimate) ? ", and upload coverage data to Code Climate" : ""),
@@ -1 +1 @@
1
- require_relative 'saxon'
1
+ require 'saxon'
@@ -6,8 +6,49 @@ require_relative 'item_type/value_to_ruby'
6
6
  module Saxon
7
7
  # Represent XDM types abstractly
8
8
  class ItemType
9
- # Error raised when a Ruby class has no equivalent XDM type to be converted
10
- # into
9
+ # lazy-loading Hash so we can avoid eager-loading saxon Jars, which prevents
10
+ # using external Saxon Jars unless the user is more careful than they should
11
+ # have to be.
12
+ class LazyReadOnlyHash
13
+ include Enumerable
14
+
15
+ attr_reader :loaded_hash, :load_mutex, :init_block
16
+ private :loaded_hash, :load_mutex, :init_block
17
+
18
+ def initialize(&init_block)
19
+ @init_block = init_block
20
+ @load_mutex = Mutex.new
21
+ @loaded_hash = nil
22
+ end
23
+
24
+ def [](key)
25
+ ensure_loaded!
26
+ loaded_hash[key]
27
+ end
28
+
29
+ def fetch(*args, &block)
30
+ ensure_loaded!
31
+ loaded_hash.fetch(*args, &block)
32
+ end
33
+
34
+ def each(&block)
35
+ ensure_loaded!
36
+ loaded_hash.each(&block)
37
+ end
38
+
39
+ private
40
+
41
+ def ensure_loaded!
42
+ return true unless loaded_hash.nil?
43
+ load_mutex.synchronize do
44
+ return true unless loaded_hash.nil?
45
+ @loaded_hash = init_block.call
46
+ end
47
+ end
48
+ end
49
+
50
+ # Error raised when a Ruby class has no equivalent XDM type to
51
+ # be converted into
11
52
  class UnmappedRubyTypeError < StandardError
12
53
  def initialize(class_name)
13
54
  @class_name = class_name
@@ -54,85 +95,89 @@ module Saxon
54
95
  }.freeze
55
96
 
56
97
  # A mapping of QNames to XDM type constants
57
- QNAME_MAPPING = Hash[{
58
- 'anyAtomicType' => :ANY_ATOMIC_VALUE,
59
- 'anyURI' => :ANY_URI,
60
- 'base64Binary' => :BASE64_BINARY,
61
- 'boolean' => :BOOLEAN,
62
- 'byte' => :BYTE,
63
- 'date' => :DATE,
64
- 'dateTime' => :DATE_TIME,
65
- 'dateTimeStamp' => :DATE_TIME_STAMP,
66
- 'dayTimeDuration' => :DAY_TIME_DURATION,
67
- 'decimal' => :DECIMAL,
68
- 'double' => :DOUBLE,
69
- 'duration' => :DURATION,
70
- 'ENTITY' => :ENTITY,
71
- 'float' => :FLOAT,
72
- 'gDay' => :G_DAY,
73
- 'gMonth' => :G_MONTH,
74
- 'gMonthDay' => :G_MONTH_DAY,
75
- 'gYear' => :G_YEAR,
76
- 'gYearMonth' => :G_YEAR_MONTH,
77
- 'hexBinary' => :HEX_BINARY,
78
- 'ID' => :ID,
79
- 'IDREF' => :IDREF,
80
- 'int' => :INT,
81
- 'integer' => :INTEGER,
82
- 'language' => :LANGUAGE,
83
- 'long' => :LONG,
84
- 'Name' => :NAME,
85
- 'NCName' => :NCNAME,
86
- 'negativeInteger' => :NEGATIVE_INTEGER,
87
- 'NMTOKEN' => :NMTOKEN,
88
- 'nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
89
- 'nonPositiveInteger' => :NON_POSITIVE_INTEGER,
90
- 'normalizedString' => :NORMALIZED_STRING,
91
- 'NOTATION' => :NOTATION,
92
- 'numeric' => :NUMERIC,
93
- 'positiveInteger' => :POSITIVE_INTEGER,
94
- 'QName' => :QNAME,
95
- 'short' => :SHORT,
96
- 'string' => :STRING,
97
- 'time' => :TIME,
98
- 'token' => :TOKEN,
99
- 'unsignedByte' => :UNSIGNED_BYTE,
100
- 'unsignedInt' => :UNSIGNED_INT,
101
- 'unsignedLong' => :UNSIGNED_LONG,
102
- 'unsignedShort' => :UNSIGNED_SHORT,
103
- 'untypedAtomic' => :UNTYPED_ATOMIC,
104
- 'yearMonthDuration' => :YEAR_MONTH_DURATION
105
- }.map { |local_name, constant|
106
- qname = Saxon::QName.create({
107
- prefix: 'xs', uri: 'http://www.w3.org/2001/XMLSchema',
108
- local_name: local_name
109
- })
110
- [qname, constant]
111
- }].freeze
98
+ QNAME_MAPPING = LazyReadOnlyHash.new do
99
+ {
100
+ 'anyAtomicType' => :ANY_ATOMIC_VALUE,
101
+ 'anyURI' => :ANY_URI,
102
+ 'base64Binary' => :BASE64_BINARY,
103
+ 'boolean' => :BOOLEAN,
104
+ 'byte' => :BYTE,
105
+ 'date' => :DATE,
106
+ 'dateTime' => :DATE_TIME,
107
+ 'dateTimeStamp' => :DATE_TIME_STAMP,
108
+ 'dayTimeDuration' => :DAY_TIME_DURATION,
109
+ 'decimal' => :DECIMAL,
110
+ 'double' => :DOUBLE,
111
+ 'duration' => :DURATION,
112
+ 'ENTITY' => :ENTITY,
113
+ 'float' => :FLOAT,
114
+ 'gDay' => :G_DAY,
115
+ 'gMonth' => :G_MONTH,
116
+ 'gMonthDay' => :G_MONTH_DAY,
117
+ 'gYear' => :G_YEAR,
118
+ 'gYearMonth' => :G_YEAR_MONTH,
119
+ 'hexBinary' => :HEX_BINARY,
120
+ 'ID' => :ID,
121
+ 'IDREF' => :IDREF,
122
+ 'int' => :INT,
123
+ 'integer' => :INTEGER,
124
+ 'language' => :LANGUAGE,
125
+ 'long' => :LONG,
126
+ 'Name' => :NAME,
127
+ 'NCName' => :NCNAME,
128
+ 'negativeInteger' => :NEGATIVE_INTEGER,
129
+ 'NMTOKEN' => :NMTOKEN,
130
+ 'nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
131
+ 'nonPositiveInteger' => :NON_POSITIVE_INTEGER,
132
+ 'normalizedString' => :NORMALIZED_STRING,
133
+ 'NOTATION' => :NOTATION,
134
+ 'numeric' => :NUMERIC,
135
+ 'positiveInteger' => :POSITIVE_INTEGER,
136
+ 'QName' => :QNAME,
137
+ 'short' => :SHORT,
138
+ 'string' => :STRING,
139
+ 'time' => :TIME,
140
+ 'token' => :TOKEN,
141
+ 'unsignedByte' => :UNSIGNED_BYTE,
142
+ 'unsignedInt' => :UNSIGNED_INT,
143
+ 'unsignedLong' => :UNSIGNED_LONG,
144
+ 'unsignedShort' => :UNSIGNED_SHORT,
145
+ 'untypedAtomic' => :UNTYPED_ATOMIC,
146
+ 'yearMonthDuration' => :YEAR_MONTH_DURATION
147
+ }.map { |local_name, constant|
148
+ qname = Saxon::QName.create({
149
+ prefix: 'xs', uri: 'http://www.w3.org/2001/XMLSchema',
150
+ local_name: local_name
151
+ })
152
+ [qname, constant]
153
+ }.to_h.freeze
154
+ end
112
155
 
113
156
  # A mapping of type names/QNames to XDM type constants
114
- STR_MAPPING = {
115
- 'array(*)' => :ANY_ARRAY,
116
- 'item()' => :ANY_ITEM,
117
- 'map(*)' => :ANY_MAP,
118
- 'node()' => :ANY_NODE
119
- }.merge(
120
- Hash[QNAME_MAPPING.map { |qname, v| [qname.to_s, v] }]
121
- ).freeze
157
+ STR_MAPPING = LazyReadOnlyHash.new do
158
+ {
159
+ 'array(*)' => :ANY_ARRAY,
160
+ 'item()' => :ANY_ITEM,
161
+ 'map(*)' => :ANY_MAP,
162
+ 'node()' => :ANY_NODE
163
+ }.merge(
164
+ Hash[QNAME_MAPPING.map { |qname, v| [qname.to_s, v] }]
165
+ ).freeze
166
+ end
122
167
 
123
168
  # convertors to generate lexical strings for a given {ItemType}, as a hash keyed on the ItemType
124
- ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS = Hash[
169
+ ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS = LazyReadOnlyHash.new do
125
170
  LexicalStringConversion::Convertors.constants.map { |const|
126
171
  [S9API::ItemType.const_get(const), LexicalStringConversion::Convertors.const_get(const)]
127
- }
128
- ].freeze
172
+ }.to_h.freeze
173
+ end
129
174
 
130
175
  # convertors from {XDM::AtomicValue} to a ruby primitve value, as a hash keyed on the ItemType
131
- ATOMIC_VALUE_TO_RUBY_CONVERTORS = Hash[
176
+ ATOMIC_VALUE_TO_RUBY_CONVERTORS = LazyReadOnlyHash.new do
132
177
  ValueToRuby::Convertors.constants.map { |const|
133
178
  [S9API::ItemType.const_get(const), ValueToRuby::Convertors.const_get(const)]
134
- }
135
- ].freeze
179
+ }.to_h.freeze
180
+ end
136
181
 
137
182
  class << self
138
183
  # Get an appropriate {ItemType} for a Ruby type or given a type name as a
@@ -0,0 +1,5 @@
1
+ unless Java::net::sf::saxon::s9api::Xslt30Transformer.instance_methods.include?(:setInitialMode)
2
+ class Java::net::sf::saxon::s9api::Xslt30Transformer
3
+ java_alias :setInitialMode, :setInitialMode, [Java::net::sf::saxon::s9api::QName]
4
+ end
5
+ end
@@ -38,60 +38,71 @@ module Saxon
38
38
  end
39
39
  end
40
40
 
41
- # @param saxon_home [String, Pathname] the path to the dir containing
42
- # Saxon's .jars. Defaults to the vendored Saxon HE
43
- # @return [true, false] Returns true if Saxon had not been loaded and
44
- # is now, and false if Saxon was already loaded
45
- def self.load!(saxon_home = nil)
46
- return false if instance_variable_defined?(:@saxon_loaded) && @saxon_loaded
47
- LOAD_SEMAPHORE.synchronize do
48
- if Saxon::S9API.const_defined?(:Processor)
41
+ class << self
42
+ # Are the Saxon jars missing from the Classpath?
43
+ # @return [Boolean] true if the Jars are not on the Classpath
44
+ def jars_not_on_classpath?
45
+ begin
46
+ Java::net.sf.saxon.s9api.Processor
49
47
  false
50
- else
51
- if jars_not_on_classpath?
52
- if saxon_home.nil?
53
- require 'saxon-rb_jars'
54
- else
55
- saxon_home = Pathname.new(saxon_home)
56
- raise NoJarsError, saxon_home unless saxon_home.directory?
57
- jars = [main_jar(saxon_home)].compact
58
- raise MissingJarError if jars.empty?
59
- jars += extra_jars(saxon_home)
48
+ rescue
49
+ true
50
+ end
51
+ end
52
+
53
+ # Are the Saxon JARs on the Classpath?
54
+ # @return [Boolean] whether the Jars are on the Classpath already
55
+ def jars_on_classpath?
56
+ !jars_not_on_classpath?
57
+ end
58
+
59
+ # @param saxon_home [String, Pathname] the path to the dir containing
60
+ # Saxon's .jars. Defaults to the vendored Saxon HE
61
+ # @return [true, false] Returns true if Saxon had not been loaded and
62
+ # is now, and false if Saxon was already loaded
63
+ def load!(saxon_home = nil)
64
+ return false if instance_variable_defined?(:@saxon_loaded) && @saxon_loaded
65
+ LOAD_SEMAPHORE.synchronize do
66
+ if Saxon::S9API.const_defined?(:Processor)
67
+ false
68
+ else
69
+ if jars_not_on_classpath?
70
+ if saxon_home.nil?
71
+ require 'saxon-rb_jars'
72
+ else
73
+ saxon_home = Pathname.new(saxon_home)
74
+ raise NoJarsError, saxon_home unless saxon_home.directory?
75
+ jars = [main_jar(saxon_home)].compact
76
+ raise MissingJarError if jars.empty?
77
+ jars += extra_jars(saxon_home)
60
78
 
61
- add_jars_to_classpath!(saxon_home, jars)
79
+ add_jars_to_classpath!(saxon_home, jars)
80
+ end
62
81
  end
63
- end
64
82
 
65
- @saxon_loaded = true
66
- true
83
+ require_relative 'jruby_bug_6197_workaround'
84
+ @saxon_loaded = true
85
+ true
86
+ end
67
87
  end
68
88
  end
69
- end
70
89
 
71
- private
72
-
73
- def self.main_jar(path)
74
- ['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
75
- end
90
+ private
76
91
 
77
- def self.extra_jars(path)
78
- optional = ['saxon9-unpack.jar', 'saxon9-sql.jar'].map { |jar| path.join(jar) }.select { |jar| jar.file? }
79
- icu = path.children.find { |jar| jar.extname == '.jar' && !jar.basename.to_s.match(/^saxon-icu|^icu4j/).nil? }
80
- ([icu] + optional).compact
81
- end
92
+ def main_jar(path)
93
+ ['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
94
+ end
82
95
 
83
- def self.jars_not_on_classpath?
84
- begin
85
- Java::net.sf.saxon.s9api.Processor
86
- false
87
- rescue
88
- true
96
+ def extra_jars(path)
97
+ optional = ['saxon9-unpack.jar', 'saxon9-sql.jar'].map { |jar| path.join(jar) }.select { |jar| jar.file? }
98
+ icu = path.children.find { |jar| jar.extname == '.jar' && !jar.basename.to_s.match(/^saxon-icu|^icu4j/).nil? }
99
+ ([icu] + optional).compact
89
100
  end
90
- end
91
101
 
92
- def self.add_jars_to_classpath!(saxon_home, jars)
93
- jars.each do |jar|
94
- $CLASSPATH << jar.to_s
102
+ def add_jars_to_classpath!(saxon_home, jars)
103
+ jars.each do |jar|
104
+ $CLASSPATH << jar.to_s
105
+ end
95
106
  end
96
107
  end
97
108
  end
@@ -11,28 +11,28 @@ module Saxon
11
11
  # alternate location for them, if they don't want to use the bundled Saxon
12
12
  # HE
13
13
  def const_missing(name)
14
- Saxon::Loader.load!
15
- begin
16
- const_set(name, imported_classes.const_get(name))
17
- rescue NameError
18
- msg = "uninitialized constant Saxon::S9API::#{name}"
19
- e = NameError.new(msg, name)
20
- raise e
21
- end
14
+ CLASS_IMPORT_SEMAPHORE.synchronize {
15
+ return const_get(name) if const_defined?(name)
16
+ Saxon::Loader.load!
17
+ begin
18
+ const_set(name, imported_classes.const_get(name))
19
+ rescue NameError
20
+ msg = "uninitialized constant Saxon::S9API::#{name}"
21
+ e = NameError.new(msg, name)
22
+ raise e
23
+ end
24
+ }
22
25
  end
23
26
 
24
27
  private
25
28
 
26
29
  def imported_classes
27
- return @imported_classes if instance_variable_defined?(:@imported_classes)
28
- CLASS_IMPORT_SEMAPHORE.synchronize do
29
- @imported_classes = Module.new {
30
- include_package 'net.sf.saxon.s9api'
31
- java_import Java::net.sf.saxon.Configuration
32
- java_import Java::net.sf.saxon.lib.FeatureKeys
33
- java_import Java::net.sf.saxon.lib.ParseOptions
34
- }
35
- end
30
+ @imported_classes ||= Module.new {
31
+ include_package 'net.sf.saxon.s9api'
32
+ java_import Java::net.sf.saxon.Configuration
33
+ java_import Java::net.sf.saxon.lib.FeatureKeys
34
+ java_import Java::net.sf.saxon.lib.ParseOptions
35
+ }
36
36
  end
37
37
  end
38
38
  end
@@ -1,9 +1,9 @@
1
- require 'saxon/s9api'
1
+ require 'saxon/version/library'
2
2
 
3
3
  module Saxon
4
4
  # Provides the saxon-rb and underlying Saxon library versions
5
5
  module Version
6
6
  # The version of the saxon-rb gem (not of Saxon itself)
7
- WRAPPER = "0.7.0"
7
+ WRAPPER = "0.8.1"
8
8
  end
9
9
  end
@@ -36,12 +36,17 @@ module Saxon
36
36
  end
37
37
  end
38
38
 
39
- # ItemType representing QNames
40
- XS_QNAME = ItemType.get_type('xs:QName')
41
- # ItemType representing NOTATION
42
- XS_NOTATION = ItemType.get_type('xs:NOTATION')
43
-
44
39
  class << self
40
+ # ItemType representing QNames
41
+ def xs_qname
42
+ @xs_qname ||= ItemType.get_type('xs:QName')
43
+ end
44
+
45
+ # ItemType representing NOTATION
46
+ def xs_notation
47
+ @xs_notation ||= ItemType.get_type('xs:NOTATION')
48
+ end
49
+
45
50
  # Convert a single Ruby value into an XDM::AtomicValue
46
51
  #
47
52
  # If no explicit {ItemType} is passed, the correct type is guessed based
@@ -71,8 +76,8 @@ module Saxon
71
76
 
72
77
  item_type = ItemType.get_type(item_type)
73
78
 
74
- return new(Saxon::S9API::XdmAtomicValue.new(value.to_java)) if item_type == XS_QNAME && value_is_qname?(value)
75
- raise NotationCannotBeDirectlyCreated if item_type == XS_NOTATION
79
+ return new(Saxon::S9API::XdmAtomicValue.new(value.to_java)) if item_type == xs_qname && value_is_qname?(value)
80
+ raise NotationCannotBeDirectlyCreated if item_type == xs_notation
76
81
 
77
82
  value_lexical_string = item_type.lexical_string(value)
78
83
  new(new_s9_xdm_atomic_value(value_lexical_string, item_type))
@@ -91,7 +96,7 @@ module Saxon
91
96
  # @return [Saxon::XDM::AtomicValue]
92
97
  def from_lexical_string(value, item_type)
93
98
  item_type = ItemType.get_type(item_type)
94
- raise CannotCreateQNameFromString if item_type == XS_QNAME
99
+ raise CannotCreateQNameFromString if item_type == xs_qname
95
100
  new(new_s9_xdm_atomic_value(value.to_s, item_type))
96
101
  end
97
102