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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +623 -44
- data/.rspec-jar-loading +2 -0
- data/.ruby-version +1 -1
- data/Rakefile +10 -4
- data/lib/saxon-rb.rb +1 -1
- data/lib/saxon/item_type.rb +116 -71
- data/lib/saxon/jruby_bug_6197_workaround.rb +5 -0
- data/lib/saxon/loader.rb +54 -43
- data/lib/saxon/s9api.rb +17 -17
- data/lib/saxon/version.rb +2 -2
- data/lib/saxon/xdm/atomic_value.rb +13 -8
- metadata +15 -13
data/.rspec-jar-loading
ADDED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
jruby-9.2.
|
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.
|
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
|
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" : ""),
|
data/lib/saxon-rb.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require 'saxon'
|
data/lib/saxon/item_type.rb
CHANGED
@@ -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
|
-
#
|
10
|
-
#
|
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 =
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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
|
data/lib/saxon/loader.rb
CHANGED
@@ -38,60 +38,71 @@ module Saxon
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
79
|
+
add_jars_to_classpath!(saxon_home, jars)
|
80
|
+
end
|
62
81
|
end
|
63
|
-
end
|
64
82
|
|
65
|
-
|
66
|
-
|
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
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
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
|
data/lib/saxon/s9api.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
data/lib/saxon/version.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require 'saxon/
|
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
|
+
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 ==
|
75
|
-
raise NotationCannotBeDirectlyCreated if item_type ==
|
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 ==
|
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
|
|