saxon-rb 0.7.0-java → 0.8.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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